CoolFace
Apppublic

sdv2500/progettojava

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
manifest_rewriter.py289 linesDownload Raw Back to services
1import re2import urllib.parse3from urllib.parse import urljoin4import xml.etree.ElementTree as ET5import logging6 7logger = logging.getLogger(__name__)8 9# Conditional import for DLHD detection10try:11    from extractors.dlhd import DLHDExtractor12except ImportError:13    DLHDExtractor = None14 15class ManifestRewriter:16    @staticmethod17    def rewrite_mpd_manifest(manifest_content: str, base_url: str, proxy_base: str, stream_headers: dict, clearkey_param: str = None, api_password: str = None) -> str:18        """Riscrive i manifest MPD (DASH) per passare attraverso il zenith."""19        try:20            # Aggiungiamo il namespace di default se non presente, per ET21            if 'xmlns' not in manifest_content:22                manifest_content = manifest_content.replace('<MPD', '<MPD xmlns="urn:mpeg:dash:schema:mpd:2011"', 1)23 24            root = ET.fromstring(manifest_content)25            ns = {'mpd': 'urn:mpeg:dash:schema:mpd:2011', 'cenc': 'urn:mpeg:cenc:2013', 'dashif': 'http://dashif.org/guidelines/clearKey'}26            27            # Registra i namespace per evitare prefissi ns028            ET.register_namespace('', ns['mpd'])29            ET.register_namespace('cenc', ns['cenc'])30            ET.register_namespace('dashif', ns['dashif'])31 32            # Includiamo tutti gli header rilevanti passati dall'estrattore33            header_params = "".join([f"&h_{urllib.parse.quote(key)}={urllib.parse.quote(value)}" for key, value in stream_headers.items()])34            35            if api_password:36                header_params += f"&api_password={api_password}"37 38            def create_proxy_url(relative_url):39                absolute_url = urljoin(base_url, relative_url)40                encoded_url = urllib.parse.quote(absolute_url, safe='')41                return f"{proxy_base}/zenith/mpd/manifest.m3u8?d={encoded_url}{header_params}"42 43            # --- GESTIONE CLEARKEY STATICA ---44            if clearkey_param:45                try:46                    kid_hex, key_hex = clearkey_param.split(':')47                    48                    # Crea l'elemento ContentProtection per ClearKey49                    cp_element = ET.Element('ContentProtection')50                    cp_element.set('schemeIdUri', 'urn:uuid:e2719d58-a985-b3c9-781a-007147f192ec')51                    cp_element.set('value', 'ClearKey')52                    53                    # Puntiamo al nostro endpoint /license54                    license_url = f"{proxy_base}/license?clearkey={clearkey_param}"55                    if api_password:56                        license_url += f"&api_password={api_password}"57                    58                    # 1. Laurl standard (namespace MPD)59                    laurl_element = ET.SubElement(cp_element, '{urn:mpeg:dash:schema:mpd:2011}Laurl')60                    laurl_element.text = license_url61                    62                    # 2. dashif:Laurl (namespace DashIF)63                    laurl_dashif = ET.SubElement(cp_element, '{http://dashif.org/guidelines/clearKey}Laurl')64                    laurl_dashif.text = license_url65                    66                    # 3. Aggiungi cenc:default_KID67                    if len(kid_hex) == 32:68                        kid_guid = f"{kid_hex[:8]}-{kid_hex[8:12]}-{kid_hex[12:16]}-{kid_hex[16:20]}-{kid_hex[20:]}"69                        cp_element.set('{urn:mpeg:cenc:2013}default_KID', kid_guid)70 71                    # Inietta ContentProtection72                    adaptation_sets = root.findall('.//mpd:AdaptationSet', ns)73                    logger.info(f"πŸ”Ž Trovati {len(adaptation_sets)} AdaptationSet nel manifest.")74                    75                    for adaptation_set in adaptation_sets:76                        # RIMUOVI altri ContentProtection (es. Widevine)77                        for cp in adaptation_set.findall('mpd:ContentProtection', ns):78                            scheme = cp.get('schemeIdUri', '').lower()79                            if 'e2719d58-a985-b3c9-781a-007147f192ec' not in scheme:80                                adaptation_set.remove(cp)81                                logger.info(f"πŸ—‘οΈ Rimosso ContentProtection conflittuale: {scheme}")82 83                        # Verifica se esiste giΓ  ClearKey84                        existing_cp = False85                        for cp in adaptation_set.findall('mpd:ContentProtection', ns):86                            if cp.get('schemeIdUri') == 'urn:uuid:e2719d58-a985-b3c9-781a-007147f192ec':87                                existing_cp = True88                                break89                        90                        if not existing_cp:91                            adaptation_set.insert(0, cp_element)92                            logger.info(f"πŸ’‰ Iniettato ContentProtection ClearKey statico in AdaptationSet")93 94                except Exception as e:95                    logger.error(f"❌ Errore nel parsing del parametro clearkey: {e}")96 97            # --- GESTIONE PROXY LICENZE ESISTENTI ---98            for cp in root.findall('.//mpd:ContentProtection', ns):99                for child in cp:100                    if 'Laurl' in child.tag and child.text:101                        original_license_url = child.text102                        encoded_license_url = urllib.parse.quote(original_license_url, safe='')103                        proxy_license_url = f"{proxy_base}/license?url={encoded_license_url}{header_params}"104                        child.text = proxy_license_url105                        logger.info(f"πŸ”„ Redirected License URL: {original_license_url} -> {proxy_license_url}")106 107            # Riscrive gli attributi URL108            for template_tag in root.findall('.//mpd:SegmentTemplate', ns):109                for attr in ['media', 'initialization']:110                    if template_tag.get(attr):111                        template_tag.set(attr, create_proxy_url(template_tag.get(attr)))112            113            for seg_url_tag in root.findall('.//mpd:SegmentURL', ns):114                if seg_url_tag.get('media'):115                    seg_url_tag.set('media', create_proxy_url(seg_url_tag.get('media')))116 117            for base_url_tag in root.findall('.//mpd:BaseURL', ns):118                if base_url_tag.text:119                    base_url_tag.text = create_proxy_url(base_url_tag.text)120 121            return ET.tostring(root, encoding='unicode', method='xml')122 123        except Exception as e:124            logger.error(f"❌ Errore durante la riscrittura del manifest MPD: {e}")125            return manifest_content126 127    @staticmethod128    async def rewrite_manifest_urls(manifest_content: str, base_url: str, proxy_base: str, stream_headers: dict, original_channel_url: str = '', api_password: str = None, get_extractor_func=None) -> str:129        """βœ… AGGIORNATA: Riscrive gli URL nei manifest HLS per passare attraverso il zenith (incluse chiavi AES)"""130        lines = manifest_content.split('\n')131        rewritten_lines = []132 133        # Determina se Γ¨ VixSrc o DLHD134        is_vixsrc_stream = False135        is_dlhd_stream = False136        logger.info(f"Manifest rewriter called with base_url: {base_url}")137        138        try:139            if get_extractor_func:140                original_request_url = stream_headers.get('referer') or stream_headers.get('Referer') or base_url141                extractor = await get_extractor_func(original_request_url, {})142                143                if hasattr(extractor, 'is_vixsrc') and extractor.is_vixsrc:144                    is_vixsrc_stream = True145                    logger.info("Rilevato stream VixSrc.")146                elif DLHDExtractor and isinstance(extractor, DLHDExtractor):147                    is_dlhd_stream = True148                    logger.info(f"βœ… Rilevato stream DLHD. SarΓ  proxato completamente.")149        except Exception as e:150            logger.error(f"Error in extractor detection: {e}")151            pass152 153        # Logica speciale SOLO per VixSrc (filtro qualitΓ )154        if is_vixsrc_stream:155            streams = []156            for i, line in enumerate(lines):157                if line.startswith('#EXT-X-STREAM-INF:'):158                    bandwidth_match = re.search(r'BANDWIDTH=(\d+)', line)159                    if bandwidth_match:160                        bandwidth = int(bandwidth_match.group(1))161                        streams.append({'bandwidth': bandwidth, 'inf': line, 'url': lines[i+1]})162            163            if streams:164                highest_quality_stream = max(streams, key=lambda x: x['bandwidth'])165                logger.info(f"VixSrc: Selezionata bandwidth {highest_quality_stream['bandwidth']}.")166                167                rewritten_lines.append('#EXTM3U')168                for line in lines:169                    if line.startswith('#EXT-X-MEDIA:') or line.startswith('#EXT-X-STREAM-INF:') or (line and not line.startswith('#')):170                        continue 171                172                rewritten_lines.extend([line for line in lines if line.startswith('#EXT-X-MEDIA:')])173                rewritten_lines.append(highest_quality_stream['inf'])174                rewritten_lines.append(highest_quality_stream['url'])175                return '\n'.join(rewritten_lines)176 177        # --- Logica Standard (incluso DLHD) ---178        header_params = "".join([f"&h_{urllib.parse.quote(key)}={urllib.parse.quote(value)}" for key, value in stream_headers.items()])179        180        if api_password:181            header_params += f"&api_password={api_password}"182 183        # Estrai query params dal base_url per ereditarli se necessario184        base_parsed = urllib.parse.urlparse(base_url)185        base_query = base_parsed.query186 187        for line in lines:188            line = line.strip()189            190            # 1. GESTIONE CHIAVI AES-128191            if line.startswith('#EXT-X-KEY:') and 'URI=' in line:192                uri_start = line.find('URI="') + 5193                uri_end = line.find('"', uri_start)194                195                if uri_start > 4 and uri_end > uri_start:196                    original_key_url = line[uri_start:uri_end]197                    absolute_key_url = urljoin(base_url, original_key_url)198                    199                    encoded_key_url = urllib.parse.quote(absolute_key_url, safe='')200                    encoded_original_channel_url = urllib.parse.quote(original_channel_url, safe='')201                    202                    # Zenith KEY URL203                    proxy_key_url = f"{proxy_base}/key?key_url={encoded_key_url}&original_channel_url={encoded_original_channel_url}"204                    205                    # Aggiungi header206                    key_header_params = "".join(207                        [f"&h_{urllib.parse.quote(key)}={urllib.parse.quote(value)}" 208                         for key, value in stream_headers.items()]209                    )210                    proxy_key_url += key_header_params211                    212                    if api_password:213                        proxy_key_url += f"&api_password={api_password}"214                    215                    new_line = line[:uri_start] + proxy_key_url + line[uri_end:]216                    rewritten_lines.append(new_line)217                    logger.info(f"πŸ”„ Redirected AES key: {absolute_key_url} -> {proxy_key_url}")218                else:219                    rewritten_lines.append(line)220            221            # 2. GESTIONE MEDIA (Sottotitoli, Audio secondario)222            elif line.startswith('#EXT-X-MEDIA:') and 'URI=' in line:223                uri_start = line.find('URI="') + 5224                uri_end = line.find('"', uri_start)225                226                if uri_start > 4 and uri_end > uri_start:227                    original_media_url = line[uri_start:uri_end]228                    absolute_media_url = urljoin(base_url, original_media_url)229                    encoded_media_url = urllib.parse.quote(absolute_media_url, safe='')230                    231                    # Usa endpoint manifest232                    proxy_media_url = f"{proxy_base}/zenith/hls/manifest.m3u8?d={encoded_media_url}{header_params}"233                    new_line = line[:uri_start] + proxy_media_url + line[uri_end:]234                    rewritten_lines.append(new_line)235                    logger.info(f"πŸ”„ Redirected Media URL: {absolute_media_url} -> {proxy_media_url}")236                else:237                    rewritten_lines.append(line)238 239            # 3. GESTIONE MAP (Init Segment per fMP4)240            elif line.startswith('#EXT-X-MAP:') and 'URI=' in line:241                uri_start = line.find('URI="') + 5242                uri_end = line.find('"', uri_start)243                244                if uri_start > 4 and uri_end > uri_start:245                    original_map_url = line[uri_start:uri_end]246                    absolute_map_url = urljoin(base_url, original_map_url)247                    encoded_map_url = urllib.parse.quote(absolute_map_url, safe='')248                    249                    # Usa endpoint segment.mp4250                    proxy_map_url = f"{proxy_base}/zenith/hls/segment.mp4?d={encoded_map_url}{header_params}"251                    252                    new_line = line[:uri_start] + proxy_map_url + line[uri_end:]253                    rewritten_lines.append(new_line)254                    logger.info(f"πŸ”„ Redirected MAP URL: {absolute_map_url} -> {proxy_map_url}")255                else:256                    rewritten_lines.append(line)257 258            # 4. GESTIONE SEGMENTI E SUB-MANIFEST259            elif line and not line.startswith('#'):260                absolute_url = urljoin(base_url, line) if not line.startswith('http') else line261 262                # Eredita query params (es. token)263                if base_query and '?' not in absolute_url:264                    absolute_url += f"?{base_query}"265 266                encoded_url = urllib.parse.quote(absolute_url, safe='')267 268                # Se Γ¨ .m3u8 usa /zenith/manifest.m3u8, altrimenti determina estensione269                if '.m3u8' in absolute_url:270                     proxy_url = f"{proxy_base}/zenith/manifest.m3u8?url={encoded_url}{header_params}"271                else:272                     # βœ… FIX: Determina estensione corretta per il segmento273                     # Se l'URL originale ha estensione mp4/m4s, usa .mp4, altrimenti default a .ts274                     # Questo aiuta i player a distinguere tra TS e fMP4275                     path = urllib.parse.urlparse(absolute_url).path276                     ext = '.ts'277                     if path.endswith('.m4s') or path.endswith('.mp4') or path.endswith('.m4v'):278                         ext = '.mp4'279                     280                     proxy_url = f"{proxy_base}/zenith/hls/segment{ext}?d={encoded_url}{header_params}"281                282                rewritten_lines.append(proxy_url)283 284            else:285                # Tutti gli altri tag (es. #EXTINF, #EXT-X-ENDLIST)286                rewritten_lines.append(line)287        288        return '\n'.join(rewritten_lines)289