fix(locallibrary): show offline/folder album covers + add source badge
Two fixes from the first Albums-grid test pass. Offline/folder covers (the reported "cover exists in the folder but isn't shown"): some albums — offline-cached CMAF bundles and folder downloads — have no artwork_path in the index even though a cover.jpg sits in the track folder (older offline-cache downloads didn't backfill it). Add a frontend- agnostic LibraryDatabase::resolve_album_cover_fallback(group_key) that finds a representative track, derives its folder (the path itself for a CMAF bundle dir, else the parent), and returns <folder>/cover.jpg when it exists. The Slint Albums mapper calls it whenever the album has no artwork. Verified against the live index: it resolves the folder covers that exist on disk (FLAC-layout + CMAF-with-cover). Older CMAF bundles that never downloaded a cover (segments only) still show the placeholder — those need a cover re-fetch (offline-cache maintenance), tracked separately. Source badge: the Local Library album card now shows the always-visible source badge inside the artwork (bottom-right) — a hard-drive glyph for local files, the Qobuz logo for offline copies, the Plex logo for Plex. Added AlbumCard.show-source-badge (default off; threaded through AlbumCollectionView /AlbumGrid) + AlbumCard.source; album_map now carries the real source and map_local_album maps user->local / qobuz_download / plex. Small rounded square, not a pill (ADR-008). Logos copied into the Slint icon assets. Built green (cargo check -p qbz-slint); runtime smoke-test pending. cc
This commit is contained in:
8 files changed
+834
-8
No files matched your search
@@ -26,6 +26,9 @@ pub struct AlbumCard {
|
||||
pub artwork_url: String,
|
||||
// List-row extras (AlbumListRow columns; ignored by the grid card).
|
||||
pub release_type: String, // "Album" | "EP" | "Single" (TYPE column)
|
||||
// "local" | "qobuz_download" | "plex" | "" — SOURCE column + the
|
||||
// always-visible source badge on the Local Library grid card.
|
||||
pub source: String,
|
||||
pub quality_detail: String, // "24-bit / 96 kHz"
|
||||
pub track_count: String, // "12"
|
||||
pub plain_year: String, // "1973"
|
||||
@@ -94,6 +97,9 @@ pub fn map_album(album: Album) -> AlbumCard {
|
||||
quality_label,
|
||||
artwork_url: album.image.best().cloned().unwrap_or_default(),
|
||||
release_type,
|
||||
// Qobuz album surfaces (Discover / Favorites / Label) hide the SOURCE
|
||||
// column and the badge, so leave it empty (preserves prior behavior).
|
||||
source: String::new(),
|
||||
quality_detail,
|
||||
track_count,
|
||||
plain_year,
|
||||
@@ -189,6 +195,7 @@ pub fn to_item(card: AlbumCard) -> AlbumCardItem {
|
||||
// List-row extras — feed the AlbumListRow columns (TYPE / QUALITY /
|
||||
// TRACKS / YEAR) for the list view toggle.
|
||||
release_type: card.release_type.into(),
|
||||
source: card.source.into(),
|
||||
quality_detail: card.quality_detail.into(),
|
||||
track_count: card.track_count.into(),
|
||||
plain_year: card.plain_year.into(),
|
||||
|
||||
@@ -109,6 +109,14 @@ pub fn map_local_album(a: qbz_library::LocalAlbum) -> crate::album_map::AlbumCar
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
// Real source for the SOURCE column + the always-visible card badge:
|
||||
// user files -> local, offline copies -> qobuz_download, Plex -> plex.
|
||||
let source = match a.source.as_str() {
|
||||
"plex" => "plex",
|
||||
"qobuz_download" => "qobuz_download",
|
||||
_ => "local",
|
||||
}
|
||||
.to_string();
|
||||
crate::album_map::AlbumCard {
|
||||
id: a.id,
|
||||
title: a.title,
|
||||
@@ -120,6 +128,7 @@ pub fn map_local_album(a: qbz_library::LocalAlbum) -> crate::album_map::AlbumCar
|
||||
quality_label,
|
||||
artwork_url: a.artwork_path.unwrap_or_default(),
|
||||
release_type: crate::album_map::classify_release_type(Some(a.track_count)).to_string(),
|
||||
source,
|
||||
quality_detail,
|
||||
track_count,
|
||||
plain_year: year,
|
||||
@@ -178,8 +187,8 @@ fn fetch_albums_page(
|
||||
} else {
|
||||
Some(trimmed)
|
||||
};
|
||||
let page = crate::library_db::with_db(|db| {
|
||||
db.get_albums_metadata_page(
|
||||
crate::library_db::with_db(|db| {
|
||||
let page = db.get_albums_metadata_page(
|
||||
offset,
|
||||
ALBUMS_PAGE,
|
||||
search_opt,
|
||||
@@ -193,12 +202,27 @@ fn fetch_albums_page(
|
||||
// filtering and Plex are their own later slices.
|
||||
false,
|
||||
None,
|
||||
)
|
||||
})?;
|
||||
let total = page.total;
|
||||
let has_more = offset + (page.albums.len() as u64) < total;
|
||||
let cards = page.albums.into_iter().map(map_local_album).collect();
|
||||
Some((cards, total, has_more))
|
||||
)?;
|
||||
let total = page.total;
|
||||
let has_more = offset + (page.albums.len() as u64) < total;
|
||||
let cards: Vec<crate::album_map::AlbumCard> = page
|
||||
.albums
|
||||
.into_iter()
|
||||
.map(|a| {
|
||||
let mut card = map_local_album(a);
|
||||
// Offline-cached / folder albums sometimes have no backfilled
|
||||
// artwork_path though a cover.jpg sits in the track folder —
|
||||
// resolve it so the cover that exists on disk actually shows.
|
||||
if card.artwork_url.is_empty() {
|
||||
if let Some(cover) = db.resolve_album_cover_fallback(&card.id) {
|
||||
card.artwork_url = cover;
|
||||
}
|
||||
}
|
||||
card
|
||||
})
|
||||
.collect();
|
||||
Ok((cards, total, has_more))
|
||||
})
|
||||
}
|
||||
|
||||
/// Build local-cover artwork jobs for a page of cards. The job url carries
|
||||
|
||||
Reference in new issue
Block a user