Update app.py
Browse files
app.py
CHANGED
@@ -2,7 +2,7 @@ from flask import Flask, request, Response
|
|
2 |
import requests
|
3 |
from urllib.parse import urlparse, urljoin, quote, unquote
|
4 |
import re
|
5 |
-
import
|
6 |
import os
|
7 |
|
8 |
app = Flask(__name__)
|
@@ -24,74 +24,28 @@ def replace_key_uri(line, headers_query):
|
|
24 |
|
25 |
def resolve_m3u8_link(url, headers=None):
|
26 |
"""
|
27 |
-
Tenta di risolvere un URL M3U8
|
28 |
-
|
|
|
29 |
"""
|
30 |
if not url:
|
31 |
print("Errore: URL non fornito.")
|
32 |
return {"resolved_url": None, "headers": {}}
|
33 |
|
34 |
print(f"Tentativo di risoluzione URL: {url}")
|
35 |
-
|
36 |
-
|
37 |
-
current_headers = headers if headers else {
|
38 |
-
'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'
|
39 |
-
}
|
40 |
-
|
41 |
-
# **SUPPORTO PER ENTRAMBE LE VERSIONI**
|
42 |
-
clean_url = url
|
43 |
-
extracted_headers = {}
|
44 |
-
|
45 |
-
# Verifica se l'URL contiene parametri header concatenati
|
46 |
-
if '&h_' in url or '%26h_' in url:
|
47 |
-
print("Rilevati parametri header nell'URL - Estrazione in corso...")
|
48 |
-
|
49 |
-
# Gestisci sia il formato normale che quello URL-encoded
|
50 |
-
if '%26h_' in url:
|
51 |
-
# Per vavoo.to, sostituisci solo %26 con & senza doppia decodifica
|
52 |
-
if 'vavoo.to' in url.lower():
|
53 |
-
url = url.replace('%26', '&')
|
54 |
-
print(f"URL vavoo.to processato: {url}")
|
55 |
-
else:
|
56 |
-
# Per altri URL, applica la doppia decodifica completa
|
57 |
-
url = unquote(unquote(url))
|
58 |
-
print(f"URL con doppia decodifica: {url}")
|
59 |
-
|
60 |
-
# Separa l'URL base dai parametri degli header
|
61 |
-
url_parts = url.split('&h_', 1)
|
62 |
-
clean_url = url_parts[0]
|
63 |
-
header_params = '&h_' + url_parts[1]
|
64 |
-
|
65 |
-
# Estrai gli header dai parametri
|
66 |
-
for param in header_params.split('&'):
|
67 |
-
if param.startswith('h_'):
|
68 |
-
try:
|
69 |
-
key_value = param[2:].split('=', 1)
|
70 |
-
if len(key_value) == 2:
|
71 |
-
key = unquote(key_value[0]).replace('_', '-')
|
72 |
-
value = unquote(key_value[1])
|
73 |
-
extracted_headers[key] = value
|
74 |
-
print(f"Header estratto: {key} = {value}")
|
75 |
-
except Exception as e:
|
76 |
-
print(f"Errore nell'estrazione dell'header {param}: {e}")
|
77 |
-
|
78 |
-
# Combina gli header estratti con quelli esistenti
|
79 |
-
current_headers.update(extracted_headers)
|
80 |
-
print(f"URL pulito: {clean_url}")
|
81 |
-
print(f"Header finali: {current_headers}")
|
82 |
-
else:
|
83 |
-
print("URL pulito rilevato - Nessuna estrazione header necessaria")
|
84 |
|
85 |
initial_response_text = None
|
86 |
final_url_after_redirects = None
|
87 |
|
88 |
# Verifica se è un URL di vavoo.to
|
89 |
-
is_vavoo = "vavoo.to" in
|
90 |
|
91 |
try:
|
92 |
with requests.Session() as session:
|
93 |
-
print(f"Passo 1: Richiesta a {
|
94 |
-
response = session.get(
|
95 |
response.raise_for_status()
|
96 |
initial_response_text = response.text
|
97 |
final_url_after_redirects = response.url
|
@@ -106,9 +60,9 @@ def resolve_m3u8_link(url, headers=None):
|
|
106 |
}
|
107 |
else:
|
108 |
# Se non è un M3U8 diretto, restituisci l'URL originale per vavoo
|
109 |
-
print(f"URL vavoo.to non è un M3U8 diretto: {
|
110 |
return {
|
111 |
-
"resolved_url":
|
112 |
"headers": current_headers
|
113 |
}
|
114 |
|
@@ -131,12 +85,9 @@ def resolve_m3u8_link(url, headers=None):
|
|
131 |
print(f"Passo 3 (Iframe): Richiesta a {url2}")
|
132 |
response = session.get(url2, headers=current_headers, timeout=(5, 15))
|
133 |
response.raise_for_status()
|
134 |
-
# Applica la codifica corretta
|
135 |
-
response.encoding = response.apparent_encoding or 'utf-8'
|
136 |
iframe_response_text = response.text
|
137 |
print("Passo 3 (Iframe) completato.")
|
138 |
|
139 |
-
# ... resto del codice iframe rimane uguale ...
|
140 |
# Quarto passo (Iframe): Estrai parametri dinamici dall'iframe response
|
141 |
channel_key_match = re.search(r'(?s) channelKey = \"([^\"]*)"', iframe_response_text)
|
142 |
auth_ts_match = re.search(r'(?s) authTs\s*= \"([^\"]*)"', iframe_response_text)
|
@@ -216,16 +167,16 @@ def resolve_m3u8_link(url, headers=None):
|
|
216 |
else:
|
217 |
print("Fallback fallito: La risposta iniziale non era un M3U8 diretto.")
|
218 |
return {
|
219 |
-
"resolved_url":
|
220 |
"headers": current_headers
|
221 |
}
|
222 |
|
223 |
except requests.exceptions.RequestException as e:
|
224 |
print(f"Errore durante la richiesta HTTP iniziale: {e}")
|
225 |
-
return {"resolved_url":
|
226 |
except Exception as e:
|
227 |
print(f"Errore generico durante la risoluzione: {e}")
|
228 |
-
return {"resolved_url":
|
229 |
|
230 |
@app.route('/proxy')
|
231 |
def proxy():
|
@@ -243,49 +194,16 @@ def proxy():
|
|
243 |
response.raise_for_status()
|
244 |
m3u_content = response.text
|
245 |
|
|
|
246 |
modified_lines = []
|
247 |
-
exthttp_headers_query_params = "" # Stringa per conservare gli header da #EXTHTTP
|
248 |
-
|
249 |
for line in m3u_content.splitlines():
|
250 |
line = line.strip()
|
251 |
-
if line.startswith('#
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
headers_dict = json.loads(json_str)
|
256 |
-
|
257 |
-
# Costruisci la stringa dei parametri di query per gli header con doppia codifica
|
258 |
-
temp_params = []
|
259 |
-
for key, value in headers_dict.items():
|
260 |
-
# Doppia codifica: prima codifica normale, poi codifica di nuovo
|
261 |
-
encoded_key = quote(quote(key))
|
262 |
-
encoded_value = quote(quote(str(value)))
|
263 |
-
temp_params.append(f"h_{encoded_key}={encoded_value}")
|
264 |
-
|
265 |
-
if temp_params:
|
266 |
-
# Usa %26 invece di & come separatore per gli header
|
267 |
-
exthttp_headers_query_params = "%26" + "%26".join(temp_params)
|
268 |
-
else:
|
269 |
-
exthttp_headers_query_params = ""
|
270 |
-
except Exception as e:
|
271 |
-
print(f"Errore nel parsing di #EXTHTTP '{line}': {e}")
|
272 |
-
exthttp_headers_query_params = "" # Resetta in caso di errore
|
273 |
-
modified_lines.append(line) # Mantieni la riga #EXTHTTP originale
|
274 |
-
elif line and not line.startswith('#'):
|
275 |
-
# Questa è una riga di URL del flusso
|
276 |
-
# Verifica se è un URL di Pluto.tv e saltalo
|
277 |
-
if 'pluto.tv' in line.lower():
|
278 |
-
modified_lines.append(line) # Mantieni l'URL originale senza proxy
|
279 |
-
exthttp_headers_query_params = "" # Resetta gli header
|
280 |
-
else:
|
281 |
-
# Applica gli header #EXTHTTP se presenti e poi resettali
|
282 |
-
# Assicurati che l'URL sia completamente codificato, inclusi gli slash
|
283 |
-
encoded_line = quote(line, safe='')
|
284 |
-
modified_line = f"http://{server_ip}/proxy/m3u?url={encoded_line}{exthttp_headers_query_params}"
|
285 |
-
modified_lines.append(modified_line)
|
286 |
-
exthttp_headers_query_params = "" # Resetta gli header dopo averli usati
|
287 |
else:
|
288 |
-
# Mantieni invariate le
|
289 |
modified_lines.append(line)
|
290 |
|
291 |
modified_content = '\n'.join(modified_lines)
|
@@ -303,25 +221,23 @@ def proxy():
|
|
303 |
|
304 |
@app.route('/proxy/m3u')
|
305 |
def proxy_m3u():
|
306 |
-
"""Proxy per file M3U e M3U8 con supporto per
|
307 |
m3u_url = request.args.get('url', '').strip()
|
308 |
if not m3u_url:
|
309 |
return "Errore: Parametro 'url' mancante", 400
|
310 |
|
311 |
default_headers = {
|
312 |
-
"User-Agent": "Mozilla/5.0 (
|
313 |
"Referer": "https://vavoo.to/",
|
314 |
"Origin": "https://vavoo.to"
|
315 |
}
|
316 |
|
317 |
-
# Estrai gli header dalla richiesta
|
318 |
request_headers = {
|
319 |
unquote(key[2:]).replace("_", "-"): unquote(value).strip()
|
320 |
for key, value in request.args.items()
|
321 |
if key.lower().startswith("h_")
|
322 |
}
|
323 |
-
|
324 |
-
# Combina header di default con quelli della richiesta
|
325 |
headers = {**default_headers, **request_headers}
|
326 |
|
327 |
# --- Logica per trasformare l'URL se necessario ---
|
@@ -329,8 +245,8 @@ def proxy_m3u():
|
|
329 |
|
330 |
# Trasforma /stream/ in /embed/ per Daddylive
|
331 |
if '/stream/stream-' in m3u_url and 'thedaddy.click' in m3u_url:
|
332 |
-
processed_url = m3u_url.replace('/
|
333 |
-
print(f"URL {m3u_url} trasformato da /
|
334 |
|
335 |
match_premium_m3u8 = re.search(r'/premium(\d+)/mono\.m3u8$', m3u_url)
|
336 |
|
@@ -358,8 +274,6 @@ def proxy_m3u():
|
|
358 |
print(f"Fetching M3U8 content from resolved URL: {resolved_url}")
|
359 |
m3u_response = requests.get(resolved_url, headers=current_headers_for_proxy, allow_redirects=True, timeout=(10, 20)) # Timeout connessione 10s, lettura 20s
|
360 |
m3u_response.raise_for_status()
|
361 |
-
# Applica la codifica corretta
|
362 |
-
m3u_response.encoding = m3u_response.apparent_encoding or 'utf-8'
|
363 |
m3u_content = m3u_response.text
|
364 |
final_url = m3u_response.url
|
365 |
|
@@ -367,7 +281,7 @@ def proxy_m3u():
|
|
367 |
file_type = detect_m3u_type(m3u_content)
|
368 |
|
369 |
if file_type == "m3u":
|
370 |
-
return Response(m3u_content, content_type="application/vnd.apple.mpegurl
|
371 |
|
372 |
# Processa contenuto M3U8
|
373 |
parsed_url = urlparse(final_url)
|
@@ -387,7 +301,7 @@ def proxy_m3u():
|
|
387 |
modified_m3u8.append(line)
|
388 |
|
389 |
modified_m3u8_content = "\n".join(modified_m3u8)
|
390 |
-
return Response(modified_m3u8_content, content_type="application/vnd.apple.mpegurl
|
391 |
|
392 |
except requests.RequestException as e:
|
393 |
print(f"Errore durante il download o la risoluzione del file: {str(e)}")
|
@@ -401,7 +315,7 @@ def proxy_resolve():
|
|
401 |
"""Proxy per risolvere e restituire un URL M3U8"""
|
402 |
url = request.args.get('url', '').strip()
|
403 |
if not url:
|
404 |
-
return "
|
405 |
|
406 |
headers = {
|
407 |
unquote(key[2:]).replace("_", "-"): unquote(value).strip()
|
@@ -421,7 +335,7 @@ def proxy_resolve():
|
|
421 |
f"#EXTM3U\n"
|
422 |
f"#EXTINF:-1,Canale Risolto\n"
|
423 |
f"/proxy/m3u?url={quote(result['resolved_url'])}&{headers_query}",
|
424 |
-
content_type="application/vnd.apple.mpegurl
|
425 |
)
|
426 |
|
427 |
except Exception as e:
|
@@ -477,151 +391,6 @@ def proxy_key():
|
|
477 |
except requests.RequestException as e:
|
478 |
return f"Errore durante il download della chiave AES-128: {str(e)}", 500
|
479 |
|
480 |
-
@app.route('/playlist/channels.m3u8')
|
481 |
-
def playlist_channels():
|
482 |
-
"""Gibt eine modifizierte Playlist mit Proxy-Links zurück"""
|
483 |
-
playlist_url = "https://raw.githubusercontent.com/MarkMCFC/NewDadProxy/refs/heads/main/channel.m3u8"
|
484 |
-
|
485 |
-
try:
|
486 |
-
host_url = request.host_url.rstrip('/')
|
487 |
-
response = requests.get(playlist_url, timeout=10)
|
488 |
-
response.raise_for_status()
|
489 |
-
playlist_content = response.text
|
490 |
-
|
491 |
-
modified_lines = []
|
492 |
-
for line in playlist_content.splitlines():
|
493 |
-
stripped_line = line.strip()
|
494 |
-
if stripped_line and not stripped_line.startswith('#'):
|
495 |
-
proxy_line = f"{host_url}/proxy/m3u?url={quote(stripped_line)}"
|
496 |
-
modified_lines.append(proxy_line)
|
497 |
-
else:
|
498 |
-
modified_lines.append(line)
|
499 |
-
|
500 |
-
modified_content = '\n'.join(modified_lines)
|
501 |
-
return Response(modified_content, content_type="application/vnd.apple.mpegurl")
|
502 |
-
|
503 |
-
except requests.RequestException as e:
|
504 |
-
return f"Fehler beim Laden der Playlist: {str(e)}", 500
|
505 |
-
except Exception as e:
|
506 |
-
return f"Allgemeiner Fehler: {str(e)}", 500
|
507 |
-
|
508 |
-
|
509 |
-
@app.route('/playlist/events.m3u8')
|
510 |
-
def playlist_events():
|
511 |
-
"""Generiert die Events-Playlist mit Proxy-Links bei jedem Aufruf"""
|
512 |
-
try:
|
513 |
-
# Hole die aktuelle Host-URL
|
514 |
-
host_url = request.host_url.rstrip('/')
|
515 |
-
|
516 |
-
# Lade die Sendeplandaten
|
517 |
-
schedule_data = fetch_schedule_data()
|
518 |
-
if not schedule_data:
|
519 |
-
return "Fehler beim Abrufen der Sendeplandaten", 500
|
520 |
-
|
521 |
-
# Konvertiere JSON in M3U mit Proxy-Links
|
522 |
-
m3u_content = json_to_m3u(schedule_data, host_url)
|
523 |
-
if not m3u_content:
|
524 |
-
return "Fehler beim Generieren der Playlist", 500
|
525 |
-
|
526 |
-
return Response(m3u_content, content_type="application/vnd.apple.mpegurl")
|
527 |
-
|
528 |
-
except Exception as e:
|
529 |
-
print(f"Fehler in /playlist/events: {str(e)}")
|
530 |
-
return f"Interner Serverfehler: {str(e)}", 500
|
531 |
-
|
532 |
-
def fetch_schedule_data():
|
533 |
-
"""Holt die aktuellen Sendeplandaten von der Website"""
|
534 |
-
url = "https://thedaddy.click/schedule/schedule-generated.php"
|
535 |
-
headers = {
|
536 |
-
"authority": "thedaddy.click",
|
537 |
-
"accept": "*/*",
|
538 |
-
"accept-encoding": "gzip, deflate, br, zstd",
|
539 |
-
"accept-language": "de-DE,de;q=0.9",
|
540 |
-
"priority": "u=1, i",
|
541 |
-
"referer": "https://thedaddy.click/",
|
542 |
-
"sec-ch-ua": '"Brave";v="137", "Chromium";v="137", "Not/A)Brand";v="24"',
|
543 |
-
"sec-ch-ua-mobile": "?0",
|
544 |
-
"sec-ch-ua-platform": '"Windows"',
|
545 |
-
"sec-fetch-dest": "empty",
|
546 |
-
"sec-fetch-mode": "cors",
|
547 |
-
"sec-fetch-site": "same-origin",
|
548 |
-
"sec-gpc": "1",
|
549 |
-
"user-agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2739.1442 Mobile Safari/537.36"
|
550 |
-
}
|
551 |
-
|
552 |
-
try:
|
553 |
-
response = requests.get(url, headers=headers, timeout=30)
|
554 |
-
if response.status_code == 200:
|
555 |
-
return response.json()
|
556 |
-
else:
|
557 |
-
print(f"Fehler beim Abrufen der Daten: Status-Code {response.status_code}")
|
558 |
-
return None
|
559 |
-
except Exception as e:
|
560 |
-
print(f"Fehler beim Abrufen der Daten: {e}")
|
561 |
-
return None
|
562 |
-
|
563 |
-
def json_to_m3u(data, host_url):
|
564 |
-
"""Konvertiert JSON-Daten in M3U-Format mit Proxy-Links im gewünschten Format"""
|
565 |
-
if not data:
|
566 |
-
return None
|
567 |
-
|
568 |
-
m3u_content = '#EXTM3U\n\n'
|
569 |
-
|
570 |
-
try:
|
571 |
-
main_key = list(data.keys())[0]
|
572 |
-
categories = data[main_key]
|
573 |
-
except Exception as e:
|
574 |
-
print(f"Fehler beim Verarbeiten der JSON-Daten: {e}")
|
575 |
-
return None
|
576 |
-
|
577 |
-
for category_name, events in categories.items():
|
578 |
-
if not isinstance(events, list):
|
579 |
-
continue
|
580 |
-
|
581 |
-
for event in events:
|
582 |
-
if not isinstance(event, dict):
|
583 |
-
continue
|
584 |
-
|
585 |
-
group_title = event.get("event", "Unknown Event")
|
586 |
-
channels_list = []
|
587 |
-
|
588 |
-
for channel_key in ["channels", "channels2"]:
|
589 |
-
channels = event.get(channel_key, [])
|
590 |
-
if isinstance(channels, dict):
|
591 |
-
channels_list.extend(channels.values())
|
592 |
-
elif isinstance(channels, list):
|
593 |
-
channels_list.extend(channels)
|
594 |
-
|
595 |
-
for channel in channels_list:
|
596 |
-
if not isinstance(channel, dict):
|
597 |
-
continue
|
598 |
-
|
599 |
-
channel_name = channel.get("channel_name", "Unknown Channel")
|
600 |
-
channel_id = channel.get("channel_id", "0")
|
601 |
-
|
602 |
-
# Generiere die Stream-URL basierend auf der ID
|
603 |
-
try:
|
604 |
-
channel_id_int = int(channel_id)
|
605 |
-
if channel_id_int > 999:
|
606 |
-
stream_url = f"https://thedaddy.click/cast/bet.php?id=bet{channel_id}"
|
607 |
-
else:
|
608 |
-
stream_url = f"https://thedaddy.click/cast/stream-{channel_id}.php"
|
609 |
-
except (ValueError, TypeError):
|
610 |
-
stream_url = f"https://thedaddy.click/cast/stream-{channel_id}.php"
|
611 |
-
|
612 |
-
# Generiere den Proxy-Link im gewünschten Format
|
613 |
-
proxy_url = f"{host_url}/proxy/m3u?url={stream_url}"
|
614 |
-
|
615 |
-
m3u_content += (
|
616 |
-
f'#EXTINF:-1 tvg-id="{channel_name}" group-title="{group_title}",{channel_name}\n'
|
617 |
-
'#EXTVLCOPT:http-referrer=https://forcedtoplay.xyz/\n'
|
618 |
-
'#EXTVLCOPT:http-origin=https://forcedtoplay.xyz\n'
|
619 |
-
'#EXTVLCOPT:http-user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 17_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1\n'
|
620 |
-
f'{proxy_url}\n\n'
|
621 |
-
)
|
622 |
-
|
623 |
-
return m3u_content
|
624 |
-
|
625 |
@app.route('/')
|
626 |
def index():
|
627 |
"""Pagina principale che mostra un messaggio di benvenuto"""
|
|
|
2 |
import requests
|
3 |
from urllib.parse import urlparse, urljoin, quote, unquote
|
4 |
import re
|
5 |
+
import traceback
|
6 |
import os
|
7 |
|
8 |
app = Flask(__name__)
|
|
|
24 |
|
25 |
def 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 default
|
37 |
+
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 = None
|
40 |
final_url_after_redirects = None
|
41 |
|
42 |
# Verifica se è un URL di vavoo.to
|
43 |
+
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.text
|
51 |
final_url_after_redirects = response.url
|
|
|
60 |
}
|
61 |
else:
|
62 |
# Se non è un M3U8 diretto, restituisci l'URL originale per vavoo
|
63 |
+
print(f"URL vavoo.to non è un M3U8 diretto: {url}")
|
64 |
return {
|
65 |
+
"resolved_url": url,
|
66 |
"headers": current_headers
|
67 |
}
|
68 |
|
|
|
85 |
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.text
|
89 |
print("Passo 3 (Iframe) completato.")
|
90 |
|
|
|
91 |
# Quarto passo (Iframe): Estrai parametri dinamici dall'iframe response
|
92 |
channel_key_match = re.search(r'(?s) channelKey = \"([^\"]*)"', iframe_response_text)
|
93 |
auth_ts_match = re.search(r'(?s) authTs\s*= \"([^\"]*)"', iframe_response_text)
|
|
|
167 |
else:
|
168 |
print("Fallback fallito: La risposta iniziale non era un M3U8 diretto.")
|
169 |
return {
|
170 |
+
"resolved_url": url,
|
171 |
"headers": current_headers
|
172 |
}
|
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')
|
182 |
def proxy():
|
|
|
194 |
response.raise_for_status()
|
195 |
m3u_content = response.text
|
196 |
|
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 normale
|
203 |
+
modified_line = f"http://{server_ip}/proxy/m3u?url={line}"
|
204 |
+
modified_lines.append(modified_line)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
205 |
else:
|
206 |
+
# Mantieni invariate le righe di metadati
|
207 |
modified_lines.append(line)
|
208 |
|
209 |
modified_content = '\n'.join(modified_lines)
|
|
|
221 |
|
222 |
@app.route('/proxy/m3u')
|
223 |
def 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", 400
|
228 |
|
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 default
|
236 |
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 ---
|
|
|
245 |
|
246 |
# Trasforma /stream/ in /embed/ per Daddylive
|
247 |
if '/stream/stream-' in m3u_url and 'thedaddy.click' 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 |
|
|
|
274 |
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 20s
|
276 |
m3u_response.raise_for_status()
|
|
|
|
|
277 |
m3u_content = m3u_response.text
|
278 |
final_url = m3u_response.url
|
279 |
|
|
|
281 |
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 M3U8
|
287 |
parsed_url = urlparse(final_url)
|
|
|
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)}")
|
|
|
315 |
"""Proxy per risolvere e restituire un URL M3U8"""
|
316 |
url = request.args.get('url', '').strip()
|
317 |
if not url:
|
318 |
+
return "Error: Input 'url' First", 400
|
319 |
|
320 |
headers = {
|
321 |
unquote(key[2:]).replace("_", "-"): unquote(value).strip()
|
|
|
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:
|
|
|
391 |
except requests.RequestException as e:
|
392 |
return f"Errore durante il download della chiave AES-128: {str(e)}", 500
|
393 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
394 |
@app.route('/')
|
395 |
def index():
|
396 |
"""Pagina principale che mostra un messaggio di benvenuto"""
|