feat(qt): carry the featured artists on Qobuz track rows
cc
This commit is contained in:
7 files changed
+28
No files matched your search
@@ -41,6 +41,9 @@ const RELEASE_PAGE_SIZE: u32 = 20;
|
|||||||
|
|
||||||
#[derive(Clone, Default, Serialize)]
|
#[derive(Clone, Default, Serialize)]
|
||||||
pub struct TrackRow {
|
pub struct TrackRow {
|
||||||
|
/// Featured performers already joined ("X, Y"); "" when none.
|
||||||
|
#[serde(rename = "featured")]
|
||||||
|
pub featured: String,
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub number: String,
|
pub number: String,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
@@ -509,6 +512,15 @@ fn build_credits(album: &Album) -> Vec<(String, String, String)> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// album.rs `map_track` (with work headers for classical albums).
|
/// album.rs `map_track` (with work headers for classical albums).
|
||||||
|
/// "X, Y" for the row's `feat.` suffix (empty = nothing to add). Qobuz rows
|
||||||
|
/// only — local / media-server rows never carry `performers`.
|
||||||
|
pub(crate) fn featured_join(performers: Option<&str>, artist: &str, title: &str) -> String {
|
||||||
|
let Some(performers) = performers else {
|
||||||
|
return String::new();
|
||||||
|
};
|
||||||
|
qbz_qobuz::performers::featured_artists(performers, artist, title).join(", ")
|
||||||
|
}
|
||||||
|
|
||||||
fn map_track(track: &Track) -> TrackRow {
|
fn map_track(track: &Track) -> TrackRow {
|
||||||
let work = track
|
let work = track
|
||||||
.work
|
.work
|
||||||
@@ -536,6 +548,7 @@ fn map_track(track: &Track) -> TrackRow {
|
|||||||
.map(|p| (p.name.clone(), p.id.to_string()))
|
.map(|p| (p.name.clone(), p.id.to_string()))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
TrackRow {
|
TrackRow {
|
||||||
|
featured: featured_join(track.performers.as_deref(), &artist, &track.title),
|
||||||
is_favorite: crate::fav_cache_qt::contains_track(track.id),
|
is_favorite: crate::fav_cache_qt::contains_track(track.id),
|
||||||
id: track.id.to_string(),
|
id: track.id.to_string(),
|
||||||
number: track.track_number.to_string(),
|
number: track.track_number.to_string(),
|
||||||
|
|||||||
@@ -594,6 +594,7 @@ fn map_track(index: usize, track: PageArtistTrack) -> TrackRow {
|
|||||||
.and_then(|r| r.streamable)
|
.and_then(|r| r.streamable)
|
||||||
.unwrap_or(true);
|
.unwrap_or(true);
|
||||||
TrackRow {
|
TrackRow {
|
||||||
|
featured: String::new(),
|
||||||
is_favorite: crate::fav_cache_qt::contains_track(track.id),
|
is_favorite: crate::fav_cache_qt::contains_track(track.id),
|
||||||
id: track.id.to_string(),
|
id: track.id.to_string(),
|
||||||
cache_status: if crate::offline_qt::is_cached(&track.id.to_string()) {
|
cache_status: if crate::offline_qt::is_cached(&track.id.to_string()) {
|
||||||
|
|||||||
@@ -1048,6 +1048,7 @@ fn to_row(track: &Track) -> TrackRow {
|
|||||||
let bit_depth = track.maximum_bit_depth;
|
let bit_depth = track.maximum_bit_depth;
|
||||||
let sample_rate = track.maximum_sampling_rate;
|
let sample_rate = track.maximum_sampling_rate;
|
||||||
TrackRow {
|
TrackRow {
|
||||||
|
featured: String::new(),
|
||||||
id: track.id.to_string(),
|
id: track.id.to_string(),
|
||||||
title,
|
title,
|
||||||
artist: track
|
artist: track
|
||||||
|
|||||||
@@ -1358,6 +1358,7 @@ fn parse_top_track(index: usize, raw: &Value) -> TrackRow {
|
|||||||
raw.get("release_date_stream").and_then(|v| v.as_str()),
|
raw.get("release_date_stream").and_then(|v| v.as_str()),
|
||||||
);
|
);
|
||||||
TrackRow {
|
TrackRow {
|
||||||
|
featured: String::new(),
|
||||||
is_favorite: id
|
is_favorite: id
|
||||||
.parse::<u64>()
|
.parse::<u64>()
|
||||||
.map(crate::fav_cache_qt::contains_track)
|
.map(crate::fav_cache_qt::contains_track)
|
||||||
|
|||||||
@@ -806,6 +806,7 @@ pub(crate) fn row_to_display(item: &RowItem) -> (PlaylistTrackRow, Option<QueueT
|
|||||||
} => {
|
} => {
|
||||||
let art = artwork_path.clone().unwrap_or_default();
|
let art = artwork_path.clone().unwrap_or_default();
|
||||||
let row = PlaylistTrackRow {
|
let row = PlaylistTrackRow {
|
||||||
|
featured: String::new(),
|
||||||
id: track_id.to_string(),
|
id: track_id.to_string(),
|
||||||
playlist_track_id: *track_id,
|
playlist_track_id: *track_id,
|
||||||
title: title.clone(),
|
title: title.clone(),
|
||||||
@@ -880,6 +881,7 @@ pub(crate) fn row_to_display(item: &RowItem) -> (PlaylistTrackRow, Option<QueueT
|
|||||||
)
|
)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let row = PlaylistTrackRow {
|
let row = PlaylistTrackRow {
|
||||||
|
featured: String::new(),
|
||||||
id: queue.id.to_string(),
|
id: queue.id.to_string(),
|
||||||
playlist_track_id: queue.id,
|
playlist_track_id: queue.id,
|
||||||
title: t.title.clone(),
|
title: t.title.clone(),
|
||||||
@@ -930,6 +932,7 @@ pub(crate) fn row_to_display(item: &RowItem) -> (PlaylistTrackRow, Option<QueueT
|
|||||||
.unwrap_or_else(|| path.clone());
|
.unwrap_or_else(|| path.clone());
|
||||||
(
|
(
|
||||||
PlaylistTrackRow {
|
PlaylistTrackRow {
|
||||||
|
featured: String::new(),
|
||||||
id: path.clone(),
|
id: path.clone(),
|
||||||
title: name,
|
title: name,
|
||||||
source: "local".to_string(),
|
source: "local".to_string(),
|
||||||
@@ -942,6 +945,7 @@ pub(crate) fn row_to_display(item: &RowItem) -> (PlaylistTrackRow, Option<QueueT
|
|||||||
}
|
}
|
||||||
RowItem::Unresolved { kind, reference } => (
|
RowItem::Unresolved { kind, reference } => (
|
||||||
PlaylistTrackRow {
|
PlaylistTrackRow {
|
||||||
|
featured: String::new(),
|
||||||
id: format!("{kind}:{reference}"),
|
id: format!("{kind}:{reference}"),
|
||||||
title: qbz_i18n::t("Unavailable track"),
|
title: qbz_i18n::t("Unavailable track"),
|
||||||
source: (*kind).to_string(),
|
source: (*kind).to_string(),
|
||||||
|
|||||||
@@ -59,6 +59,9 @@ use serde::Serialize;
|
|||||||
|
|
||||||
#[derive(Clone, Default, Serialize)]
|
#[derive(Clone, Default, Serialize)]
|
||||||
pub struct PlaylistTrackRow {
|
pub struct PlaylistTrackRow {
|
||||||
|
/// Featured performers already joined ("X, Y"); "" when none.
|
||||||
|
#[serde(rename = "featured")]
|
||||||
|
pub featured: String,
|
||||||
pub id: String,
|
pub id: String,
|
||||||
/// The playlist membership row id (== catalog track id for Qobuz
|
/// The playlist membership row id (== catalog track id for Qobuz
|
||||||
/// playlists — what remove_tracks_from_playlist takes).
|
/// playlists — what remove_tracks_from_playlist takes).
|
||||||
@@ -862,6 +865,7 @@ pub(crate) fn map_track(track: &Track) -> PlaylistTrackRow {
|
|||||||
.map(|p| (p.name, p.id.to_string()))
|
.map(|p| (p.name, p.id.to_string()))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
PlaylistTrackRow {
|
PlaylistTrackRow {
|
||||||
|
featured: crate::album_qt::featured_join(track.performers.as_deref(), &artist, &track.title),
|
||||||
// Heart state at build time, from the favourite-id cache — the same
|
// Heart state at build time, from the favourite-id cache — the same
|
||||||
// O(1) read `album_qt` / `artist_qt` / `label_qt` rows use. It was
|
// O(1) read `album_qt` / `artist_qt` / `label_qt` rows use. It was
|
||||||
// never stamped here, so `TrackRow.qml` saw `undefined` on every
|
// never stamped here, so `TrackRow.qml` saw `undefined` on every
|
||||||
|
|||||||
@@ -194,6 +194,9 @@ pub struct CardRow {
|
|||||||
|
|
||||||
#[derive(Clone, Default, Serialize)]
|
#[derive(Clone, Default, Serialize)]
|
||||||
pub struct TrackRow {
|
pub struct TrackRow {
|
||||||
|
/// Featured performers already joined ("X, Y"); "" when none.
|
||||||
|
#[serde(rename = "featured")]
|
||||||
|
pub featured: String,
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub artist: String,
|
pub artist: String,
|
||||||
@@ -404,6 +407,7 @@ fn map_track(track: &Track) -> TrackRow {
|
|||||||
.map(|p| (p.name, p.id.to_string()))
|
.map(|p| (p.name, p.id.to_string()))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
TrackRow {
|
TrackRow {
|
||||||
|
featured: crate::album_qt::featured_join(track.performers.as_deref(), &artist, &track.title),
|
||||||
// Same story as `map_album`: the field was declared (line 186) and
|
// Same story as `map_album`: the field was declared (line 186) and
|
||||||
// never stamped, so every search track row drew the empty heart and
|
// never stamped, so every search track row drew the empty heart and
|
||||||
// the first click sent `favorite/delete` on a track the user had
|
// the first click sent `favorite/delete` on a track the user had
|
||||||
|
|||||||
Reference in new issue
Block a user