Add queue download progress

This commit is contained in:
Codex
2026-08-09 12:55:06 +02:00
parent 499271c3b8
commit 11f43bba05
7 changed files with 271 additions and 14 deletions
+119 -13
View File
@@ -63,6 +63,11 @@ def bridge(command, provider, *args):
raise RuntimeError(f"Provider bridge returned invalid JSON: {exc}") from exc
def emit_progress(**payload):
clean = {key: value for key, value in payload.items() if value is not None}
print(f"KAIZOKU_PROGRESS {json.dumps(clean, sort_keys=True)}", flush=True)
def provider_order(primary):
ordered = []
primary = str(primary or "").strip().lower()
@@ -361,7 +366,7 @@ def decrypt_aes128_segment(data, key, iv_value):
return proc.stdout
def download_hls_segments(stream, input_url, target, partial):
def download_hls_segments(stream, input_url, target, partial, episode_number=None, episode_index=None, episode_total=None):
input_url, playlist = hls_media_playlist(stream, input_url)
segments = parse_hls_segments(input_url, playlist)
if not segments:
@@ -373,7 +378,19 @@ def download_hls_segments(stream, input_url, target, partial):
segment_dir.mkdir(parents=True, exist_ok=True)
key_cache = {}
try:
print(f"Downloading {len(segments)} HLS playlist segments directly...")
message = f"Downloading {len(segments)} HLS playlist segments directly..."
print(message, flush=True)
emit_progress(
phase="segments",
message=message,
episode=episode_number,
episode_index=episode_index,
episode_total=episode_total,
segment=0,
segment_total=len(segments),
percent=0,
)
last_percent = -1
with ts_file.open("wb") as joined:
for index, segment in enumerate(segments, start=1):
data = fetch_bytes(segment["url"], headers=stream.get("headers") or {}, timeout=60)
@@ -386,8 +403,31 @@ def download_hls_segments(stream, input_url, target, partial):
if not data:
raise RuntimeError(f"Segment {index} was empty.")
joined.write(data)
if index == 1 or index == len(segments) or index % 25 == 0:
print(f"Downloaded segment {index}/{len(segments)}")
percent = int(index * 100 / len(segments))
if index == 1 or index == len(segments) or percent >= last_percent + 2:
last_percent = percent
message = f"Episode {episode_number}: downloaded segment {index}/{len(segments)} ({percent}%)."
print(message, flush=True)
emit_progress(
phase="segments",
message=message,
episode=episode_number,
episode_index=episode_index,
episode_total=episode_total,
segment=index,
segment_total=len(segments),
percent=percent,
)
emit_progress(
phase="remux",
message=f"Episode {episode_number}: remuxing downloaded segments.",
episode=episode_number,
episode_index=episode_index,
episode_total=episode_total,
segment=len(segments),
segment_total=len(segments),
percent=99,
)
cmd = [
"ffmpeg",
"-hide_banner",
@@ -446,7 +486,7 @@ def download_subtitles(subtitles, output_base):
return saved
def download_episode(stream, target):
def download_episode(stream, target, episode_number=None, episode_index=None, episode_total=None):
input_url = resolve_hls_input_url(stream)
partial = target.with_suffix(target.suffix + ".part")
for path in (target, partial):
@@ -459,8 +499,25 @@ def download_episode(stream, target):
media_url, playlist = hls_media_playlist(stream, input_url)
segments = parse_hls_segments(media_url, playlist)
if hls_segments_need_native_download(segments):
print("HLS playlist uses extensionless provider segments; skipping direct ffmpeg.")
code = download_hls_segments(stream, media_url, target, partial)
message = "HLS playlist uses extensionless provider segments; skipping direct ffmpeg."
print(message, flush=True)
emit_progress(
phase="preflight",
message=message,
episode=episode_number,
episode_index=episode_index,
episode_total=episode_total,
percent=0,
)
code = download_hls_segments(
stream,
media_url,
target,
partial,
episode_number=episode_number,
episode_index=episode_index,
episode_total=episode_total,
)
if code == 0:
return 0
return code
@@ -510,7 +567,15 @@ def download_episode(stream, target):
return 0
if stream.get("isM3U8") or ".m3u8" in str(input_url):
try:
code = download_hls_segments(stream, input_url, target, partial)
code = download_hls_segments(
stream,
input_url,
target,
partial,
episode_number=episode_number,
episode_index=episode_index,
episode_total=episode_total,
)
if code == 0:
return 0
except Exception as exc:
@@ -567,7 +632,7 @@ def main():
print(f"Title: {series_title}")
print(f"Episodes: {', '.join(str(ep.get('number')) for ep in wanted)}")
exit_code = 0
for ep in wanted:
for episode_index, ep in enumerate(wanted, start=1):
number = ep.get("number")
ep_id = ep.get("id")
padded = f"{int(float(number)):02d}" if str(number).replace(".", "", 1).isdigit() else str(number)
@@ -575,13 +640,46 @@ def main():
target = output_dir / f"{basename}.mp4"
last_error = None
downloaded = False
emit_progress(
phase="episode",
message=f"Starting episode {number} ({episode_index}/{len(wanted)}).",
episode=number,
episode_index=episode_index,
episode_total=len(wanted),
percent=0,
)
for active_provider, _active_show_id, active_ep in provider_episode_candidates(provider, provider_show_id, args.title or info.get("title"), ep):
try:
active_ep_id = active_ep.get("id")
print(f"Resolving episode {number} on {active_provider} ({args.mode}, {args.quality})...")
emit_progress(
phase="resolve",
message=f"Resolving episode {number} on {active_provider} ({args.mode}, {args.quality}).",
episode=number,
episode_index=episode_index,
episode_total=len(wanted),
provider=active_provider,
percent=0,
)
print(f"Resolving episode {number} on {active_provider} ({args.mode}, {args.quality})...", flush=True)
stream = bridge("resolve", active_provider, active_ep_id, args.mode, args.quality)
print(f"Downloading episode {number} from {active_provider} {stream.get('quality') or 'auto'}...")
code = download_episode(stream, target)
emit_progress(
phase="download",
message=f"Downloading episode {number} from {active_provider} {stream.get('quality') or 'auto'}.",
episode=number,
episode_index=episode_index,
episode_total=len(wanted),
provider=active_provider,
quality=stream.get("quality") or "auto",
percent=0,
)
print(f"Downloading episode {number} from {active_provider} {stream.get('quality') or 'auto'}...", flush=True)
code = download_episode(
stream,
target,
episode_number=number,
episode_index=episode_index,
episode_total=len(wanted),
)
if code != 0:
last_error = RuntimeError(f"ffmpeg failed on {active_provider} with exit code {code}")
print(str(last_error), file=sys.stderr)
@@ -591,7 +689,15 @@ def main():
print(f"Saved subtitle: {subtitle_path.name}")
except Exception as exc:
print(f"Subtitle download skipped: {exc}")
print(f"Saved: {target.name}")
emit_progress(
phase="done",
message=f"Episode {number} saved.",
episode=number,
episode_index=episode_index,
episode_total=len(wanted),
percent=100,
)
print(f"Saved: {target.name}", flush=True)
downloaded = True
break
except Exception as exc: