Spaces:
Running
Running
Fix VTV proxy SSL: verify=False for vtvdigital self-signed cert + rewrite nested m3u8
Browse files- vtv_api.py +25 -2
vtv_api.py
CHANGED
|
@@ -5,6 +5,8 @@ EPG data scraped from https://vtv.vn/lich-phat-song.htm
|
|
| 5 |
"""
|
| 6 |
import re, time, threading
|
| 7 |
import requests
|
|
|
|
|
|
|
| 8 |
from fastapi import APIRouter, Query
|
| 9 |
from fastapi.responses import JSONResponse, Response
|
| 10 |
from bs4 import BeautifulSoup
|
|
@@ -219,7 +221,9 @@ def proxy_vtv_m3u8(url: str = Query(...)):
|
|
| 219 |
headers["Origin"] = "https://fptplay.vn"
|
| 220 |
elif "xemtv" in url:
|
| 221 |
headers["Referer"] = "https://hd.xemtv.net/"
|
| 222 |
-
|
|
|
|
|
|
|
| 223 |
if r.status_code != 200:
|
| 224 |
return Response(status_code=502, content="upstream error")
|
| 225 |
content = r.text
|
|
@@ -246,10 +250,29 @@ def proxy_vtv_segment(url: str = Query(...)):
|
|
| 246 |
if "fptplay" in url:
|
| 247 |
headers["Referer"] = "https://fptplay.vn/"
|
| 248 |
headers["Origin"] = "https://fptplay.vn"
|
| 249 |
-
|
|
|
|
|
|
|
| 250 |
if r.status_code != 200:
|
| 251 |
return Response(status_code=502, content="upstream error")
|
| 252 |
data = r.content
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 253 |
if len(data) > 188 and data[0:4] == b'\x89PNG' and data[188] == 0x47:
|
| 254 |
data = data[188:]
|
| 255 |
return Response(content=data, media_type="video/mp2t", headers={"Access-Control-Allow-Origin": "*", "Cache-Control": "public, max-age=3600"})
|
|
|
|
| 5 |
"""
|
| 6 |
import re, time, threading
|
| 7 |
import requests
|
| 8 |
+
import urllib3
|
| 9 |
+
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
| 10 |
from fastapi import APIRouter, Query
|
| 11 |
from fastapi.responses import JSONResponse, Response
|
| 12 |
from bs4 import BeautifulSoup
|
|
|
|
| 221 |
headers["Origin"] = "https://fptplay.vn"
|
| 222 |
elif "xemtv" in url:
|
| 223 |
headers["Referer"] = "https://hd.xemtv.net/"
|
| 224 |
+
# vtvdigital uses self-signed cert — skip SSL verification
|
| 225 |
+
verify_ssl = "vtvdigital" not in url
|
| 226 |
+
r = requests.get(url, headers=headers, timeout=15, allow_redirects=True, verify=verify_ssl)
|
| 227 |
if r.status_code != 200:
|
| 228 |
return Response(status_code=502, content="upstream error")
|
| 229 |
content = r.text
|
|
|
|
| 250 |
if "fptplay" in url:
|
| 251 |
headers["Referer"] = "https://fptplay.vn/"
|
| 252 |
headers["Origin"] = "https://fptplay.vn"
|
| 253 |
+
# vtvdigital uses self-signed cert — skip SSL verification
|
| 254 |
+
verify_ssl = "vtvdigital" not in url
|
| 255 |
+
r = requests.get(url, headers=headers, timeout=30, allow_redirects=True, verify=verify_ssl)
|
| 256 |
if r.status_code != 200:
|
| 257 |
return Response(status_code=502, content="upstream error")
|
| 258 |
data = r.content
|
| 259 |
+
# If response is an m3u8 sub-playlist, rewrite segment URLs to proxy
|
| 260 |
+
content_type = r.headers.get("Content-Type", "")
|
| 261 |
+
if b"#EXTM3U" in data[:500] or ".m3u8" in url:
|
| 262 |
+
lines = data.decode("utf-8", errors="replace").split('\n')
|
| 263 |
+
rewritten = []
|
| 264 |
+
base_url = url.rsplit('/', 1)[0] + '/'
|
| 265 |
+
for line in lines:
|
| 266 |
+
line = line.strip()
|
| 267 |
+
if not line or line.startswith('#'):
|
| 268 |
+
rewritten.append(line)
|
| 269 |
+
else:
|
| 270 |
+
seg_url = line
|
| 271 |
+
if not seg_url.startswith('http'):
|
| 272 |
+
seg_url = base_url + seg_url
|
| 273 |
+
rewritten.append("/api/proxy/seg/vtv?url=" + requests.utils.quote(seg_url, safe=""))
|
| 274 |
+
return Response(content='\n'.join(rewritten).encode("utf-8"), media_type="application/vnd.apple.mpegurl", headers={"Access-Control-Allow-Origin": "*", "Cache-Control": "no-cache"})
|
| 275 |
+
# PNG fix
|
| 276 |
if len(data) > 188 and data[0:4] == b'\x89PNG' and data[188] == 0x47:
|
| 277 |
data = data[188:]
|
| 278 |
return Response(content=data, media_type="video/mp2t", headers={"Access-Control-Allow-Origin": "*", "Cache-Control": "public, max-age=3600"})
|