fix(album): accept awarded_at as integer or string on PageArtistAward
/album/get returns award.awarded_at as a Unix timestamp integer while /discover/index returns it as a YYYY-MM-DD string — our struct only accepted String, so /album/get started failing with 'invalid type: integer `…`, expected a string' as soon as awards were added to Album in Phase 2. Every 'Load album' call broke. Adds a custom deserializer that accepts both shapes and normalizes to String downstream. Applied in both qbz-models (V2) and src-tauri legacy api::models for symmetry. cc
This commit is contained in:
2 files changed
+74
No files matched your search
@@ -467,6 +467,47 @@ pub struct LabelPageGenericList {
|
||||
pub items: Option<Vec<serde_json::Value>>,
|
||||
}
|
||||
|
||||
// ============ Award Page Types (/award/page) ============
|
||||
|
||||
/// Magazine/publisher behind a press award.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AwardMagazine {
|
||||
pub id: Option<String>,
|
||||
pub name: Option<String>,
|
||||
pub image: Option<String>,
|
||||
}
|
||||
|
||||
/// Top-level response from /award/page.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AwardPageData {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
pub image: Option<String>,
|
||||
#[serde(default)]
|
||||
pub awarded_at: Option<String>,
|
||||
#[serde(default)]
|
||||
pub magazine: Option<AwardMagazine>,
|
||||
/// Categorized containers of award-winning releases (matches
|
||||
/// Android's `releases: List<GenericContainerDto<AlbumDto>>`).
|
||||
#[serde(default)]
|
||||
pub releases: Option<Vec<AwardPageContainer>>,
|
||||
#[serde(default)]
|
||||
pub playlists: Option<AwardPageGenericList>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AwardPageContainer {
|
||||
pub id: Option<String>,
|
||||
pub data: Option<AwardPageGenericList>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AwardPageGenericList {
|
||||
pub has_more: Option<bool>,
|
||||
pub items: Option<Vec<serde_json::Value>>,
|
||||
}
|
||||
|
||||
/// Response from /label/explore
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct LabelExploreResponse {
|
||||
@@ -785,9 +826,26 @@ pub struct PageArtistRights {
|
||||
pub struct PageArtistAward {
|
||||
pub id: u64,
|
||||
pub name: String,
|
||||
/// Qobuz is inconsistent — /discover/index returns this as a
|
||||
/// "YYYY-MM-DD" string, /album/get returns a Unix timestamp integer.
|
||||
/// Accept both and emit a string downstream.
|
||||
#[serde(default, deserialize_with = "deserialize_awarded_at")]
|
||||
pub awarded_at: Option<String>,
|
||||
}
|
||||
|
||||
fn deserialize_awarded_at<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let value = Option::<serde_json::Value>::deserialize(deserializer)?;
|
||||
Ok(match value {
|
||||
Some(serde_json::Value::String(s)) => Some(s),
|
||||
Some(serde_json::Value::Number(n)) => Some(n.to_string()),
|
||||
Some(serde_json::Value::Null) | None => None,
|
||||
Some(_) => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Track from /artist/page
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PageArtistTrack {
|
||||
|
||||
Reference in new issue
Block a user