feat(models): albums and tracks carry the catalog's purchasable flag

Qobuz answers `purchasable` on every album and track object (captured in
the API samples); the models never read it. `Some(false)` is the catalog
saying a release is not sold, absence stays unknown. The exhaustive test
fixtures in qbz-core and qbz-playlist-import spell the field out.

cc
This commit is contained in:
vicrodh committed 2026-09-13 20:26:11 -06:00
1 parent 10fb9eba84
commit 22e064f340
3 files changed
+11

No files matched your search

+2
View File
@@ -3863,6 +3863,7 @@ mod tests {
.collect(); .collect();
Album { Album {
favorited_at: None, favorited_at: None,
purchasable: None,
id: String::new(), id: String::new(),
title: String::new(), title: String::new(),
artist: Artist { artist: Artist {
@@ -3900,6 +3901,7 @@ mod tests {
fn track_with(performer_id: Option<u64>, composer_id: Option<u64>) -> Track { fn track_with(performer_id: Option<u64>, composer_id: Option<u64>) -> Track {
Track { Track {
favorited_at: None, favorited_at: None,
purchasable: None,
id: 0, id: 0,
title: String::new(), title: String::new(),
version: None, version: None,
+8
View File
@@ -518,6 +518,10 @@ pub struct Track {
/// Favourites-list rows only (unix seconds); see `Album::favorited_at`. /// Favourites-list rows only (unix seconds); see `Album::favorited_at`.
#[serde(default, deserialize_with = "crate::purchase_serde::lenient_option")] #[serde(default, deserialize_with = "crate::purchase_serde::lenient_option")]
pub favorited_at: Option<i64>, pub favorited_at: Option<i64>,
/// Store flag from the catalog (`purchasable`): `Some(false)` is the API
/// saying this release is not sold on Qobuz; absence is unknown, not no.
#[serde(default)]
pub purchasable: Option<bool>,
#[serde(default)] #[serde(default)]
pub title: String, pub title: String,
/// Subtitle/edition info from Qobuz (e.g. "Player's Ball Mix", /// Subtitle/edition info from Qobuz (e.g. "Player's Ball Mix",
@@ -730,6 +734,10 @@ pub struct Album {
/// it (unix seconds); every other album shape leaves it `None`. /// it (unix seconds); every other album shape leaves it `None`.
#[serde(default, deserialize_with = "crate::purchase_serde::lenient_option")] #[serde(default, deserialize_with = "crate::purchase_serde::lenient_option")]
pub favorited_at: Option<i64>, pub favorited_at: Option<i64>,
/// Store flag from the catalog (`purchasable`): `Some(false)` is the API
/// saying this release is not sold on Qobuz; absence is unknown, not no.
#[serde(default)]
pub purchasable: Option<bool>,
#[serde(default)] #[serde(default)]
pub title: String, pub title: String,
#[serde(default)] #[serde(default)]
@@ -393,6 +393,7 @@ mod tests {
fn qobuz_track(id: u64, title: &str, artist: &str) -> Track { fn qobuz_track(id: u64, title: &str, artist: &str) -> Track {
Track { Track {
favorited_at: None, favorited_at: None,
purchasable: None,
id, id,
title: title.to_string(), title: title.to_string(),
version: None, version: None,