feat(api): expose active-track segment counts and transfer speed in job progress (#124)

The job progress already reports a weighted percentage, phase and track counts.
The per-track tee in build_job_progress_callables already tracks each track's
segment counts and sees the downloader's speed string, so also surface the
currently-downloading track's segments_done / segments_total and speed in the
emitted progress (and on the job + its to_dict). Lets a client show
"197/663 segments, 3.2 MB/s" instead of only a percentage.
This commit is contained in:
AviDev
2026-06-21 12:40:16 -06:00
committed by GitHub
parent 733cb02391
commit 40eafce17b
2 changed files with 29 additions and 1 deletions
+15
View File
@@ -104,6 +104,10 @@ class DownloadJob:
completed_tracks: int = 0
total_tracks: int = 0
active_tracks: List[str] = field(default_factory=list)
# Segment counts and transfer speed of the track downloading now (granular display)
segments_done: float = 0.0
segments_total: float = 0.0
speed: Optional[str] = None
# Subtitles skipped under skip_subtitle_errors (non-fatal). Each entry is a dl.SkippedSubtitle
# dict (id / language / title) so a client can report which weren't available.
@@ -125,6 +129,9 @@ class DownloadJob:
"completed_tracks": self.completed_tracks,
"total_tracks": self.total_tracks,
"active_tracks": self.active_tracks,
"segments_done": self.segments_done,
"segments_total": self.segments_total,
"speed": self.speed,
"skipped_subtitles": self.skipped_subtitles,
}
@@ -756,6 +763,14 @@ class DownloadQueueManager:
job.completed_tracks = int(progress_data["completed_tracks"])
if "active_tracks" in progress_data:
job.active_tracks = list(progress_data["active_tracks"])
for _sk in ("segments_done", "segments_total"):
if _sk in progress_data:
try:
setattr(job, _sk, float(progress_data[_sk]))
except (TypeError, ValueError):
pass
if progress_data.get("speed"):
job.speed = str(progress_data["speed"])
if progress_data.get("skipped_subtitles"):
job.skipped_subtitles = progress_data["skipped_subtitles"]
if "progress" in progress_data:
+14 -1
View File
@@ -6,7 +6,7 @@ a bitrate-weighted percentage, track counts, and the labels of the tracks downlo
from __future__ import annotations
from typing import Any, Callable
from typing import Any, Callable, Optional
from unshackle.core.constants import AnyTrack
@@ -70,6 +70,9 @@ def build_job_progress_callables(
fractions = [0.0] * total
done = [False] * total
started = [False] * total
seg_done = [0.0] * total
seg_total = [0.0] * total
speeds: list[Optional[str]] = [None] * total
def emit() -> None:
completed = sum(done)
@@ -82,6 +85,8 @@ def build_job_progress_callables(
phase += f" (+{len(active) - 3} more)"
else:
phase = f"downloading {completed}/{total} tracks"
# segment counts and transfer speed of the track downloading now, for a granular display
active_i = next((i for i in range(total) if started[i] and not done[i]), None)
sink(
{
"progress": progress,
@@ -89,6 +94,9 @@ def build_job_progress_callables(
"completed_tracks": completed,
"total_tracks": total,
"active_tracks": active,
"segments_done": seg_done[active_i] if active_i is not None else 0.0,
"segments_total": seg_total[active_i] if active_i is not None else 0.0,
"speed": speeds[active_i] if active_i is not None else None,
"status": "downloading",
}
)
@@ -104,6 +112,11 @@ def build_job_progress_callables(
counts["completed"] = kwargs["completed"]
if "advance" in kwargs:
counts["completed"] += kwargs["advance"]
seg_done[index] = counts["completed"]
seg_total[index] = counts["total"]
_dl = kwargs.get("downloaded")
if isinstance(_dl, str) and _dl.endswith("/s"):
speeds[index] = _dl
if kwargs.get("downloaded") in JOB_PROGRESS_TERMINAL_STATES:
done[index] = True
fractions[index] = 1.0