feat: For You — radio, album suggestions, rediscover, more-from-library

Completes the remaining For You sections by porting two Qobuz API
endpoints down into qbz-core and adding the local "forgotten
favorites" approximation.

Backend ports (qbz-models + qbz-qobuz + qbz-core):
  - /album/suggest -> AlbumSuggestResponse, QobuzClient::get_album_suggest,
    QbzCore::get_album_suggest.
  - /radio/artist + /radio/album -> RadioResponse, get_radio_artist /
    get_radio_album on the client + core. (Faithful ports of the legacy
    src-tauri/api/client.rs methods — same paths, params, shapes.)

For You sections:
  - Radio Stations — album-seeded radio tiles (recent + favorite
    albums, deduped, capped 12), rendered with RadioCard; click starts
    a real Qobuz album radio.
  - More From Your Library — /album/suggest seeded from the most recent
    played album (else first favorite).
  - Rediscover Your Library — favorite albums not in the local
    play-history (approximation of Tauri's reco-DB forgotten
    favorites).
  - Spotlight's RADIO card + the artist "radio" action now create a
    real Qobuz artist radio (playback::play_artist_radio /
    play_album_radio build a queue from the radio track list and play).

Section order now matches Tauri: Qobuz Mixes, Release Watch, Radio
Stations, Recently Played Tracks/Albums, Your Top Artists, More From
Your Library, Rediscover, Artists to Follow, Spotlight.

Still backend-blocked: the Qobuz mix detail views (DailyQ/WeeklyQ/etc.)
and the smart radio_engine (the Qobuz radio used here is the simpler
/radio/* endpoint, not the local pool builder).

cc
This commit is contained in:
vicrodh committed 2026-05-21 06:43:53 -06:00
1 parent 83b7721b10
commit c19a5b84dd
12 files changed
+457 -14

No files matched your search

+39
View File
@@ -1099,6 +1099,45 @@ impl<A: FrontendAdapter + Send + Sync + 'static> QbzCore<A> {
.map_err(CoreError::Api)
}
/// Albums similar to a seed album (`/album/suggest`).
pub async fn get_album_suggest(
&self,
album_id: &str,
) -> Result<qbz_models::AlbumSuggestResponse, CoreError> {
let client = self.client.read().await;
let client = client.as_ref().ok_or(CoreError::NotInitialized)?;
client
.get_album_suggest(album_id)
.await
.map_err(CoreError::Api)
}
/// Qobuz artist radio (`/radio/artist`) — a generated track list.
pub async fn get_radio_artist(
&self,
artist_id: &str,
) -> Result<qbz_models::RadioResponse, CoreError> {
let client = self.client.read().await;
let client = client.as_ref().ok_or(CoreError::NotInitialized)?;
client
.get_radio_artist(artist_id)
.await
.map_err(CoreError::Api)
}
/// Qobuz album radio (`/radio/album`).
pub async fn get_radio_album(
&self,
album_id: &str,
) -> Result<qbz_models::RadioResponse, CoreError> {
let client = self.client.read().await;
let client = client.as_ref().ok_or(CoreError::NotInitialized)?;
client
.get_radio_album(album_id)
.await
.map_err(CoreError::Api)
}
/// Get artist with albums (for album pagination)
pub async fn get_artist_with_albums(
&self,