feat(ui): album art thumbs in track lists via standard cover pipeline (#1280)

This commit is contained in:
cucadmuh
2026-07-13 04:16:55 +03:00
committed by GitHub
parent ad5d88d105
commit 9ca762dfd6
69 changed files with 1196 additions and 69 deletions
@@ -110,6 +110,20 @@ pub fn resolve_album_cover(
.map(str::trim)
.filter(|s| !s.is_empty())
.unwrap_or(album);
// Navidrome track-only libraries: keep consensus `mf-*` fetch (library picks the
// first track per album) while the disk slot stays album-scoped.
if !distinct_disc_covers && fetch.starts_with("mf-") && fetch != album {
return Some(CoverEntry {
cache_kind: "album",
cache_entity_id: album.to_string(),
fetch_cover_art_id: fetch.to_string(),
});
}
let fetch_id = if !distinct_disc_covers && fetch == album {
format!("al-{album}_0")
} else {
fetch.to_string()
};
let cache_entity_id = if distinct_disc_covers && fetch != album {
fetch.to_string()
} else {
@@ -118,7 +132,7 @@ pub fn resolve_album_cover(
Some(CoverEntry {
cache_kind: "album",
cache_entity_id,
fetch_cover_art_id: fetch.to_string(),
fetch_cover_art_id: fetch_id,
})
}
@@ -271,6 +285,19 @@ mod tests {
assert_eq!(e.cache_entity_id, "mf-d2");
}
#[test]
fn resolve_album_keeps_mf_fetch_on_album_bucket() {
let e = resolve_album_cover("al-box", Some("mf-track"), false).unwrap();
assert_eq!(e.cache_entity_id, "al-box");
assert_eq!(e.fetch_cover_art_id, "mf-track");
}
#[test]
fn resolve_album_navidrome_bare_id() {
let e = resolve_album_cover("2lsdR1ogDKiFcAD6Pcvk4f", None, false).unwrap();
assert_eq!(e.fetch_cover_art_id, "al-2lsdR1ogDKiFcAD6Pcvk4f_0");
}
fn test_server_dir(label: &str) -> std::path::PathBuf {
let base = std::env::temp_dir().join(format!("psysonic-cover-layout-{label}"));
let _ = std::fs::remove_dir_all(&base);
@@ -606,7 +606,10 @@ mod tests {
.unwrap();
assert_eq!(batch.cover_ids, vec!["0DurV2S7arIOBQVEknOPWX".to_string()]);
assert_eq!(batch.items[0].cache_kind, "album");
assert_eq!(batch.items[0].fetch_cover_art_id, "0DurV2S7arIOBQVEknOPWX");
assert_eq!(
batch.items[0].fetch_cover_art_id,
"al-0DurV2S7arIOBQVEknOPWX_0"
);
}
#[test]
@@ -107,7 +107,9 @@ pub fn resolve_album_cover_entry(
)
.optional()
})? {
None => return Ok(None),
None => {
return track_only_album_backfill_entry(store, library_server_id, album_id);
}
Some(v) => v,
};
// Album rows synced without a cover id (created from a starred/tag/browse path
@@ -409,11 +411,9 @@ mod tests {
assert_eq!(e.fetch_cover_art_id, "al-ca78bec6_60fc987f");
}
// #1252: an album row synced without a cover id (e.g. via a starred/tag path)
// must fall back to the album's track cover, not the bare album id, so the
// detail header and browse tiles resolve the same fetch id.
// #1252: album row without cover id — use first track mf when present.
#[test]
fn resolve_album_falls_back_to_track_cover_when_row_cover_null() {
fn resolve_album_falls_back_to_track_mf_when_row_cover_null() {
let store = LibraryStore::open_in_memory();
seed_album(&store, "srv", "al-nocover", None);
seed_track(&store, "srv", "tr1", "al-nocover", 1, Some("mf-cover"));
@@ -424,6 +424,27 @@ mod tests {
assert_eq!(e.fetch_cover_art_id, "mf-cover");
}
#[test]
fn resolve_album_without_album_row_uses_track_only_backfill() {
let store = LibraryStore::open_in_memory();
seed_track(
&store,
"srv",
"tr1",
"2lsdR1ogDKiFcAD6Pcvk4f",
1,
Some("mf-fis8alFzjMGlcncxrvmpUV_67afa52a"),
);
let e = resolve_album_cover_entry(&store, "srv", "2lsdR1ogDKiFcAD6Pcvk4f")
.unwrap()
.unwrap();
assert_eq!(e.cache_entity_id, "2lsdR1ogDKiFcAD6Pcvk4f");
assert_eq!(
e.fetch_cover_art_id,
"mf-fis8alFzjMGlcncxrvmpUV_67afa52a"
);
}
#[test]
fn resolve_album_keeps_row_cover_over_track_cover() {
let store = LibraryStore::open_in_memory();