fix: cover art stuck on the previous album with native streaming - #406
Conversation
`current_playback_snapshot()` sourced `image_url` from the polled `/v1/me/player` context even while native librespot metadata was authoritative for title, artist and album. That context lags librespot by seconds after a skip, and `get_current_playback()` deliberately refuses to overwrite it while stale, so the art stayed pinned to the previous album. The runner's cover-art loop only refetches when the resolved URL changes, so an unchanged stale URL meant no refetch at all. A natively queued track makes it worse: it plays via a direct `player.load` that Spirc never reports, so the context is not merely late, it can stay wrong for the whole song. librespot's own `TrackChanged` payload already carries the answer. `audio_item.covers` is a widest-first list of fully resolved `https://i.scdn.co/image/<file_id>` URLs, the same CDN form the Web API returns. Store the widest into a new `NativeTrackInfo.image_url` at the event boundary and prefer it in the snapshot, falling back to the context item when librespot reports no covers (local files, whose art is embedded in the file). No extra network calls, and no dependence on the poll loop. Empty cover URLs are filtered out: librespot substitutes into a session-supplied url template, so a blank template would yield `Some("")` and defeat the fallback instead of deferring to it. Fixes #402
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthrough
ChangesNative artwork synchronization
Estimated code review effort: 2 (Simple) | ~15 minutes 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
✨ Simplify code
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/infra/player/events.rs`:
- Around line 688-700: Update the cover selection in the audio item mapping to
choose the largest entry from audio_item.covers rather than assuming .first() is
widest. Preserve the existing URL cloning and empty-string filtering, and use
the cover dimensions or established size metadata to compare candidates.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 6b40cf32-ba85-4c39-9821-569bbbadc4a9
📒 Files selected for processing (5)
src/core/app.rssrc/infra/media_metadata.rssrc/infra/player/events.rssrc/infra/scripting/tests.rssrc/tui/runner.rs
CodeRabbit read `.first()` on `audio_item.covers` as assuming an order that is not guaranteed. It is guaranteed here: the pinned librespot fork sorts covers descending by width in `get_covers` (metadata/src/audio/item.rs) and the filter that follows preserves that order. The comment asserted widest-first without saying where the ordering comes from, so cite the source instead of changing the selection. No behavior change.
The stale-context read shipped in v0.40.2, so this is a fix to released behavior and belongs under Unreleased > Fixed.
Summary
Fixes #402: with native streaming, advancing through the queue left the cover art
(and the
image_urlbehind Discord RPC, MPRIS and now-playing sync) showing theprevious track's album.
current_playback_snapshot()sourcedimage_urlfrom the polled/v1/me/playercontext even while native librespot metadata was authoritative for title, artist
and album. That context lags librespot by seconds after a skip, and
get_current_playback()deliberately refuses to overwrite it while stale, so theart stayed pinned to the previous album. Because the runner's cover-art loop only
refetches when the resolved URL changes, an unchanged stale URL meant no refetch
at all. A natively queued track makes it worse: it plays via a direct
player.loadthat Spirc never reports, so the context is not merely late, it canstay wrong for the whole song.
librespot's
TrackChangedpayload already carries the answer.audio_item.coversis a widest-first list of fully resolved
https://i.scdn.co/image/<file_id>URLs,the same CDN form the Web API returns. This stores the widest into a new
NativeTrackInfo.image_urlat the event boundary and prefers it in the snapshot,falling back to the context item when librespot reports no covers. No extra
network calls and no dependence on the poll loop.
The issue reporter kindly included an alternative patch that adds
IoEvent::FetchNativeTrackArtcallingGET /v1/tracks/{id}from insideget_current_playback(). I did not take that route:get_current_playback()runson a poll loop, so it would re-dispatch that fetch on every poll for as long as the
context stays stale, which for a queued track is the whole song. It also keys off
app.last_track_idrather than the event that actually knows which track started.Testing
cargo fmt --allcargo clippy -- -D warnings(exit 0)cargo clippy --no-default-features --features telemetry -- -D warnings(exit 0)cargo test-> 756 passed, 0 failedcargo test --no-default-features --features telemetry-> 496 passed, 0 failedThree new tests in
media_metadata.rs:native_track_art_wins_over_a_stale_context_item(the bug)native_track_falls_back_to_context_art_without_librespot_covers(the fallback)context_art_is_used_once_native_info_is_cleared(the handoff boundary)Manually verified in the TUI on the full
cargo runbuild against a live Spotifysession: the cover art now follows the current track when advancing through the
queue.
Additional notes
Known limits and follow-up work, none introduced by this change:
api_confirms_native_info_is_currentaccepts a bare title match before checkingIDs, asserted intentional by the existing
api_confirms_native_info_when_names_matchtest. When two consecutive tracks share a title but differ in album, native info is
cleared early and the snapshot reverts to the API item, so the new field is never
read. Left alone deliberately: that shortcut most likely exists for Spotify track
relinking (same track, different ID per market), so changing it risks a regression
outside this issue.
context_art_is_used_once_native_info_is_clearedpins theboundary.
about direct
player.loads, i.e. server-side state rather than the local snapshot.covers: vec, so they still fall through to whatever the Spotify context
holds. Pre-existing gap, worth its own issue.
Summary by CodeRabbit