Bruno Mars "24K Magic" track 1 repeatedly downloaded as a 0-byte file. The
worker log showed the real cause: streamrip's track download hit
HTTPException("got more than 100 headers") — Qobuz's CDN returns >100 response
headers for some tracks and Python's http.client rejects them at the default
_MAXHEADERS=100, so streamrip skips the track and leaves an empty placeholder.
- Raise http.client._MAXHEADERS to 1000 on import of the Qobuz adapter, so those
responses parse and the track downloads.
- _count_staged_audio now skips 0-byte files (a failed-track placeholder is not a
real track) — improves the download-progress estimate and stops counting dead
tracks. (A verify-level "reject 0-byte tracks" safety net is a follow-up under
#16 per-track duration matching — it needs the pipeline fakes to write real
files, deferred to avoid disproportionate churn here.)
worker 223 tests / 7-skip.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Each search/download called asyncio.run, creating and closing a fresh event loop.
streamrip's module-global download-concurrency Semaphore binds to the loop on first
use, so the 2nd+ download reused a Semaphore bound to a closed loop -> "Semaphore is
bound to a different event loop", tracks failed, album arrived short -> "incomplete
download" / needs_attention. Run all streamrip coroutines on one persistent loop.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- worker had no IPv6 route -> Qobuz CDN (dual-stack) failed ~half the tracks
with 'Network is unreachable'; disable IPv6 in the container (IPv4 only).
- StreamripClient counted metadata tracks (always full) -> a partial download
falsely imported; now count the actual files written (short count -> needs_attention).
- streamrip writes into a nested 'Artist - Album [..]/' folder the tagger never saw;
flatten audio files up into the album dir so tagging/organization runs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Qobuz's email/password API login returns 401 even with valid credentials
(a known Qobuz limitation, unaffected by streamrip version). Add settings
fields for a Qobuz user ID + auth token (token stored encrypted); when both
are present StreamripClient uses use_auth_token=True, else falls back to
email/password.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
streamrip's email/password login expects the MD5 digest, not plaintext
(caused 'Invalid credentials'). Also wrap login() so streamrip's exception —
which embeds the email/password/app_id — can never reach the worker logs;
re-raise a clean credential-free RuntimeError instead.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>