From 49920d890890d59d39ae0d3ebb62b9f5a917e4eb Mon Sep 17 00:00:00 2001 From: Vladimir Zagainov Date: Sun, 18 Oct 2026 13:24:41 +0300 Subject: [PATCH] Add missing Track, Album, Artist model types and client methods Track: Added FileInfo, FileDownloadInfo, TrackFullInfo, SimilarTracks types Album: Added AlbumSimilarEntities, AlbumTrailer, SimilarEntityItem types Artist: Added ArtistSimilar, ArtistLinks, ArtistAbout, ArtistInfo, ArtistSkeleton, ArtistTrailerStatus, ArtistLink, Stats, SkeletonBlock types Client methods added: - artists.go: GetArtistBriefInfo, GetArtistSimilar, GetArtistLinks, GetArtistAlsoAlbums, GetArtistDiscographyAlbums, GetArtistSafeDirectAlbums, GetArtistTrackIds, GetArtistAbout, GetArtistDonation, GetArtistInfo, GetArtistSkeleton, GetArtistTrailer - albums.go: GetAlbumSimilarEntities, GetAlbumTrailer - tracks.go: GetTracksFileInfo, GetTracksSimilar, GetTrackTrailer, GetTrackFullInfo --- internal/client/albums.go | 21 +++ internal/client/artists.go | 138 +++++++++++++++ internal/client/tracks.go | 52 ++++++ internal/models/album.go | 104 +++++++++++ internal/models/artist.go | 350 +++++++++++++++++++++++++++++++++++++ internal/models/track.go | 118 +++++++++++++ 6 files changed, 783 insertions(+) diff --git a/internal/client/albums.go b/internal/client/albums.go index ffbef6c..9be15b6 100644 --- a/internal/client/albums.go +++ b/internal/client/albums.go @@ -3,6 +3,7 @@ package client import ( "context" + "fmt" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils" @@ -69,3 +70,23 @@ func (c *Client) GetAlbumsWithTracks(ctx context.Context, albumIDs []string) ([] } return albums, nil } + +// GetAlbumSimilarEntities gets similar entities for an album (GET /albums/{id}/similar-entities). +func (c *Client) GetAlbumSimilarEntities(ctx context.Context, albumID string) (*models.AlbumSimilarEntities, error) { + var result models.AlbumSimilarEntities + if err := c.request.Get(ctx, fmt.Sprintf("/albums/%s/similar-entities", albumID), nil, &result); err != nil { + return nil, err + } + result.SetClient(c) + return &result, nil +} + +// GetAlbumTrailer gets album trailer (GET /albums/{id}/trailer). +func (c *Client) GetAlbumTrailer(ctx context.Context, albumID string) (*models.AlbumTrailer, error) { + var result models.AlbumTrailer + if err := c.request.Get(ctx, fmt.Sprintf("/albums/%s/trailer", albumID), nil, &result); err != nil { + return nil, err + } + result.SetClient(c) + return &result, nil +} diff --git a/internal/client/artists.go b/internal/client/artists.go index 1222a55..b976d6e 100644 --- a/internal/client/artists.go +++ b/internal/client/artists.go @@ -77,3 +77,141 @@ func (c *Client) GetArtistTracks(ctx context.Context, artistID string, page, pag result.SetClient(c) return &result, nil } + +// GetArtistBriefInfo gets brief info about an artist (GET /artists/{artist_id}/brief-info). +func (c *Client) GetArtistBriefInfo(ctx context.Context, artistID string) (*models.ArtistBriefInfo, error) { + var result models.ArtistBriefInfo + if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/brief-info", artistID), nil, &result); err != nil { + return nil, err + } + result.SetClient(c) + return &result, nil +} + +// GetArtistSimilar gets similar artists (GET /artists/{artist_id}/similar). +func (c *Client) GetArtistSimilar(ctx context.Context, artistID string) (*models.ArtistSimilar, error) { + var result models.ArtistSimilar + if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/similar", artistID), nil, &result); err != nil { + return nil, err + } + result.SetClient(c) + return &result, nil +} + +// GetArtistLinks gets links to artist's pages (GET /artists/{artist_id}/artist-links). +func (c *Client) GetArtistLinks(ctx context.Context, artistID string) (*models.ArtistLinks, error) { + var result models.ArtistLinks + if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/artist-links", artistID), nil, &result); err != nil { + return nil, err + } + result.SetClient(c) + return &result, nil +} + +// GetArtistAlsoAlbums gets albums where artist participated (GET /artists/{artist_id}/also-albums). +func (c *Client) GetArtistAlsoAlbums(ctx context.Context, artistID string, page, pageSize int, sortBy string) (*models.ArtistAlbums, error) { + params := map[string]string{ + "page": fmt.Sprintf("%d", page), + "page-size": fmt.Sprintf("%d", pageSize), + "sort-by": sortBy, + } + var result models.ArtistAlbums + if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/also-albums", artistID), params, &result); err != nil { + return nil, err + } + result.SetClient(c) + return &result, nil +} + +// GetArtistDiscographyAlbums gets artist discography (GET /artists/{artist_id}/discography-albums). +func (c *Client) GetArtistDiscographyAlbums(ctx context.Context, artistID string, page, pageSize int, sortBy string) (*models.ArtistAlbums, error) { + params := map[string]string{ + "page": fmt.Sprintf("%d", page), + "page-size": fmt.Sprintf("%d", pageSize), + "sort-by": sortBy, + } + var result models.ArtistAlbums + if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/discography-albums", artistID), params, &result); err != nil { + return nil, err + } + result.SetClient(c) + return &result, nil +} + +// GetArtistSafeDirectAlbums gets safe direct albums (GET /artists/{artist_id}/safe-direct-albums). +func (c *Client) GetArtistSafeDirectAlbums(ctx context.Context, artistID string, sortBy, sortOrder string, limit int) (*models.ArtistAlbums, error) { + params := map[string]string{ + "sort-by": sortBy, + "sort-order": sortOrder, + "limit": fmt.Sprintf("%d", limit), + } + var result models.ArtistAlbums + if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/safe-direct-albums", artistID), params, &result); err != nil { + return nil, err + } + result.SetClient(c) + return &result, nil +} + +// GetArtistTrackIds gets track IDs by artist (GET /artists/{artist_id}/track-ids). +func (c *Client) GetArtistTrackIds(ctx context.Context, artistID string, page, pageSize int) ([]string, error) { + params := map[string]string{ + "page": fmt.Sprintf("%d", page), + "page-size": fmt.Sprintf("%d", pageSize), + } + var result []string + if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/track-ids", artistID), params, &result); err != nil { + return nil, err + } + return result, nil +} + +// GetArtistAbout gets "About artist" info (GET /artists/{artist_id}/about-artist). +func (c *Client) GetArtistAbout(ctx context.Context, artistID string) (*models.ArtistAbout, error) { + var result models.ArtistAbout + if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/about-artist", artistID), nil, &result); err != nil { + return nil, err + } + result.SetClient(c) + return &result, nil +} + +// GetArtistDonation gets artist donation info (GET /artists/{artist_id}/blocks/artist-donation). +func (c *Client) GetArtistDonation(ctx context.Context, artistID string) (*models.ArtistDonations, error) { + var result models.ArtistDonations + if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/blocks/artist-donation", artistID), nil, &result); err != nil { + return nil, err + } + result.SetClient(c) + return &result, nil +} + +// GetArtistInfo gets detailed artist info (GET /artists/{artist_id}/info). +func (c *Client) GetArtistInfo(ctx context.Context, artistID string) (*models.ArtistInfo, error) { + var result models.ArtistInfo + if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/info", artistID), nil, &result); err != nil { + return nil, err + } + result.SetClient(c) + return &result, nil +} + +// GetArtistSkeleton gets artist page skeleton (GET /artists/{artist_id}/skeletons/{skeleton_id}). +func (c *Client) GetArtistSkeleton(ctx context.Context, artistID, skeletonID string) (*models.ArtistSkeleton, error) { + var result models.ArtistSkeleton + if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/skeletons/%s", artistID, skeletonID), nil, &result); err != nil { + return nil, err + } + result.SetClient(c) + return &result, nil +} + +// GetArtistTrailer gets artist trailer (GET /artists/{artist_id}/trailer). +func (c *Client) GetArtistTrailer(ctx context.Context, artistID string) (*models.ArtistTrailerStatus, error) { + var result models.ArtistTrailerStatus + if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/trailer", artistID), nil, &result); err != nil { + return nil, err + } + result.SetClient(c) + return &result, nil +} diff --git a/internal/client/tracks.go b/internal/client/tracks.go index b7906be..1c6d9e9 100644 --- a/internal/client/tracks.go +++ b/internal/client/tracks.go @@ -6,6 +6,7 @@ import ( "crypto/md5" "encoding/xml" "fmt" + "strings" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils" @@ -172,6 +173,57 @@ func (c *Client) DownloadTrack(ctx context.Context, trackID, format string, bitr return c.request.Download(ctx, url) } +// GetTracksFileInfo gets file info for a track (GET /get-file-info). +func (c *Client) GetTracksFileInfo(ctx context.Context, trackID, quality string, codecs []string, transport string) (*models.FileInfo, error) { + params := map[string]string{ + "track_id": trackID, + "quality": quality, + } + if len(codecs) > 0 { + params["codecs"] = strings.Join(codecs, ",") + } + if transport != "" { + params["transport"] = transport + } + + var result models.FileInfo + if err := c.request.Get(ctx, "/get-file-info", params, &result); err != nil { + return nil, err + } + result.SetClient(c) + return &result, nil +} + +// GetTracksSimilar gets similar tracks (GET /tracks/{track_id}/similar). +func (c *Client) GetTracksSimilar(ctx context.Context, trackID string) (*models.SimilarTracks, error) { + var result models.SimilarTracks + if err := c.request.Get(ctx, fmt.Sprintf("/tracks/%s/similar", trackID), nil, &result); err != nil { + return nil, err + } + result.SetClient(c) + return &result, nil +} + +// GetTrackTrailer gets track trailer (GET /tracks/{track_id}/trailer). +func (c *Client) GetTrackTrailer(ctx context.Context, trackID string) (*models.TrackTrailer, error) { + var result models.TrackTrailer + if err := c.request.Get(ctx, fmt.Sprintf("/tracks/%s/trailer", trackID), nil, &result); err != nil { + return nil, err + } + result.SetClient(c) + return &result, nil +} + +// GetTrackFullInfo gets full track info (GET /tracks/{track_id}/full-info). +func (c *Client) GetTrackFullInfo(ctx context.Context, trackID string) (*models.TrackFullInfo, error) { + var result models.TrackFullInfo + if err := c.request.Get(ctx, fmt.Sprintf("/tracks/%s/full-info", trackID), nil, &result); err != nil { + return nil, err + } + result.SetClient(c) + return &result, nil +} + // SearchResult represents search results containing tracks. type SearchResult struct { Tracks *SearchTracksResult `json:"tracks"` diff --git a/internal/models/album.go b/internal/models/album.go index ec21f6a..0cd2336 100644 --- a/internal/models/album.go +++ b/internal/models/album.go @@ -422,3 +422,107 @@ type AlbumActionButton struct { ViewBrowser bool `json:"view_browser"` } +// AlbumSimilarEntities represents similar entities for an album. +type AlbumSimilarEntities struct { + BaseModel + Items []SimilarEntityItem `json:"items"` +} + +// DeJSON deserializes AlbumSimilarEntities from JSON data. +func (ae *AlbumSimilarEntities) DeJSON(data []byte, c ClientInterface) (Model, error) { + var raw map[string]json.RawMessage + if err := json.Unmarshal(data, &raw); err != nil { + return nil, err + } + + entities := &AlbumSimilarEntities{BaseModel: *NewBaseModel(c)} + + if v, ok := raw["items"]; ok { + var items []SimilarEntityItem + json.Unmarshal(v, &items) + entities.Items = items + } + + return entities, nil +} + +// DeList deserializes a list of AlbumSimilarEntities from JSON data. +func (ae *AlbumSimilarEntities) 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 { + entities := &AlbumSimilarEntities{BaseModel: *NewBaseModel(c)} + if err := json.Unmarshal(item, entities); err != nil { + continue + } + result = append(result, entities) + } + return result, nil +} + +// SimilarEntityItem represents a similar entity item. +type SimilarEntityItem struct { + BaseModel + ID JSONID `json:"id"` + Type string `json:"type"` + Data json.RawMessage `json:"data"` +} + +// AlbumTrailer represents album trailer info. +type AlbumTrailer struct { + BaseModel + Album *Album `json:"album"` + Artists []Artist `json:"artists"` + Trailer *TrailerInfo `json:"trailer"` +} + +// DeJSON deserializes AlbumTrailer from JSON data. +func (at *AlbumTrailer) 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 := &AlbumTrailer{BaseModel: *NewBaseModel(c)} + + if v, ok := raw["album"]; ok { + var album Album + json.Unmarshal(v, &album) + trailer.Album = &album + } + if v, ok := raw["artists"]; ok { + var artists []Artist + json.Unmarshal(v, &artists) + trailer.Artists = artists + } + if v, ok := raw["trailer"]; ok { + var trailerInfo TrailerInfo + json.Unmarshal(v, &trailerInfo) + trailer.Trailer = &trailerInfo + } + + return trailer, nil +} + +// DeList deserializes a list of AlbumTrailer from JSON data. +func (at *AlbumTrailer) 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 := &AlbumTrailer{BaseModel: *NewBaseModel(c)} + if err := json.Unmarshal(item, trailer); err != nil { + continue + } + result = append(result, trailer) + } + return result, nil +} + diff --git a/internal/models/artist.go b/internal/models/artist.go index d36a224..350f17f 100644 --- a/internal/models/artist.go +++ b/internal/models/artist.go @@ -193,12 +193,14 @@ type Cover struct { // ArtistBriefInfo represents brief artist info. type ArtistBriefInfo struct { + BaseModel Description string `json:"description"` Available bool `json:"available"` } // ArtistDonations represents artist donations info. type ArtistDonations struct { + BaseModel Items []DonationItem `json:"items"` Total int `json:"total"` Goal int `json:"goal"` @@ -535,3 +537,351 @@ func (c *ClipShort) DeList(data []byte, client ClientInterface) ([]Model, error) } return result, nil } + +// ArtistSimilar represents similar artists. +type ArtistSimilar struct { + BaseModel + Artist *Artist `json:"artist"` + SimilarArtists []Artist `json:"similar_artists"` +} + +// DeJSON deserializes ArtistSimilar from JSON data. +func (as *ArtistSimilar) DeJSON(data []byte, c ClientInterface) (Model, error) { + var raw map[string]json.RawMessage + if err := json.Unmarshal(data, &raw); err != nil { + return nil, err + } + + similar := &ArtistSimilar{BaseModel: *NewBaseModel(c)} + + if v, ok := raw["artist"]; ok { + var artist Artist + json.Unmarshal(v, &artist) + similar.Artist = &artist + } + if v, ok := raw["similar_artists"]; ok { + var artists []Artist + json.Unmarshal(v, &artists) + similar.SimilarArtists = artists + } + + return similar, nil +} + +// DeList deserializes a list of ArtistSimilar from JSON data. +func (as *ArtistSimilar) 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 { + similar := &ArtistSimilar{BaseModel: *NewBaseModel(c)} + if err := json.Unmarshal(item, similar); err != nil { + continue + } + result = append(result, similar) + } + return result, nil +} + +// ArtistLinks represents links to artist's official pages or social networks. +type ArtistLinks struct { + BaseModel + Links []Link `json:"links"` +} + +// DeJSON deserializes ArtistLinks from JSON data. +func (al *ArtistLinks) DeJSON(data []byte, c ClientInterface) (Model, error) { + var raw map[string]json.RawMessage + if err := json.Unmarshal(data, &raw); err != nil { + return nil, err + } + + links := &ArtistLinks{BaseModel: *NewBaseModel(c)} + + if v, ok := raw["links"]; ok { + var linkList []Link + json.Unmarshal(v, &linkList) + links.Links = linkList + } + + return links, nil +} + +// DeList deserializes a list of ArtistLinks from JSON data. +func (al *ArtistLinks) 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 { + links := &ArtistLinks{BaseModel: *NewBaseModel(c)} + if err := json.Unmarshal(item, links); err != nil { + continue + } + result = append(result, links) + } + return result, nil +} + +// ArtistAbout represents information "About artist". +type ArtistAbout struct { + BaseModel + Artist *Artist `json:"artist"` + Stats *Stats `json:"stats"` + Description string `json:"description"` + Links []ArtistLink `json:"links"` + Covers []Cover `json:"covers"` + ArtistType string `json:"artist_type"` +} + +// DeJSON deserializes ArtistAbout from JSON data. +func (aa *ArtistAbout) DeJSON(data []byte, c ClientInterface) (Model, error) { + var raw map[string]json.RawMessage + if err := json.Unmarshal(data, &raw); err != nil { + return nil, err + } + + about := &ArtistAbout{BaseModel: *NewBaseModel(c)} + + if v, ok := raw["artist"]; ok { + var artist Artist + json.Unmarshal(v, &artist) + about.Artist = &artist + } + if v, ok := raw["stats"]; ok { + var stats Stats + json.Unmarshal(v, &stats) + about.Stats = &stats + } + if v, ok := raw["description"]; ok { + json.Unmarshal(v, &about.Description) + } + if v, ok := raw["links"]; ok { + var linkList []ArtistLink + json.Unmarshal(v, &linkList) + about.Links = linkList + } + if v, ok := raw["covers"]; ok { + var covers []Cover + json.Unmarshal(v, &covers) + about.Covers = covers + } + if v, ok := raw["artist_type"]; ok { + json.Unmarshal(v, &about.ArtistType) + } + + return about, nil +} + +// DeList deserializes a list of ArtistAbout from JSON data. +func (aa *ArtistAbout) 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 { + about := &ArtistAbout{BaseModel: *NewBaseModel(c)} + if err := json.Unmarshal(item, about); err != nil { + continue + } + result = append(result, about) + } + return result, nil +} + +// ArtistInfo represents detailed artist info. +type ArtistInfo struct { + BaseModel + Artist *Artist `json:"artist"` + LikesCount int `json:"likes_count"` + Stats *Stats `json:"stats"` + Trailer *ArtistTrailerStatus `json:"trailer"` + Covers []Cover `json:"covers"` + Description string `json:"description"` + ArtistType string `json:"artist_type"` +} + +// DeJSON deserializes ArtistInfo from JSON data. +func (ai *ArtistInfo) 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 := &ArtistInfo{BaseModel: *NewBaseModel(c)} + + if v, ok := raw["artist"]; ok { + var artist Artist + json.Unmarshal(v, &artist) + info.Artist = &artist + } + if v, ok := raw["likes_count"]; ok { + json.Unmarshal(v, &info.LikesCount) + } + if v, ok := raw["stats"]; ok { + var stats Stats + json.Unmarshal(v, &stats) + info.Stats = &stats + } + if v, ok := raw["trailer"]; ok { + var trailer ArtistTrailerStatus + json.Unmarshal(v, &trailer) + info.Trailer = &trailer + } + if v, ok := raw["covers"]; ok { + var covers []Cover + json.Unmarshal(v, &covers) + info.Covers = covers + } + if v, ok := raw["description"]; ok { + json.Unmarshal(v, &info.Description) + } + if v, ok := raw["artist_type"]; ok { + json.Unmarshal(v, &info.ArtistType) + } + + return info, nil +} + +// DeList deserializes a list of ArtistInfo from JSON data. +func (ai *ArtistInfo) 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 := &ArtistInfo{BaseModel: *NewBaseModel(c)} + if err := json.Unmarshal(item, info); err != nil { + continue + } + result = append(result, info) + } + return result, nil +} + +// ArtistSkeleton represents artist page skeleton. +type ArtistSkeleton struct { + BaseModel + ID string `json:"id"` + Title string `json:"title"` + Blocks []SkeletonBlock `json:"blocks"` +} + +// DeJSON deserializes ArtistSkeleton from JSON data. +func (as *ArtistSkeleton) DeJSON(data []byte, c ClientInterface) (Model, error) { + var raw map[string]json.RawMessage + if err := json.Unmarshal(data, &raw); err != nil { + return nil, err + } + + skeleton := &ArtistSkeleton{BaseModel: *NewBaseModel(c)} + + if v, ok := raw["id"]; ok { + json.Unmarshal(v, &skeleton.ID) + } + if v, ok := raw["title"]; ok { + json.Unmarshal(v, &skeleton.Title) + } + if v, ok := raw["blocks"]; ok { + var blocks []SkeletonBlock + json.Unmarshal(v, &blocks) + skeleton.Blocks = blocks + } + + return skeleton, nil +} + +// DeList deserializes a list of ArtistSkeleton from JSON data. +func (as *ArtistSkeleton) 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 { + skeleton := &ArtistSkeleton{BaseModel: *NewBaseModel(c)} + if err := json.Unmarshal(item, skeleton); err != nil { + continue + } + result = append(result, skeleton) + } + return result, nil +} + +// ArtistTrailerStatus represents trailer availability for artist. +type ArtistTrailerStatus struct { + BaseModel + Available bool `json:"available"` +} + +// DeJSON deserializes ArtistTrailerStatus from JSON data. +func (ats *ArtistTrailerStatus) 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 := &ArtistTrailerStatus{BaseModel: *NewBaseModel(c)} + + if v, ok := raw["available"]; ok { + json.Unmarshal(v, &status.Available) + } + + return status, nil +} + +// DeList deserializes a list of ArtistTrailerStatus from JSON data. +func (ats *ArtistTrailerStatus) 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 := &ArtistTrailerStatus{BaseModel: *NewBaseModel(c)} + if err := json.Unmarshal(item, status); err != nil { + continue + } + result = append(result, status) + } + return result, nil +} + +// ArtistLink represents a link for artist about page. +type ArtistLink struct { + BaseModel + Title string `json:"title"` + Subtitle string `json:"subtitle,omitempty"` + URL string `json:"url,omitempty"` + ImgURL string `json:"img_url,omitempty"` +} + +// Stats represents artist stats. +type Stats struct { + BaseModel + Rating int `json:"rating"` + Votes int `json:"votes"` + Month int `json:"month"` + Week int `json:"week"` + Day int `json:"day"` +} + +// SkeletonBlock represents a skeleton block. +type SkeletonBlock struct { + BaseModel + ID string `json:"id"` + Type string `json:"type"` + Data json.RawMessage `json:"data"` +} + diff --git a/internal/models/track.go b/internal/models/track.go index 767c43e..15d78a7 100644 --- a/internal/models/track.go +++ b/internal/models/track.go @@ -535,3 +535,121 @@ func (tt *TrackTrailer) DeList(data []byte, c ClientInterface) ([]Model, error) } return result, nil } + +// FileInfo represents file info for a track. +type FileInfo struct { + BaseModel + DownloadInfo *FileDownloadInfo `json:"download_info"` +} + +// DeJSON deserializes FileInfo from JSON data. +func (fi *FileInfo) 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 := &FileInfo{BaseModel: *NewBaseModel(c)} + + if v, ok := raw["download_info"]; ok { + var di FileDownloadInfo + json.Unmarshal(v, &di) + info.DownloadInfo = &di + } + + return info, nil +} + +// DeList deserializes a list of FileInfo from JSON data. +func (fi *FileInfo) 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 := &FileInfo{BaseModel: *NewBaseModel(c)} + if err := json.Unmarshal(item, info); err != nil { + continue + } + result = append(result, info) + } + return result, nil +} + +// FileDownloadInfo represents file download info. +type FileDownloadInfo struct { + BaseModel + TrackID string `json:"track_id"` + Quality string `json:"quality"` + Codec string `json:"codec"` + Bitrate int `json:"bitrate"` + Transport string `json:"transport"` + URL string `json:"url"` + URLs []string `json:"urls"` + RealID string `json:"real_id"` + Gain bool `json:"gain"` + Key string `json:"key"` +} + +// DeJSON deserializes FileDownloadInfo from JSON data. +func (fdi *FileDownloadInfo) 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 := &FileDownloadInfo{BaseModel: *NewBaseModel(c)} + + if v, ok := raw["track_id"]; ok { + json.Unmarshal(v, &info.TrackID) + } + if v, ok := raw["quality"]; ok { + json.Unmarshal(v, &info.Quality) + } + if v, ok := raw["codec"]; ok { + json.Unmarshal(v, &info.Codec) + } + if v, ok := raw["bitrate"]; ok { + json.Unmarshal(v, &info.Bitrate) + } + if v, ok := raw["transport"]; ok { + json.Unmarshal(v, &info.Transport) + } + if v, ok := raw["url"]; ok { + json.Unmarshal(v, &info.URL) + } + if v, ok := raw["urls"]; ok { + json.Unmarshal(v, &info.URLs) + } + if v, ok := raw["real_id"]; ok { + json.Unmarshal(v, &info.RealID) + } + if v, ok := raw["gain"]; ok { + json.Unmarshal(v, &info.Gain) + } + if v, ok := raw["key"]; ok { + json.Unmarshal(v, &info.Key) + } + + return info, nil +} + +// DeList deserializes a list of FileDownloadInfo from JSON data. +func (fdi *FileDownloadInfo) 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 := &FileDownloadInfo{BaseModel: *NewBaseModel(c)} + if err := json.Unmarshal(item, info); err != nil { + continue + } + result = append(result, info) + } + return result, nil +}