fix(discover): preserve sources on existing Most Played album cards
Use one mapper for the carousel and full history page. Carry the stored routing source and normalized badge vocabulary through both surfaces, including existing rows, downloads and media servers. Cover existing snapshots and catalog aliases in the Qt suite; all 741 tests pass with temporary XDG data. cx
This commit is contained in:
2 files changed
+64
-32
No files matched your search
@@ -777,25 +777,7 @@ fn load_history(generation: u64) {
|
|||||||
|| r.title.to_lowercase().contains(&needle)
|
|| r.title.to_lowercase().contains(&needle)
|
||||||
|| r.artist.to_lowercase().contains(&needle)
|
|| r.artist.to_lowercase().contains(&needle)
|
||||||
})
|
})
|
||||||
.map(|r| HomeCard {
|
.map(crate::home_qt::map_played_album)
|
||||||
is_pinned: crate::sidebar_qt::is_pinned("album", &r.album_id),
|
|
||||||
// Heart, same seed as every other album card — this
|
|
||||||
// page's rows are built here rather than through
|
|
||||||
// `home_qt::map_*`, so it needs its own stamp.
|
|
||||||
is_favorite: crate::fav_cache_qt::is_album_favorite(&r.album_id),
|
|
||||||
id: r.album_id,
|
|
||||||
title: r.title,
|
|
||||||
artist: r.artist,
|
|
||||||
artist_id: r.artist_id,
|
|
||||||
year: r.year,
|
|
||||||
quality_tier: r.quality_tier,
|
|
||||||
quality_label: r.quality_label,
|
|
||||||
art_url: r.artwork_url,
|
|
||||||
// The 20px the 286px card is taller than every other
|
|
||||||
// album card (AlbumCard.slint:508).
|
|
||||||
plays: r.plays,
|
|
||||||
..HomeCard::default()
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
} else {
|
} else {
|
||||||
crate::recently_qt::load_albums()
|
crate::recently_qt::load_albums()
|
||||||
|
|||||||
@@ -878,19 +878,7 @@ fn build_candidates(
|
|||||||
// (`album_play_history`, the shared per-app SQLite store). No network.
|
// (`album_play_history`, the shared per-app SQLite store). No network.
|
||||||
let most_played: Vec<HomeCard> = qbz_app::settings::album_play_history::top_albums(20)
|
let most_played: Vec<HomeCard> = qbz_app::settings::album_play_history::top_albums(20)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|r| HomeCard {
|
.map(map_played_album)
|
||||||
is_pinned: crate::sidebar_qt::is_pinned("album", &r.album_id),
|
|
||||||
is_favorite: crate::fav_cache_qt::is_album_favorite(&r.album_id),
|
|
||||||
id: r.album_id,
|
|
||||||
title: r.title,
|
|
||||||
artist: r.artist,
|
|
||||||
artist_id: r.artist_id,
|
|
||||||
year: r.year,
|
|
||||||
quality_tier: r.quality_tier,
|
|
||||||
quality_label: r.quality_label,
|
|
||||||
art_url: r.artwork_url,
|
|
||||||
..HomeCard::default()
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
if !most_played.is_empty() {
|
if !most_played.is_empty() {
|
||||||
out.push(HomeSection {
|
out.push(HomeSection {
|
||||||
@@ -2080,6 +2068,41 @@ pub(crate) fn map_flat_album(album: Album) -> HomeCard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// History stores keep their raw routing source. Badge data uses the shared
|
||||||
|
/// display vocabulary, including old rows, without requiring another play.
|
||||||
|
fn history_source_badges(source: &str) -> Vec<String> {
|
||||||
|
if source.is_empty() {
|
||||||
|
return Vec::new(); // Legacy history means catalog, never inferred local.
|
||||||
|
}
|
||||||
|
vec![if source == "qobuz_purchase" {
|
||||||
|
source.to_string()
|
||||||
|
} else {
|
||||||
|
qbz_source::SourceBadge::from_word(Some(source))
|
||||||
|
.as_str()
|
||||||
|
.into()
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shared by the Most Played carousel and its complete, filtered page.
|
||||||
|
pub(crate) fn map_played_album(r: qbz_app::settings::album_play_history::AlbumPlayRow) -> HomeCard {
|
||||||
|
HomeCard {
|
||||||
|
is_pinned: crate::sidebar_qt::is_pinned("album", &r.album_id),
|
||||||
|
is_favorite: crate::fav_cache_qt::is_album_favorite(&r.album_id),
|
||||||
|
sources: history_source_badges(&r.source),
|
||||||
|
source: r.source,
|
||||||
|
id: r.album_id,
|
||||||
|
title: r.title,
|
||||||
|
artist: r.artist,
|
||||||
|
artist_id: r.artist_id,
|
||||||
|
year: r.year,
|
||||||
|
quality_tier: r.quality_tier,
|
||||||
|
quality_label: r.quality_label,
|
||||||
|
art_url: r.artwork_url,
|
||||||
|
plays: r.plays,
|
||||||
|
..HomeCard::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Map one recently-played album (local history) onto a card. The stored ISO
|
/// Map one recently-played album (local history) onto a card. The stored ISO
|
||||||
/// release date is localized here exactly as the discover cards do.
|
/// release date is localized here exactly as the discover cards do.
|
||||||
pub(crate) fn map_recent_album(a: crate::recently_qt::RecentAlbum) -> HomeCard {
|
pub(crate) fn map_recent_album(a: crate::recently_qt::RecentAlbum) -> HomeCard {
|
||||||
@@ -2099,6 +2122,7 @@ pub(crate) fn map_recent_album(a: crate::recently_qt::RecentAlbum) -> HomeCard {
|
|||||||
},
|
},
|
||||||
quality_tier: a.quality_tier,
|
quality_tier: a.quality_tier,
|
||||||
quality_label: a.quality_label,
|
quality_label: a.quality_label,
|
||||||
|
sources: history_source_badges(&a.source),
|
||||||
source: a.source,
|
source: a.source,
|
||||||
art_url: a.artwork_url,
|
art_url: a.artwork_url,
|
||||||
..HomeCard::default()
|
..HomeCard::default()
|
||||||
@@ -2384,6 +2408,32 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn old_most_played_rows_keep_their_source_on_both_card_surfaces() {
|
||||||
|
for (source, badge) in [
|
||||||
|
("local", "local"),
|
||||||
|
("plex", "plex"),
|
||||||
|
("jellyfin", "jellyfin"),
|
||||||
|
("navidrome", "subsonic"),
|
||||||
|
("qobuz_download", "offline"),
|
||||||
|
("qobuz_purchase", "qobuz_purchase"),
|
||||||
|
("qobuz", "qobuz"),
|
||||||
|
("qobuz_connect_remote", "qobuz"),
|
||||||
|
] {
|
||||||
|
let card = map_played_album(qbz_app::settings::album_play_history::AlbumPlayRow {
|
||||||
|
album_id: "stored-before-source-badge-fix".into(),
|
||||||
|
source: source.into(),
|
||||||
|
plays: 23,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
let wire = serde_json::to_value(card).unwrap();
|
||||||
|
assert_eq!(wire["source"], source);
|
||||||
|
assert_eq!(wire["sources"], serde_json::json!([badge]));
|
||||||
|
assert_eq!(wire["plays"], 23);
|
||||||
|
}
|
||||||
|
assert!(history_source_badges("").is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn quality_tier_mapping() {
|
fn quality_tier_mapping() {
|
||||||
assert_eq!(quality_tier(None), "");
|
assert_eq!(quality_tier(None), "");
|
||||||
|
|||||||
Reference in new issue
Block a user