Add queue download progress
This commit is contained in:
+119
-13
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user