Spaces:
Paused
Paused
Upload app.py
Browse files
app.py
CHANGED
|
@@ -395,19 +395,65 @@ async def vkmovie_stream(url: str, request: Request):
|
|
| 395 |
if range_header:
|
| 396 |
req_headers["range"] = range_header
|
| 397 |
|
| 398 |
-
# Визначаємо чи це HLS сегмент (.ts, .aac, .vtt) чи великий MP4
|
| 399 |
url_lower = url.lower().split("?")[0]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 400 |
is_segment = (
|
| 401 |
any(url_lower.endswith(ext) for ext in [".ts", ".aac", ".vtt", ".webvtt", ".m4s", ".m4a"]) or
|
| 402 |
-
# VK path-based URLs для сегментів містять /video/ або /type/5/
|
| 403 |
"/video/" in url_lower or
|
| 404 |
"type%2f5" in url.lower() or
|
| 405 |
"type/5" in url_lower
|
| 406 |
)
|
| 407 |
|
| 408 |
if is_segment:
|
| 409 |
-
# HLS сегменти — завантажуємо повністю
|
| 410 |
-
# (уникаємо ERR_HTTP2_PROTOCOL_ERROR при стримінгу)
|
| 411 |
async with httpx.AsyncClient(timeout=30, follow_redirects=True) as client:
|
| 412 |
try:
|
| 413 |
r = await client.get(url, headers=req_headers)
|
|
|
|
| 395 |
if range_header:
|
| 396 |
req_headers["range"] = range_header
|
| 397 |
|
|
|
|
| 398 |
url_lower = url.lower().split("?")[0]
|
| 399 |
+
# Якщо це явно m3u8 або sub-playlist — обробляємо як /vkmovie/hls
|
| 400 |
+
is_playlist = url_lower.endswith(".m3u8") or "type%2f2" in url.lower() or "type/2" in url_lower
|
| 401 |
+
|
| 402 |
+
if is_playlist:
|
| 403 |
+
# Делегуємо до /vkmovie/hls логіки
|
| 404 |
+
hf = str(request.base_url).rstrip("/").replace("http://", "https://")
|
| 405 |
+
async with httpx.AsyncClient(timeout=15, follow_redirects=True) as client:
|
| 406 |
+
try:
|
| 407 |
+
r = await client.get(url, headers=req_headers)
|
| 408 |
+
text = r.text
|
| 409 |
+
except Exception as e:
|
| 410 |
+
return Response(content=f"error: {e}", status_code=500)
|
| 411 |
+
|
| 412 |
+
content_type = r.headers.get("content-type", "")
|
| 413 |
+
if "mpegurl" in content_type or text.strip().startswith("#EXTM3U"):
|
| 414 |
+
base = url.rsplit("/", 1)[0] + "/"
|
| 415 |
+
def rewrite(seg):
|
| 416 |
+
seg = seg.strip()
|
| 417 |
+
if not seg: return seg
|
| 418 |
+
if seg.startswith("http://") or seg.startswith("https://"):
|
| 419 |
+
abs_url = seg
|
| 420 |
+
elif seg.startswith("//"):
|
| 421 |
+
abs_url = "https:" + seg
|
| 422 |
+
else:
|
| 423 |
+
abs_url = urljoin(base, seg)
|
| 424 |
+
return f"{hf}/vkmovie/stream?url={quote(abs_url, safe='')}"
|
| 425 |
+
|
| 426 |
+
lines = text.splitlines()
|
| 427 |
+
out = []
|
| 428 |
+
for line in lines:
|
| 429 |
+
stripped = line.strip()
|
| 430 |
+
if not stripped:
|
| 431 |
+
out.append(line)
|
| 432 |
+
elif stripped.startswith("#"):
|
| 433 |
+
rewritten = re.sub(r'URI="([^"]+)"', lambda m: f'URI="{rewrite(m.group(1))}"', line)
|
| 434 |
+
out.append(rewritten)
|
| 435 |
+
else:
|
| 436 |
+
out.append(rewrite(stripped))
|
| 437 |
+
return Response(
|
| 438 |
+
content="\n".join(out),
|
| 439 |
+
media_type="application/vnd.apple.mpegurl",
|
| 440 |
+
headers={"Access-Control-Allow-Origin": "*"}
|
| 441 |
+
)
|
| 442 |
+
else:
|
| 443 |
+
return Response(content=r.content, status_code=r.status_code,
|
| 444 |
+
media_type=content_type,
|
| 445 |
+
headers={"Access-Control-Allow-Origin": "*"})
|
| 446 |
+
|
| 447 |
+
# Визначаємо чи це HLS сегмент чи великий MP4
|
| 448 |
is_segment = (
|
| 449 |
any(url_lower.endswith(ext) for ext in [".ts", ".aac", ".vtt", ".webvtt", ".m4s", ".m4a"]) or
|
|
|
|
| 450 |
"/video/" in url_lower or
|
| 451 |
"type%2f5" in url.lower() or
|
| 452 |
"type/5" in url_lower
|
| 453 |
)
|
| 454 |
|
| 455 |
if is_segment:
|
| 456 |
+
# HLS сегменти — завантажуємо повністю
|
|
|
|
| 457 |
async with httpx.AsyncClient(timeout=30, follow_redirects=True) as client:
|
| 458 |
try:
|
| 459 |
r = await client.get(url, headers=req_headers)
|