Issue #637: a Qobuz outage + forced re-login left a user's account
transiently 403'd, and the no-backoff prefetch retry storm escalated it
into a per-IP edge/WAF block (the "error decoding response body" lines are
that non-JSON WAF body hitting a bare .json()). The bundle bump b019->b034
is exonerated: extraction yields byte-identical app_id/secret/private_key.
- ForbiddenBreaker: after repeated 403s the hot streaming/favorites paths
short-circuit without touching the network, with an exponential cooldown
and a half-open probe; success resets it.
- get_stream_url: typed 401/403 + body preview; abort the quality fallback
loop on 403/401/breaker-open instead of firing all five qualities.
- CMAF session/start + file/url: feed the breaker, map 403.
- get_favorites + qws/createToken: check status before .json() so a 403
surfaces as a 403, not a misleading "error decoding response body".
- Prefetch: per-track failure cooldown (negative cache) so a failed track
is not re-hammered every queue tick.
- Audible path: a 403/back-off no longer counts as an "unavailable" skip;
stop cleanly with a toast.
- QBZ_FORCE_403 test hook to smoke the back-off path on a healthy account.
- Header action-button tooltips for AlbumView/ArtistView/PlaylistView,
translated across all 8 locales.
cc
Cover art now goes through the QBZ image cache instead of being
downloaded directly every time, matching the Tauri behavior.
- Moves ImageCacheService / ImageCacheStats into the qbz-cache crate
(LRU disk cache, MD5-keyed, SQLite index). src-tauri/image_cache.rs
re-exports it and keeps the Tauri managed-state wrapper, so the Tauri
app stays behavior-identical and both shells share one implementation
and one on-disk cache (~/.cache/qbz/images).
- qbz-slint artwork.rs checks the cache first, downloads and stores on
a miss, and trims the cache to 200MB on startup.
cc
Create qbz-cache crate with:
- AudioCache: L1 in-memory LRU cache (~300MB)
- PlaybackCache: L2 disk-based cache (~500MB)
- LRU eviction with spillover from L1 to L2
src-tauri now re-exports from qbz-cache for backwards compatibility.
This enables TUI/headless clients to use caching without Tauri.