diff --git a/crates/qbz-models/src/types.rs b/crates/qbz-models/src/types.rs index 974f5d47b..60ce97e09 100644 --- a/crates/qbz-models/src/types.rs +++ b/crates/qbz-models/src/types.rs @@ -273,6 +273,23 @@ pub struct Album { /// Editorial awards (Qobuzissime, Album of the Week, press accolades). #[serde(default)] pub awards: Option>, + /// Parental advisory / explicit content marker. + #[serde(default)] + pub parental_warning: Option, + /// Full artist contributor list including roles. The primary artist is + /// duplicated here as `roles: ["main-artist"]`; non-main entries are + /// the album's featured artists. + #[serde(default)] + pub artists: Option>, +} + +/// Album artist contributor entry (main artist + featured artists). +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AlbumArtist { + pub id: u64, + pub name: String, + #[serde(default)] + pub roles: Option>, } /// A downloadable extra bundled with an album (e.g. PDF booklet) diff --git a/src/lib/adapters/qobuzAdapters.ts b/src/lib/adapters/qobuzAdapters.ts index 11da19780..2649186e6 100644 --- a/src/lib/adapters/qobuzAdapters.ts +++ b/src/lib/adapters/qobuzAdapters.ts @@ -358,12 +358,23 @@ export function convertQobuzAlbum(album: QobuzAlbum): AlbumDetail { album.maximum_sampling_rate ); + const mainArtistId = album.artist?.id; + const featuredArtists = (album.artists ?? []) + .filter((entry) => { + const isMain = entry.roles?.some((role) => role === 'main-artist'); + const isMainById = mainArtistId !== undefined && entry.id === mainArtistId; + return !isMain && !isMainById; + }) + .map((entry) => ({ id: entry.id, name: entry.name })); + return { id: album.id, artwork, title: album.title, artist: album.artist?.name || 'Unknown Artist', artistId: album.artist?.id, + featuredArtists: featuredArtists.length > 0 ? featuredArtists : undefined, + parentalWarning: album.parental_warning ?? false, year: album.release_date_original?.split('-')[0] || '', releaseDate: album.release_date_original, label: album.label?.name || '', diff --git a/src/lib/types/index.ts b/src/lib/types/index.ts index 2f364e467..3332514df 100644 --- a/src/lib/types/index.ts +++ b/src/lib/types/index.ts @@ -96,6 +96,7 @@ export interface QobuzAlbum { title: string; description?: string; artist: { id?: number; name: string }; + artists?: { id: number; name: string; roles?: string[] }[]; image: QobuzImage; release_date_original?: string; hires_streamable?: boolean; @@ -202,6 +203,10 @@ export interface AlbumDetail { title: string; artist: string; artistId?: number; + /** Featured artists (excluding the main artist), in API order. */ + featuredArtists?: { id: number; name: string }[]; + /** Parental advisory marker — show explicit badge next to artist line. */ + parentalWarning?: boolean; year: string; releaseDate?: string; // Full date in YYYY-MM-DD format label: string; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index a1ba7e4e8..45bfb6831 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -5922,6 +5922,7 @@ isPlaybackActive={isPlaying} onBack={navGoBack} onArtistClick={() => selectedAlbum?.artistId && handleArtistClick(selectedAlbum.artistId)} + onFeaturedArtistClick={(artistId) => handleArtistClick(artistId)} onLabelClick={handleLabelClick} onAwardClick={handleAwardClick} onTrackPlay={handleAlbumTrackPlay}