From b38d6bd867f070e8608aaf9fcc33ca515577123b Mon Sep 17 00:00:00 2001 From: vicrodh <103399283+vicrodh@users.noreply.github.com> Date: Fri, 21 Aug 2026 18:57:42 -0600 Subject: [PATCH] fix(player): attach a cover to queued rows that were enqueued without one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sibling of patch_plex_quality, for the other thing a row can learn after it was enqueued. A disc carries no artwork — it is fetched, and the fetch outlives the click that started playback — so a CD queued at second two is still holding artwork_url: None when the cover lands at second ten. Every consumer downstream reads the QUEUE row (the now-playing bar, the miniplayer, MPRIS through art_url_for), so patching the session store alone left all of them blank. Rows that already have art are left alone: this fills a gap rather than overwriting a cover somebody else resolved. It answers two questions separately, because they drive different work — any patch makes the queue panel's thumbnails stale, only the current one needs the now-playing stamp re-pushed. cc --- crates/qbz-core/src/core.rs | 17 ++++++++++++++ crates/qbz-player/src/queue.rs | 42 ++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/crates/qbz-core/src/core.rs b/crates/qbz-core/src/core.rs index d683850d5..24b571801 100644 --- a/crates/qbz-core/src/core.rs +++ b/crates/qbz-core/src/core.rs @@ -902,6 +902,23 @@ impl QbzCore { current_patched } + /// Attach a resolved cover to any queued track in `ids` that has none. + /// Frontend-agnostic hook for art that resolves AFTER the row was + /// enqueued — a disc's cover is fetched, and the fetch outlives the click + /// that started playback. Returns true if the CURRENT track was patched; + /// the caller then re-pushes the now-playing stamp. + pub async fn patch_queue_artwork(&self, ids: &[u64], url: &str) -> bool { + let queue = self.queue.write().await; + let (any, current_patched) = queue.patch_artwork(ids, url); + if any { + self.emit(CoreEvent::QueueUpdated { + state: queue.get_state(), + }) + .await; + } + current_patched + } + // ==================== Search & Catalog ==================== /// Search for albums diff --git a/crates/qbz-player/src/queue.rs b/crates/qbz-player/src/queue.rs index 8050c06b9..b3fe70ac9 100644 --- a/crates/qbz-player/src/queue.rs +++ b/crates/qbz-player/src/queue.rs @@ -119,6 +119,48 @@ impl QueueManager { current_patched } + /// Attach an artwork url to every queued track whose id is in `ids`. + /// + /// The sibling of [`Self::patch_plex_quality`], for the other thing a row + /// can learn AFTER it was enqueued: a cover. A disc carries none — it is + /// fetched, and the fetch outlives the click that started playback — so a + /// CD queued at second two is still holding `artwork_url: None` when the + /// cover lands at second ten. Every consumer downstream reads the QUEUE + /// row (the now-playing bar, the miniplayer, MPRIS through + /// `art_url_for`), so patching the session store alone leaves all of them + /// blank. + /// + /// Rows that already have art are left alone: this fills a gap, it does + /// not overwrite a cover somebody else resolved. + /// + /// Returns `(any patched, the CURRENT track was patched)`. The two are + /// separate answers because they drive different work: any patch makes the + /// queue panel's thumbnails stale, while only the current one needs the + /// now-playing stamp re-pushed. + pub fn patch_artwork(&self, ids: &[u64], url: &str) -> (bool, bool) { + if ids.is_empty() || url.is_empty() { + return (false, false); + } + let mut state = self.state.lock().unwrap(); + let current_idx = state.current_index; + let mut any = false; + let mut current_patched = false; + for (idx, track) in state.tracks.iter_mut().enumerate() { + if track.artwork_url.as_deref().is_some_and(|u| !u.is_empty()) { + continue; + } + if !ids.contains(&track.id) { + continue; + } + track.artwork_url = Some(url.to_string()); + any = true; + if current_idx == Some(idx) { + current_patched = true; + } + } + (any, current_patched) + } + /// Add a track to the end of the queue pub fn add_track(&self, track: QueueTrack) { let mut state = self.state.lock().unwrap();