Add curl fallback for protected HLS segments

This commit is contained in:
Dymas
2026-09-07 21:10:46 +02:00
parent ebe749398c
commit 5f75fef4de
6 changed files with 111 additions and 5 deletions
+69 -2
View File
@@ -10,6 +10,7 @@ import shutil
import subprocess
import sys
import time
import urllib.error
from urllib.parse import urljoin, urlparse
import urllib.request
from pathlib import Path
@@ -19,6 +20,7 @@ PROJECT_ROOT = Path(__file__).resolve().parent
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:"
def clean_component(value, default="Anime"):
@@ -212,10 +214,75 @@ def request_headers(headers=None, referer=None):
return merged
def curl_candidates():
configured = str(os.environ.get("KAIZOKU_CURL_BIN") or "").strip()
names = [
configured,
"curl_chrome120",
"curl_chrome116",
"curl_chrome110",
"curl-impersonate",
"curl",
]
candidates = []
for name in names:
if not name or name in candidates:
continue
resolved = shutil.which(name)
if resolved:
candidates.append(resolved)
return candidates
def curl_fetch_bytes(url, headers=None, timeout=30):
candidates = curl_candidates()
if not candidates:
raise RuntimeError("curl is not available for media fetch fallback.")
clean_headers = request_headers(headers)
last_error = None
for curl_bin in candidates:
cmd = [
curl_bin,
"--location",
"--silent",
"--show-error",
"--max-time",
str(max(1, int(timeout or 30))),
]
for key, value in clean_headers.items():
if value:
cmd.extend(["--header", f"{key}: {value}"])
cmd.extend(["--write-out", f"{CURL_STATUS_MARKER.decode('ascii')}%{{http_code}}", "--output", "-", str(url)])
proc = subprocess.run(cmd, capture_output=True, check=False)
payload, marker, status_bytes = proc.stdout.rpartition(CURL_STATUS_MARKER)
if marker:
try:
status = int(status_bytes.strip() or b"0")
except ValueError:
status = 0
else:
payload = proc.stdout
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()
last_error = urllib.error.HTTPError(str(url), status or 0, message, hdrs={}, fp=None)
raise last_error or RuntimeError("curl media fetch failed.")
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))
with urllib.request.urlopen(request, timeout=timeout) as response:
return response.read()
try:
with urllib.request.urlopen(request, timeout=timeout) as response:
return response.read()
except urllib.error.HTTPError as exc:
if client != "urllib" and exc.code == 403 and curl_candidates():
print(f"urllib media fetch got HTTP 403; retrying with curl fallback.", file=sys.stderr, flush=True)
return curl_fetch_bytes(url, headers=headers, timeout=timeout)
raise
def int_env(name, default, minimum=0):