feat(slint): ArtistView overhaul — server-driven buckets, dedicated discography, sort/load-more, Magazine, image fixes

Bucketing now trusts the server release_type: drop the foreign-artist filter
that hid band/guest releases (e.g. Vicky Psarakis' Albums were credited to
Sicksense), render every non-empty bucket, hide 'Purchase Only' (download),
and move 'Other' to the end, collapsed by default. Drop the misleading
per-section item counts.

New 'Latest release' highlight (last_release) and 'Appears On' track section
(tracks_appears_on, previously mislabeled as Compilations).

Per-section persisted 5-mode sort (artist_prefs) + per-section load-more
capped at 4 pages, reusing get_releases_grid. New dedicated discography page
(ArtistReleasesView + artist_releases controller) with sort + infinite scroll,
reached via each section's 'See discography' link.

Magazine/Stories sidebar tab (artist/story) with 1:1 thumbnails and a
Network|Magazine tab switcher; hide the empty similar-artists block to remove
the 'no similar' over populated-discovery incoherence. Header Share action.
Per-release award ribbons.

Fix custom artist AND album images: decode + apply immediately on upload and
resolve the local override on navigation, so a custom image now shows for
artists/albums with no Qobuz artwork.

Data layer: type last_release as PageArtistRelease, add /artist/story endpoint
+ core/client methods + ArtistStory* models, export ArtistReleases* globals.

cc
This commit is contained in:
vicrodh committed 2026-06-21 21:42:22 -06:00
1 parent 9d6b3533bc
commit 391cbb86b9
18 files changed
+1894 -225

No files matched your search

+30
View File
@@ -2606,6 +2606,36 @@ impl QobuzClient {
Ok(serde_json::from_value(response)?)
}
/// Get artist Magazine stories (editorial articles about the artist).
/// REST header-auth only (no per-op signing). Web client calls offset=0 limit=2.
pub async fn get_artist_story(
&self,
artist_id: u64,
offset: u32,
limit: u32,
) -> Result<ArtistStoryResponse> {
let url = endpoints::build_url(paths::ARTIST_STORY);
let query = vec![
("artist_id", artist_id.to_string()),
("offset", offset.to_string()),
("limit", limit.to_string()),
];
log::debug!(
"[API] get_artist_story({}) offset={} limit={}",
artist_id,
offset,
limit
);
let response: serde_json::Value = self
.signed_get(&url, "artiststory", &query.iter().map(|(k, v)| (*k, v.clone())).collect::<Vec<_>>())
.await?
.json()
.await?;
Ok(serde_json::from_value(response)?)
}
// === CMAF streaming endpoints ===
/// Ensure we have a valid CMAF session, renewing if expired.
+1
View File
@@ -39,6 +39,7 @@ pub mod paths {
pub const ARTIST_PAGE: &str = "/artist/page";
pub const ARTIST_RELEASES_GRID: &str = "/artist/getReleasesGrid";
pub const ARTIST_RELEASES_LIST: &str = "/artist/getReleasesList";
pub const ARTIST_STORY: &str = "/artist/story";
// Playlist
pub const PLAYLIST_GET: &str = "/playlist/get";