From 577c4a418c14cd8905ea14f5f9253c14a79659e1 Mon Sep 17 00:00:00 2001 From: Vladimir Zagainov Date: Sun, 18 Oct 2026 12:44:17 +0300 Subject: [PATCH] Fix Artist model to match Python reference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Link field removed (not in Python Artist model) - Description: string → *Description object (text, uri fields) - SocialLinks []SocialLink → Links []Link (title, href, type, social_network) - Donations *ArtistDonations → DonationInfo *ArtistDonationInfo (title, url) - Decomposed []Artist → []Decomposed (accepts string or Artist object) - Added Decomposed, Link, Description, ArtistDonationInfo types --- internal/models/artist.go | 227 +++++++++++++++++++++++++++++++++----- 1 file changed, 197 insertions(+), 30 deletions(-) diff --git a/internal/models/artist.go b/internal/models/artist.go index 37c74d4..86c88b0 100644 --- a/internal/models/artist.go +++ b/internal/models/artist.go @@ -14,25 +14,24 @@ type Artist struct { Genres []string `json:"genres"` Tracks int `json:"tracks"` Albums int `json:"albums"` - Link string `json:"link"` Available bool `json:"available"` - Description string `json:"description"` Composer bool `json:"composer"` Various bool `json:"various"` - Decomposed []Artist `json:"decomposed"` + Decomposed []Decomposed `json:"decomposed"` Ratings *Ratings `json:"ratings"` Counts *Counts `json:"counts"` - SocialLinks []SocialLink `json:"social_links"` + Links []Link `json:"social_links"` + Description *Description `json:"description"` // Additional fields - IDForFrom string `json:"id_for_from"` - Cover *Cover `json:"cover"` - BriefInfo *ArtistBriefInfo `json:"brief_info"` - Donations *ArtistDonations `json:"donations"` - Concerts []Concert `json:"concerts"` - Clips []Clip `json:"clips"` - Similar []Artist `json:"similar"` - AlbumsData []Album `json:"albums_data"` - TracksData []Track `json:"tracks_data"` + IDForFrom string `json:"id_for_from"` + Cover *Cover `json:"cover"` + BriefInfo *ArtistBriefInfo `json:"brief_info"` + DonationInfo *ArtistDonationInfo `json:"donation_info"` + Concerts []Concert `json:"concerts"` + Clips []Clip `json:"clips"` + Similar []Artist `json:"similar"` + AlbumsData []Album `json:"albums_data"` + TracksData []Track `json:"tracks_data"` } // DeJSON deserializes an Artist from JSON data. @@ -63,15 +62,9 @@ func (a *Artist) DeJSON(data []byte, c ClientInterface) (Model, error) { if v, ok := raw["albums"]; ok { json.Unmarshal(v, &artist.Albums) } - if v, ok := raw["link"]; ok { - json.Unmarshal(v, &artist.Link) - } if v, ok := raw["available"]; ok { json.Unmarshal(v, &artist.Available) } - if v, ok := raw["description"]; ok { - json.Unmarshal(v, &artist.Description) - } if v, ok := raw["composer"]; ok { json.Unmarshal(v, &artist.Composer) } @@ -84,9 +77,10 @@ func (a *Artist) DeJSON(data []byte, c ClientInterface) (Model, error) { // Nested objects if v, ok := raw["decomposed"]; ok { - var decomposed []Artist - json.Unmarshal(v, &decomposed) - artist.Decomposed = decomposed + var decomposed []Decomposed + if err := json.Unmarshal(v, &decomposed); err == nil { + artist.Decomposed = decomposed + } } if v, ok := raw["ratings"]; ok { var ratings Ratings @@ -99,9 +93,10 @@ func (a *Artist) DeJSON(data []byte, c ClientInterface) (Model, error) { artist.Counts = &counts } if v, ok := raw["social_links"]; ok { - var links []SocialLink - json.Unmarshal(v, &links) - artist.SocialLinks = links + var links []Link + if err := json.Unmarshal(v, &links); err == nil { + artist.Links = links + } } if v, ok := raw["cover"]; ok { var cover Cover @@ -113,10 +108,10 @@ func (a *Artist) DeJSON(data []byte, c ClientInterface) (Model, error) { json.Unmarshal(v, &brief) artist.BriefInfo = &brief } - if v, ok := raw["donations"]; ok { - var donations ArtistDonations - json.Unmarshal(v, &donations) - artist.Donations = &donations + if v, ok := raw["donation_info"]; ok { + var donation ArtistDonationInfo + json.Unmarshal(v, &donation) + artist.DonationInfo = &donation } if v, ok := raw["concerts"]; ok { var concerts []Concert @@ -210,7 +205,7 @@ type ArtistDonations struct { // DonationItem represents a donation item. type DonationItem struct { - ID JSONID `json:"id"` + ID JSONID `json:"id"` Title string `json:"title"` Description string `json:"description"` Amount int `json:"amount"` @@ -219,6 +214,178 @@ type DonationItem struct { Currency string `json:"currency"` } +// Decomposed represents a decomposed artist that can be either a string or an Artist object. +type Decomposed struct { + Artist *Artist `json:"-"` + Str string `json:"-"` + IsStr bool `json:"-"` +} + +// UnmarshalJSON implements json.Unmarshaler, accepting both string and Artist object. +func (d *Decomposed) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err == nil { + d.Str = s + d.IsStr = true + return nil + } + var artist Artist + if err := json.Unmarshal(data, &artist); err != nil { + return err + } + d.Artist = &artist + d.IsStr = false + return nil +} + +// MarshalJSON implements json.Marshaler. +func (d Decomposed) MarshalJSON() ([]byte, error) { + if d.IsStr { + return json.Marshal(d.Str) + } + return json.Marshal(d.Artist) +} + +// Link represents a link to an artist's official page or social network. +type Link struct { + BaseModel + Title string `json:"title"` + Href string `json:"href"` + Type string `json:"type"` + SocialNetwork string `json:"social_network"` +} + +// DeJSON deserializes Link from JSON data. +func (l *Link) DeJSON(data []byte, c ClientInterface) (Model, error) { + var raw map[string]json.RawMessage + if err := json.Unmarshal(data, &raw); err != nil { + return nil, err + } + + link := &Link{BaseModel: *NewBaseModel(c)} + + if v, ok := raw["title"]; ok { + json.Unmarshal(v, &link.Title) + } + if v, ok := raw["href"]; ok { + json.Unmarshal(v, &link.Href) + } + if v, ok := raw["type"]; ok { + json.Unmarshal(v, &link.Type) + } + if v, ok := raw["social_network"]; ok { + json.Unmarshal(v, &link.SocialNetwork) + } + + return link, nil +} + +// DeList deserializes a list of Links from JSON data. +func (l *Link) 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 { + link := &Link{BaseModel: *NewBaseModel(c)} + if err := json.Unmarshal(item, link); err != nil { + continue + } + result = append(result, link) + } + return result, nil +} + +// Description represents artist description. +type Description struct { + BaseModel + Text string `json:"text"` + URI string `json:"uri"` +} + +// DeJSON deserializes Description from JSON data. +func (d *Description) DeJSON(data []byte, c ClientInterface) (Model, error) { + var raw map[string]json.RawMessage + if err := json.Unmarshal(data, &raw); err != nil { + return nil, err + } + + desc := &Description{BaseModel: *NewBaseModel(c)} + + if v, ok := raw["text"]; ok { + json.Unmarshal(v, &desc.Text) + } + if v, ok := raw["uri"]; ok { + json.Unmarshal(v, &desc.URI) + } + + return desc, nil +} + +// DeList deserializes a list of Descriptions from JSON data. +func (d *Description) 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 { + desc := &Description{BaseModel: *NewBaseModel(c)} + if err := json.Unmarshal(item, desc); err != nil { + continue + } + result = append(result, desc) + } + return result, nil +} + +// ArtistDonationInfo represents artist donation info. +type ArtistDonationInfo struct { + BaseModel + Title string `json:"title"` + URL string `json:"url"` +} + +// DeJSON deserializes ArtistDonationInfo from JSON data. +func (adi *ArtistDonationInfo) DeJSON(data []byte, c ClientInterface) (Model, error) { + var raw map[string]json.RawMessage + if err := json.Unmarshal(data, &raw); err != nil { + return nil, err + } + + info := &ArtistDonationInfo{BaseModel: *NewBaseModel(c)} + + if v, ok := raw["title"]; ok { + json.Unmarshal(v, &info.Title) + } + if v, ok := raw["url"]; ok { + json.Unmarshal(v, &info.URL) + } + + return info, nil +} + +// DeList deserializes a list of ArtistDonationInfo from JSON data. +func (adi *ArtistDonationInfo) 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 { + info := &ArtistDonationInfo{BaseModel: *NewBaseModel(c)} + if err := json.Unmarshal(item, info); err != nil { + continue + } + result = append(result, info) + } + return result, nil +} + // ConcertShort represents a concert (minimal for artist). type ConcertShort struct { BaseModel