- Labels: changed from []Label to []LabelOrString (API returns both objects and strings) - Trailer: changed from *TrailerInfo to *AlbumTrailerStatus (Python has AlbumTrailerStatus with only 'available' field) - Added LabelOrString type with custom UnmarshalJSON - Added AlbumTrailerStatus type with Available field
363 lines
9.7 KiB
Go
363 lines
9.7 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 []LabelOrString `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 *AlbumTrailerStatus `json:"trailer"`
|
|
}
|
|
|
|
// LabelOrString represents a label that can be either a Label object or a string.
|
|
// The Yandex Music API sometimes returns labels as objects and sometimes as strings.
|
|
type LabelOrString struct {
|
|
Label *Label `json:"-"`
|
|
Str string `json:"-"`
|
|
IsStr bool `json:"-"`
|
|
}
|
|
|
|
// UnmarshalJSON implements json.Unmarshaler, accepting both Label objects and strings.
|
|
func (l *LabelOrString) UnmarshalJSON(data []byte) error {
|
|
// Try string first
|
|
var s string
|
|
if err := json.Unmarshal(data, &s); err == nil {
|
|
l.Str = s
|
|
l.IsStr = true
|
|
return nil
|
|
}
|
|
// Try Label object
|
|
var label Label
|
|
if err := json.Unmarshal(data, &label); err != nil {
|
|
return err
|
|
}
|
|
l.Label = &label
|
|
l.IsStr = false
|
|
return nil
|
|
}
|
|
|
|
// MarshalJSON implements json.Marshaler.
|
|
func (l LabelOrString) MarshalJSON() ([]byte, error) {
|
|
if l.IsStr {
|
|
return json.Marshal(l.Str)
|
|
}
|
|
return json.Marshal(l.Label)
|
|
}
|
|
|
|
// String returns the label as a string.
|
|
func (l LabelOrString) String() string {
|
|
if l.IsStr {
|
|
return l.Str
|
|
}
|
|
if l.Label != nil {
|
|
return l.Label.Name
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// AlbumTrailerStatus represents trailer availability status for an album.
|
|
type AlbumTrailerStatus struct {
|
|
BaseModel
|
|
Available bool `json:"available"`
|
|
}
|
|
|
|
// DeJSON deserializes AlbumTrailerStatus from JSON data.
|
|
func (ats *AlbumTrailerStatus) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
|
var raw map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &raw); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
status := &AlbumTrailerStatus{BaseModel: *NewBaseModel(c)}
|
|
|
|
if v, ok := raw["available"]; ok {
|
|
json.Unmarshal(v, &status.Available)
|
|
}
|
|
|
|
return status, nil
|
|
}
|
|
|
|
// DeList deserializes a list of AlbumTrailerStatus from JSON data.
|
|
func (ats *AlbumTrailerStatus) 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 {
|
|
status := &AlbumTrailerStatus{BaseModel: *NewBaseModel(c)}
|
|
if err := json.Unmarshal(item, status); err != nil {
|
|
continue
|
|
}
|
|
result = append(result, status)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// 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 []LabelOrString
|
|
if err := json.Unmarshal(v, &labels); err == nil {
|
|
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 AlbumTrailerStatus
|
|
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
|
|
}
|