fix(worker): smooth Qobuz download progress via streamrip byte hook

streamrip reports progress only per concurrent track, so Job.downloadProgress
came solely from the file-count poller — 0% until every track landed, then a
jump to 100%. Patch streamrip.media.track.get_progress_callback to aggregate
per-track byte deltas over an estimated album total (avg started-track size ×
len(album.tracks)) into one smooth, generally-climbing 0..1 fraction routed
through the pipeline's on_progress. Capped at 0.99; the pipeline sets 1.0 on
completion. No signature changes — track count read from the resolved album.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jonathan
2026-07-15 02:37:08 +02:00
parent c7bab8e997
commit 0325467aba
2 changed files with 104 additions and 1 deletions
+41
View File
@@ -0,0 +1,41 @@
from lyra_worker.adapters._streamrip import _ProgressAggregator
def _record():
seen: list[float] = []
return seen, seen.append
def test_equal_size_tracks_aggregate_monotonically_to_near_one():
# 4 tracks of 100 bytes each, all started up front (concurrency covers the album), then
# each downloaded in 25-byte chunks. Fraction must climb monotonically 0→~1.
seen, cb = _record()
agg = _ProgressAggregator(4, cb)
for _ in range(4):
agg.start_track(100)
for _ in range(16): # 16 chunks * 25 bytes = 400 bytes = full album
agg.advance(25)
assert seen == sorted(seen) # monotonic non-decreasing
assert all(0.0 <= f <= 0.99 for f in seen) # never negative, capped at 0.99
assert seen[-1] == 0.99 # reaches the cap when all bytes are in
assert seen[len(seen) // 2] > 0.3 # genuinely climbs mid-download (no 0→100 jump)
def test_estimate_uses_track_count_before_all_tracks_start():
# Only the first of 4 tracks has started; with a known track count the estimate already
# spans the whole album, so finishing track 1 reports ~1/4, not ~1/1.
seen, cb = _record()
agg = _ProgressAggregator(4, cb)
agg.start_track(100)
agg.advance(100) # track 1 fully downloaded
assert abs(seen[-1] - 0.25) < 0.01
def test_zero_or_missing_sizes_never_emit_and_never_divide_by_zero():
seen, cb = _record()
agg = _ProgressAggregator(0, cb)
agg.advance(50) # no track started yet -> no emit
agg.start_track(0) # a track with unknown size
agg.advance(50) # est_total still 0 -> still no emit, no crash
assert seen == []