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"`