Compare commits
11
Commits
dbb823ade3
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dbf9a46bb2 | ||
|
|
73775a8526 | ||
|
|
49920d8908 | ||
|
|
9d3bcc4a9b | ||
|
|
463e0bcd1a | ||
|
|
bc026d82b4 | ||
|
|
a9be10b5e5 | ||
|
|
577c4a418c | ||
|
|
277a3e6924 | ||
|
|
6312c24b0f | ||
|
|
ad83d84531 |
No files matched your search
@@ -3,6 +3,7 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models"
|
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models"
|
||||||
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils"
|
"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
|
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
|
||||||
|
}
|
||||||
@@ -77,3 +77,141 @@ func (c *Client) GetArtistTracks(ctx context.Context, artistID string, page, pag
|
|||||||
result.SetClient(c)
|
result.SetClient(c)
|
||||||
return &result, nil
|
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
|
||||||
|
}
|
||||||
@@ -52,6 +52,16 @@ func (c *Client) GetArtistClips(ctx context.Context, artistID string) (*models.A
|
|||||||
return &result, nil
|
return &result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetClipsDisclaimer gets clips disclaimer (GET /clips/{id}/disclaimer).
|
||||||
|
func (c *Client) GetClipsDisclaimer(ctx context.Context, clipID string) (*models.Disclaimer, error) {
|
||||||
|
var result models.Disclaimer
|
||||||
|
if err := c.request.Get(ctx, fmt.Sprintf("/clips/%s/disclaimer", clipID), nil, &result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result.SetClient(c)
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetShots gets shots with pagination (GET /shots).
|
// GetShots gets shots with pagination (GET /shots).
|
||||||
func (c *Client) GetShots(ctx context.Context, page, pageSize int) ([]*models.Shot, error) {
|
func (c *Client) GetShots(ctx context.Context, page, pageSize int) ([]*models.Shot, error) {
|
||||||
params := map[string]string{
|
params := map[string]string{
|
||||||
|
|||||||
@@ -63,3 +63,23 @@ func (c *Client) GetConcertTabs(ctx context.Context) (*models.ConcertTabConfig,
|
|||||||
}
|
}
|
||||||
return &result, nil
|
return &result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetArtistConcerts gets concerts by artist (GET /artists/{id}/concerts).
|
||||||
|
func (c *Client) GetArtistConcerts(ctx context.Context, artistID string) (*models.ArtistConcerts, error) {
|
||||||
|
var result models.ArtistConcerts
|
||||||
|
if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/concerts", artistID), nil, &result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result.SetClient(c)
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConcertSkeleton gets concert skeleton (GET /concerts/{id}/skeletons/{skeleton_id}).
|
||||||
|
func (c *Client) GetConcertSkeleton(ctx context.Context, concertID, skeletonID string) (*models.ConcertSkeleton, error) {
|
||||||
|
var result models.ConcertSkeleton
|
||||||
|
if err := c.request.Get(ctx, fmt.Sprintf("/concerts/%s/skeletons/%s", concertID, skeletonID), nil, &result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result.SetClient(c)
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
@@ -7,6 +7,21 @@ import (
|
|||||||
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models"
|
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GetQueuesList gets the queues list (GET /queues).
|
||||||
|
func (c *Client) GetQueuesList(ctx context.Context) ([]*models.QueueItem, error) {
|
||||||
|
var result []models.QueueItem
|
||||||
|
if err := c.request.Get(ctx, "/queues", nil, &result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
items := make([]*models.QueueItem, len(result))
|
||||||
|
for i := range result {
|
||||||
|
result[i].SetClient(c)
|
||||||
|
items[i] = &result[i]
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetQueue gets the current queue
|
// GetQueue gets the current queue
|
||||||
func (c *Client) GetQueue(ctx context.Context) (*models.Queue, error) {
|
func (c *Client) GetQueue(ctx context.Context) (*models.Queue, error) {
|
||||||
var result models.Queue
|
var result models.Queue
|
||||||
|
|||||||
@@ -121,3 +121,61 @@ func (c *Client) RotorSessionFeedback(ctx context.Context, sessionID, trackID, f
|
|||||||
var result map[string]any
|
var result map[string]any
|
||||||
return c.request.Post(ctx, fmt.Sprintf("/rotor/station/%s/feedback", sessionID), params, &result)
|
return c.request.Post(ctx, fmt.Sprintf("/rotor/station/%s/feedback", sessionID), params, &result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRotorStationsDashboard gets rotor stations dashboard (GET /rotor/stations/dashboard).
|
||||||
|
func (c *Client) GetRotorStationsDashboard(ctx context.Context) (*models.Dashboard, error) {
|
||||||
|
var result models.Dashboard
|
||||||
|
if err := c.request.Get(ctx, "/rotor/stations/dashboard", nil, &result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result.SetClient(c)
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRotorStationsList gets rotor stations list (GET /rotor/stations/list).
|
||||||
|
func (c *Client) GetRotorStationsList(ctx context.Context) ([]*models.StationResult, error) {
|
||||||
|
var result []models.StationResult
|
||||||
|
if err := c.request.Get(ctx, "/rotor/stations/list", nil, &result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
stations := make([]*models.StationResult, len(result))
|
||||||
|
for i := range result {
|
||||||
|
result[i].SetClient(c)
|
||||||
|
stations[i] = &result[i]
|
||||||
|
}
|
||||||
|
return stations, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRotorStationInfo gets rotor station info (GET /rotor/station/{id}/info).
|
||||||
|
func (c *Client) GetRotorStationInfo(ctx context.Context, stationID string) (*models.RotorStation, error) {
|
||||||
|
var result models.RotorStation
|
||||||
|
if err := c.request.Get(ctx, fmt.Sprintf("/rotor/station/%s/info", stationID), nil, &result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result.SetClient(c)
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRotorStationStream gets rotor station stream (GET /rotor/station/{id}/stream).
|
||||||
|
func (c *Client) GetRotorStationStream(ctx context.Context, stationID string) (*models.GenerativeStream, error) {
|
||||||
|
var result models.GenerativeStream
|
||||||
|
if err := c.request.Get(ctx, fmt.Sprintf("/rotor/station/%s/stream", stationID), nil, &result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result.SetClient(c)
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRotorStationSettings gets rotor station settings (POST /rotor/station/{id}/settings3).
|
||||||
|
func (c *Client) GetRotorStationSettings(ctx context.Context, stationID string, moodEnergy, diversity, language string) (bool, error) {
|
||||||
|
params := map[string]string{
|
||||||
|
"mood-energy": moodEnergy,
|
||||||
|
"diversity": diversity,
|
||||||
|
"language": language,
|
||||||
|
}
|
||||||
|
var result bool
|
||||||
|
if err := c.request.Post(ctx, fmt.Sprintf("/rotor/station/%s/settings3", stationID), params, &result); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
@@ -129,6 +129,26 @@ type Sign struct {
|
|||||||
Value string
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTracksCredits gets track credits (GET /tracks/{id}/credits).
|
||||||
|
func (c *Client) GetTracksCredits(ctx context.Context, trackID string) (*models.Credits, error) {
|
||||||
|
var result models.Credits
|
||||||
|
if err := c.request.Get(ctx, fmt.Sprintf("/tracks/%s/credits", trackID), nil, &result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result.SetClient(c)
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetClipsCredits gets clip credits (GET /clips/{id}/credits).
|
||||||
|
func (c *Client) GetClipsCredits(ctx context.Context, clipID string) (*models.Credits, error) {
|
||||||
|
var result models.Credits
|
||||||
|
if err := c.request.Get(ctx, fmt.Sprintf("/clips/%s/credits", clipID), nil, &result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result.SetClient(c)
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetSignRequest creates a signature for a track request (exported for external use).
|
// GetSignRequest creates a signature for a track request (exported for external use).
|
||||||
// It delegates to utils.GetSignRequest with the default signing key.
|
// It delegates to utils.GetSignRequest with the default signing key.
|
||||||
func GetSignRequest(trackID string) Sign {
|
func GetSignRequest(trackID string) Sign {
|
||||||
|
|||||||
+145
-11
@@ -3,12 +3,48 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models"
|
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models"
|
||||||
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils"
|
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const downloadSignSalt = "XGRlBW9FXlekgbPrRHuSiA"
|
||||||
|
|
||||||
|
// downloadInfoXML represents the XML response from download_info_url.
|
||||||
|
type downloadInfoXML struct {
|
||||||
|
Host string `xml:"host"`
|
||||||
|
Path string `xml:"path"`
|
||||||
|
Ts string `xml:"ts"`
|
||||||
|
S string `xml:"s"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolveDownloadURL downloads the XML from downloadInfoURL, parses host/path/ts/s,
|
||||||
|
// computes the MD5 signature, and returns the signed download URL:
|
||||||
|
// https://{host}/get-mp3/{sign}/{ts}{path}
|
||||||
|
func resolveDownloadURL(ctx context.Context, downloadInfoURL string, ri models.RequestInterface) (string, error) {
|
||||||
|
data, err := ri.Download(ctx, downloadInfoURL)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var xmlData downloadInfoXML
|
||||||
|
if err := xml.Unmarshal(data, &xmlData); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to parse download info XML: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if xmlData.Path == "" || xmlData.S == "" {
|
||||||
|
return "", utils.NewYandexMusicError("invalid download info XML: missing path or s", utils.ErrBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
sign := fmt.Sprintf("%x", md5.Sum([]byte(downloadSignSalt+xmlData.Path[1:]+xmlData.S)))
|
||||||
|
|
||||||
|
return fmt.Sprintf("https://%s/get-mp3/%s/%s%s", xmlData.Host, sign, xmlData.Ts, xmlData.Path), nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetTracks gets tracks by their IDs (GET /tracks).
|
// GetTracks gets tracks by their IDs (GET /tracks).
|
||||||
// Returns a slice of Track pointers, or nil if no IDs are provided.
|
// Returns a slice of Track pointers, or nil if no IDs are provided.
|
||||||
func (c *Client) GetTracks(ctx context.Context, trackIDs []string) ([]*models.Track, error) {
|
func (c *Client) GetTracks(ctx context.Context, trackIDs []string) ([]*models.Track, error) {
|
||||||
@@ -49,11 +85,12 @@ func (c *Client) GetTrack(ctx context.Context, trackID string) (*models.Track, e
|
|||||||
// GetTrackDownloadInfo gets download info for a track (GET /tracks/{track_id}/download-info).
|
// GetTrackDownloadInfo gets download info for a track (GET /tracks/{track_id}/download-info).
|
||||||
// The quality parameter specifies the desired audio quality (e.g., "mp3").
|
// The quality parameter specifies the desired audio quality (e.g., "mp3").
|
||||||
func (c *Client) GetTrackDownloadInfo(ctx context.Context, trackID, quality string) ([]models.DownloadInfo, error) {
|
func (c *Client) GetTrackDownloadInfo(ctx context.Context, trackID, quality string) ([]models.DownloadInfo, error) {
|
||||||
// This requires signing - use the sign utility
|
|
||||||
// For now, return basic implementation
|
|
||||||
params := map[string]string{
|
params := map[string]string{
|
||||||
"track_id": trackID,
|
"track_id": trackID,
|
||||||
}
|
}
|
||||||
|
if quality != "" {
|
||||||
|
params["quality"] = quality
|
||||||
|
}
|
||||||
|
|
||||||
var result []models.DownloadInfo
|
var result []models.DownloadInfo
|
||||||
if err := c.request.Get(ctx, fmt.Sprintf("/tracks/%s/download-info", trackID), params, &result); err != nil {
|
if err := c.request.Get(ctx, fmt.Sprintf("/tracks/%s/download-info", trackID), params, &result); err != nil {
|
||||||
@@ -62,8 +99,45 @@ func (c *Client) GetTrackDownloadInfo(ctx context.Context, trackID, quality stri
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTrackDownloadURL returns a signed direct download URL for a track.
|
||||||
|
// It fetches download info, finds a matching entry, downloads the XML from its
|
||||||
|
// download_info_url, parses host/path/ts/s, computes the MD5 signature, and
|
||||||
|
// returns https://{host}/get-mp3/{sign}/{ts}{path}.
|
||||||
|
func (c *Client) GetTrackDownloadURL(ctx context.Context, trackID, format string, bitrate int) (string, error) {
|
||||||
|
info, err := c.GetTrackDownloadInfo(ctx, trackID, format)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find matching download info
|
||||||
|
var di *models.DownloadInfo
|
||||||
|
for i := range info {
|
||||||
|
if info[i].Codec == format && (bitrate == 0 || info[i].Bitrate == bitrate) {
|
||||||
|
di = &info[i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if di == nil && len(info) > 0 {
|
||||||
|
di = &info[0]
|
||||||
|
}
|
||||||
|
if di == nil {
|
||||||
|
return "", utils.NewYandexMusicError("no download info available", utils.ErrNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
if di.URL != "" {
|
||||||
|
return di.URL, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if di.DownloadInfoURL == "" {
|
||||||
|
return "", utils.NewYandexMusicError("no download URL available", utils.ErrNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolveDownloadURL(ctx, di.DownloadInfoURL, c.request)
|
||||||
|
}
|
||||||
|
|
||||||
// DownloadTrack downloads a track by ID.
|
// DownloadTrack downloads a track by ID.
|
||||||
// It first fetches download info, finds a matching format/bitrate, then downloads the file content.
|
// It first fetches download info, finds a matching format/bitrate, resolves the
|
||||||
|
// signed URL if needed, then downloads the file content.
|
||||||
func (c *Client) DownloadTrack(ctx context.Context, trackID, format string, bitrate int) ([]byte, error) {
|
func (c *Client) DownloadTrack(ctx context.Context, trackID, format string, bitrate int) ([]byte, error) {
|
||||||
info, err := c.GetTrackDownloadInfo(ctx, trackID, format)
|
info, err := c.GetTrackDownloadInfo(ctx, trackID, format)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -71,25 +145,85 @@ func (c *Client) DownloadTrack(ctx context.Context, trackID, format string, bitr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find matching download info
|
// Find matching download info
|
||||||
var url string
|
var di *models.DownloadInfo
|
||||||
for _, di := range info {
|
for i := range info {
|
||||||
if di.Codec == format && (bitrate == 0 || di.Bitrate == bitrate) {
|
if info[i].Codec == format && (bitrate == 0 || info[i].Bitrate == bitrate) {
|
||||||
url = di.URL
|
di = &info[i]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if di == nil && len(info) > 0 {
|
||||||
if url == "" && len(info) > 0 {
|
di = &info[0]
|
||||||
url = info[0].URL
|
}
|
||||||
|
if di == nil {
|
||||||
|
return nil, utils.NewYandexMusicError("no download info available", utils.ErrNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
url := di.URL
|
||||||
if url == "" {
|
if url == "" {
|
||||||
return nil, utils.NewYandexMusicError("no download URL available", utils.ErrNotFound)
|
if di.DownloadInfoURL == "" {
|
||||||
|
return nil, utils.NewYandexMusicError("no download URL available", utils.ErrNotFound)
|
||||||
|
}
|
||||||
|
url, err = resolveDownloadURL(ctx, di.DownloadInfoURL, c.request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.request.Download(ctx, url)
|
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.
|
// SearchResult represents search results containing tracks.
|
||||||
type SearchResult struct {
|
type SearchResult struct {
|
||||||
Tracks *SearchTracksResult `json:"tracks"`
|
Tracks *SearchTracksResult `json:"tracks"`
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func TestClient_GetTracks(t *testing.T) {
|
|||||||
"id": "12345",
|
"id": "12345",
|
||||||
"title": "Test Track",
|
"title": "Test Track",
|
||||||
"duration_ms": 180000,
|
"duration_ms": 180000,
|
||||||
"storage_id": "67890",
|
"storage_dir": "67890",
|
||||||
"cover_uri": "https://example.com/cover.jpg",
|
"cover_uri": "https://example.com/cover.jpg",
|
||||||
"genre": "rock",
|
"genre": "rock",
|
||||||
"version": "album",
|
"version": "album",
|
||||||
@@ -42,7 +42,7 @@ func TestClient_GetTracks(t *testing.T) {
|
|||||||
"artists": [{"id": "1", "name": "Artist 1", "cover_uri": "https://example.com/artist.jpg"}],
|
"artists": [{"id": "1", "name": "Artist 1", "cover_uri": "https://example.com/artist.jpg"}],
|
||||||
"albums": [{"id": "789", "title": "Test Album", "cover_uri": "https://example.com/album.jpg", "year": 2023}],
|
"albums": [{"id": "789", "title": "Test Album", "cover_uri": "https://example.com/album.jpg", "year": 2023}],
|
||||||
"major": {"id": "major1", "name": "Major Label", "description": "Big label"},
|
"major": {"id": "major1", "name": "Major Label", "description": "Big label"},
|
||||||
"lyrics": {"id": "lyr1", "text": "Test lyrics", "available": true, "has_text": true}
|
"lyrics_info": {"has_available_sync_lyrics": true, "has_available_text_lyrics": true}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}`),
|
}`),
|
||||||
@@ -63,8 +63,8 @@ func TestClient_GetTracks(t *testing.T) {
|
|||||||
if tr.Major == nil || tr.Major.Name != "Major Label" {
|
if tr.Major == nil || tr.Major.Name != "Major Label" {
|
||||||
t.Errorf("Expected Major 'Major Label'")
|
t.Errorf("Expected Major 'Major Label'")
|
||||||
}
|
}
|
||||||
if tr.Lyrics == nil || tr.Lyrics.Text != "Test lyrics" {
|
if tr.LyricsInfo == nil || tr.LyricsInfo.HasAvailableSyncLyrics != true {
|
||||||
t.Errorf("Expected Lyrics 'Test lyrics'")
|
t.Errorf("Expected LyricsInfo.HasAvailableSyncLyrics true")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -73,8 +73,8 @@ func TestClient_GetTracks(t *testing.T) {
|
|||||||
trackIDs: []string{"12345", "67890"},
|
trackIDs: []string{"12345", "67890"},
|
||||||
mockResponse: []byte(`{
|
mockResponse: []byte(`{
|
||||||
"result": [
|
"result": [
|
||||||
{"id": "12345", "title": "Track 1", "duration_ms": 100000, "storage_id": "1", "cover_uri": "", "genre": "", "version": "", "explicit": false, "available": true, "available_for_premium_users": true, "r128": 0, "track_sharing_flag": "", "artists": [], "albums": []},
|
{"id": "12345", "title": "Track 1", "duration_ms": 100000, "storage_dir": "1", "cover_uri": "", "genre": "", "version": "", "explicit": false, "available": true, "available_for_premium_users": true, "r128": 0, "track_sharing_flag": "", "artists": [], "albums": []},
|
||||||
{"id": "67890", "title": "Track 2", "duration_ms": 200000, "storage_id": "2", "cover_uri": "", "genre": "", "version": "", "explicit": false, "available": true, "available_for_premium_users": true, "r128": 0, "track_sharing_flag": "", "artists": [], "albums": []}
|
{"id": "67890", "title": "Track 2", "duration_ms": 200000, "storage_dir": "2", "cover_uri": "", "genre": "", "version": "", "explicit": false, "available": true, "available_for_premium_users": true, "r128": 0, "track_sharing_flag": "", "artists": [], "albums": []}
|
||||||
]
|
]
|
||||||
}`),
|
}`),
|
||||||
expectedTracks: 2,
|
expectedTracks: 2,
|
||||||
@@ -189,7 +189,7 @@ func TestClient_GetTrack(t *testing.T) {
|
|||||||
"id": "12345",
|
"id": "12345",
|
||||||
"title": "Test Track",
|
"title": "Test Track",
|
||||||
"duration_ms": 180000,
|
"duration_ms": 180000,
|
||||||
"storage_id": "67890",
|
"storage_dir": "67890",
|
||||||
"cover_uri": "https://example.com/cover.jpg",
|
"cover_uri": "https://example.com/cover.jpg",
|
||||||
"genre": "rock",
|
"genre": "rock",
|
||||||
"version": "album",
|
"version": "album",
|
||||||
@@ -201,7 +201,7 @@ func TestClient_GetTrack(t *testing.T) {
|
|||||||
"artists": [{"id": "1", "name": "Artist 1", "cover_uri": "https://example.com/artist.jpg"}],
|
"artists": [{"id": "1", "name": "Artist 1", "cover_uri": "https://example.com/artist.jpg"}],
|
||||||
"albums": [{"id": "789", "title": "Test Album", "cover_uri": "https://example.com/album.jpg", "year": 2023}],
|
"albums": [{"id": "789", "title": "Test Album", "cover_uri": "https://example.com/album.jpg", "year": 2023}],
|
||||||
"major": {"id": "major1", "name": "Major Label", "description": "Big label"},
|
"major": {"id": "major1", "name": "Major Label", "description": "Big label"},
|
||||||
"lyrics": {"id": "lyr1", "text": "Test lyrics", "available": true, "has_text": true}
|
"lyrics_info": {"has_available_sync_lyrics": true, "has_available_text_lyrics": true}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}`),
|
}`),
|
||||||
@@ -311,7 +311,7 @@ func TestClient_SearchTracks(t *testing.T) {
|
|||||||
"id": "1",
|
"id": "1",
|
||||||
"title": "Track 1",
|
"title": "Track 1",
|
||||||
"duration_ms": 100000,
|
"duration_ms": 100000,
|
||||||
"storage_id": "1",
|
"storage_dir": "1",
|
||||||
"cover_uri": "",
|
"cover_uri": "",
|
||||||
"genre": "",
|
"genre": "",
|
||||||
"version": "",
|
"version": "",
|
||||||
@@ -327,7 +327,7 @@ func TestClient_SearchTracks(t *testing.T) {
|
|||||||
"id": "2",
|
"id": "2",
|
||||||
"title": "Track 2",
|
"title": "Track 2",
|
||||||
"duration_ms": 200000,
|
"duration_ms": 200000,
|
||||||
"storage_id": "2",
|
"storage_dir": "2",
|
||||||
"cover_uri": "",
|
"cover_uri": "",
|
||||||
"genre": "",
|
"genre": "",
|
||||||
"version": "",
|
"version": "",
|
||||||
@@ -408,7 +408,7 @@ func TestClient_SearchTracks_Mocked(t *testing.T) {
|
|||||||
"id": "1",
|
"id": "1",
|
||||||
"title": "Track 1",
|
"title": "Track 1",
|
||||||
"duration_ms": 100000,
|
"duration_ms": 100000,
|
||||||
"storage_id": "1",
|
"storage_dir": "1",
|
||||||
"cover_uri": "",
|
"cover_uri": "",
|
||||||
"genre": "",
|
"genre": "",
|
||||||
"version": "",
|
"version": "",
|
||||||
@@ -424,7 +424,7 @@ func TestClient_SearchTracks_Mocked(t *testing.T) {
|
|||||||
"id": "2",
|
"id": "2",
|
||||||
"title": "Track 2",
|
"title": "Track 2",
|
||||||
"duration_ms": 200000,
|
"duration_ms": 200000,
|
||||||
"storage_id": "2",
|
"storage_dir": "2",
|
||||||
"cover_uri": "",
|
"cover_uri": "",
|
||||||
"genre": "",
|
"genre": "",
|
||||||
"version": "",
|
"version": "",
|
||||||
|
|||||||
+282
-29
@@ -8,31 +8,118 @@ import (
|
|||||||
// Album represents a full album from the Yandex Music API.
|
// Album represents a full album from the Yandex Music API.
|
||||||
type Album struct {
|
type Album struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
ID JSONID `json:"id"`
|
ID JSONID `json:"id"`
|
||||||
Title string `json:"title"`
|
Error string `json:"error"`
|
||||||
CoverURI string `json:"cover_uri"`
|
Title string `json:"title"`
|
||||||
Genre string `json:"genre"`
|
TrackCount int `json:"track_count"`
|
||||||
Year int `json:"year"`
|
Artists []Artist `json:"artists"`
|
||||||
TrackCount int `json:"track_count"`
|
Labels []LabelOrString `json:"labels"`
|
||||||
Available bool `json:"available"`
|
Available bool `json:"available"`
|
||||||
ReleaseDate string `json:"release_date"`
|
AvailableForPremiumUsers bool `json:"available_for_premium_users"`
|
||||||
Artists []Artist `json:"artists"`
|
Version string `json:"version"`
|
||||||
Labels []Label `json:"labels"`
|
CoverURI string `json:"cover_uri"`
|
||||||
Volumes [][]Track `json:"volumes"`
|
ContentWarning string `json:"content_warning"`
|
||||||
Description string `json:"description"`
|
OriginalReleaseYear int `json:"original_release_year"`
|
||||||
Meta *AlbumMeta `json:"meta"`
|
Genre string `json:"genre"`
|
||||||
Best *Best `json:"best"`
|
TextColor string `json:"text_color"`
|
||||||
// Additional fields
|
ShortDescription string `json:"short_description"`
|
||||||
Region string `json:"region"`
|
Description string `json:"description"`
|
||||||
AvailableForPremiumUsers bool `json:"available_for_premium_users"`
|
IsPremiere bool `json:"is_premiere"`
|
||||||
AvailableForPremiumUsersOnly bool `json:"available_for_premium_users_only"`
|
IsBanner bool `json:"is_banner"`
|
||||||
ContentWarning string `json:"content_warning"`
|
MetaType string `json:"meta_type"`
|
||||||
Label string `json:"label"`
|
StorageDir string `json:"storage_dir"`
|
||||||
CatalogNumber string `json:"catalog_number"`
|
OgImage string `json:"og_image"`
|
||||||
Options []string `json:"options"`
|
Buy []json.RawMessage `json:"buy"`
|
||||||
TrackPositions []TrackPosition `json:"track_positions"`
|
Recent bool `json:"recent"`
|
||||||
SimilarAlbums []Album `json:"similar_albums"`
|
VeryImportant bool `json:"very_important"`
|
||||||
Trailer *TrailerInfo `json:"trailer"`
|
AvailableForMobile bool `json:"available_for_mobile"`
|
||||||
|
AvailablePartially bool `json:"available_partially"`
|
||||||
|
Bests []int `json:"bests"`
|
||||||
|
Duplicates []Album `json:"duplicates"`
|
||||||
|
Prerolls []json.RawMessage `json:"prerolls"`
|
||||||
|
Volumes [][]Track `json:"volumes"`
|
||||||
|
Year int `json:"year"`
|
||||||
|
ReleaseDate string `json:"release_date"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
TrackPosition *TrackPosition `json:"track_position"`
|
||||||
|
Regions []string `json:"regions"`
|
||||||
|
AvailableAsRbt bool `json:"available_as_rbt"`
|
||||||
|
LyricsAvailable bool `json:"lyrics_available"`
|
||||||
|
RememberPosition bool `json:"remember_position"`
|
||||||
|
Albums []Album `json:"albums"`
|
||||||
|
DurationMs int `json:"duration_ms"`
|
||||||
|
Explicit bool `json:"explicit"`
|
||||||
|
StartDate string `json:"start_date"`
|
||||||
|
LikesCount int `json:"likes_count"`
|
||||||
|
Deprecation *Deprecation `json:"deprecation"`
|
||||||
|
AvailableRegions []string `json:"available_regions"`
|
||||||
|
AvailableForOptions []string `json:"available_for_options"`
|
||||||
|
ListeningFinished bool `json:"listening_finished"`
|
||||||
|
Disclaimers []string `json:"disclaimers"`
|
||||||
|
ActionButton *AlbumActionButton `json:"action_button"`
|
||||||
|
Cover *Cover `json:"cover"`
|
||||||
|
DerivedColors *CoverDerivedColors `json:"derived_colors"`
|
||||||
|
MetaTagID string `json:"meta_tag_id"`
|
||||||
|
ChildContent bool `json:"child_content"`
|
||||||
|
BackgroundImageURL string `json:"background_image_url"`
|
||||||
|
CustomWave *CustomWave `json:"custom_wave"`
|
||||||
|
DurationSec int `json:"duration_sec"`
|
||||||
|
HasTrailer bool `json:"has_trailer"`
|
||||||
|
Trailer *AlbumTrailerStatus `json:"trailer"`
|
||||||
|
Pager *Pager `json:"pager"`
|
||||||
|
SortOrder string `json:"sort_order"`
|
||||||
|
Meta *AlbumMeta `json:"meta"`
|
||||||
|
Best *Best `json:"best"`
|
||||||
|
Region string `json:"region"`
|
||||||
|
AvailableForPremiumUsersOnly bool `json:"available_for_premium_users_only"`
|
||||||
|
Label string `json:"label"`
|
||||||
|
CatalogNumber string `json:"catalog_number"`
|
||||||
|
Options []string `json:"options"`
|
||||||
|
TrackPositions []TrackPosition `json:"track_positions"`
|
||||||
|
SimilarAlbums []Album `json:"similar_albums"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LabelOrString represents a label that can be either a Label object or a string.
|
||||||
|
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 {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(data, &s); err == nil {
|
||||||
|
l.Str = s
|
||||||
|
l.IsStr = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
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 ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeJSON deserializes an Album from JSON data.
|
// DeJSON deserializes an Album from JSON data.
|
||||||
@@ -48,6 +135,9 @@ func (a *Album) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
|||||||
if v, ok := raw["id"]; ok {
|
if v, ok := raw["id"]; ok {
|
||||||
json.Unmarshal(v, &album.ID)
|
json.Unmarshal(v, &album.ID)
|
||||||
}
|
}
|
||||||
|
if v, ok := raw["error"]; ok {
|
||||||
|
json.Unmarshal(v, &album.Error)
|
||||||
|
}
|
||||||
if v, ok := raw["title"]; ok {
|
if v, ok := raw["title"]; ok {
|
||||||
json.Unmarshal(v, &album.Title)
|
json.Unmarshal(v, &album.Title)
|
||||||
}
|
}
|
||||||
@@ -101,9 +191,10 @@ func (a *Album) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
|||||||
album.Artists = artists
|
album.Artists = artists
|
||||||
}
|
}
|
||||||
if v, ok := raw["labels"]; ok {
|
if v, ok := raw["labels"]; ok {
|
||||||
var labels []Label
|
var labels []LabelOrString
|
||||||
json.Unmarshal(v, &labels)
|
if err := json.Unmarshal(v, &labels); err == nil {
|
||||||
album.Labels = labels
|
album.Labels = labels
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if v, ok := raw["volumes"]; ok {
|
if v, ok := raw["volumes"]; ok {
|
||||||
var volumes [][]Track
|
var volumes [][]Track
|
||||||
@@ -131,7 +222,7 @@ func (a *Album) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
|||||||
album.SimilarAlbums = similar
|
album.SimilarAlbums = similar
|
||||||
}
|
}
|
||||||
if v, ok := raw["trailer"]; ok {
|
if v, ok := raw["trailer"]; ok {
|
||||||
var trailer TrailerInfo
|
var trailer AlbumTrailerStatus
|
||||||
json.Unmarshal(v, &trailer)
|
json.Unmarshal(v, &trailer)
|
||||||
album.Trailer = &trailer
|
album.Trailer = &trailer
|
||||||
}
|
}
|
||||||
@@ -273,3 +364,165 @@ func (ti *TrailerInfo) DeList(data []byte, c ClientInterface) ([]Model, error) {
|
|||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecation represents album deprecation info.
|
||||||
|
type Deprecation struct {
|
||||||
|
BaseModel
|
||||||
|
TargetAlbumID JSONID `json:"target_album_id"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Done bool `json:"done"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AlbumActionButton represents an action button for an album.
|
||||||
|
type AlbumActionButton struct {
|
||||||
|
BaseModel
|
||||||
|
Text string `json:"text"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Color string `json:"color"`
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ func TestAlbum_DeJSON(t *testing.T) {
|
|||||||
"best": {"tracks": [{"id": "bt1", "title": "Best Track", "duration_ms": 200000}]},
|
"best": {"tracks": [{"id": "bt1", "title": "Best Track", "duration_ms": 200000}]},
|
||||||
"track_positions": [{"revision": 1, "timestamp": "2023-01-01T00:00:00Z", "position": 1, "from_cache": false}],
|
"track_positions": [{"revision": 1, "timestamp": "2023-01-01T00:00:00Z", "position": 1, "from_cache": false}],
|
||||||
"similar_albums": [{"id": "sim1", "title": "Similar Album", "cover_uri": "https://example.com/sim.jpg", "year": 2022}],
|
"similar_albums": [{"id": "sim1", "title": "Similar Album", "cover_uri": "https://example.com/sim.jpg", "year": 2022}],
|
||||||
"trailer": {"preview_uri": "https://example.com/preview.mp3", "track_id": "t1", "album_id": "123", "duration": 30}
|
"trailer": {"available": true}
|
||||||
}`)
|
}`)
|
||||||
|
|
||||||
album := &Album{BaseModel: *NewBaseModel(client)}
|
album := &Album{BaseModel: *NewBaseModel(client)}
|
||||||
@@ -101,8 +101,8 @@ func TestAlbum_DeJSON(t *testing.T) {
|
|||||||
if len(resultAlbum.Labels) != 1 {
|
if len(resultAlbum.Labels) != 1 {
|
||||||
t.Errorf("Expected 1 label, got %d", len(resultAlbum.Labels))
|
t.Errorf("Expected 1 label, got %d", len(resultAlbum.Labels))
|
||||||
}
|
}
|
||||||
if resultAlbum.Labels[0].Name != "Label 1" {
|
if resultAlbum.Labels[0].String() != "Label 1" {
|
||||||
t.Errorf("Expected label name 'Label 1', got '%s'", resultAlbum.Labels[0].Name)
|
t.Errorf("Expected label name 'Label 1', got '%s'", resultAlbum.Labels[0].String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test volumes
|
// Test volumes
|
||||||
@@ -164,17 +164,8 @@ func TestAlbum_DeJSON(t *testing.T) {
|
|||||||
if resultAlbum.Trailer == nil {
|
if resultAlbum.Trailer == nil {
|
||||||
t.Fatal("Expected Trailer to be non-nil")
|
t.Fatal("Expected Trailer to be non-nil")
|
||||||
}
|
}
|
||||||
if resultAlbum.Trailer.PreviewURI != "https://example.com/preview.mp3" {
|
if !resultAlbum.Trailer.Available {
|
||||||
t.Errorf("Expected PreviewURI 'https://example.com/preview.mp3', got '%s'", resultAlbum.Trailer.PreviewURI)
|
t.Errorf("Expected Trailer.Available true, got false")
|
||||||
}
|
|
||||||
if resultAlbum.Trailer.TrackID != "t1" {
|
|
||||||
t.Errorf("Expected TrackID 't1', got '%s'", resultAlbum.Trailer.TrackID)
|
|
||||||
}
|
|
||||||
if resultAlbum.Trailer.AlbumID != "123" {
|
|
||||||
t.Errorf("Expected AlbumID '123', got '%s'", resultAlbum.Trailer.AlbumID)
|
|
||||||
}
|
|
||||||
if resultAlbum.Trailer.Duration != 30 {
|
|
||||||
t.Errorf("Expected Duration 30, got %d", resultAlbum.Trailer.Duration)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,50 +299,38 @@ func TestAlbumMeta_DeList(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTrailerInfo_DeJSON(t *testing.T) {
|
func TestAlbumTrailerStatus_DeJSON(t *testing.T) {
|
||||||
client := newMockClient()
|
client := newMockClient()
|
||||||
data := []byte(`{
|
data := []byte(`{
|
||||||
"preview_uri": "https://example.com/preview.mp3",
|
"available": true
|
||||||
"track_id": "track123",
|
|
||||||
"album_id": "album456",
|
|
||||||
"duration": 45
|
|
||||||
}`)
|
}`)
|
||||||
|
|
||||||
trailer := &TrailerInfo{BaseModel: *NewBaseModel(client)}
|
status := &AlbumTrailerStatus{BaseModel: *NewBaseModel(client)}
|
||||||
result, err := trailer.DeJSON(data, client)
|
result, err := status.DeJSON(data, client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("DeJSON failed: %v", err)
|
t.Fatalf("DeJSON failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
resultTrailer := result.(*TrailerInfo)
|
resultStatus := result.(*AlbumTrailerStatus)
|
||||||
if resultTrailer.PreviewURI != "https://example.com/preview.mp3" {
|
if !resultStatus.Available {
|
||||||
t.Errorf("Expected PreviewURI 'https://example.com/preview.mp3', got '%s'", resultTrailer.PreviewURI)
|
t.Errorf("Expected Available true, got false")
|
||||||
}
|
|
||||||
if resultTrailer.TrackID != "track123" {
|
|
||||||
t.Errorf("Expected TrackID 'track123', got '%s'", resultTrailer.TrackID)
|
|
||||||
}
|
|
||||||
if resultTrailer.AlbumID != "album456" {
|
|
||||||
t.Errorf("Expected AlbumID 'album456', got '%s'", resultTrailer.AlbumID)
|
|
||||||
}
|
|
||||||
if resultTrailer.Duration != 45 {
|
|
||||||
t.Errorf("Expected Duration 45, got %d", resultTrailer.Duration)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTrailerInfo_DeList(t *testing.T) {
|
func TestAlbumTrailerStatus_DeList(t *testing.T) {
|
||||||
client := newMockClient()
|
client := newMockClient()
|
||||||
data := []byte(`[
|
data := []byte(`[
|
||||||
{"preview_uri": "https://example.com/preview1.mp3", "track_id": "t1", "album_id": "a1", "duration": 30},
|
{"available": true},
|
||||||
{"preview_uri": "https://example.com/preview2.mp3", "track_id": "t2", "album_id": "a2", "duration": 45}
|
{"available": false}
|
||||||
]`)
|
]`)
|
||||||
|
|
||||||
trailer := &TrailerInfo{BaseModel: *NewBaseModel(client)}
|
status := &AlbumTrailerStatus{BaseModel: *NewBaseModel(client)}
|
||||||
result, err := trailer.DeList(data, client)
|
result, err := status.DeList(data, client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("DeList failed: %v", err)
|
t.Fatalf("DeList failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(result) != 2 {
|
if len(result) != 2 {
|
||||||
t.Errorf("Expected 2 trailers, got %d", len(result))
|
t.Errorf("Expected 2 statuses, got %d", len(result))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+551
-32
@@ -14,25 +14,24 @@ type Artist struct {
|
|||||||
Genres []string `json:"genres"`
|
Genres []string `json:"genres"`
|
||||||
Tracks int `json:"tracks"`
|
Tracks int `json:"tracks"`
|
||||||
Albums int `json:"albums"`
|
Albums int `json:"albums"`
|
||||||
Link string `json:"link"`
|
|
||||||
Available bool `json:"available"`
|
Available bool `json:"available"`
|
||||||
Description string `json:"description"`
|
|
||||||
Composer bool `json:"composer"`
|
Composer bool `json:"composer"`
|
||||||
Various bool `json:"various"`
|
Various bool `json:"various"`
|
||||||
Decomposed []Artist `json:"decomposed"`
|
Decomposed []Decomposed `json:"decomposed"`
|
||||||
Ratings *Ratings `json:"ratings"`
|
Ratings *Ratings `json:"ratings"`
|
||||||
Counts *Counts `json:"counts"`
|
Counts *Counts `json:"counts"`
|
||||||
SocialLinks []SocialLink `json:"social_links"`
|
Links []Link `json:"social_links"`
|
||||||
|
Description *Description `json:"description"`
|
||||||
// Additional fields
|
// Additional fields
|
||||||
IDForFrom string `json:"id_for_from"`
|
IDForFrom string `json:"id_for_from"`
|
||||||
Cover *Cover `json:"cover"`
|
Cover *Cover `json:"cover"`
|
||||||
BriefInfo *ArtistBriefInfo `json:"brief_info"`
|
BriefInfo *ArtistBriefInfo `json:"brief_info"`
|
||||||
Donations *ArtistDonations `json:"donations"`
|
DonationInfo *ArtistDonationInfo `json:"donation_info"`
|
||||||
Concerts []Concert `json:"concerts"`
|
Concerts []Concert `json:"concerts"`
|
||||||
Clips []Clip `json:"clips"`
|
Clips []Clip `json:"clips"`
|
||||||
Similar []Artist `json:"similar"`
|
Similar []Artist `json:"similar"`
|
||||||
AlbumsData []Album `json:"albums_data"`
|
AlbumsData []Album `json:"albums_data"`
|
||||||
TracksData []Track `json:"tracks_data"`
|
TracksData []Track `json:"tracks_data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeJSON deserializes an Artist from JSON 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 {
|
if v, ok := raw["albums"]; ok {
|
||||||
json.Unmarshal(v, &artist.Albums)
|
json.Unmarshal(v, &artist.Albums)
|
||||||
}
|
}
|
||||||
if v, ok := raw["link"]; ok {
|
|
||||||
json.Unmarshal(v, &artist.Link)
|
|
||||||
}
|
|
||||||
if v, ok := raw["available"]; ok {
|
if v, ok := raw["available"]; ok {
|
||||||
json.Unmarshal(v, &artist.Available)
|
json.Unmarshal(v, &artist.Available)
|
||||||
}
|
}
|
||||||
if v, ok := raw["description"]; ok {
|
|
||||||
json.Unmarshal(v, &artist.Description)
|
|
||||||
}
|
|
||||||
if v, ok := raw["composer"]; ok {
|
if v, ok := raw["composer"]; ok {
|
||||||
json.Unmarshal(v, &artist.Composer)
|
json.Unmarshal(v, &artist.Composer)
|
||||||
}
|
}
|
||||||
@@ -84,9 +77,10 @@ func (a *Artist) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
|||||||
|
|
||||||
// Nested objects
|
// Nested objects
|
||||||
if v, ok := raw["decomposed"]; ok {
|
if v, ok := raw["decomposed"]; ok {
|
||||||
var decomposed []Artist
|
var decomposed []Decomposed
|
||||||
json.Unmarshal(v, &decomposed)
|
if err := json.Unmarshal(v, &decomposed); err == nil {
|
||||||
artist.Decomposed = decomposed
|
artist.Decomposed = decomposed
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if v, ok := raw["ratings"]; ok {
|
if v, ok := raw["ratings"]; ok {
|
||||||
var ratings Ratings
|
var ratings Ratings
|
||||||
@@ -99,9 +93,10 @@ func (a *Artist) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
|||||||
artist.Counts = &counts
|
artist.Counts = &counts
|
||||||
}
|
}
|
||||||
if v, ok := raw["social_links"]; ok {
|
if v, ok := raw["social_links"]; ok {
|
||||||
var links []SocialLink
|
var links []Link
|
||||||
json.Unmarshal(v, &links)
|
if err := json.Unmarshal(v, &links); err == nil {
|
||||||
artist.SocialLinks = links
|
artist.Links = links
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if v, ok := raw["cover"]; ok {
|
if v, ok := raw["cover"]; ok {
|
||||||
var cover Cover
|
var cover Cover
|
||||||
@@ -113,10 +108,10 @@ func (a *Artist) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
|||||||
json.Unmarshal(v, &brief)
|
json.Unmarshal(v, &brief)
|
||||||
artist.BriefInfo = &brief
|
artist.BriefInfo = &brief
|
||||||
}
|
}
|
||||||
if v, ok := raw["donations"]; ok {
|
if v, ok := raw["donation_info"]; ok {
|
||||||
var donations ArtistDonations
|
var donation ArtistDonationInfo
|
||||||
json.Unmarshal(v, &donations)
|
json.Unmarshal(v, &donation)
|
||||||
artist.Donations = &donations
|
artist.DonationInfo = &donation
|
||||||
}
|
}
|
||||||
if v, ok := raw["concerts"]; ok {
|
if v, ok := raw["concerts"]; ok {
|
||||||
var concerts []Concert
|
var concerts []Concert
|
||||||
@@ -167,8 +162,10 @@ func (a *Artist) DeList(data []byte, c ClientInterface) ([]Model, error) {
|
|||||||
|
|
||||||
// Ratings represents artist ratings.
|
// Ratings represents artist ratings.
|
||||||
type Ratings struct {
|
type Ratings struct {
|
||||||
Rating float64 `json:"rating"`
|
Rating int `json:"rating"`
|
||||||
Votes int `json:"votes"`
|
Votes int `json:"votes"`
|
||||||
|
Week int `json:"week"`
|
||||||
|
Day int `json:"day"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Counts represents artist counts.
|
// Counts represents artist counts.
|
||||||
@@ -196,12 +193,14 @@ type Cover struct {
|
|||||||
|
|
||||||
// ArtistBriefInfo represents brief artist info.
|
// ArtistBriefInfo represents brief artist info.
|
||||||
type ArtistBriefInfo struct {
|
type ArtistBriefInfo struct {
|
||||||
|
BaseModel
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Available bool `json:"available"`
|
Available bool `json:"available"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ArtistDonations represents artist donations info.
|
// ArtistDonations represents artist donations info.
|
||||||
type ArtistDonations struct {
|
type ArtistDonations struct {
|
||||||
|
BaseModel
|
||||||
Items []DonationItem `json:"items"`
|
Items []DonationItem `json:"items"`
|
||||||
Total int `json:"total"`
|
Total int `json:"total"`
|
||||||
Goal int `json:"goal"`
|
Goal int `json:"goal"`
|
||||||
@@ -210,7 +209,7 @@ type ArtistDonations struct {
|
|||||||
|
|
||||||
// DonationItem represents a donation item.
|
// DonationItem represents a donation item.
|
||||||
type DonationItem struct {
|
type DonationItem struct {
|
||||||
ID JSONID `json:"id"`
|
ID JSONID `json:"id"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Amount int `json:"amount"`
|
Amount int `json:"amount"`
|
||||||
@@ -219,6 +218,178 @@ type DonationItem struct {
|
|||||||
Currency string `json:"currency"`
|
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).
|
// ConcertShort represents a concert (minimal for artist).
|
||||||
type ConcertShort struct {
|
type ConcertShort struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
@@ -366,3 +537,351 @@ func (c *ClipShort) DeList(data []byte, client ClientInterface) ([]Model, error)
|
|||||||
}
|
}
|
||||||
return result, nil
|
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"`
|
||||||
|
}
|
||||||
|
|
||||||
+53
-17
@@ -111,33 +111,69 @@ func (id JSONID) String() string {
|
|||||||
return string(id)
|
return string(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSONR128 is a flexible R128 loudness normalization value that accepts both JSON numbers and objects.
|
// R128 represents EBU R 128 loudness normalization parameters.
|
||||||
// The Yandex Music API sometimes returns r128 as a number (e.g., -23.5) and sometimes
|
// The Yandex Music API returns this either as a number (e.g., -23.5) or as an object
|
||||||
// as an object (e.g., {"value": -23.5}). JSONR128 accepts both and stores as float64.
|
// with fields i, tp, important_secs.
|
||||||
type JSONR128 float64
|
type R128 struct {
|
||||||
|
BaseModel
|
||||||
|
I float64 `json:"i"`
|
||||||
|
Tp float64 `json:"tp"`
|
||||||
|
ImportantSecs int `json:"important_secs"`
|
||||||
|
}
|
||||||
|
|
||||||
// UnmarshalJSON implements json.Unmarshaler, accepting both JSON numbers and objects.
|
// UnmarshalJSON implements json.Unmarshaler, accepting both JSON numbers and objects.
|
||||||
func (r *JSONR128) UnmarshalJSON(data []byte) error {
|
func (r *R128) UnmarshalJSON(data []byte) error {
|
||||||
// Try float64 first
|
// Try float64 first (API sometimes returns just a number)
|
||||||
var f float64
|
var f float64
|
||||||
if err := json.Unmarshal(data, &f); err == nil {
|
if err := json.Unmarshal(data, &f); err == nil {
|
||||||
*r = JSONR128(f)
|
r.I = f
|
||||||
|
r.Tp = 0
|
||||||
|
r.ImportantSecs = 0
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Try object with "value" key
|
// Try object with fields i, tp, important_secs
|
||||||
var obj struct {
|
var obj struct {
|
||||||
Value float64 `json:"value"`
|
I float64 `json:"i"`
|
||||||
|
Tp float64 `json:"tp"`
|
||||||
|
ImportantSecs int `json:"important_secs"`
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal(data, &obj); err == nil {
|
if err := json.Unmarshal(data, &obj); err != nil {
|
||||||
*r = JSONR128(obj.Value)
|
return err
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
// Default to 0 if neither format works
|
r.I = obj.I
|
||||||
*r = 0
|
r.Tp = obj.Tp
|
||||||
|
r.ImportantSecs = obj.ImportantSecs
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Float64 returns the R128 value as a float64.
|
// Float64 returns the integrated loudness (i) value as a float64.
|
||||||
func (r JSONR128) Float64() float64 {
|
func (r R128) Float64() float64 {
|
||||||
return float64(r)
|
return r.I
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeJSON deserializes an R128 from JSON data.
|
||||||
|
func (r *R128) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
||||||
|
r128 := &R128{BaseModel: *NewBaseModel(c)}
|
||||||
|
if err := r128.UnmarshalJSON(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return r128, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeList deserializes a list of R128 from JSON data.
|
||||||
|
func (r *R128) 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 {
|
||||||
|
r128 := &R128{BaseModel: *NewBaseModel(c)}
|
||||||
|
if err := r128.UnmarshalJSON(item); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, r128)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
+69
-61
@@ -8,21 +8,24 @@ import (
|
|||||||
// Clip represents a clip (short video) from the Yandex Music API.
|
// Clip represents a clip (short video) from the Yandex Music API.
|
||||||
type Clip struct {
|
type Clip struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
ID JSONID `json:"id"`
|
ID JSONID `json:"id"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Duration int `json:"duration"`
|
Version string `json:"version"`
|
||||||
CoverURI string `json:"cover_uri"`
|
PlayerID string `json:"player_id"`
|
||||||
PreviewURI string `json:"preview_uri"`
|
UUID string `json:"uuid"`
|
||||||
Available bool `json:"available"`
|
Thumbnail string `json:"thumbnail"`
|
||||||
Views int `json:"views"`
|
PreviewURL string `json:"preview_url"`
|
||||||
Likes int `json:"likes"`
|
Duration int `json:"duration"`
|
||||||
CreatedAt string `json:"created_at"`
|
TrackIDs []int `json:"track_ids"`
|
||||||
Artist *Artist `json:"artist"`
|
Artists []Artist `json:"artists"`
|
||||||
Track *Track `json:"track"`
|
Disclaimers []string `json:"disclaimers"`
|
||||||
Playlist *Playlist `json:"playlist"`
|
Explicit bool `json:"explicit"`
|
||||||
ClipType string `json:"clip_type"`
|
Cover *Cover `json:"cover"`
|
||||||
IsVertical bool `json:"is_vertical"`
|
ContentRestrictions *ContentRestrictions `json:"content_restrictions"`
|
||||||
AspectRatio string `json:"aspect_ratio"`
|
Client *ClientInterface `json:"-"`
|
||||||
|
ClipType string `json:"clip_type"`
|
||||||
|
IsVertical bool `json:"is_vertical"`
|
||||||
|
AspectRatio string `json:"aspect_ratio"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeJSON deserializes a Clip from JSON data.
|
// DeJSON deserializes a Clip from JSON data.
|
||||||
@@ -40,26 +43,47 @@ func (c *Clip) DeJSON(data []byte, client ClientInterface) (Model, error) {
|
|||||||
if v, ok := raw["title"]; ok {
|
if v, ok := raw["title"]; ok {
|
||||||
json.Unmarshal(v, &clip.Title)
|
json.Unmarshal(v, &clip.Title)
|
||||||
}
|
}
|
||||||
|
if v, ok := raw["version"]; ok {
|
||||||
|
json.Unmarshal(v, &clip.Version)
|
||||||
|
}
|
||||||
|
if v, ok := raw["player_id"]; ok {
|
||||||
|
json.Unmarshal(v, &clip.PlayerID)
|
||||||
|
}
|
||||||
|
if v, ok := raw["uuid"]; ok {
|
||||||
|
json.Unmarshal(v, &clip.UUID)
|
||||||
|
}
|
||||||
|
if v, ok := raw["thumbnail"]; ok {
|
||||||
|
json.Unmarshal(v, &clip.Thumbnail)
|
||||||
|
}
|
||||||
|
if v, ok := raw["preview_url"]; ok {
|
||||||
|
json.Unmarshal(v, &clip.PreviewURL)
|
||||||
|
}
|
||||||
if v, ok := raw["duration"]; ok {
|
if v, ok := raw["duration"]; ok {
|
||||||
json.Unmarshal(v, &clip.Duration)
|
json.Unmarshal(v, &clip.Duration)
|
||||||
}
|
}
|
||||||
if v, ok := raw["cover_uri"]; ok {
|
if v, ok := raw["track_ids"]; ok {
|
||||||
json.Unmarshal(v, &clip.CoverURI)
|
json.Unmarshal(v, &clip.TrackIDs)
|
||||||
}
|
}
|
||||||
if v, ok := raw["preview_uri"]; ok {
|
if v, ok := raw["artists"]; ok {
|
||||||
json.Unmarshal(v, &clip.PreviewURI)
|
var artists []Artist
|
||||||
|
json.Unmarshal(v, &artists)
|
||||||
|
clip.Artists = artists
|
||||||
}
|
}
|
||||||
if v, ok := raw["available"]; ok {
|
if v, ok := raw["disclaimers"]; ok {
|
||||||
json.Unmarshal(v, &clip.Available)
|
json.Unmarshal(v, &clip.Disclaimers)
|
||||||
}
|
}
|
||||||
if v, ok := raw["views"]; ok {
|
if v, ok := raw["explicit"]; ok {
|
||||||
json.Unmarshal(v, &clip.Views)
|
json.Unmarshal(v, &clip.Explicit)
|
||||||
}
|
}
|
||||||
if v, ok := raw["likes"]; ok {
|
if v, ok := raw["cover"]; ok {
|
||||||
json.Unmarshal(v, &clip.Likes)
|
var cover Cover
|
||||||
|
json.Unmarshal(v, &cover)
|
||||||
|
clip.Cover = &cover
|
||||||
}
|
}
|
||||||
if v, ok := raw["created_at"]; ok {
|
if v, ok := raw["content_restrictions"]; ok {
|
||||||
json.Unmarshal(v, &clip.CreatedAt)
|
var cr ContentRestrictions
|
||||||
|
json.Unmarshal(v, &cr)
|
||||||
|
clip.ContentRestrictions = &cr
|
||||||
}
|
}
|
||||||
if v, ok := raw["clip_type"]; ok {
|
if v, ok := raw["clip_type"]; ok {
|
||||||
json.Unmarshal(v, &clip.ClipType)
|
json.Unmarshal(v, &clip.ClipType)
|
||||||
@@ -71,22 +95,6 @@ func (c *Clip) DeJSON(data []byte, client ClientInterface) (Model, error) {
|
|||||||
json.Unmarshal(v, &clip.AspectRatio)
|
json.Unmarshal(v, &clip.AspectRatio)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, ok := raw["artist"]; ok {
|
|
||||||
var artist Artist
|
|
||||||
json.Unmarshal(v, &artist)
|
|
||||||
clip.Artist = &artist
|
|
||||||
}
|
|
||||||
if v, ok := raw["track"]; ok {
|
|
||||||
var track Track
|
|
||||||
json.Unmarshal(v, &track)
|
|
||||||
clip.Track = &track
|
|
||||||
}
|
|
||||||
if v, ok := raw["playlist"]; ok {
|
|
||||||
var playlist Playlist
|
|
||||||
json.Unmarshal(v, &playlist)
|
|
||||||
clip.Playlist = &playlist
|
|
||||||
}
|
|
||||||
|
|
||||||
return clip, nil
|
return clip, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,23 +140,23 @@ type ArtistClipItem struct {
|
|||||||
// Shot represents a shot (short video).
|
// Shot represents a shot (short video).
|
||||||
type Shot struct {
|
type Shot struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
ID JSONID `json:"id"`
|
ID JSONID `json:"id"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Duration int `json:"duration"`
|
Duration int `json:"duration"`
|
||||||
CoverURI string `json:"cover_uri"`
|
CoverURI string `json:"cover_uri"`
|
||||||
PreviewURI string `json:"preview_uri"`
|
PreviewURI string `json:"preview_uri"`
|
||||||
Available bool `json:"available"`
|
Available bool `json:"available"`
|
||||||
Views int `json:"views"`
|
Views int `json:"views"`
|
||||||
Likes int `json:"likes"`
|
Likes int `json:"likes"`
|
||||||
Comments int `json:"comments"`
|
Comments int `json:"comments"`
|
||||||
CreatedAt string `json:"created_at"`
|
CreatedAt string `json:"created_at"`
|
||||||
Author *User `json:"author"`
|
Author *User `json:"author"`
|
||||||
Track *Track `json:"track"`
|
Track *Track `json:"track"`
|
||||||
ShotType string `json:"shot_type"`
|
ShotType string `json:"shot_type"`
|
||||||
Tags []string `json:"tags"`
|
Tags []string `json:"tags"`
|
||||||
IsVertical bool `json:"is_vertical"`
|
IsVertical bool `json:"is_vertical"`
|
||||||
AspectRatio string `json:"aspect_ratio"`
|
AspectRatio string `json:"aspect_ratio"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeJSON deserializes a Shot from JSON data.
|
// DeJSON deserializes a Shot from JSON data.
|
||||||
@@ -258,7 +266,7 @@ type ShotEvent struct {
|
|||||||
// ShotType represents a shot type.
|
// ShotType represents a shot type.
|
||||||
type ShotType struct {
|
type ShotType struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
ID JSONID `json:"id"`
|
ID JSONID `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Icon string `json:"icon"`
|
Icon string `json:"icon"`
|
||||||
|
|||||||
+97
-19
@@ -8,25 +8,33 @@ import (
|
|||||||
// Concert represents a concert from the Yandex Music API.
|
// Concert represents a concert from the Yandex Music API.
|
||||||
type Concert struct {
|
type Concert struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
ID JSONID `json:"id"`
|
ID JSONID `json:"id"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Date string `json:"date"`
|
Date string `json:"date"`
|
||||||
Time string `json:"time"`
|
Time string `json:"time"`
|
||||||
Venue *ConcertVenue `json:"venue"`
|
Datetime string `json:"datetime"`
|
||||||
Location *ConcertLocation `json:"location"`
|
Venue *ConcertVenue `json:"venue"`
|
||||||
MinPrice int `json:"min_price"`
|
Location *ConcertLocation `json:"location"`
|
||||||
MaxPrice int `json:"max_price"`
|
MinPrice int `json:"min_price"`
|
||||||
Currency string `json:"currency"`
|
MaxPrice int `json:"max_price"`
|
||||||
TicketURI string `json:"ticket_uri"`
|
Currency string `json:"currency"`
|
||||||
ImageURI string `json:"image_uri"`
|
TicketURI string `json:"ticket_uri"`
|
||||||
Available bool `json:"available"`
|
ImageURI string `json:"image_uri"`
|
||||||
SoldOut bool `json:"sold_out"`
|
Available bool `json:"available"`
|
||||||
Artists []Artist `json:"artists"`
|
SoldOut bool `json:"sold_out"`
|
||||||
Genres []string `json:"genres"`
|
Artists []Artist `json:"artists"`
|
||||||
Cashback *ConcertCashback `json:"cashback"`
|
Genres []string `json:"genres"`
|
||||||
Tabs []ConcertTabConfig `json:"tabs"`
|
Cashback *ConcertCashback `json:"cashback"`
|
||||||
Skeleton *ConcertSkeleton `json:"skeleton"`
|
Tabs []ConcertTabConfig `json:"tabs"`
|
||||||
|
Skeleton *ConcertSkeleton `json:"skeleton"`
|
||||||
|
AfishaURL string `json:"afisha_url"`
|
||||||
|
City string `json:"city"`
|
||||||
|
Place string `json:"place"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
ContentRating string `json:"content_rating"`
|
||||||
|
DataSessionID string `json:"data_session_id"`
|
||||||
|
EventInfo *ConcertEventInfo `json:"event_info"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeJSON deserializes a Concert from JSON data.
|
// DeJSON deserializes a Concert from JSON data.
|
||||||
@@ -77,6 +85,30 @@ func (c *Concert) DeJSON(data []byte, client ClientInterface) (Model, error) {
|
|||||||
if v, ok := raw["genres"]; ok {
|
if v, ok := raw["genres"]; ok {
|
||||||
json.Unmarshal(v, &concert.Genres)
|
json.Unmarshal(v, &concert.Genres)
|
||||||
}
|
}
|
||||||
|
if v, ok := raw["datetime"]; ok {
|
||||||
|
json.Unmarshal(v, &concert.Datetime)
|
||||||
|
}
|
||||||
|
if v, ok := raw["afisha_url"]; ok {
|
||||||
|
json.Unmarshal(v, &concert.AfishaURL)
|
||||||
|
}
|
||||||
|
if v, ok := raw["city"]; ok {
|
||||||
|
json.Unmarshal(v, &concert.City)
|
||||||
|
}
|
||||||
|
if v, ok := raw["place"]; ok {
|
||||||
|
json.Unmarshal(v, &concert.Place)
|
||||||
|
}
|
||||||
|
if v, ok := raw["address"]; ok {
|
||||||
|
json.Unmarshal(v, &concert.Address)
|
||||||
|
}
|
||||||
|
if v, ok := raw["content_rating"]; ok {
|
||||||
|
json.Unmarshal(v, &concert.ContentRating)
|
||||||
|
}
|
||||||
|
if v, ok := raw["data_session_id"]; ok {
|
||||||
|
json.Unmarshal(v, &concert.DataSessionID)
|
||||||
|
}
|
||||||
|
if v, ok := raw["event_info"]; ok {
|
||||||
|
json.Unmarshal(v, &concert.EventInfo)
|
||||||
|
}
|
||||||
|
|
||||||
if v, ok := raw["venue"]; ok {
|
if v, ok := raw["venue"]; ok {
|
||||||
var venue ConcertVenue
|
var venue ConcertVenue
|
||||||
@@ -242,3 +274,49 @@ type ConcertSkeletonBlock struct {
|
|||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Data json.RawMessage `json:"data"`
|
Data json.RawMessage `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ArtistConcerts represents concerts by an artist.
|
||||||
|
type ArtistConcerts struct {
|
||||||
|
BaseModel
|
||||||
|
ArtistTitle string `json:"artist_title"`
|
||||||
|
Concerts []Concert `json:"concerts"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeJSON deserializes ArtistConcerts from JSON data.
|
||||||
|
func (ac *ArtistConcerts) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
||||||
|
var raw map[string]json.RawMessage
|
||||||
|
if err := json.Unmarshal(data, &raw); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
acObj := &ArtistConcerts{BaseModel: *NewBaseModel(c)}
|
||||||
|
|
||||||
|
if v, ok := raw["artist_title"]; ok {
|
||||||
|
json.Unmarshal(v, &acObj.ArtistTitle)
|
||||||
|
}
|
||||||
|
if v, ok := raw["concerts"]; ok {
|
||||||
|
var concerts []Concert
|
||||||
|
json.Unmarshal(v, &concerts)
|
||||||
|
acObj.Concerts = concerts
|
||||||
|
}
|
||||||
|
|
||||||
|
return acObj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeList deserializes a list of ArtistConcerts from JSON data.
|
||||||
|
func (ac *ArtistConcerts) 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 {
|
||||||
|
ac := &ArtistConcerts{BaseModel: *NewBaseModel(c)}
|
||||||
|
if err := json.Unmarshal(item, ac); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, ac)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
@@ -170,3 +170,85 @@ func (ltp *LicenceTextPart) DeList(data []byte, client ClientInterface) ([]Model
|
|||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disclaimer represents a disclaimer.
|
||||||
|
type Disclaimer struct {
|
||||||
|
BaseModel
|
||||||
|
ForeignAgent *ForeignAgent `json:"foreign_agent"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeJSON deserializes Disclaimer from JSON data.
|
||||||
|
func (d *Disclaimer) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
||||||
|
var raw map[string]json.RawMessage
|
||||||
|
if err := json.Unmarshal(data, &raw); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
disclaimer := &Disclaimer{BaseModel: *NewBaseModel(c)}
|
||||||
|
|
||||||
|
if v, ok := raw["foreign_agent"]; ok {
|
||||||
|
var fa ForeignAgent
|
||||||
|
json.Unmarshal(v, &fa)
|
||||||
|
disclaimer.ForeignAgent = &fa
|
||||||
|
}
|
||||||
|
|
||||||
|
return disclaimer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeList deserializes a list of Disclaimers from JSON data.
|
||||||
|
func (d *Disclaimer) 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 {
|
||||||
|
disclaimer := &Disclaimer{BaseModel: *NewBaseModel(c)}
|
||||||
|
if err := json.Unmarshal(item, disclaimer); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, disclaimer)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForeignAgent represents a foreign agent.
|
||||||
|
type ForeignAgent struct {
|
||||||
|
BaseModel
|
||||||
|
Text string `json:"text"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeJSON deserializes ForeignAgent from JSON data.
|
||||||
|
func (fa *ForeignAgent) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
||||||
|
var raw map[string]json.RawMessage
|
||||||
|
if err := json.Unmarshal(data, &raw); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
agent := &ForeignAgent{BaseModel: *NewBaseModel(c)}
|
||||||
|
|
||||||
|
if v, ok := raw["text"]; ok {
|
||||||
|
json.Unmarshal(v, &agent.Text)
|
||||||
|
}
|
||||||
|
|
||||||
|
return agent, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeList deserializes a list of ForeignAgent from JSON data.
|
||||||
|
func (fa *ForeignAgent) 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 {
|
||||||
|
agent := &ForeignAgent{BaseModel: *NewBaseModel(c)}
|
||||||
|
if err := json.Unmarshal(item, agent); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, agent)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
+27
-10
@@ -8,9 +8,15 @@ import (
|
|||||||
// Feed represents the main feed from the Yandex Music API.
|
// Feed represents the main feed from the Yandex Music API.
|
||||||
type Feed struct {
|
type Feed struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
Days []Day `json:"days"`
|
CanGetMoreEvents bool `json:"can_get_more_events"`
|
||||||
GeneratedPlaylists []GeneratedPlaylist `json:"generated_playlists"`
|
Pumpkin bool `json:"pumpkin"`
|
||||||
PlayContexts []PlayContext `json:"play_contexts"`
|
IsWizardPassed bool `json:"is_wizard_passed"`
|
||||||
|
Headlines []string `json:"headlines"`
|
||||||
|
Today string `json:"today"`
|
||||||
|
Days []Day `json:"days"`
|
||||||
|
GeneratedPlaylists []GeneratedPlaylist `json:"generated_playlists"`
|
||||||
|
PlayContexts []PlayContext `json:"play_contexts"`
|
||||||
|
NextRevision string `json:"next_revision"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeJSON deserializes a Feed from JSON data.
|
// DeJSON deserializes a Feed from JSON data.
|
||||||
@@ -22,6 +28,21 @@ func (f *Feed) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
|||||||
|
|
||||||
feed := &Feed{BaseModel: *NewBaseModel(c)}
|
feed := &Feed{BaseModel: *NewBaseModel(c)}
|
||||||
|
|
||||||
|
if v, ok := raw["can_get_more_events"]; ok {
|
||||||
|
json.Unmarshal(v, &feed.CanGetMoreEvents)
|
||||||
|
}
|
||||||
|
if v, ok := raw["pumpkin"]; ok {
|
||||||
|
json.Unmarshal(v, &feed.Pumpkin)
|
||||||
|
}
|
||||||
|
if v, ok := raw["is_wizard_passed"]; ok {
|
||||||
|
json.Unmarshal(v, &feed.IsWizardPassed)
|
||||||
|
}
|
||||||
|
if v, ok := raw["headlines"]; ok {
|
||||||
|
json.Unmarshal(v, &feed.Headlines)
|
||||||
|
}
|
||||||
|
if v, ok := raw["today"]; ok {
|
||||||
|
json.Unmarshal(v, &feed.Today)
|
||||||
|
}
|
||||||
if v, ok := raw["days"]; ok {
|
if v, ok := raw["days"]; ok {
|
||||||
var days []Day
|
var days []Day
|
||||||
json.Unmarshal(v, &days)
|
json.Unmarshal(v, &days)
|
||||||
@@ -37,6 +58,9 @@ func (f *Feed) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
|||||||
json.Unmarshal(v, &contexts)
|
json.Unmarshal(v, &contexts)
|
||||||
feed.PlayContexts = contexts
|
feed.PlayContexts = contexts
|
||||||
}
|
}
|
||||||
|
if v, ok := raw["next_revision"]; ok {
|
||||||
|
json.Unmarshal(v, &feed.NextRevision)
|
||||||
|
}
|
||||||
|
|
||||||
return feed, nil
|
return feed, nil
|
||||||
}
|
}
|
||||||
@@ -270,10 +294,3 @@ type Ad struct {
|
|||||||
Duration int `json:"duration"`
|
Duration int `json:"duration"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AdParams represents ad parameters.
|
|
||||||
type AdParams struct {
|
|
||||||
BaseModel
|
|
||||||
Frequency int `json:"frequency"`
|
|
||||||
MaxPerDay int `json:"max_per_day"`
|
|
||||||
Targeting string `json:"targeting"`
|
|
||||||
}
|
|
||||||
@@ -12,8 +12,11 @@ type Label struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
CoverURI string `json:"cover_uri"`
|
CoverURI string `json:"cover_uri"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
|
DescriptionFormatted string `json:"description_formatted"`
|
||||||
|
Image string `json:"image"`
|
||||||
Website string `json:"website"`
|
Website string `json:"website"`
|
||||||
SocialLinks []SocialLink `json:"social_links"`
|
Links []Link `json:"links"`
|
||||||
|
Type string `json:"type"`
|
||||||
Available bool `json:"available"`
|
Available bool `json:"available"`
|
||||||
Owner *User `json:"owner"`
|
Owner *User `json:"owner"`
|
||||||
}
|
}
|
||||||
@@ -45,10 +48,19 @@ func (l *Label) DeJSON(data []byte, client ClientInterface) (Model, error) {
|
|||||||
if v, ok := raw["available"]; ok {
|
if v, ok := raw["available"]; ok {
|
||||||
json.Unmarshal(v, &label.Available)
|
json.Unmarshal(v, &label.Available)
|
||||||
}
|
}
|
||||||
if v, ok := raw["social_links"]; ok {
|
if v, ok := raw["description_formatted"]; ok {
|
||||||
var links []SocialLink
|
json.Unmarshal(v, &label.DescriptionFormatted)
|
||||||
|
}
|
||||||
|
if v, ok := raw["image"]; ok {
|
||||||
|
json.Unmarshal(v, &label.Image)
|
||||||
|
}
|
||||||
|
if v, ok := raw["links"]; ok {
|
||||||
|
var links []Link
|
||||||
json.Unmarshal(v, &links)
|
json.Unmarshal(v, &links)
|
||||||
label.SocialLinks = links
|
label.Links = links
|
||||||
|
}
|
||||||
|
if v, ok := raw["type"]; ok {
|
||||||
|
json.Unmarshal(v, &label.Type)
|
||||||
}
|
}
|
||||||
if v, ok := raw["owner"]; ok {
|
if v, ok := raw["owner"]; ok {
|
||||||
var user User
|
var user User
|
||||||
|
|||||||
@@ -8,13 +8,15 @@ import (
|
|||||||
// Landing represents a landing page from the Yandex Music API.
|
// Landing represents a landing page from the Yandex Music API.
|
||||||
type Landing struct {
|
type Landing struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
ID JSONID `json:"id"`
|
ID JSONID `json:"id"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
CoverURI string `json:"cover_uri"`
|
CoverURI string `json:"cover_uri"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Blocks []LandingBlock `json:"blocks"`
|
Blocks []LandingBlock `json:"blocks"`
|
||||||
Available bool `json:"available"`
|
Available bool `json:"available"`
|
||||||
|
Pumpkin bool `json:"pumpkin"`
|
||||||
|
ContentID string `json:"content_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeJSON deserializes a Landing from JSON data.
|
// DeJSON deserializes a Landing from JSON data.
|
||||||
@@ -44,6 +46,12 @@ func (l *Landing) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
|||||||
if v, ok := raw["available"]; ok {
|
if v, ok := raw["available"]; ok {
|
||||||
json.Unmarshal(v, &landing.Available)
|
json.Unmarshal(v, &landing.Available)
|
||||||
}
|
}
|
||||||
|
if v, ok := raw["pumpkin"]; ok {
|
||||||
|
json.Unmarshal(v, &landing.Pumpkin)
|
||||||
|
}
|
||||||
|
if v, ok := raw["content_id"]; ok {
|
||||||
|
json.Unmarshal(v, &landing.ContentID)
|
||||||
|
}
|
||||||
|
|
||||||
if v, ok := raw["blocks"]; ok {
|
if v, ok := raw["blocks"]; ok {
|
||||||
var blocks []LandingBlock
|
var blocks []LandingBlock
|
||||||
@@ -94,10 +102,61 @@ type LandingBlockData struct {
|
|||||||
// LandingList represents a list of landings.
|
// LandingList represents a list of landings.
|
||||||
type LandingList struct {
|
type LandingList struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
Landings []Landing `json:"landings"`
|
Type string `json:"type"`
|
||||||
Total int `json:"total"`
|
TypeForFrom string `json:"type_for_from"`
|
||||||
Page int `json:"page"`
|
Title string `json:"title"`
|
||||||
PageSize int `json:"page_size"`
|
ID string `json:"id"`
|
||||||
|
Landings []Landing `json:"landings"`
|
||||||
|
NewReleases []int `json:"new_releases"`
|
||||||
|
NewPlaylists []PlaylistID `json:"new_playlists"`
|
||||||
|
Podcasts []int `json:"podcasts"`
|
||||||
|
Total int `json:"total"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
PageSize int `json:"page_size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlaylistID represents a playlist ID reference.
|
||||||
|
type PlaylistID struct {
|
||||||
|
BaseModel
|
||||||
|
UID int `json:"uid"`
|
||||||
|
Kind int `json:"kind"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeJSON deserializes a PlaylistID from JSON data.
|
||||||
|
func (pid *PlaylistID) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
||||||
|
var raw map[string]json.RawMessage
|
||||||
|
if err := json.Unmarshal(data, &raw); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
playlistID := &PlaylistID{BaseModel: *NewBaseModel(c)}
|
||||||
|
|
||||||
|
if v, ok := raw["uid"]; ok {
|
||||||
|
json.Unmarshal(v, &playlistID.UID)
|
||||||
|
}
|
||||||
|
if v, ok := raw["kind"]; ok {
|
||||||
|
json.Unmarshal(v, &playlistID.Kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
return playlistID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeList deserializes a list of PlaylistID from JSON data.
|
||||||
|
func (pid *PlaylistID) 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 {
|
||||||
|
pid := &PlaylistID{BaseModel: *NewBaseModel(c)}
|
||||||
|
if err := json.Unmarshal(item, pid); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, pid)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeJSON deserializes a LandingList from JSON data.
|
// DeJSON deserializes a LandingList from JSON data.
|
||||||
|
|||||||
@@ -132,8 +132,8 @@ type MetatagSortByValue struct {
|
|||||||
// MetatagArtistEntry represents a metatag artist entry.
|
// MetatagArtistEntry represents a metatag artist entry.
|
||||||
type MetatagArtistEntry struct {
|
type MetatagArtistEntry struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
Artist *Artist `json:"artist"`
|
Artist *Artist `json:"artist"`
|
||||||
Score float64 `json:"score"`
|
PopularTracks []Track `json:"popular_tracks"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Genre represents a genre from the Yandex Music API.
|
// Genre represents a genre from the Yandex Music API.
|
||||||
|
|||||||
@@ -110,8 +110,7 @@ type MusicHistoryItemData struct {
|
|||||||
PlayContext *PlayContext `json:"play_context"`
|
PlayContext *PlayContext `json:"play_context"`
|
||||||
Position int `json:"position"`
|
Position int `json:"position"`
|
||||||
Duration int `json:"duration"`
|
Duration int `json:"duration"`
|
||||||
Progress float64 `json:"progress"`
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// MusicHistoryItemID represents music history item ID.
|
// MusicHistoryItemID represents music history item ID.
|
||||||
type MusicHistoryItemID struct {
|
type MusicHistoryItemID struct {
|
||||||
|
|||||||
+50
-5
@@ -8,7 +8,7 @@ import (
|
|||||||
// Pin represents a pin from the Yandex Music API.
|
// Pin represents a pin from the Yandex Music API.
|
||||||
type Pin struct {
|
type Pin struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
ID JSONID `json:"id"`
|
ID JSONID `json:"id"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Image string `json:"image"`
|
Image string `json:"image"`
|
||||||
@@ -21,6 +21,7 @@ type Pin struct {
|
|||||||
CreatedAt string `json:"created_at"`
|
CreatedAt string `json:"created_at"`
|
||||||
ExpiresAt string `json:"expires_at"`
|
ExpiresAt string `json:"expires_at"`
|
||||||
Active bool `json:"active"`
|
Active bool `json:"active"`
|
||||||
|
ContentRestrictions *ContentRestrictions `json:"content_restrictions"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeJSON deserializes a Pin from JSON data.
|
// DeJSON deserializes a Pin from JSON data.
|
||||||
@@ -59,6 +60,11 @@ func (p *Pin) DeJSON(data []byte, client ClientInterface) (Model, error) {
|
|||||||
if v, ok := raw["active"]; ok {
|
if v, ok := raw["active"]; ok {
|
||||||
json.Unmarshal(v, &pin.Active)
|
json.Unmarshal(v, &pin.Active)
|
||||||
}
|
}
|
||||||
|
if v, ok := raw["content_restrictions"]; ok {
|
||||||
|
var cr ContentRestrictions
|
||||||
|
json.Unmarshal(v, &cr)
|
||||||
|
pin.ContentRestrictions = &cr
|
||||||
|
}
|
||||||
|
|
||||||
if v, ok := raw["track"]; ok {
|
if v, ok := raw["track"]; ok {
|
||||||
var track Track
|
var track Track
|
||||||
@@ -121,10 +127,12 @@ type PinsList struct {
|
|||||||
// Presaves represents presaves.
|
// Presaves represents presaves.
|
||||||
type Presaves struct {
|
type Presaves struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
Presaves []Presave `json:"presaves"`
|
UpcomingAlbums []Album `json:"upcoming_albums"`
|
||||||
Total int `json:"total"`
|
ReleasedAlbums []Album `json:"released_albums"`
|
||||||
Page int `json:"page"`
|
Presaves []Presave `json:"presaves"`
|
||||||
PageSize int `json:"page_size"`
|
Total int `json:"total"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
PageSize int `json:"page_size"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Presave represents a presave.
|
// Presave represents a presave.
|
||||||
@@ -213,3 +221,40 @@ func (p *Presave) DeList(data []byte, client ClientInterface) ([]Model, error) {
|
|||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeJSON deserializes Presaves from JSON data.
|
||||||
|
func (p *Presaves) DeJSON(data []byte, client ClientInterface) (Model, error) {
|
||||||
|
var raw map[string]json.RawMessage
|
||||||
|
if err := json.Unmarshal(data, &raw); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
presaves := &Presaves{BaseModel: *NewBaseModel(client)}
|
||||||
|
|
||||||
|
if v, ok := raw["upcoming_albums"]; ok {
|
||||||
|
var albums []Album
|
||||||
|
json.Unmarshal(v, &albums)
|
||||||
|
presaves.UpcomingAlbums = albums
|
||||||
|
}
|
||||||
|
if v, ok := raw["released_albums"]; ok {
|
||||||
|
var albums []Album
|
||||||
|
json.Unmarshal(v, &albums)
|
||||||
|
presaves.ReleasedAlbums = albums
|
||||||
|
}
|
||||||
|
if v, ok := raw["presaves"]; ok {
|
||||||
|
var presaveList []Presave
|
||||||
|
json.Unmarshal(v, &presaveList)
|
||||||
|
presaves.Presaves = presaveList
|
||||||
|
}
|
||||||
|
if v, ok := raw["total"]; ok {
|
||||||
|
json.Unmarshal(v, &presaves.Total)
|
||||||
|
}
|
||||||
|
if v, ok := raw["page"]; ok {
|
||||||
|
json.Unmarshal(v, &presaves.Page)
|
||||||
|
}
|
||||||
|
if v, ok := raw["page_size"]; ok {
|
||||||
|
json.Unmarshal(v, &presaves.PageSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
return presaves, nil
|
||||||
|
}
|
||||||
@@ -93,13 +93,15 @@ func (q *Queue) DeList(data []byte, c ClientInterface) ([]Model, error) {
|
|||||||
// QueueItem represents a queue item.
|
// QueueItem represents a queue item.
|
||||||
type QueueItem struct {
|
type QueueItem struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
TrackID string `json:"track_id"`
|
ID JSONID `json:"id"`
|
||||||
Track *Track `json:"track"`
|
TrackID string `json:"track_id"`
|
||||||
Position int `json:"position"`
|
Track *Track `json:"track"`
|
||||||
AddedAt string `json:"added_at"`
|
Position int `json:"position"`
|
||||||
AddedBy string `json:"added_by"`
|
AddedAt string `json:"added_at"`
|
||||||
Playable bool `json:"playable"`
|
AddedBy string `json:"added_by"`
|
||||||
PlayContext *PlayContext `json:"play_context"`
|
Playable bool `json:"playable"`
|
||||||
|
Modified string `json:"modified"`
|
||||||
|
Context *PlayContext `json:"context"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeJSON deserializes a QueueItem from JSON data.
|
// DeJSON deserializes a QueueItem from JSON data.
|
||||||
@@ -132,10 +134,10 @@ func (qi *QueueItem) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
|||||||
json.Unmarshal(v, &track)
|
json.Unmarshal(v, &track)
|
||||||
item.Track = &track
|
item.Track = &track
|
||||||
}
|
}
|
||||||
if v, ok := raw["play_context"]; ok {
|
if v, ok := raw["context"]; ok {
|
||||||
var ctx PlayContext
|
var ctx PlayContext
|
||||||
json.Unmarshal(v, &ctx)
|
json.Unmarshal(v, &ctx)
|
||||||
item.PlayContext = &ctx
|
item.Context = &ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
return item, nil
|
return item, nil
|
||||||
|
|||||||
+393
-2
@@ -192,8 +192,11 @@ func (r *RotorStation) DeList(data []byte, client ClientInterface) ([]Model, err
|
|||||||
// RotorSettings represents rotor station settings.
|
// RotorSettings represents rotor station settings.
|
||||||
type RotorSettings struct {
|
type RotorSettings struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
Diversity float64 `json:"diversity"`
|
Language string `json:"language"`
|
||||||
Energy float64 `json:"energy"`
|
Diversity string `json:"diversity"`
|
||||||
|
Mood int `json:"mood"`
|
||||||
|
Energy int `json:"energy"`
|
||||||
|
MoodEnergy string `json:"mood_energy"`
|
||||||
Acousticness float64 `json:"acousticness"`
|
Acousticness float64 `json:"acousticness"`
|
||||||
Instrumentalness float64 `json:"instrumentalness"`
|
Instrumentalness float64 `json:"instrumentalness"`
|
||||||
Live float64 `json:"live"`
|
Live float64 `json:"live"`
|
||||||
@@ -314,6 +317,70 @@ func (r *RotorSession) DeList(data []byte, client ClientInterface) ([]Model, err
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AdParams represents ad parameters.
|
||||||
|
type AdParams struct {
|
||||||
|
BaseModel
|
||||||
|
PartnerID string `json:"partner_id"`
|
||||||
|
CategoryID string `json:"category_id"`
|
||||||
|
PageRef string `json:"page_ref"`
|
||||||
|
TargetRef string `json:"target_ref"`
|
||||||
|
OtherParams string `json:"other_params"`
|
||||||
|
GenreID string `json:"genre_id"`
|
||||||
|
GenreName string `json:"genre_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeJSON deserializes AdParams from JSON data.
|
||||||
|
func (ap *AdParams) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
||||||
|
var raw map[string]json.RawMessage
|
||||||
|
if err := json.Unmarshal(data, &raw); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
params := &AdParams{BaseModel: *NewBaseModel(c)}
|
||||||
|
|
||||||
|
if v, ok := raw["partner_id"]; ok {
|
||||||
|
json.Unmarshal(v, ¶ms.PartnerID)
|
||||||
|
}
|
||||||
|
if v, ok := raw["category_id"]; ok {
|
||||||
|
json.Unmarshal(v, ¶ms.CategoryID)
|
||||||
|
}
|
||||||
|
if v, ok := raw["page_ref"]; ok {
|
||||||
|
json.Unmarshal(v, ¶ms.PageRef)
|
||||||
|
}
|
||||||
|
if v, ok := raw["target_ref"]; ok {
|
||||||
|
json.Unmarshal(v, ¶ms.TargetRef)
|
||||||
|
}
|
||||||
|
if v, ok := raw["other_params"]; ok {
|
||||||
|
json.Unmarshal(v, ¶ms.OtherParams)
|
||||||
|
}
|
||||||
|
if v, ok := raw["genre_id"]; ok {
|
||||||
|
json.Unmarshal(v, ¶ms.GenreID)
|
||||||
|
}
|
||||||
|
if v, ok := raw["genre_name"]; ok {
|
||||||
|
json.Unmarshal(v, ¶ms.GenreName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return params, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeList deserializes a list of AdParams from JSON data.
|
||||||
|
func (ap *AdParams) 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 {
|
||||||
|
params := &AdParams{BaseModel: *NewBaseModel(c)}
|
||||||
|
if err := json.Unmarshal(item, params); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, params)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
// SessionFeedback represents session feedback.
|
// SessionFeedback represents session feedback.
|
||||||
type SessionFeedback struct {
|
type SessionFeedback struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
@@ -327,3 +394,327 @@ type SessionFeedbacks struct {
|
|||||||
BaseModel
|
BaseModel
|
||||||
Feedbacks []SessionFeedback `json:"feedbacks"`
|
Feedbacks []SessionFeedback `json:"feedbacks"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dashboard represents rotor stations dashboard.
|
||||||
|
type Dashboard struct {
|
||||||
|
BaseModel
|
||||||
|
DashboardID string `json:"dashboard_id"`
|
||||||
|
Stations []StationResult `json:"stations"`
|
||||||
|
Pumpkin bool `json:"pumpkin"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeJSON deserializes Dashboard from JSON data.
|
||||||
|
func (d *Dashboard) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
||||||
|
var raw map[string]json.RawMessage
|
||||||
|
if err := json.Unmarshal(data, &raw); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dashboard := &Dashboard{BaseModel: *NewBaseModel(c)}
|
||||||
|
|
||||||
|
if v, ok := raw["dashboard_id"]; ok {
|
||||||
|
json.Unmarshal(v, &dashboard.DashboardID)
|
||||||
|
}
|
||||||
|
if v, ok := raw["stations"]; ok {
|
||||||
|
var stations []StationResult
|
||||||
|
json.Unmarshal(v, &stations)
|
||||||
|
dashboard.Stations = stations
|
||||||
|
}
|
||||||
|
if v, ok := raw["pumpkin"]; ok {
|
||||||
|
json.Unmarshal(v, &dashboard.Pumpkin)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dashboard, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeList deserializes a list of Dashboards from JSON data.
|
||||||
|
func (d *Dashboard) 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 {
|
||||||
|
dashboard := &Dashboard{BaseModel: *NewBaseModel(c)}
|
||||||
|
if err := json.Unmarshal(item, dashboard); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, dashboard)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StationResult represents a station result.
|
||||||
|
type StationResult struct {
|
||||||
|
BaseModel
|
||||||
|
Station *Station `json:"station"`
|
||||||
|
Settings *RotorSettings `json:"settings"`
|
||||||
|
Settings2 *RotorSettings `json:"settings2"`
|
||||||
|
AdParams *AdParams `json:"ad_params"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeJSON deserializes StationResult from JSON data.
|
||||||
|
func (sr *StationResult) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
||||||
|
var raw map[string]json.RawMessage
|
||||||
|
if err := json.Unmarshal(data, &raw); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := &StationResult{BaseModel: *NewBaseModel(c)}
|
||||||
|
|
||||||
|
if v, ok := raw["station"]; ok {
|
||||||
|
var station Station
|
||||||
|
json.Unmarshal(v, &station)
|
||||||
|
result.Station = &station
|
||||||
|
}
|
||||||
|
if v, ok := raw["settings"]; ok {
|
||||||
|
var settings RotorSettings
|
||||||
|
json.Unmarshal(v, &settings)
|
||||||
|
result.Settings = &settings
|
||||||
|
}
|
||||||
|
if v, ok := raw["settings2"]; ok {
|
||||||
|
var settings2 RotorSettings
|
||||||
|
json.Unmarshal(v, &settings2)
|
||||||
|
result.Settings2 = &settings2
|
||||||
|
}
|
||||||
|
if v, ok := raw["ad_params"]; ok {
|
||||||
|
var adParams AdParams
|
||||||
|
json.Unmarshal(v, &adParams)
|
||||||
|
result.AdParams = &adParams
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeList deserializes a list of StationResults from JSON data.
|
||||||
|
func (sr *StationResult) 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 {
|
||||||
|
sr := &StationResult{BaseModel: *NewBaseModel(c)}
|
||||||
|
if err := json.Unmarshal(item, sr); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, sr)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerativeStream represents a generative stream.
|
||||||
|
type GenerativeStream struct {
|
||||||
|
BaseModel
|
||||||
|
Data *GenerativeStreamData `json:"data"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
Stream *GenerativeStreamInfo `json:"stream"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeJSON deserializes GenerativeStream from JSON data.
|
||||||
|
func (gs *GenerativeStream) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
||||||
|
var raw map[string]json.RawMessage
|
||||||
|
if err := json.Unmarshal(data, &raw); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stream := &GenerativeStream{BaseModel: *NewBaseModel(c)}
|
||||||
|
|
||||||
|
if v, ok := raw["data"]; ok {
|
||||||
|
var data GenerativeStreamData
|
||||||
|
json.Unmarshal(v, &data)
|
||||||
|
stream.Data = &data
|
||||||
|
}
|
||||||
|
if v, ok := raw["version"]; ok {
|
||||||
|
json.Unmarshal(v, &stream.Version)
|
||||||
|
}
|
||||||
|
if v, ok := raw["stream"]; ok {
|
||||||
|
var streamInfo GenerativeStreamInfo
|
||||||
|
json.Unmarshal(v, &streamInfo)
|
||||||
|
stream.Stream = &streamInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
return stream, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeList deserializes a list of GenerativeStreams from JSON data.
|
||||||
|
func (gs *GenerativeStream) 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 {
|
||||||
|
stream := &GenerativeStream{BaseModel: *NewBaseModel(c)}
|
||||||
|
if err := json.Unmarshal(item, stream); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, stream)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Station represents a radio station (used in StationResult).
|
||||||
|
type Station struct {
|
||||||
|
BaseModel
|
||||||
|
ID JSONID `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Icon string `json:"icon"`
|
||||||
|
IDForFrom string `json:"id_for_from"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeJSON deserializes Station from JSON data.
|
||||||
|
func (s *Station) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
||||||
|
var raw map[string]json.RawMessage
|
||||||
|
if err := json.Unmarshal(data, &raw); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
station := &Station{BaseModel: *NewBaseModel(c)}
|
||||||
|
|
||||||
|
if v, ok := raw["id"]; ok {
|
||||||
|
json.Unmarshal(v, &station.ID)
|
||||||
|
}
|
||||||
|
if v, ok := raw["name"]; ok {
|
||||||
|
json.Unmarshal(v, &station.Name)
|
||||||
|
}
|
||||||
|
if v, ok := raw["icon"]; ok {
|
||||||
|
json.Unmarshal(v, &station.Icon)
|
||||||
|
}
|
||||||
|
if v, ok := raw["id_for_from"]; ok {
|
||||||
|
json.Unmarshal(v, &station.IDForFrom)
|
||||||
|
}
|
||||||
|
|
||||||
|
return station, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeList deserializes a list of Stations from JSON data.
|
||||||
|
func (s *Station) 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 {
|
||||||
|
station := &Station{BaseModel: *NewBaseModel(c)}
|
||||||
|
if err := json.Unmarshal(item, station); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, station)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerativeStreamData represents generative stream data.
|
||||||
|
type GenerativeStreamData struct {
|
||||||
|
BaseModel
|
||||||
|
Title string `json:"title"`
|
||||||
|
Subtitle string `json:"subtitle"`
|
||||||
|
BackgroundColor string `json:"background_color"`
|
||||||
|
DerivedColors *CoverDerivedColors `json:"derived_colors"`
|
||||||
|
ImageURL string `json:"image_url"`
|
||||||
|
VideoCoverURI string `json:"video_cover_uri"`
|
||||||
|
Explanations string `json:"explanations"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeJSON deserializes GenerativeStreamData from JSON data.
|
||||||
|
func (gsd *GenerativeStreamData) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
||||||
|
var raw map[string]json.RawMessage
|
||||||
|
if err := json.Unmarshal(data, &raw); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
gsdData := &GenerativeStreamData{BaseModel: *NewBaseModel(c)}
|
||||||
|
|
||||||
|
if v, ok := raw["title"]; ok {
|
||||||
|
json.Unmarshal(v, &gsdData.Title)
|
||||||
|
}
|
||||||
|
if v, ok := raw["subtitle"]; ok {
|
||||||
|
json.Unmarshal(v, &gsdData.Subtitle)
|
||||||
|
}
|
||||||
|
if v, ok := raw["background_color"]; ok {
|
||||||
|
json.Unmarshal(v, &gsdData.BackgroundColor)
|
||||||
|
}
|
||||||
|
if v, ok := raw["derived_colors"]; ok {
|
||||||
|
var dc CoverDerivedColors
|
||||||
|
json.Unmarshal(v, &dc)
|
||||||
|
gsdData.DerivedColors = &dc
|
||||||
|
}
|
||||||
|
if v, ok := raw["image_url"]; ok {
|
||||||
|
json.Unmarshal(v, &gsdData.ImageURL)
|
||||||
|
}
|
||||||
|
if v, ok := raw["video_cover_uri"]; ok {
|
||||||
|
json.Unmarshal(v, &gsdData.VideoCoverURI)
|
||||||
|
}
|
||||||
|
if v, ok := raw["explanations"]; ok {
|
||||||
|
json.Unmarshal(v, &gsdData.Explanations)
|
||||||
|
}
|
||||||
|
|
||||||
|
return gsdData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeList deserializes a list of GenerativeStreamData from JSON data.
|
||||||
|
func (gsd *GenerativeStreamData) 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 {
|
||||||
|
data := &GenerativeStreamData{BaseModel: *NewBaseModel(c)}
|
||||||
|
if err := json.Unmarshal(item, data); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, data)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerativeStreamInfo represents generative stream info.
|
||||||
|
type GenerativeStreamInfo struct {
|
||||||
|
BaseModel
|
||||||
|
ID string `json:"id"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeJSON deserializes GenerativeStreamInfo from JSON data.
|
||||||
|
func (gsi *GenerativeStreamInfo) 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 := &GenerativeStreamInfo{BaseModel: *NewBaseModel(c)}
|
||||||
|
|
||||||
|
if v, ok := raw["id"]; ok {
|
||||||
|
json.Unmarshal(v, &info.ID)
|
||||||
|
}
|
||||||
|
if v, ok := raw["url"]; ok {
|
||||||
|
json.Unmarshal(v, &info.URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeList deserializes a list of GenerativeStreamInfo from JSON data.
|
||||||
|
func (gsi *GenerativeStreamInfo) 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 := &GenerativeStreamInfo{BaseModel: *NewBaseModel(c)}
|
||||||
|
if err := json.Unmarshal(item, info); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, info)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
@@ -3,12 +3,25 @@ package models
|
|||||||
|
|
||||||
// SearchResult represents search results from the Yandex Music API.
|
// SearchResult represents search results from the Yandex Music API.
|
||||||
type SearchResult struct {
|
type SearchResult struct {
|
||||||
Tracks *SearchTracksResult `json:"tracks"`
|
SearchRequestID string `json:"search_request_id"`
|
||||||
Albums *SearchAlbumsResult `json:"albums"`
|
Text string `json:"text"`
|
||||||
Artists *SearchArtistsResult `json:"artists"`
|
Best *Best `json:"best"`
|
||||||
Playlists *SearchPlaylistsResult `json:"playlists"`
|
Tracks *SearchTracksResult `json:"tracks"`
|
||||||
Videos *SearchVideosResult `json:"videos"`
|
Albums *SearchAlbumsResult `json:"albums"`
|
||||||
Users *SearchUsersResult `json:"users"`
|
Artists *SearchArtistsResult `json:"artists"`
|
||||||
|
Playlists *SearchPlaylistsResult `json:"playlists"`
|
||||||
|
Videos *SearchVideosResult `json:"videos"`
|
||||||
|
Users *SearchUsersResult `json:"users"`
|
||||||
|
Podcasts *SearchAlbumsResult `json:"podcasts"`
|
||||||
|
PodcastEpisodes *SearchTracksResult `json:"podcast_episodes"`
|
||||||
|
Clips *SearchVideosResult `json:"clips"`
|
||||||
|
Banner *SearchBanner `json:"banner"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
PerPage int `json:"per_page"`
|
||||||
|
MisspellResult bool `json:"misspell_result"`
|
||||||
|
MisspellOriginal string `json:"misspell_original"`
|
||||||
|
MisspellCorrected bool `json:"misspell_corrected"`
|
||||||
|
Nocorrect bool `json:"nocorrect"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SearchTracksResult represents track search results.
|
// SearchTracksResult represents track search results.
|
||||||
|
|||||||
+518
-37
@@ -8,24 +8,68 @@ import (
|
|||||||
// Track represents a full track from the Yandex Music API.
|
// Track represents a full track from the Yandex Music API.
|
||||||
type Track struct {
|
type Track struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
ID JSONID `json:"id"`
|
ID JSONID `json:"id"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
DurationMs int `json:"duration_ms"`
|
Available bool `json:"available"`
|
||||||
StorageID string `json:"storage_id"`
|
Artists []Artist `json:"artists"`
|
||||||
CoverURI string `json:"cover_uri"`
|
Albums []Album `json:"albums"`
|
||||||
Artists []Artist `json:"artists"`
|
AvailableForPremiumUsers bool `json:"available_for_premium_users"`
|
||||||
Albums []Album `json:"albums"`
|
LyricsAvailable bool `json:"lyrics_available"`
|
||||||
Genre string `json:"genre"`
|
Best bool `json:"best"`
|
||||||
Version string `json:"version"`
|
RealID JSONID `json:"real_id"`
|
||||||
Explicit bool `json:"explicit"`
|
OgImage string `json:"og_image"`
|
||||||
Available bool `json:"available"`
|
Type string `json:"type"`
|
||||||
AvailableForPremiumUsers bool `json:"available_for_premium_users"`
|
CoverURI string `json:"cover_uri"`
|
||||||
R128 JSONR128 `json:"r128"`
|
Major *Major `json:"major"`
|
||||||
TrackSharingFlag string `json:"track_sharing_flag"`
|
DurationMs int `json:"duration_ms"`
|
||||||
Major *Major `json:"major"`
|
StorageDir string `json:"storage_dir"`
|
||||||
Lyrics *TrackLyricsInfo `json:"lyrics"`
|
FileSize int64 `json:"file_size"`
|
||||||
SimilarTracks []Track `json:"similar_tracks"`
|
Genre string `json:"genre"`
|
||||||
DownloadInfo []DownloadInfo `json:"download_info"`
|
AvailableAsRingtone bool `json:"available_as_ringtone"`
|
||||||
|
RingtoneID string `json:"ringtone_id"`
|
||||||
|
SimilarTracks []Track `json:"similar_tracks"`
|
||||||
|
Position *TrackPosition `json:"position"`
|
||||||
|
Substituted *Track `json:"substituted"`
|
||||||
|
MatchedTrack *Track `json:"matched_track"`
|
||||||
|
Normalization *Normalization `json:"normalization"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
CanPublish bool `json:"can_publish"`
|
||||||
|
State string `json:"state"`
|
||||||
|
DesiredVisibility string `json:"desired_visibility"`
|
||||||
|
Filename string `json:"filename"`
|
||||||
|
UserInfo *User `json:"user_info"`
|
||||||
|
MetaData *MetaData `json:"meta_data"`
|
||||||
|
Regions []string `json:"regions"`
|
||||||
|
AvailableAsRbt bool `json:"available_as_rbt"`
|
||||||
|
ContentWarning string `json:"content_warning"`
|
||||||
|
Explicit bool `json:"explicit"`
|
||||||
|
PreviewDurationMs int `json:"preview_duration_ms"`
|
||||||
|
AvailableFullWithoutPermission bool `json:"available_full_without_permission"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
RememberPosition bool `json:"remember_position"`
|
||||||
|
BackgroundVideoURI string `json:"background_video_uri"`
|
||||||
|
ShortDescription string `json:"short_description"`
|
||||||
|
IsSuitableForChildren bool `json:"is_suitable_for_children"`
|
||||||
|
TrackSource string `json:"track_source"`
|
||||||
|
AvailableForOptions []string `json:"available_for_options"`
|
||||||
|
R128 *R128 `json:"r128"`
|
||||||
|
LyricsInfo *TrackLyricsInfo `json:"lyrics_info"`
|
||||||
|
TrackSharingFlag string `json:"track_sharing_flag"`
|
||||||
|
DerivedColors *CoverDerivedColors `json:"derived_colors"`
|
||||||
|
Fade *Fade `json:"fade"`
|
||||||
|
SmartPreviewParams *SmartPreviewParams `json:"smart_preview_params"`
|
||||||
|
SpecialAudioResources []string `json:"special_audio_resources"`
|
||||||
|
Disclaimers []string `json:"disclaimers"`
|
||||||
|
BackgroundVideoID string `json:"background_video_id"`
|
||||||
|
PlayerID string `json:"player_id"`
|
||||||
|
MixFade *Fade `json:"mix_fade"`
|
||||||
|
Chart *Chart `json:"chart"`
|
||||||
|
ClipIDs []int `json:"clip_ids"`
|
||||||
|
Isrc string `json:"isrc"`
|
||||||
|
UgcArtistName string `json:"ugc_artist_name"`
|
||||||
|
PodcastEpisodeType string `json:"podcast_episode_type"`
|
||||||
|
PubDate string `json:"pub_date"`
|
||||||
|
DownloadInfo []DownloadInfo `json:"download_info"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeJSON deserializes a Track from JSON data.
|
// DeJSON deserializes a Track from JSON data.
|
||||||
@@ -47,8 +91,8 @@ func (t *Track) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
|||||||
if v, ok := raw["duration_ms"]; ok {
|
if v, ok := raw["duration_ms"]; ok {
|
||||||
json.Unmarshal(v, &track.DurationMs)
|
json.Unmarshal(v, &track.DurationMs)
|
||||||
}
|
}
|
||||||
if v, ok := raw["storage_id"]; ok {
|
if v, ok := raw["storage_dir"]; ok {
|
||||||
json.Unmarshal(v, &track.StorageID)
|
json.Unmarshal(v, &track.StorageDir)
|
||||||
}
|
}
|
||||||
if v, ok := raw["cover_uri"]; ok {
|
if v, ok := raw["cover_uri"]; ok {
|
||||||
json.Unmarshal(v, &track.CoverURI)
|
json.Unmarshal(v, &track.CoverURI)
|
||||||
@@ -74,6 +118,12 @@ func (t *Track) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
|||||||
if v, ok := raw["track_sharing_flag"]; ok {
|
if v, ok := raw["track_sharing_flag"]; ok {
|
||||||
json.Unmarshal(v, &track.TrackSharingFlag)
|
json.Unmarshal(v, &track.TrackSharingFlag)
|
||||||
}
|
}
|
||||||
|
if v, ok := raw["available_as_ringtone"]; ok {
|
||||||
|
json.Unmarshal(v, &track.AvailableAsRingtone)
|
||||||
|
}
|
||||||
|
if v, ok := raw["ringtone_id"]; ok {
|
||||||
|
json.Unmarshal(v, &track.RingtoneID)
|
||||||
|
}
|
||||||
|
|
||||||
// Nested objects
|
// Nested objects
|
||||||
if v, ok := raw["artists"]; ok {
|
if v, ok := raw["artists"]; ok {
|
||||||
@@ -94,10 +144,10 @@ func (t *Track) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
|||||||
track.Major = &major
|
track.Major = &major
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if v, ok := raw["lyrics"]; ok {
|
if v, ok := raw["lyrics_info"]; ok {
|
||||||
var lyrics TrackLyricsInfo
|
var lyrics TrackLyricsInfo
|
||||||
if err := json.Unmarshal(v, &lyrics); err == nil {
|
if err := json.Unmarshal(v, &lyrics); err == nil {
|
||||||
track.Lyrics = &lyrics
|
track.LyricsInfo = &lyrics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if v, ok := raw["similar_tracks"]; ok {
|
if v, ok := raw["similar_tracks"]; ok {
|
||||||
@@ -112,6 +162,12 @@ func (t *Track) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
|||||||
track.DownloadInfo = downloadInfo
|
track.DownloadInfo = downloadInfo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if v, ok := raw["position"]; ok {
|
||||||
|
var pos TrackPosition
|
||||||
|
if err := json.Unmarshal(v, &pos); err == nil {
|
||||||
|
track.Position = &pos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return track, nil
|
return track, nil
|
||||||
}
|
}
|
||||||
@@ -144,9 +200,6 @@ func (t *Track) GetDownloadInfo(quality string) *DownloadInfo {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: Artist and Album types are defined in their respective files
|
|
||||||
// These are imported automatically since they're in the same package
|
|
||||||
|
|
||||||
// Major represents major label info for a track.
|
// Major represents major label info for a track.
|
||||||
type Major struct {
|
type Major struct {
|
||||||
ID JSONID `json:"id"`
|
ID JSONID `json:"id"`
|
||||||
@@ -154,21 +207,449 @@ type Major struct {
|
|||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrackLyricsInfo represents minimal lyrics info for a track.
|
// TrackLyricsInfo represents lyrics availability info for a track.
|
||||||
type TrackLyricsInfo struct {
|
type TrackLyricsInfo struct {
|
||||||
ID JSONID `json:"id"`
|
BaseModel
|
||||||
Text string `json:"text"`
|
HasAvailableSyncLyrics bool `json:"has_available_sync_lyrics"`
|
||||||
Major *Major `json:"major"`
|
HasAvailableTextLyrics bool `json:"has_available_text_lyrics"`
|
||||||
Available bool `json:"available"`
|
}
|
||||||
HasText bool `json:"has_text"`
|
|
||||||
|
// DeJSON deserializes TrackLyricsInfo from JSON data.
|
||||||
|
func (tli *TrackLyricsInfo) 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 := &TrackLyricsInfo{BaseModel: *NewBaseModel(c)}
|
||||||
|
|
||||||
|
if v, ok := raw["has_available_sync_lyrics"]; ok {
|
||||||
|
json.Unmarshal(v, &info.HasAvailableSyncLyrics)
|
||||||
|
}
|
||||||
|
if v, ok := raw["has_available_text_lyrics"]; ok {
|
||||||
|
json.Unmarshal(v, &info.HasAvailableTextLyrics)
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeList deserializes a list of TrackLyricsInfo from JSON data.
|
||||||
|
func (tli *TrackLyricsInfo) 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 := &TrackLyricsInfo{BaseModel: *NewBaseModel(c)}
|
||||||
|
if err := json.Unmarshal(item, info); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, info)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DownloadInfo represents file download info for a track.
|
// DownloadInfo represents file download info for a track.
|
||||||
type DownloadInfo struct {
|
type DownloadInfo struct {
|
||||||
Quality string `json:"quality"`
|
Quality string `json:"quality"`
|
||||||
Codec string `json:"codec"`
|
Codec string `json:"codec"`
|
||||||
Bitrate int `json:"bitrate"`
|
Bitrate int `json:"bitrate"`
|
||||||
Size int64 `json:"size"`
|
Size int64 `json:"size"`
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
DirectLink bool `json:"direct_link"`
|
DirectLink bool `json:"direct_link"`
|
||||||
|
DownloadInfoURL string `json:"download_info_url"`
|
||||||
|
DirectLinkURL string `json:"-"` // Computed signed download URL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalization represents track normalization values.
|
||||||
|
type Normalization struct {
|
||||||
|
BaseModel
|
||||||
|
Gain float64 `json:"gain"`
|
||||||
|
Peak int `json:"peak"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetaData represents track metadata.
|
||||||
|
type MetaData struct {
|
||||||
|
BaseModel
|
||||||
|
Album string `json:"album"`
|
||||||
|
Volume int `json:"volume"`
|
||||||
|
Year int `json:"year"`
|
||||||
|
Number int `json:"number"`
|
||||||
|
Genre string `json:"genre"`
|
||||||
|
Lyricist string `json:"lyricist"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
Composer string `json:"composer"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fade represents fade parameters for a track.
|
||||||
|
type Fade struct {
|
||||||
|
BaseModel
|
||||||
|
InStart float64 `json:"in_start"`
|
||||||
|
InStop float64 `json:"in_stop"`
|
||||||
|
OutStart float64 `json:"out_start"`
|
||||||
|
OutStop float64 `json:"out_stop"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmartPreviewParams represents smart preview parameters.
|
||||||
|
type SmartPreviewParams struct {
|
||||||
|
BaseModel
|
||||||
|
DurationMs int `json:"duration_ms"`
|
||||||
|
Fade *Fade `json:"fade"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CoverDerivedColors represents derived colors from cover art.
|
||||||
|
type CoverDerivedColors struct {
|
||||||
|
BaseModel
|
||||||
|
Average string `json:"average"`
|
||||||
|
WaveText string `json:"wave_text"`
|
||||||
|
MiniPlayer string `json:"mini_player"`
|
||||||
|
Accent string `json:"accent"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomWave represents a custom wave for playlists.
|
||||||
|
type CustomWave struct {
|
||||||
|
BaseModel
|
||||||
|
Title string `json:"title"`
|
||||||
|
AnimationURL string `json:"animation_url"`
|
||||||
|
Position string `json:"position"`
|
||||||
|
Header string `json:"header"`
|
||||||
|
BackgroundImageURL string `json:"background_image_url"`
|
||||||
|
SquareAgentAnimation string `json:"square_agent_animation"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrackId represents a track ID reference.
|
||||||
|
type TrackId struct {
|
||||||
|
BaseModel
|
||||||
|
ID JSONID `json:"id"`
|
||||||
|
TrackID JSONID `json:"track_id"`
|
||||||
|
AlbumID JSONID `json:"album_id"`
|
||||||
|
From string `json:"from"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeJSON deserializes a TrackId from JSON data.
|
||||||
|
func (ti *TrackId) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
||||||
|
var raw map[string]json.RawMessage
|
||||||
|
if err := json.Unmarshal(data, &raw); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tid := &TrackId{BaseModel: *NewBaseModel(c)}
|
||||||
|
|
||||||
|
if v, ok := raw["id"]; ok {
|
||||||
|
json.Unmarshal(v, &tid.ID)
|
||||||
|
}
|
||||||
|
if v, ok := raw["track_id"]; ok {
|
||||||
|
json.Unmarshal(v, &tid.TrackID)
|
||||||
|
}
|
||||||
|
if v, ok := raw["album_id"]; ok {
|
||||||
|
json.Unmarshal(v, &tid.AlbumID)
|
||||||
|
}
|
||||||
|
if v, ok := raw["from"]; ok {
|
||||||
|
json.Unmarshal(v, &tid.From)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeList deserializes a list of TrackId from JSON data.
|
||||||
|
func (ti *TrackId) 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 {
|
||||||
|
tid := &TrackId{BaseModel: *NewBaseModel(c)}
|
||||||
|
if err := json.Unmarshal(item, tid); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, tid)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrackFullInfo represents full track info.
|
||||||
|
type TrackFullInfo struct {
|
||||||
|
BaseModel
|
||||||
|
Track *Track `json:"track"`
|
||||||
|
SimilarTracks []Track `json:"similar_tracks"`
|
||||||
|
AlsoInAlbums []Track `json:"also_in_albums"`
|
||||||
|
Aliases []string `json:"aliases"`
|
||||||
|
Artists []Artist `json:"artists"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeJSON deserializes a TrackFullInfo from JSON data.
|
||||||
|
func (tfi *TrackFullInfo) 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 := &TrackFullInfo{BaseModel: *NewBaseModel(c)}
|
||||||
|
|
||||||
|
if v, ok := raw["track"]; ok {
|
||||||
|
var track Track
|
||||||
|
if err := json.Unmarshal(v, &track); err == nil {
|
||||||
|
info.Track = &track
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v, ok := raw["similar_tracks"]; ok {
|
||||||
|
var tracks []Track
|
||||||
|
if err := json.Unmarshal(v, &tracks); err == nil {
|
||||||
|
info.SimilarTracks = tracks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v, ok := raw["also_in_albums"]; ok {
|
||||||
|
var tracks []Track
|
||||||
|
if err := json.Unmarshal(v, &tracks); err == nil {
|
||||||
|
info.AlsoInAlbums = tracks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v, ok := raw["aliases"]; ok {
|
||||||
|
json.Unmarshal(v, &info.Aliases)
|
||||||
|
}
|
||||||
|
if v, ok := raw["artists"]; ok {
|
||||||
|
var artists []Artist
|
||||||
|
if err := json.Unmarshal(v, &artists); err == nil {
|
||||||
|
info.Artists = artists
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeList deserializes a list of TrackFullInfo from JSON data.
|
||||||
|
func (tfi *TrackFullInfo) 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 := &TrackFullInfo{BaseModel: *NewBaseModel(c)}
|
||||||
|
if err := json.Unmarshal(item, info); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, info)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SimilarTracks represents similar tracks.
|
||||||
|
type SimilarTracks struct {
|
||||||
|
BaseModel
|
||||||
|
Track *Track `json:"track"`
|
||||||
|
SimilarTracks []Track `json:"similar_tracks"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeJSON deserializes SimilarTracks from JSON data.
|
||||||
|
func (st *SimilarTracks) 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 := &SimilarTracks{BaseModel: *NewBaseModel(c)}
|
||||||
|
|
||||||
|
if v, ok := raw["track"]; ok {
|
||||||
|
var track Track
|
||||||
|
if err := json.Unmarshal(v, &track); err == nil {
|
||||||
|
similar.Track = &track
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v, ok := raw["similar_tracks"]; ok {
|
||||||
|
var tracks []Track
|
||||||
|
if err := json.Unmarshal(v, &tracks); err == nil {
|
||||||
|
similar.SimilarTracks = tracks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return similar, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeList deserializes a list of SimilarTracks from JSON data.
|
||||||
|
func (st *SimilarTracks) 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 := &SimilarTracks{BaseModel: *NewBaseModel(c)}
|
||||||
|
if err := json.Unmarshal(item, similar); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, similar)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrackTrailer represents track trailer info.
|
||||||
|
type TrackTrailer struct {
|
||||||
|
BaseModel
|
||||||
|
Title string `json:"title"`
|
||||||
|
Track *Track `json:"track"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeJSON deserializes a TrackTrailer from JSON data.
|
||||||
|
func (tt *TrackTrailer) 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 := &TrackTrailer{BaseModel: *NewBaseModel(c)}
|
||||||
|
|
||||||
|
if v, ok := raw["title"]; ok {
|
||||||
|
json.Unmarshal(v, &trailer.Title)
|
||||||
|
}
|
||||||
|
if v, ok := raw["track"]; ok {
|
||||||
|
var track Track
|
||||||
|
if err := json.Unmarshal(v, &track); err == nil {
|
||||||
|
trailer.Track = &track
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return trailer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeList deserializes a list of TrackTrailer from JSON data.
|
||||||
|
func (tt *TrackTrailer) 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 := &TrackTrailer{BaseModel: *NewBaseModel(c)}
|
||||||
|
if err := json.Unmarshal(item, trailer); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, trailer)
|
||||||
|
}
|
||||||
|
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
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@ type TrackShort struct {
|
|||||||
ID JSONID `json:"id"`
|
ID JSONID `json:"id"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
DurationMs int `json:"duration_ms"`
|
DurationMs int `json:"duration_ms"`
|
||||||
StorageID string `json:"storage_id"`
|
StorageDir string `json:"storage_dir"`
|
||||||
CoverURI string `json:"cover_uri"`
|
CoverURI string `json:"cover_uri"`
|
||||||
Artists []Artist `json:"artists"`
|
Artists []Artist `json:"artists"`
|
||||||
Albums []Album `json:"albums"`
|
Albums []Album `json:"albums"`
|
||||||
@@ -20,10 +20,10 @@ type TrackShort struct {
|
|||||||
Explicit bool `json:"explicit"`
|
Explicit bool `json:"explicit"`
|
||||||
Available bool `json:"available"`
|
Available bool `json:"available"`
|
||||||
AvailableForPremiumUsers bool `json:"available_for_premium_users"`
|
AvailableForPremiumUsers bool `json:"available_for_premium_users"`
|
||||||
R128 JSONR128 `json:"r128"`
|
R128 *R128 `json:"r128"`
|
||||||
TrackSharingFlag string `json:"track_sharing_flag"`
|
TrackSharingFlag string `json:"track_sharing_flag"`
|
||||||
Major *Major `json:"major"`
|
Major *Major `json:"major"`
|
||||||
Lyrics *SupplementLyrics `json:"lyrics"`
|
Lyrics *TrackLyricsInfo `json:"lyrics"`
|
||||||
SimilarTracks []TrackShort `json:"similar_tracks"`
|
SimilarTracks []TrackShort `json:"similar_tracks"`
|
||||||
DownloadInfo []DownloadInfo `json:"download_info"`
|
DownloadInfo []DownloadInfo `json:"download_info"`
|
||||||
// Additional fields from track_short.py
|
// Additional fields from track_short.py
|
||||||
@@ -51,8 +51,8 @@ func (t *TrackShort) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
|||||||
if v, ok := raw["duration_ms"]; ok {
|
if v, ok := raw["duration_ms"]; ok {
|
||||||
json.Unmarshal(v, &track.DurationMs)
|
json.Unmarshal(v, &track.DurationMs)
|
||||||
}
|
}
|
||||||
if v, ok := raw["storage_id"]; ok {
|
if v, ok := raw["storage_dir"]; ok {
|
||||||
json.Unmarshal(v, &track.StorageID)
|
json.Unmarshal(v, &track.StorageDir)
|
||||||
}
|
}
|
||||||
if v, ok := raw["cover_uri"]; ok {
|
if v, ok := raw["cover_uri"]; ok {
|
||||||
json.Unmarshal(v, &track.CoverURI)
|
json.Unmarshal(v, &track.CoverURI)
|
||||||
@@ -105,7 +105,7 @@ func (t *TrackShort) DeJSON(data []byte, c ClientInterface) (Model, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if v, ok := raw["lyrics"]; ok {
|
if v, ok := raw["lyrics"]; ok {
|
||||||
var lyrics SupplementLyrics
|
var lyrics TrackLyricsInfo
|
||||||
if err := json.Unmarshal(v, &lyrics); err == nil {
|
if err := json.Unmarshal(v, &lyrics); err == nil {
|
||||||
track.Lyrics = &lyrics
|
track.Lyrics = &lyrics
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ func TestTrack_DeJSON(t *testing.T) {
|
|||||||
"id": "123",
|
"id": "123",
|
||||||
"title": "Test Track",
|
"title": "Test Track",
|
||||||
"duration_ms": 180000,
|
"duration_ms": 180000,
|
||||||
"storage_id": "456",
|
"storage_dir": "456",
|
||||||
"cover_uri": "https://example.com/cover.jpg",
|
"cover_uri": "https://example.com/cover.jpg",
|
||||||
"genre": "rock",
|
"genre": "rock",
|
||||||
"version": "album",
|
"version": "album",
|
||||||
@@ -55,7 +55,7 @@ func TestTrack_DeJSON(t *testing.T) {
|
|||||||
"artists": [{"id": "1", "name": "Artist 1", "cover_uri": "https://example.com/artist.jpg"}],
|
"artists": [{"id": "1", "name": "Artist 1", "cover_uri": "https://example.com/artist.jpg"}],
|
||||||
"albums": [{"id": "789", "title": "Test Album", "cover_uri": "https://example.com/album.jpg", "year": 2023}],
|
"albums": [{"id": "789", "title": "Test Album", "cover_uri": "https://example.com/album.jpg", "year": 2023}],
|
||||||
"major": {"id": "major1", "name": "Major Label", "description": "Big label"},
|
"major": {"id": "major1", "name": "Major Label", "description": "Big label"},
|
||||||
"lyrics": {"id": "lyr1", "text": "Test lyrics", "available": true, "has_text": true}
|
"lyrics_info": {"has_available_sync_lyrics": true, "has_available_text_lyrics": true}
|
||||||
}`)
|
}`)
|
||||||
|
|
||||||
track := &Track{BaseModel: *NewBaseModel(client)}
|
track := &Track{BaseModel: *NewBaseModel(client)}
|
||||||
@@ -80,8 +80,8 @@ func TestTrack_DeJSON(t *testing.T) {
|
|||||||
if resultTrack.Major == nil || resultTrack.Major.Name != "Major Label" {
|
if resultTrack.Major == nil || resultTrack.Major.Name != "Major Label" {
|
||||||
t.Errorf("Expected Major 'Major Label'")
|
t.Errorf("Expected Major 'Major Label'")
|
||||||
}
|
}
|
||||||
if resultTrack.Lyrics == nil || resultTrack.Lyrics.Text != "Test lyrics" {
|
if resultTrack.LyricsInfo == nil || resultTrack.LyricsInfo.HasAvailableSyncLyrics != true {
|
||||||
t.Errorf("Expected Lyrics 'Test lyrics'")
|
t.Errorf("Expected LyricsInfo.HasAvailableSyncLyrics true")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user