Preserve HLS media session cookies

This commit is contained in:
Dymas
2026-09-07 21:52:08 +02:00
parent baf5298631
commit d39d783738
5 changed files with 141 additions and 6 deletions
+44 -2
View File
@@ -3,12 +3,15 @@
"""Download episodes from Kaizoku's StrawVerse-compatible provider bridge."""
import argparse
import atexit
import http.cookiejar
import json
import os
import re
import shutil
import subprocess
import sys
import tempfile
import time
import urllib.error
from urllib.parse import urljoin, urlparse
@@ -21,6 +24,8 @@ BRIDGE = PROJECT_ROOT / "providers" / "bridge.js"
PROVIDERS = ("anikoto", "anineko", "pahe")
RETRYABLE_HTTP_STATUS = {408, 425, 429, 500, 502, 503, 504}
CURL_STATUS_MARKER = b"\nKAIZOKU_HTTP_STATUS:"
MEDIA_COOKIE_JAR = http.cookiejar.CookieJar()
MEDIA_CURL_COOKIE_DIR = None
def clean_component(value, default="Anime"):
@@ -214,6 +219,28 @@ def request_headers(headers=None, referer=None):
return merged
def media_request(url, headers=None):
request = urllib.request.Request(url, headers=request_headers(headers))
MEDIA_COOKIE_JAR.add_cookie_header(request)
return request
def media_cookie_header(url):
request = urllib.request.Request(url)
MEDIA_COOKIE_JAR.add_cookie_header(request)
return request.get_header("Cookie") or ""
def curl_cookie_file():
global MEDIA_CURL_COOKIE_DIR
if MEDIA_CURL_COOKIE_DIR is None:
MEDIA_CURL_COOKIE_DIR = tempfile.TemporaryDirectory(prefix="kaizoku-media-cookies-")
atexit.register(MEDIA_CURL_COOKIE_DIR.cleanup)
cookie_file = Path(MEDIA_CURL_COOKIE_DIR.name) / "cookies.txt"
cookie_file.touch(mode=0o600)
return Path(MEDIA_CURL_COOKIE_DIR.name) / "cookies.txt"
def curl_candidates():
def candidate_score(name):
text = Path(str(name or "")).name
@@ -279,8 +306,13 @@ def curl_fetch_bytes(url, headers=None, timeout=30):
if not candidates:
raise RuntimeError("curl is not available for media fetch fallback.")
last_error = None
failures = []
cookie_file = curl_cookie_file()
for curl_bin in candidates:
clean_headers = curl_request_headers(curl_bin, headers)
cookie_header = media_cookie_header(url)
if cookie_header and not any(str(key).lower() == "cookie" for key in clean_headers):
clean_headers["Cookie"] = cookie_header
cmd = [
curl_bin,
"--location",
@@ -288,6 +320,10 @@ def curl_fetch_bytes(url, headers=None, timeout=30):
"--show-error",
"--max-time",
str(max(1, int(timeout or 30))),
"--cookie",
str(cookie_file),
"--cookie-jar",
str(cookie_file),
]
for key, value in clean_headers.items():
if value:
@@ -305,7 +341,12 @@ def curl_fetch_bytes(url, headers=None, timeout=30):
status = 0
if 200 <= status < 300 and proc.returncode == 0:
return payload
message = (proc.stderr or b"curl media fetch failed").decode("utf-8", errors="replace").strip()
client_name = Path(curl_bin).name
detail = (proc.stderr or b"").decode("utf-8", errors="replace").strip()
failures.append(f"{client_name}: HTTP {status or 0}" if status else f"{client_name}: exit {proc.returncode}")
message = "; ".join(failures)
if detail:
message = f"{message} ({detail})"
last_error = urllib.error.HTTPError(str(url), status or 0, message, hdrs={}, fp=None)
raise last_error or RuntimeError("curl media fetch failed.")
@@ -314,9 +355,10 @@ def fetch_bytes(url, headers=None, timeout=30):
client = str(os.environ.get("KAIZOKU_MEDIA_HTTP_CLIENT") or "auto").strip().lower()
if client == "curl":
return curl_fetch_bytes(url, headers=headers, timeout=timeout)
request = urllib.request.Request(url, headers=request_headers(headers))
request = media_request(url, headers=headers)
try:
with urllib.request.urlopen(request, timeout=timeout) as response:
MEDIA_COOKIE_JAR.extract_cookies(response, request)
return response.read()
except urllib.error.HTTPError as exc:
if client != "urllib" and exc.code == 403 and curl_candidates():