From 5535d2a9413347f40707bb7fcc6d41e9b36c8612 Mon Sep 17 00:00:00 2001 From: Vladimir Zagainov Date: Sun, 18 Oct 2026 10:21:58 +0300 Subject: [PATCH] Fix ID fields to accept both JSON strings and numbers The Yandex Music API returns IDs as both JSON strings and numbers depending on the endpoint. Added JSONID custom type in internal/models/base.go that implements json.Unmarshaler to accept both formats and store as string. Changed ID fields from string to JSONID: - Track.ID, Major.ID, TrackLyricsInfo.ID (track.go) - Album.ID, TrailerInfo.TrackID, TrailerInfo.AlbumID (album.go) Updated example to convert JSONID to string when passing to client methods. Co-Authored-By: Claude Code --- cmd/examples/main.go | 2 +- internal/models/album.go | 32 ++++++++++++++++---------------- internal/models/base.go | 29 +++++++++++++++++++++++++++++ internal/models/track.go | 6 +++--- 4 files changed, 49 insertions(+), 20 deletions(-) diff --git a/cmd/examples/main.go b/cmd/examples/main.go index 91d92c9..2e6dc04 100644 --- a/cmd/examples/main.go +++ b/cmd/examples/main.go @@ -57,7 +57,7 @@ func main() { // Example: Get track by ID (if we have results) if len(tracks) > 0 { fmt.Println("\n--- Getting track details ---") - track, err := client.GetTrack(ctx, tracks[0].ID) + track, err := client.GetTrack(ctx, string(tracks[0].ID)) if err != nil { log.Printf("Get track failed: %v", err) } else { diff --git a/internal/models/album.go b/internal/models/album.go index f16d4be..328df8e 100644 --- a/internal/models/album.go +++ b/internal/models/album.go @@ -8,20 +8,20 @@ import ( // Album represents a full album from the Yandex Music API. type Album struct { BaseModel - ID string `json:"id"` - Title string `json:"title"` - CoverURI string `json:"cover_uri"` - Genre string `json:"genre"` - Year int `json:"year"` - TrackCount int `json:"track_count"` - Available bool `json:"available"` - ReleaseDate string `json:"release_date"` - Artists []Artist `json:"artists"` - Labels []Label `json:"labels"` - Volumes [][]Track `json:"volumes"` - Description string `json:"description"` - Meta *AlbumMeta `json:"meta"` - Best *Best `json:"best"` + ID JSONID `json:"id"` + Title string `json:"title"` + CoverURI string `json:"cover_uri"` + Genre string `json:"genre"` + Year int `json:"year"` + TrackCount int `json:"track_count"` + Available bool `json:"available"` + ReleaseDate string `json:"release_date"` + Artists []Artist `json:"artists"` + Labels []Label `json:"labels"` + Volumes [][]Track `json:"volumes"` + Description string `json:"description"` + Meta *AlbumMeta `json:"meta"` + Best *Best `json:"best"` // Additional fields Region string `json:"region"` AvailableForPremiumUsers bool `json:"available_for_premium_users"` @@ -226,8 +226,8 @@ type Best struct { type TrailerInfo struct { BaseModel PreviewURI string `json:"preview_uri"` - TrackID string `json:"track_id"` - AlbumID string `json:"album_id"` + TrackID JSONID `json:"track_id"` + AlbumID JSONID `json:"album_id"` Duration int `json:"duration"` } diff --git a/internal/models/base.go b/internal/models/base.go index d5cb68d..daf3f96 100644 --- a/internal/models/base.go +++ b/internal/models/base.go @@ -4,6 +4,8 @@ package models import ( "encoding/json" + "fmt" + "strconv" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils" ) @@ -81,3 +83,30 @@ func (b *BaseModel) SetClient(c ClientInterface) { func (b *BaseModel) GetClient() ClientInterface { return b.Client } + +// JSONID is an ID that can be represented as either a JSON string or a JSON number. +// The Yandex Music API sometimes returns IDs as numbers (e.g., 12345) and sometimes +// as strings (e.g., "12345"). JSONID accepts both and stores the value as a string. +type JSONID string + +// UnmarshalJSON implements json.Unmarshaler, accepting both JSON strings and numbers. +func (id *JSONID) UnmarshalJSON(data []byte) error { + // Try string first + var s string + if err := json.Unmarshal(data, &s); err == nil { + *id = JSONID(s) + return nil + } + // Try number (float64 is the default JSON number decoding) + var f float64 + if err := json.Unmarshal(data, &f); err != nil { + return fmt.Errorf("cannot unmarshal %v into JSONID: %w", string(data), err) + } + *id = JSONID(strconv.FormatFloat(f, 'f', -1, 64)) + return nil +} + +// String returns the ID as a Go string. +func (id JSONID) String() string { + return string(id) +} diff --git a/internal/models/track.go b/internal/models/track.go index 0b02ef1..1490ff8 100644 --- a/internal/models/track.go +++ b/internal/models/track.go @@ -8,7 +8,7 @@ import ( // Track represents a full track from the Yandex Music API. type Track struct { BaseModel - ID string `json:"id"` + ID JSONID `json:"id"` Title string `json:"title"` DurationMs int `json:"duration_ms"` StorageID string `json:"storage_id"` @@ -149,14 +149,14 @@ func (t *Track) GetDownloadInfo(quality string) *DownloadInfo { // Major represents major label info for a track. type Major struct { - ID string `json:"id"` + ID JSONID `json:"id"` Name string `json:"name"` Description string `json:"description"` } // TrackLyricsInfo represents minimal lyrics info for a track. type TrackLyricsInfo struct { - ID string `json:"id"` + ID JSONID `json:"id"` Text string `json:"text"` Major *Major `json:"major"` Available bool `json:"available"`