fix(album-awards): accept LegacyAwardDto's awardId/awardedAt field names

Root cause of 'Discoteca Ideal Qobuz not clickable': /album/get
returns awards as LegacyAwardDto (per a20/c.java + LegacyAlbumDto
in the Android decompilation) whose id field is named awardId, not
id. Our AlbumAward struct only looked for 'id', so every award from
/album/get deserialized with id=None and the click fell back to the
catalog resolver — which was itself incomplete.

Adds serde aliases 'awardId' / 'award_id' for the id field and
'awardedAt' for awarded_at. Applies to both qbz-models (V2) and the
src-tauri legacy mirror.

Endpoint coverage:
- /discover/index  → {id, name, awarded_at}       (primary match)
- /album/get       → {awardId, name, awardedAt, …} (alias match)
- /artist/page     → {id, name, awarded_at}       (primary match)

cc
This commit is contained in:
vicrodh committed 2026-04-14 16:02:08 -06:00
1 parent 776e7846cd
commit c4c4bdf244
2 files changed
+33 -9

No files matched your search

+20 -6
View File
@@ -849,18 +849,32 @@ pub struct PageArtistAward {
}
/// Award attached to an album. Shape is intentionally lenient because
/// Qobuz returns different field types across endpoints:
/// - `/discover/index` — id=int, name=string, awarded_at="YYYY-MM-DD"
/// - `/album/get` — id=string, awarded_at may be Unix epoch int
/// Qobuz uses three different embedded shapes across endpoints:
/// - `/discover/index` — {id: int, name, awarded_at: "YYYY-MM-DD"}
/// - `/album/get` — LegacyAwardDto {awardId: string, name,
/// publicationId, publicationName, awardSlug,
/// awardedAt: long, …}
/// - `/artist/page` — PageArtistAward {id: int, name, awarded_at}
/// id is emitted as String downstream so the frontend has a single
/// type to carry into /award/page and /award/getAlbums.
/// type to carry into /award/page and /award/getAlbums. The `alias`
/// list covers the LegacyAwardDto field name the web app never sees
/// but the mobile API uses.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct AlbumAward {
#[serde(default, deserialize_with = "deserialize_award_id")]
#[serde(
default,
alias = "awardId",
alias = "award_id",
deserialize_with = "deserialize_award_id"
)]
pub id: Option<String>,
#[serde(default)]
pub name: String,
#[serde(default, deserialize_with = "deserialize_award_awarded_at")]
#[serde(
default,
alias = "awardedAt",
deserialize_with = "deserialize_award_awarded_at"
)]
pub awarded_at: Option<String>,
}
+13 -3
View File
@@ -969,14 +969,24 @@ pub struct PageArtistAward {
}
/// Tolerant award struct used in Album responses where Qobuz is
/// inconsistent about field types. id is emitted as String downstream.
/// inconsistent about field types. Aliases cover LegacyAwardDto's
/// awardId / awardedAt field names used in /album/get responses.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct AlbumAward {
#[serde(default, deserialize_with = "deserialize_album_award_id")]
#[serde(
default,
alias = "awardId",
alias = "award_id",
deserialize_with = "deserialize_album_award_id"
)]
pub id: Option<String>,
#[serde(default)]
pub name: String,
#[serde(default, deserialize_with = "deserialize_album_awarded_at")]
#[serde(
default,
alias = "awardedAt",
deserialize_with = "deserialize_album_awarded_at"
)]
pub awarded_at: Option<String>,
}