Files
yandex-music-api-go/internal/models/album.go
T
MrixsandClaude Code 5535d2a941 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 <noreply@anthropic.com>
2026-10-18 10:21:58 +03:00

276 lines
7.4 KiB
Go

// Package models provides data models for the Yandex Music API.
package models
import (
"encoding/json"
)
// Album represents a full album from the Yandex Music API.
type Album struct {
BaseModel
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"`
AvailableForPremiumUsersOnly bool `json:"available_for_premium_users_only"`
ContentWarning string `json:"content_warning"`
Label string `json:"label"`
CatalogNumber string `json:"catalog_number"`
Options []string `json:"options"`
TrackPositions []TrackPosition `json:"track_positions"`
SimilarAlbums []Album `json:"similar_albums"`
Trailer *TrailerInfo `json:"trailer"`
}
// DeJSON deserializes an Album from JSON data.
func (a *Album) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
album := &Album{BaseModel: *NewBaseModel(c)}
// Simple fields
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &album.ID)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &album.Title)
}
if v, ok := raw["cover_uri"]; ok {
json.Unmarshal(v, &album.CoverURI)
}
if v, ok := raw["genre"]; ok {
json.Unmarshal(v, &album.Genre)
}
if v, ok := raw["year"]; ok {
json.Unmarshal(v, &album.Year)
}
if v, ok := raw["track_count"]; ok {
json.Unmarshal(v, &album.TrackCount)
}
if v, ok := raw["available"]; ok {
json.Unmarshal(v, &album.Available)
}
if v, ok := raw["release_date"]; ok {
json.Unmarshal(v, &album.ReleaseDate)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &album.Description)
}
if v, ok := raw["region"]; ok {
json.Unmarshal(v, &album.Region)
}
if v, ok := raw["available_for_premium_users"]; ok {
json.Unmarshal(v, &album.AvailableForPremiumUsers)
}
if v, ok := raw["available_for_premium_users_only"]; ok {
json.Unmarshal(v, &album.AvailableForPremiumUsersOnly)
}
if v, ok := raw["content_warning"]; ok {
json.Unmarshal(v, &album.ContentWarning)
}
if v, ok := raw["label"]; ok {
json.Unmarshal(v, &album.Label)
}
if v, ok := raw["catalog_number"]; ok {
json.Unmarshal(v, &album.CatalogNumber)
}
if v, ok := raw["options"]; ok {
json.Unmarshal(v, &album.Options)
}
// Nested objects
if v, ok := raw["artists"]; ok {
var artists []Artist
json.Unmarshal(v, &artists)
album.Artists = artists
}
if v, ok := raw["labels"]; ok {
var labels []Label
json.Unmarshal(v, &labels)
album.Labels = labels
}
if v, ok := raw["volumes"]; ok {
var volumes [][]Track
json.Unmarshal(v, &volumes)
album.Volumes = volumes
}
if v, ok := raw["meta"]; ok {
var meta AlbumMeta
json.Unmarshal(v, &meta)
album.Meta = &meta
}
if v, ok := raw["best"]; ok {
var best Best
json.Unmarshal(v, &best)
album.Best = &best
}
if v, ok := raw["track_positions"]; ok {
var positions []TrackPosition
json.Unmarshal(v, &positions)
album.TrackPositions = positions
}
if v, ok := raw["similar_albums"]; ok {
var similar []Album
json.Unmarshal(v, &similar)
album.SimilarAlbums = similar
}
if v, ok := raw["trailer"]; ok {
var trailer TrailerInfo
json.Unmarshal(v, &trailer)
album.Trailer = &trailer
}
return album, nil
}
// DeList deserializes a list of Albums from JSON data.
func (a *Album) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
album := &Album{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, album); err != nil {
continue
}
result = append(result, album)
}
return result, nil
}
// AlbumMeta represents album metadata.
type AlbumMeta struct {
BaseModel
Availability *Availability `json:"availability"`
Upsell *Upsell `json:"upsell"`
}
// DeJSON deserializes an AlbumMeta from JSON data.
func (am *AlbumMeta) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
meta := &AlbumMeta{BaseModel: *NewBaseModel(c)}
if v, ok := raw["availability"]; ok {
var avail Availability
json.Unmarshal(v, &avail)
meta.Availability = &avail
}
if v, ok := raw["upsell"]; ok {
var upsell Upsell
json.Unmarshal(v, &upsell)
meta.Upsell = &upsell
}
return meta, nil
}
// DeList deserializes a list of AlbumMeta from JSON data.
func (am *AlbumMeta) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
meta := &AlbumMeta{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, meta); err != nil {
continue
}
result = append(result, meta)
}
return result, nil
}
// Availability represents availability info for an album.
type Availability struct {
AvailableForPremiumUsers bool `json:"available_for_premium_users"`
Territories []string `json:"territories"`
}
// Upsell represents upsell info for an album.
type Upsell struct {
Type string `json:"type"`
Description string `json:"description"`
}
// Best represents best tracks info for an album.
type Best struct {
Tracks []Track `json:"tracks"`
}
// TrailerInfo represents album/track trailer info.
type TrailerInfo struct {
BaseModel
PreviewURI string `json:"preview_uri"`
TrackID JSONID `json:"track_id"`
AlbumID JSONID `json:"album_id"`
Duration int `json:"duration"`
}
// DeJSON deserializes a TrailerInfo from JSON data.
func (ti *TrailerInfo) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
trailer := &TrailerInfo{BaseModel: *NewBaseModel(c)}
if v, ok := raw["preview_uri"]; ok {
json.Unmarshal(v, &trailer.PreviewURI)
}
if v, ok := raw["track_id"]; ok {
json.Unmarshal(v, &trailer.TrackID)
}
if v, ok := raw["album_id"]; ok {
json.Unmarshal(v, &trailer.AlbumID)
}
if v, ok := raw["duration"]; ok {
json.Unmarshal(v, &trailer.Duration)
}
return trailer, nil
}
// DeList deserializes a list of TrailerInfo from JSON data.
func (ti *TrailerInfo) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
trailer := &TrailerInfo{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, trailer); err != nil {
continue
}
result = append(result, trailer)
}
return result, nil
}