CoolFace
Apppublic

fevcak/vpn

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
app.py401 linesDownload Raw Back to root
1from flask import Flask, request, Response2import requests3from urllib.parse import urlparse, urljoin, quote, unquote4import re5import traceback6import os7 8app = Flask(__name__)9 10def detect_m3u_type(content):11    """Rileva se è un M3U (lista IPTV) o un M3U8 (flusso HLS)"""12    if "#EXTM3U" in content and "#EXTINF" in content:13        return "m3u8"14    return "m3u"15 16def replace_key_uri(line, headers_query):17    """Sostituisce l'URI della chiave AES-128 con il proxy"""18    match = re.search(r'URI="([^"]+)"', line)19    if match:20        key_url = match.group(1)21        proxied_key_url = f"/proxy/key?url={quote(key_url)}&{headers_query}"22        return line.replace(key_url, proxied_key_url)23    return line24 25def resolve_m3u8_link(url, headers=None):26    """27    Tenta di risolvere un URL M3U8.28    Prova prima la logica specifica per iframe (tipo Daddylive), inclusa la lookup della server_key.29    Se fallisce, verifica se l'URL iniziale era un M3U8 diretto e lo restituisce.30    """31    if not url:32        print("Errore: URL non fornito.")33        return {"resolved_url": None, "headers": {}}34 35    print(f"Tentativo di risoluzione URL: {url}")36    # Utilizza gli header forniti, altrimenti usa un User-Agent di default37    current_headers = headers if headers else {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0+Safari/537.36'}38 39    initial_response_text = None40    final_url_after_redirects = None41 42    # Verifica se è un URL di vavoo.to43    is_vavoo = "vavoo.to" in url.lower()44 45    try:46        with requests.Session() as session:47            print(f"Passo 1: Richiesta a {url}")48            response = session.get(url, headers=current_headers, allow_redirects=True, timeout=(5, 15))49            response.raise_for_status()50            initial_response_text = response.text51            final_url_after_redirects = response.url52            print(f"Passo 1 completato. URL finale dopo redirect: {final_url_after_redirects}")53 54            # Se è un URL di vavoo.to, salta la logica dell'iframe55            if is_vavoo:56                if initial_response_text and initial_response_text.strip().startswith('#EXTM3U'):57                    return {58                        "resolved_url": final_url_after_redirects,59                        "headers": current_headers60                    }61                else:62                    # Se non è un M3U8 diretto, restituisci l'URL originale per vavoo63                    print(f"URL vavoo.to non è un M3U8 diretto: {url}")64                    return {65                        "resolved_url": url,66                        "headers": current_headers67                    }68 69            # Prova la logica dell'iframe per gli altri URL70            print("Tentativo con logica iframe...")71            try:72                # Secondo passo (Iframe): Trova l'iframe src nella risposta iniziale73                iframes = re.findall(r'iframe src="([^"]+)"', initial_response_text)74                if not iframes:75                    raise ValueError("Nessun iframe src trovato.")76 77                url2 = iframes[0]78                print(f"Passo 2 (Iframe): Trovato iframe URL: {url2}")79 80                # Terzo passo (Iframe): Richiesta all'URL dell'iframe81                referer_raw = urlparse(url2).scheme + "://" + urlparse(url2).netloc + "/"82                origin_raw = urlparse(url2).scheme + "://" + urlparse(url2).netloc83                current_headers['Referer'] = referer_raw84                current_headers['Origin'] = origin_raw85                print(f"Passo 3 (Iframe): Richiesta a {url2}")86                response = session.get(url2, headers=current_headers, timeout=(5, 15))87                response.raise_for_status()88                iframe_response_text = response.text89                print("Passo 3 (Iframe) completato.")90 91                # Quarto passo (Iframe): Estrai parametri dinamici dall'iframe response92                channel_key_match = re.search(r'(?s) channelKey = \"([^\"]*)"', iframe_response_text)93                auth_ts_match = re.search(r'(?s) authTs\s*= \"([^\"]*)"', iframe_response_text)94                auth_rnd_match = re.search(r'(?s) authRnd\s*= \"([^\"]*)"', iframe_response_text)95                auth_sig_match = re.search(r'(?s) authSig\s*= \"([^\"]*)"', iframe_response_text)96                auth_host_match = re.search(r'\}\s*fetchWithRetry\(\s*\'([^\']*)\'', iframe_response_text)97                server_lookup_match = re.search(r'n fetchWithRetry\(\s*\'([^\']*)\'', iframe_response_text)98 99                if not all([channel_key_match, auth_ts_match, auth_rnd_match, auth_sig_match, auth_host_match, server_lookup_match]):100                    raise ValueError("Impossibile estrarre tutti i parametri dinamici dall'iframe response.")101                102                channel_key = channel_key_match.group(1)103                auth_ts = auth_ts_match.group(1)104                auth_rnd = auth_rnd_match.group(1)105                auth_sig = quote(auth_sig_match.group(1))106                auth_host = auth_host_match.group(1)107                server_lookup = server_lookup_match.group(1)108 109                print("Passo 4 (Iframe): Parametri dinamici estratti.")110 111                # Quinto passo (Iframe): Richiesta di autenticazione112                auth_url = f'{auth_host}{channel_key}&ts={auth_ts}&rnd={auth_rnd}&sig={auth_sig}'113                print(f"Passo 5 (Iframe): Richiesta di autenticazione a {auth_url}")114                auth_response = session.get(auth_url, headers=current_headers, timeout=(5, 15))115                auth_response.raise_for_status()116                print("Passo 5 (Iframe) completato.")117 118                # Sesto passo (Iframe): Richiesta di server lookup per ottenere la server_key119                server_lookup_url = f"https://{urlparse(url2).netloc}{server_lookup}{channel_key}"120                print(f"Passo 6 (Iframe): Richiesta server lookup a {server_lookup_url}")121                server_lookup_response = session.get(server_lookup_url, headers=current_headers, timeout=(5, 15))122                server_lookup_response.raise_for_status()123                server_lookup_data = server_lookup_response.json()124                print("Passo 6 (Iframe) completato.")125 126                # Settimo passo (Iframe): Estrai server_key dalla risposta di server lookup127                server_key = server_lookup_data.get('server_key')128                if not server_key:129                    raise ValueError("'server_key' non trovato nella risposta di server lookup.")130                print(f"Passo 7 (Iframe): Estratto server_key: {server_key}")131 132                # Ottavo passo (Iframe): Costruisci il link finale133                host_match = re.search('(?s)m3u8 =.*?:.*?:.*?".*?".*?"([^"]*)"', iframe_response_text)134                if not host_match:135                    raise ValueError("Impossibile trovare l'host finale per l'm3u8.")136                host = host_match.group(1)137                print(f"Passo 8 (Iframe): Trovato host finale per m3u8: {host}")138 139                # Costruisci l'URL finale del flusso140                final_stream_url = (141                    f'https://{server_key}{host}{server_key}/{channel_key}/mono.m3u8'142                )143 144                # Prepara gli header per lo streaming145                stream_headers = {146                    'User-Agent': current_headers.get('User-Agent', ''),147                    'Referer': referer_raw,148                    'Origin': origin_raw149                }150                151                return {152                    "resolved_url": final_stream_url,153                    "headers": stream_headers154                }155 156            except (ValueError, requests.exceptions.RequestException) as e:157                print(f"Logica iframe fallita: {e}")158                print("Tentativo fallback: verifica se l'URL iniziale era un M3U8 diretto...")159 160                # Fallback: Verifica se la risposta iniziale era un file M3U8 diretto161                if initial_response_text and initial_response_text.strip().startswith('#EXTM3U'):162                    print("Fallback riuscito: Trovato file M3U8 diretto.")163                    return {164                        "resolved_url": final_url_after_redirects,165                        "headers": current_headers166                    }167                else:168                    print("Fallback fallito: La risposta iniziale non era un M3U8 diretto.")169                    return {170                        "resolved_url": url,171                        "headers": current_headers172                    }173 174    except requests.exceptions.RequestException as e:175        print(f"Errore durante la richiesta HTTP iniziale: {e}")176        return {"resolved_url": url, "headers": current_headers}177    except Exception as e:178        print(f"Errore generico durante la risoluzione: {e}")179        return {"resolved_url": url, "headers": current_headers}180 181@app.route('/proxy')182def proxy():183    """Proxy per liste M3U che aggiunge automaticamente /proxy/m3u?url= con IP prima dei link"""184    m3u_url = request.args.get('url', '').strip()185    if not m3u_url:186        return "Errore: Parametro 'url' mancante", 400187 188    try:189        # Ottieni l'IP del server190        server_ip = request.host191        192        # Scarica la lista M3U originale193        response = requests.get(m3u_url, timeout=(10, 30)) # Timeout connessione 10s, lettura 30s194        response.raise_for_status()195        m3u_content = response.text196        197        # Modifica solo le righe che contengono URL (non iniziano con #)198        modified_lines = []199        for line in m3u_content.splitlines():200            line = line.strip()201            if line and not line.startswith('#'):202                # Per tutti i link, usa il proxy normale203                modified_line = f"http://{server_ip}/proxy/m3u?url={line}"204                modified_lines.append(modified_line)205            else:206                # Mantieni invariate le righe di metadati207                modified_lines.append(line)208        209        modified_content = '\n'.join(modified_lines)210 211        # Estrai il nome del file dall'URL originale212        parsed_m3u_url = urlparse(m3u_url)213        original_filename = os.path.basename(parsed_m3u_url.path)214        215        return Response(modified_content, content_type="application/vnd.apple.mpegurl", headers={'Content-Disposition': f'attachment; filename="{original_filename}"'})216        217    except requests.RequestException as e:218        return f"Errore durante il download della lista M3U: {str(e)}", 500219    except Exception as e:220        return f"Errore generico: {str(e)}", 500221 222@app.route('/proxy/m3u')223def proxy_m3u():224    """Proxy per file M3U e M3U8 con supporto per redirezioni e header personalizzati"""225    m3u_url = request.args.get('url', '').strip()226    if not m3u_url:227        return "Errore: Parametro 'url' mancante", 400228 229    default_headers = {230        "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/33.0 Mobile/15E148 Safari/605.1.15",231        "Referer": "https://vavoo.to/",232        "Origin": "https://vavoo.to"233    }234 235    # Estrai gli header dalla richiesta, sovrascrivendo i default236    request_headers = {237        unquote(key[2:]).replace("_", "-"): unquote(value).strip()238        for key, value in request.args.items()239        if key.lower().startswith("h_")240    }241    headers = {**default_headers, **request_headers}242 243    # --- Logica per trasformare l'URL se necessario ---244    processed_url = m3u_url245    246    # Trasforma /stream/ in /embed/ per Daddylive247    if '/stream/stream-' in m3u_url and 'daddylive.dad' in m3u_url:248        processed_url = m3u_url.replace('/stream/stream-', '/embed/stream-')249        print(f"URL {m3u_url} trasformato da /stream/ a /embed/: {processed_url}")250    251    match_premium_m3u8 = re.search(r'/premium(\d+)/mono\.m3u8$', m3u_url)252 253    if match_premium_m3u8:254        channel_number = match_premium_m3u8.group(1)255        transformed_url = f"https://daddylive.dad/embed/stream-{channel_number}.php"256        print(f"URL {m3u_url} corrisponde al pattern premium. Trasformato in: {transformed_url}")257        processed_url = transformed_url258    else:259        print(f"URL {processed_url} processato per la risoluzione.")260 261    try:262        print(f"Chiamata a resolve_m3u8_link per URL processato: {processed_url}")263        result = resolve_m3u8_link(processed_url, headers)264 265        if not result["resolved_url"]:266            return "Errore: Impossibile risolvere l'URL in un M3U8 valido.", 500267 268        resolved_url = result["resolved_url"]269        current_headers_for_proxy = result["headers"]270 271        print(f"Risoluzione completata. URL M3U8 finale: {resolved_url}")272 273        # Fetchare il contenuto M3U8 effettivo dall'URL risolto274        print(f"Fetching M3U8 content from resolved URL: {resolved_url}")275        m3u_response = requests.get(resolved_url, headers=current_headers_for_proxy, allow_redirects=True, timeout=(10, 20)) # Timeout connessione 10s, lettura 20s276        m3u_response.raise_for_status()277        m3u_content = m3u_response.text278        final_url = m3u_response.url279 280        # Processa il contenuto M3U8281        file_type = detect_m3u_type(m3u_content)282 283        if file_type == "m3u":284            return Response(m3u_content, content_type="application/vnd.apple.mpegurl")285 286        # Processa contenuto M3U8287        parsed_url = urlparse(final_url)288        base_url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path.rsplit('/', 1)[0]}/"289 290        # Prepara la query degli header per segmenti/chiavi proxati291        headers_query = "&".join([f"h_{quote(k)}={quote(v)}" for k, v in current_headers_for_proxy.items()])292 293        modified_m3u8 = []294        for line in m3u_content.splitlines():295            line = line.strip()296            if line.startswith("#EXT-X-KEY") and 'URI="' in line:297                line = replace_key_uri(line, headers_query)298            elif line and not line.startswith("#"):299                segment_url = urljoin(base_url, line)300                line = f"/proxy/ts?url={quote(segment_url)}&{headers_query}"301            modified_m3u8.append(line)302 303        modified_m3u8_content = "\n".join(modified_m3u8)304        return Response(modified_m3u8_content, content_type="application/vnd.apple.mpegurl")305 306    except requests.RequestException as e:307        print(f"Errore durante il download o la risoluzione del file: {str(e)}")308        return f"Errore durante il download o la risoluzione del file M3U/M3U8: {str(e)}", 500309    except Exception as e:310        print(f"Errore generico nella funzione proxy_m3u: {str(e)}")311        return f"Errore generico durante l'elaborazione: {str(e)}", 500312 313@app.route('/proxy/resolve')314def proxy_resolve():315    """Proxy per risolvere e restituire un URL M3U8"""316    url = request.args.get('url', '').strip()317    if not url:318        return "Errore: Parametro 'url' mancante", 400319 320    headers = {321        unquote(key[2:]).replace("_", "-"): unquote(value).strip()322        for key, value in request.args.items()323        if key.lower().startswith("h_")324    }325 326    try:327        result = resolve_m3u8_link(url, headers)328        329        if not result["resolved_url"]:330            return "Errore: Impossibile risolvere l'URL", 500331            332        headers_query = "&".join([f"h_{quote(k)}={quote(v)}" for k, v in result["headers"].items()])333        334        return Response(335            f"#EXTM3U\n"336            f"#EXTINF:-1,Canale Risolto\n"337            f"/proxy/m3u?url={quote(result['resolved_url'])}&{headers_query}",338            content_type="application/vnd.apple.mpegurl"339        )340        341    except Exception as e:342        return f"Errore durante la risoluzione dell'URL: {str(e)}", 500343 344@app.route('/proxy/ts')345def proxy_ts():346    """Proxy per segmenti .TS con headers personalizzati - SENZA CACHE"""347    ts_url = request.args.get('url', '').strip()348    if not ts_url:349        return "Errore: Parametro 'url' mancante", 400350 351    headers = {352        unquote(key[2:]).replace("_", "-"): unquote(value).strip()353        for key, value in request.args.items()354        if key.lower().startswith("h_")355    }356 357    try:358        # Stream diretto senza cache per evitare freezing359        response = requests.get(ts_url, headers=headers, stream=True, allow_redirects=True, timeout=(10, 30)) # Timeout di connessione 10s, lettura 30s360        response.raise_for_status()361        362        def generate():363            for chunk in response.iter_content(chunk_size=8192):364                if chunk:365                    yield chunk366        367        return Response(generate(), content_type="video/mp2t")368    369    except requests.RequestException as e:370        return f"Errore durante il download del segmento TS: {str(e)}", 500371 372@app.route('/proxy/key')373def proxy_key():374    """Proxy per la chiave AES-128 con header personalizzati"""375    key_url = request.args.get('url', '').strip()376    if not key_url:377        return "Errore: Parametro 'url' mancante per la chiave", 400378 379    headers = {380        unquote(key[2:]).replace("_", "-"): unquote(value).strip()381        for key, value in request.args.items()382        if key.lower().startswith("h_")383    }384 385    try:386        response = requests.get(key_url, headers=headers, allow_redirects=True, timeout=(5, 15)) # Timeout connessione 5s, lettura 15s387        response.raise_for_status()388        389        return Response(response.content, content_type="application/octet-stream")390    391    except requests.RequestException as e:392        return f"Errore durante il download della chiave AES-128: {str(e)}", 500393 394@app.route('/')395def index():396    """Pagina principale che mostra un messaggio di benvenuto"""397    return "Proxy started!"398 399if __name__ == '__main__':400    print("Proxy started!")401    app.run(host="0.0.0.0", port=7860, debug=False)