Initial commit

This commit is contained in:
Mrixs committed 2026-10-14 23:04:51 +03:00
commit 02a6dabcfb
62 files changed
+15751

No files matched your search

+315
View File
@@ -0,0 +1,315 @@
package models
import (
"encoding/json"
)
// AccountSettings represents account settings
type AccountSettings struct {
BaseModel
Language string `json:"language"`
Region string `json:"region"`
Email string `json:"email"`
Phone string `json:"phone"`
Notifications bool `json:"notifications"`
AutoPlay bool `json:"auto_play"`
HighQuality bool `json:"high_quality"`
CacheSize int `json:"cache_size"`
DownloadQuality string `json:"download_quality"`
ExplicitContent bool `json:"explicit_content"`
NormalizeVolume bool `json:"normalize_volume"`
Crossfade bool `json:"crossfade"`
GaplessPlayback bool `json:"gapless_playback"`
ShowLyrics bool `json:"show_lyrics"`
ShowCredits bool `json:"show_credits"`
AutoDownload bool `json:"auto_download"`
DownloadOnWifiOnly bool `json:"download_on_wifi_only"`
}
// DeJSON deserializes AccountSettings from JSON
func (a *AccountSettings) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
settings := &AccountSettings{BaseModel: *NewBaseModel(c)}
if v, ok := raw["language"]; ok {
json.Unmarshal(v, &settings.Language)
}
if v, ok := raw["region"]; ok {
json.Unmarshal(v, &settings.Region)
}
if v, ok := raw["email"]; ok {
json.Unmarshal(v, &settings.Email)
}
if v, ok := raw["phone"]; ok {
json.Unmarshal(v, &settings.Phone)
}
if v, ok := raw["notifications"]; ok {
json.Unmarshal(v, &settings.Notifications)
}
if v, ok := raw["auto_play"]; ok {
json.Unmarshal(v, &settings.AutoPlay)
}
if v, ok := raw["high_quality"]; ok {
json.Unmarshal(v, &settings.HighQuality)
}
if v, ok := raw["cache_size"]; ok {
json.Unmarshal(v, &settings.CacheSize)
}
if v, ok := raw["download_quality"]; ok {
json.Unmarshal(v, &settings.DownloadQuality)
}
if v, ok := raw["explicit_content"]; ok {
json.Unmarshal(v, &settings.ExplicitContent)
}
if v, ok := raw["normalize_volume"]; ok {
json.Unmarshal(v, &settings.NormalizeVolume)
}
if v, ok := raw["crossfade"]; ok {
json.Unmarshal(v, &settings.Crossfade)
}
if v, ok := raw["gapless_playback"]; ok {
json.Unmarshal(v, &settings.GaplessPlayback)
}
if v, ok := raw["show_lyrics"]; ok {
json.Unmarshal(v, &settings.ShowLyrics)
}
if v, ok := raw["show_credits"]; ok {
json.Unmarshal(v, &settings.ShowCredits)
}
if v, ok := raw["auto_download"]; ok {
json.Unmarshal(v, &settings.AutoDownload)
}
if v, ok := raw["download_on_wifi_only"]; ok {
json.Unmarshal(v, &settings.DownloadOnWifiOnly)
}
return settings, nil
}
// DeList deserializes a list of AccountSettings
func (a *AccountSettings) 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 {
settings := &AccountSettings{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, settings); err != nil {
continue
}
result = append(result, settings)
}
return result, nil
}
// Subscription represents subscription info
type Subscription struct {
BaseModel
ID string `json:"id"`
Type string `json:"type"`
Status string `json:"status"`
StartDate string `json:"start_date"`
EndDate string `json:"end_date"`
AutoRenew bool `json:"auto_renew"`
PaymentMethod string `json:"payment_method"`
Price int `json:"price"`
Currency string `json:"currency"`
Features []string `json:"features"`
CanCancel bool `json:"can_cancel"`
CanUpgrade bool `json:"can_upgrade"`
TrialAvailable bool `json:"trial_available"`
TrialUsed bool `json:"trial_used"`
}
// DeJSON deserializes Subscription from JSON
func (s *Subscription) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
sub := &Subscription{BaseModel: *NewBaseModel(c)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &sub.ID)
}
if v, ok := raw["type"]; ok {
json.Unmarshal(v, &sub.Type)
}
if v, ok := raw["status"]; ok {
json.Unmarshal(v, &sub.Status)
}
if v, ok := raw["start_date"]; ok {
json.Unmarshal(v, &sub.StartDate)
}
if v, ok := raw["end_date"]; ok {
json.Unmarshal(v, &sub.EndDate)
}
if v, ok := raw["auto_renew"]; ok {
json.Unmarshal(v, &sub.AutoRenew)
}
if v, ok := raw["payment_method"]; ok {
json.Unmarshal(v, &sub.PaymentMethod)
}
if v, ok := raw["price"]; ok {
json.Unmarshal(v, &sub.Price)
}
if v, ok := raw["currency"]; ok {
json.Unmarshal(v, &sub.Currency)
}
if v, ok := raw["features"]; ok {
json.Unmarshal(v, &sub.Features)
}
if v, ok := raw["can_cancel"]; ok {
json.Unmarshal(v, &sub.CanCancel)
}
if v, ok := raw["can_upgrade"]; ok {
json.Unmarshal(v, &sub.CanUpgrade)
}
if v, ok := raw["trial_available"]; ok {
json.Unmarshal(v, &sub.TrialAvailable)
}
if v, ok := raw["trial_used"]; ok {
json.Unmarshal(v, &sub.TrialUsed)
}
return sub, nil
}
// DeList deserializes a list of Subscriptions
func (s *Subscription) 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 {
sub := &Subscription{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, sub); err != nil {
continue
}
result = append(result, sub)
}
return result, nil
}
// PlusInfo represents Plus subscription info (from /account/plus or account status)
type PlusInfo struct {
BaseModel
HasPlus bool `json:"has_plus"`
IsTutorialCompleted bool `json:"is_tutorial_completed"`
Migrated *bool `json:"migrated,omitempty"`
}
// DeJSON deserializes PlusInfo from JSON
func (p *PlusInfo) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
plus := &PlusInfo{BaseModel: *NewBaseModel(c)}
if v, ok := raw["has_plus"]; ok {
json.Unmarshal(v, &plus.HasPlus)
}
if v, ok := raw["is_tutorial_completed"]; ok {
json.Unmarshal(v, &plus.IsTutorialCompleted)
}
if v, ok := raw["migrated"]; ok {
var val bool
json.Unmarshal(v, &val)
plus.Migrated = &val
}
return plus, nil
}
// DeList deserializes a list of PlusInfo
func (p *PlusInfo) 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 {
plus := &PlusInfo{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, plus); err != nil {
continue
}
result = append(result, plus)
}
return result, nil
}
// PromoCodeStatus represents promo code status
type PromoCodeStatus struct {
BaseModel
Code string `json:"code"`
Status string `json:"status"`
Description string `json:"description"`
Discount int `json:"discount"`
Months int `json:"months"`
ExpiresAt string `json:"expires_at"`
Activated bool `json:"activated"`
}
// DeJSON deserializes PromoCodeStatus from JSON
func (p *PromoCodeStatus) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
promo := &PromoCodeStatus{BaseModel: *NewBaseModel(c)}
if v, ok := raw["code"]; ok {
json.Unmarshal(v, &promo.Code)
}
if v, ok := raw["status"]; ok {
json.Unmarshal(v, &promo.Status)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &promo.Description)
}
if v, ok := raw["discount"]; ok {
json.Unmarshal(v, &promo.Discount)
}
if v, ok := raw["months"]; ok {
json.Unmarshal(v, &promo.Months)
}
if v, ok := raw["expires_at"]; ok {
json.Unmarshal(v, &promo.ExpiresAt)
}
if v, ok := raw["activated"]; ok {
json.Unmarshal(v, &promo.Activated)
}
return promo, nil
}
// DeList deserializes a list of PromoCodeStatus
func (p *PromoCodeStatus) 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 {
promo := &PromoCodeStatus{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, promo); err != nil {
continue
}
result = append(result, promo)
}
return result, nil
}
+274
View File
@@ -0,0 +1,274 @@
package models
import (
"encoding/json"
)
// Album represents a full album
type Album struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
CoverURI string `json:"cover_uri"`
Genre string `json:"genre"`
Year int `json:"year"`
TrackCount int `json:"track_count"`
Available bool `json:"available"`
ReleaseDate string `json:"release_date"`
Artists []Artist `json:"artists"`
Labels []Label `json:"labels"`
Volumes [][]Track `json:"volumes"`
Description string `json:"description"`
Meta *AlbumMeta `json:"meta"`
Best *Best `json:"best"`
// Additional fields
Region string `json:"region"`
AvailableForPremiumUsers bool `json:"available_for_premium_users"`
AvailableForPremiumUsersOnly bool `json:"available_for_premium_users_only"`
ContentWarning string `json:"content_warning"`
Label string `json:"label"`
CatalogNumber string `json:"catalog_number"`
Options []string `json:"options"`
TrackPositions []TrackPosition `json:"track_positions"`
SimilarAlbums []Album `json:"similar_albums"`
Trailer *TrailerInfo `json:"trailer"`
}
// DeJSON deserializes an Album from JSON
func (a *Album) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
album := &Album{BaseModel: *NewBaseModel(c)}
// Simple fields
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &album.ID)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &album.Title)
}
if v, ok := raw["cover_uri"]; ok {
json.Unmarshal(v, &album.CoverURI)
}
if v, ok := raw["genre"]; ok {
json.Unmarshal(v, &album.Genre)
}
if v, ok := raw["year"]; ok {
json.Unmarshal(v, &album.Year)
}
if v, ok := raw["track_count"]; ok {
json.Unmarshal(v, &album.TrackCount)
}
if v, ok := raw["available"]; ok {
json.Unmarshal(v, &album.Available)
}
if v, ok := raw["release_date"]; ok {
json.Unmarshal(v, &album.ReleaseDate)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &album.Description)
}
if v, ok := raw["region"]; ok {
json.Unmarshal(v, &album.Region)
}
if v, ok := raw["available_for_premium_users"]; ok {
json.Unmarshal(v, &album.AvailableForPremiumUsers)
}
if v, ok := raw["available_for_premium_users_only"]; ok {
json.Unmarshal(v, &album.AvailableForPremiumUsersOnly)
}
if v, ok := raw["content_warning"]; ok {
json.Unmarshal(v, &album.ContentWarning)
}
if v, ok := raw["label"]; ok {
json.Unmarshal(v, &album.Label)
}
if v, ok := raw["catalog_number"]; ok {
json.Unmarshal(v, &album.CatalogNumber)
}
if v, ok := raw["options"]; ok {
json.Unmarshal(v, &album.Options)
}
// Nested objects
if v, ok := raw["artists"]; ok {
var artists []Artist
json.Unmarshal(v, &artists)
album.Artists = artists
}
if v, ok := raw["labels"]; ok {
var labels []Label
json.Unmarshal(v, &labels)
album.Labels = labels
}
if v, ok := raw["volumes"]; ok {
var volumes [][]Track
json.Unmarshal(v, &volumes)
album.Volumes = volumes
}
if v, ok := raw["meta"]; ok {
var meta AlbumMeta
json.Unmarshal(v, &meta)
album.Meta = &meta
}
if v, ok := raw["best"]; ok {
var best Best
json.Unmarshal(v, &best)
album.Best = &best
}
if v, ok := raw["track_positions"]; ok {
var positions []TrackPosition
json.Unmarshal(v, &positions)
album.TrackPositions = positions
}
if v, ok := raw["similar_albums"]; ok {
var similar []Album
json.Unmarshal(v, &similar)
album.SimilarAlbums = similar
}
if v, ok := raw["trailer"]; ok {
var trailer TrailerInfo
json.Unmarshal(v, &trailer)
album.Trailer = &trailer
}
return album, nil
}
// DeList deserializes a list of Albums
func (a *Album) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
album := &Album{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, album); err != nil {
continue
}
result = append(result, album)
}
return result, nil
}
// AlbumMeta represents album metadata
type AlbumMeta struct {
BaseModel
Availability *Availability `json:"availability"`
Upsell *Upsell `json:"upsell"`
}
// DeJSON deserializes an AlbumMeta from JSON
func (am *AlbumMeta) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
meta := &AlbumMeta{BaseModel: *NewBaseModel(c)}
if v, ok := raw["availability"]; ok {
var avail Availability
json.Unmarshal(v, &avail)
meta.Availability = &avail
}
if v, ok := raw["upsell"]; ok {
var upsell Upsell
json.Unmarshal(v, &upsell)
meta.Upsell = &upsell
}
return meta, nil
}
// DeList deserializes a list of AlbumMeta
func (am *AlbumMeta) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
meta := &AlbumMeta{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, meta); err != nil {
continue
}
result = append(result, meta)
}
return result, nil
}
// Availability represents availability info
type Availability struct {
AvailableForPremiumUsers bool `json:"available_for_premium_users"`
Territories []string `json:"territories"`
}
// Upsell represents upsell info
type Upsell struct {
Type string `json:"type"`
Description string `json:"description"`
}
// Best represents best tracks info
type Best struct {
Tracks []Track `json:"tracks"`
}
// TrailerInfo represents album/track trailer info
type TrailerInfo struct {
BaseModel
PreviewURI string `json:"preview_uri"`
TrackID string `json:"track_id"`
AlbumID string `json:"album_id"`
Duration int `json:"duration"`
}
// DeJSON deserializes a TrailerInfo from JSON
func (ti *TrailerInfo) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
trailer := &TrailerInfo{BaseModel: *NewBaseModel(c)}
if v, ok := raw["preview_uri"]; ok {
json.Unmarshal(v, &trailer.PreviewURI)
}
if v, ok := raw["track_id"]; ok {
json.Unmarshal(v, &trailer.TrackID)
}
if v, ok := raw["album_id"]; ok {
json.Unmarshal(v, &trailer.AlbumID)
}
if v, ok := raw["duration"]; ok {
json.Unmarshal(v, &trailer.Duration)
}
return trailer, nil
}
// DeList deserializes a list of TrailerInfo
func (ti *TrailerInfo) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
trailer := &TrailerInfo{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, trailer); err != nil {
continue
}
result = append(result, trailer)
}
return result, nil
}
+357
View File
@@ -0,0 +1,357 @@
package models
import (
"testing"
)
func TestAlbum_DeJSON(t *testing.T) {
client := newMockClient()
data := []byte(`{
"id": "123",
"title": "Test Album",
"cover_uri": "https://example.com/cover.jpg",
"genre": "rock",
"year": 2023,
"track_count": 10,
"available": true,
"release_date": "2023-01-15",
"description": "Test album description",
"region": "RU",
"available_for_premium_users": true,
"available_for_premium_users_only": false,
"content_warning": "explicit",
"label": "Test Label",
"catalog_number": "CAT-123",
"options": ["option1", "option2"],
"artists": [{"id": "1", "name": "Artist 1", "cover_uri": "https://example.com/artist.jpg"}],
"labels": [{"id": "label1", "name": "Label 1"}],
"volumes": [[{"id": "t1", "title": "Track 1", "duration_ms": 180000}]],
"meta": {"availability": {"available_for_premium_users": true, "territories": ["RU", "US"]}},
"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}],
"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}
}`)
album := &Album{BaseModel: *NewBaseModel(client)}
result, err := album.DeJSON(data, client)
if err != nil {
t.Fatalf("DeJSON failed: %v", err)
}
resultAlbum := result.(*Album)
if resultAlbum.ID != "123" {
t.Errorf("Expected ID '123', got '%s'", resultAlbum.ID)
}
if resultAlbum.Title != "Test Album" {
t.Errorf("Expected Title 'Test Album', got '%s'", resultAlbum.Title)
}
if resultAlbum.CoverURI != "https://example.com/cover.jpg" {
t.Errorf("Expected CoverURI 'https://example.com/cover.jpg', got '%s'", resultAlbum.CoverURI)
}
if resultAlbum.Genre != "rock" {
t.Errorf("Expected Genre 'rock', got '%s'", resultAlbum.Genre)
}
if resultAlbum.Year != 2023 {
t.Errorf("Expected Year 2023, got %d", resultAlbum.Year)
}
if resultAlbum.TrackCount != 10 {
t.Errorf("Expected TrackCount 10, got %d", resultAlbum.TrackCount)
}
if !resultAlbum.Available {
t.Errorf("Expected Available true, got false")
}
if resultAlbum.ReleaseDate != "2023-01-15" {
t.Errorf("Expected ReleaseDate '2023-01-15', got '%s'", resultAlbum.ReleaseDate)
}
if resultAlbum.Description != "Test album description" {
t.Errorf("Expected Description 'Test album description', got '%s'", resultAlbum.Description)
}
if resultAlbum.Region != "RU" {
t.Errorf("Expected Region 'RU', got '%s'", resultAlbum.Region)
}
if !resultAlbum.AvailableForPremiumUsers {
t.Errorf("Expected AvailableForPremiumUsers true, got false")
}
if resultAlbum.AvailableForPremiumUsersOnly {
t.Errorf("Expected AvailableForPremiumUsersOnly false, got true")
}
if resultAlbum.ContentWarning != "explicit" {
t.Errorf("Expected ContentWarning 'explicit', got '%s'", resultAlbum.ContentWarning)
}
if resultAlbum.Label != "Test Label" {
t.Errorf("Expected Label 'Test Label', got '%s'", resultAlbum.Label)
}
if resultAlbum.CatalogNumber != "CAT-123" {
t.Errorf("Expected CatalogNumber 'CAT-123', got '%s'", resultAlbum.CatalogNumber)
}
if len(resultAlbum.Options) != 2 {
t.Errorf("Expected 2 options, got %d", len(resultAlbum.Options))
}
// Test nested artists
if len(resultAlbum.Artists) != 1 {
t.Errorf("Expected 1 artist, got %d", len(resultAlbum.Artists))
}
if resultAlbum.Artists[0].Name != "Artist 1" {
t.Errorf("Expected artist name 'Artist 1', got '%s'", resultAlbum.Artists[0].Name)
}
// Test nested labels
if len(resultAlbum.Labels) != 1 {
t.Errorf("Expected 1 label, got %d", len(resultAlbum.Labels))
}
if resultAlbum.Labels[0].Name != "Label 1" {
t.Errorf("Expected label name 'Label 1', got '%s'", resultAlbum.Labels[0].Name)
}
// Test volumes
if len(resultAlbum.Volumes) != 1 {
t.Errorf("Expected 1 volume, got %d", len(resultAlbum.Volumes))
}
if len(resultAlbum.Volumes[0]) != 1 {
t.Errorf("Expected 1 track in volume, got %d", len(resultAlbum.Volumes[0]))
}
if resultAlbum.Volumes[0][0].Title != "Track 1" {
t.Errorf("Expected track title 'Track 1', got '%s'", resultAlbum.Volumes[0][0].Title)
}
// Test meta
if resultAlbum.Meta == nil {
t.Fatal("Expected Meta to be non-nil")
}
if resultAlbum.Meta.Availability == nil {
t.Fatal("Expected Availability to be non-nil")
}
if !resultAlbum.Meta.Availability.AvailableForPremiumUsers {
t.Errorf("Expected AvailableForPremiumUsers true in meta")
}
if len(resultAlbum.Meta.Availability.Territories) != 2 {
t.Errorf("Expected 2 territories, got %d", len(resultAlbum.Meta.Availability.Territories))
}
// Test best
if resultAlbum.Best == nil {
t.Fatal("Expected Best to be non-nil")
}
if len(resultAlbum.Best.Tracks) != 1 {
t.Errorf("Expected 1 best track, got %d", len(resultAlbum.Best.Tracks))
}
if resultAlbum.Best.Tracks[0].Title != "Best Track" {
t.Errorf("Expected best track title 'Best Track', got '%s'", resultAlbum.Best.Tracks[0].Title)
}
// Test track positions
if len(resultAlbum.TrackPositions) != 1 {
t.Errorf("Expected 1 track position, got %d", len(resultAlbum.TrackPositions))
}
if resultAlbum.TrackPositions[0].Position != 1 {
t.Errorf("Expected position 1, got %d", resultAlbum.TrackPositions[0].Position)
}
if resultAlbum.TrackPositions[0].Revision != 1 {
t.Errorf("Expected revision 1, got %d", resultAlbum.TrackPositions[0].Revision)
}
// Test similar albums
if len(resultAlbum.SimilarAlbums) != 1 {
t.Errorf("Expected 1 similar album, got %d", len(resultAlbum.SimilarAlbums))
}
if resultAlbum.SimilarAlbums[0].Title != "Similar Album" {
t.Errorf("Expected similar album title 'Similar Album', got '%s'", resultAlbum.SimilarAlbums[0].Title)
}
// Test trailer
if resultAlbum.Trailer == nil {
t.Fatal("Expected Trailer to be non-nil")
}
if resultAlbum.Trailer.PreviewURI != "https://example.com/preview.mp3" {
t.Errorf("Expected PreviewURI 'https://example.com/preview.mp3', got '%s'", resultAlbum.Trailer.PreviewURI)
}
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)
}
}
func TestAlbum_DeJSON_Minimal(t *testing.T) {
client := newMockClient()
data := []byte(`{
"id": "minimal",
"title": "Minimal Album"
}`)
album := &Album{BaseModel: *NewBaseModel(client)}
result, err := album.DeJSON(data, client)
if err != nil {
t.Fatalf("DeJSON failed: %v", err)
}
resultAlbum := result.(*Album)
if resultAlbum.ID != "minimal" {
t.Errorf("Expected ID 'minimal', got '%s'", resultAlbum.ID)
}
if resultAlbum.Title != "Minimal Album" {
t.Errorf("Expected Title 'Minimal Album', got '%s'", resultAlbum.Title)
}
// Other fields should be zero values
if resultAlbum.Year != 0 {
t.Errorf("Expected Year 0, got %d", resultAlbum.Year)
}
if resultAlbum.Available {
t.Errorf("Expected Available false, got true")
}
}
func TestAlbum_DeList(t *testing.T) {
client := newMockClient()
data := []byte(`[
{"id": "1", "title": "Album 1", "year": 2022, "track_count": 5, "available": true},
{"id": "2", "title": "Album 2", "year": 2023, "track_count": 8, "available": true},
{"id": "3", "title": "Album 3", "year": 2021, "track_count": 12, "available": false}
]`)
album := &Album{BaseModel: *NewBaseModel(client)}
result, err := album.DeList(data, client)
if err != nil {
t.Fatalf("DeList failed: %v", err)
}
if len(result) != 3 {
t.Errorf("Expected 3 albums, got %d", len(result))
}
albums := make([]*Album, len(result))
for i, a := range result {
albums[i] = a.(*Album)
}
if albums[0].ID != "1" || albums[0].Title != "Album 1" || albums[0].Year != 2022 {
t.Errorf("Album 1 mismatch: %+v", albums[0])
}
if albums[1].ID != "2" || albums[1].Title != "Album 2" || albums[1].Year != 2023 {
t.Errorf("Album 2 mismatch: %+v", albums[1])
}
if albums[2].ID != "3" || albums[2].Title != "Album 3" || albums[2].Year != 2021 {
t.Errorf("Album 3 mismatch: %+v", albums[2])
}
}
func TestAlbum_DeList_Empty(t *testing.T) {
client := newMockClient()
data := []byte(`[]`)
album := &Album{BaseModel: *NewBaseModel(client)}
result, err := album.DeList(data, client)
if err != nil {
t.Fatalf("DeList failed: %v", err)
}
if len(result) != 0 {
t.Errorf("Expected 0 albums, got %d", len(result))
}
}
func TestAlbumMeta_DeJSON(t *testing.T) {
client := newMockClient()
data := []byte(`{
"availability": {"available_for_premium_users": true, "territories": ["RU", "US", "EU"]},
"upsell": {"type": "premium", "description": "Upgrade to premium for full access"}
}`)
meta := &AlbumMeta{BaseModel: *NewBaseModel(client)}
result, err := meta.DeJSON(data, client)
if err != nil {
t.Fatalf("DeJSON failed: %v", err)
}
resultMeta := result.(*AlbumMeta)
if resultMeta.Availability == nil {
t.Fatal("Expected Availability to be non-nil")
}
if !resultMeta.Availability.AvailableForPremiumUsers {
t.Errorf("Expected AvailableForPremiumUsers true")
}
if len(resultMeta.Availability.Territories) != 3 {
t.Errorf("Expected 3 territories, got %d", len(resultMeta.Availability.Territories))
}
if resultMeta.Upsell == nil {
t.Fatal("Expected Upsell to be non-nil")
}
if resultMeta.Upsell.Type != "premium" {
t.Errorf("Expected Upsell Type 'premium', got '%s'", resultMeta.Upsell.Type)
}
if resultMeta.Upsell.Description != "Upgrade to premium for full access" {
t.Errorf("Expected Upsell Description 'Upgrade to premium for full access', got '%s'", resultMeta.Upsell.Description)
}
}
func TestAlbumMeta_DeList(t *testing.T) {
client := newMockClient()
data := []byte(`[
{"availability": {"available_for_premium_users": true}},
{"availability": {"available_for_premium_users": false}}
]`)
meta := &AlbumMeta{BaseModel: *NewBaseModel(client)}
result, err := meta.DeList(data, client)
if err != nil {
t.Fatalf("DeList failed: %v", err)
}
if len(result) != 2 {
t.Errorf("Expected 2 meta objects, got %d", len(result))
}
}
func TestTrailerInfo_DeJSON(t *testing.T) {
client := newMockClient()
data := []byte(`{
"preview_uri": "https://example.com/preview.mp3",
"track_id": "track123",
"album_id": "album456",
"duration": 45
}`)
trailer := &TrailerInfo{BaseModel: *NewBaseModel(client)}
result, err := trailer.DeJSON(data, client)
if err != nil {
t.Fatalf("DeJSON failed: %v", err)
}
resultTrailer := result.(*TrailerInfo)
if resultTrailer.PreviewURI != "https://example.com/preview.mp3" {
t.Errorf("Expected PreviewURI 'https://example.com/preview.mp3', got '%s'", resultTrailer.PreviewURI)
}
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) {
client := newMockClient()
data := []byte(`[
{"preview_uri": "https://example.com/preview1.mp3", "track_id": "t1", "album_id": "a1", "duration": 30},
{"preview_uri": "https://example.com/preview2.mp3", "track_id": "t2", "album_id": "a2", "duration": 45}
]`)
trailer := &TrailerInfo{BaseModel: *NewBaseModel(client)}
result, err := trailer.DeList(data, client)
if err != nil {
t.Fatalf("DeList failed: %v", err)
}
if len(result) != 2 {
t.Errorf("Expected 2 trailers, got %d", len(result))
}
}
+367
View File
@@ -0,0 +1,367 @@
package models
import (
"encoding/json"
)
// Artist represents a full artist
type Artist struct {
BaseModel
ID string `json:"id"`
Name string `json:"name"`
CoverURI string `json:"cover_uri"`
Genres []string `json:"genres"`
Tracks int `json:"tracks"`
Albums int `json:"albums"`
Link string `json:"link"`
Available bool `json:"available"`
Description string `json:"description"`
Composer bool `json:"composer"`
Various bool `json:"various"`
Decomposed []Artist `json:"decomposed"`
Ratings *Ratings `json:"ratings"`
Counts *Counts `json:"counts"`
SocialLinks []SocialLink `json:"social_links"`
// Additional fields
IDForFrom string `json:"id_for_from"`
Cover *Cover `json:"cover"`
BriefInfo *ArtistBriefInfo `json:"brief_info"`
Donations *ArtistDonations `json:"donations"`
Concerts []Concert `json:"concerts"`
Clips []Clip `json:"clips"`
Similar []Artist `json:"similar"`
AlbumsData []Album `json:"albums_data"`
TracksData []Track `json:"tracks_data"`
}
// DeJSON deserializes an Artist from JSON
func (a *Artist) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
artist := &Artist{BaseModel: *NewBaseModel(c)}
// Simple fields
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &artist.ID)
}
if v, ok := raw["name"]; ok {
json.Unmarshal(v, &artist.Name)
}
if v, ok := raw["cover_uri"]; ok {
json.Unmarshal(v, &artist.CoverURI)
}
if v, ok := raw["genres"]; ok {
json.Unmarshal(v, &artist.Genres)
}
if v, ok := raw["tracks"]; ok {
json.Unmarshal(v, &artist.Tracks)
}
if v, ok := raw["albums"]; ok {
json.Unmarshal(v, &artist.Albums)
}
if v, ok := raw["link"]; ok {
json.Unmarshal(v, &artist.Link)
}
if v, ok := raw["available"]; ok {
json.Unmarshal(v, &artist.Available)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &artist.Description)
}
if v, ok := raw["composer"]; ok {
json.Unmarshal(v, &artist.Composer)
}
if v, ok := raw["various"]; ok {
json.Unmarshal(v, &artist.Various)
}
if v, ok := raw["id_for_from"]; ok {
json.Unmarshal(v, &artist.IDForFrom)
}
// Nested objects
if v, ok := raw["decomposed"]; ok {
var decomposed []Artist
json.Unmarshal(v, &decomposed)
artist.Decomposed = decomposed
}
if v, ok := raw["ratings"]; ok {
var ratings Ratings
json.Unmarshal(v, &ratings)
artist.Ratings = &ratings
}
if v, ok := raw["counts"]; ok {
var counts Counts
json.Unmarshal(v, &counts)
artist.Counts = &counts
}
if v, ok := raw["social_links"]; ok {
var links []SocialLink
json.Unmarshal(v, &links)
artist.SocialLinks = links
}
if v, ok := raw["cover"]; ok {
var cover Cover
json.Unmarshal(v, &cover)
artist.Cover = &cover
}
if v, ok := raw["brief_info"]; ok {
var brief ArtistBriefInfo
json.Unmarshal(v, &brief)
artist.BriefInfo = &brief
}
if v, ok := raw["donations"]; ok {
var donations ArtistDonations
json.Unmarshal(v, &donations)
artist.Donations = &donations
}
if v, ok := raw["concerts"]; ok {
var concerts []Concert
json.Unmarshal(v, &concerts)
artist.Concerts = concerts
}
if v, ok := raw["clips"]; ok {
var clips []Clip
json.Unmarshal(v, &clips)
artist.Clips = clips
}
if v, ok := raw["similar"]; ok {
var similar []Artist
json.Unmarshal(v, &similar)
artist.Similar = similar
}
if v, ok := raw["albums"]; ok {
var albums []Album
json.Unmarshal(v, &albums)
artist.AlbumsData = albums
}
if v, ok := raw["tracks"]; ok {
var tracks []Track
json.Unmarshal(v, &tracks)
artist.TracksData = tracks
}
return artist, nil
}
// DeList deserializes a list of Artists
func (a *Artist) 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 {
artist := &Artist{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, artist); err != nil {
continue
}
result = append(result, artist)
}
return result, nil
}
// Ratings represents artist ratings
type Ratings struct {
Rating float64 `json:"rating"`
Votes int `json:"votes"`
}
// Counts represents artist counts
type Counts struct {
Tracks int `json:"tracks"`
Albums int `json:"albums"`
Folders int `json:"folders"`
}
// SocialLink represents a social media link
type SocialLink struct {
URL string `json:"url"`
Title string `json:"title"`
Type string `json:"type"`
}
// Cover represents cover image info
type Cover struct {
URI string `json:"uri"`
Type string `json:"type"`
Prefix string `json:"prefix"`
Pattern string `json:"pattern"`
Background string `json:"background"`
}
// ArtistBriefInfo represents brief artist info
type ArtistBriefInfo struct {
Description string `json:"description"`
Available bool `json:"available"`
}
// ArtistDonations represents artist donations info
type ArtistDonations struct {
Items []DonationItem `json:"items"`
Total int `json:"total"`
Goal int `json:"goal"`
Raised int `json:"raised"`
}
// DonationItem represents a donation item
type DonationItem struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Amount int `json:"amount"`
Goal int `json:"goal"`
Raised int `json:"raised"`
Currency string `json:"currency"`
}
// ConcertShort represents a concert (minimal for artist)
type ConcertShort struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
Date string `json:"date"`
Venue string `json:"venue"`
Location string `json:"location"`
TicketURI string `json:"ticket_uri"`
ImageURI string `json:"image_uri"`
MinPrice int `json:"min_price"`
MaxPrice int `json:"max_price"`
Available bool `json:"available"`
}
// DeJSON deserializes a ConcertShort from JSON
func (c *ConcertShort) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
concert := &ConcertShort{BaseModel: *NewBaseModel(client)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &concert.ID)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &concert.Title)
}
if v, ok := raw["date"]; ok {
json.Unmarshal(v, &concert.Date)
}
if v, ok := raw["venue"]; ok {
json.Unmarshal(v, &concert.Venue)
}
if v, ok := raw["location"]; ok {
json.Unmarshal(v, &concert.Location)
}
if v, ok := raw["ticket_uri"]; ok {
json.Unmarshal(v, &concert.TicketURI)
}
if v, ok := raw["image_uri"]; ok {
json.Unmarshal(v, &concert.ImageURI)
}
if v, ok := raw["min_price"]; ok {
json.Unmarshal(v, &concert.MinPrice)
}
if v, ok := raw["max_price"]; ok {
json.Unmarshal(v, &concert.MaxPrice)
}
if v, ok := raw["available"]; ok {
json.Unmarshal(v, &concert.Available)
}
return concert, nil
}
// DeList deserializes a list of ConcertShort
func (c *ConcertShort) DeList(data []byte, client 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 {
concert := &ConcertShort{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, concert); err != nil {
continue
}
result = append(result, concert)
}
return result, nil
}
// ClipShort represents a clip (minimal for artist)
type ClipShort struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
Duration int `json:"duration"`
CoverURI string `json:"cover_uri"`
PreviewURI string `json:"preview_uri"`
Available bool `json:"available"`
Views int `json:"views"`
Likes int `json:"likes"`
CreatedAt string `json:"created_at"`
}
// DeJSON deserializes a ClipShort from JSON
func (c *ClipShort) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
clip := &ClipShort{BaseModel: *NewBaseModel(client)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &clip.ID)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &clip.Title)
}
if v, ok := raw["duration"]; ok {
json.Unmarshal(v, &clip.Duration)
}
if v, ok := raw["cover_uri"]; ok {
json.Unmarshal(v, &clip.CoverURI)
}
if v, ok := raw["preview_uri"]; ok {
json.Unmarshal(v, &clip.PreviewURI)
}
if v, ok := raw["available"]; ok {
json.Unmarshal(v, &clip.Available)
}
if v, ok := raw["views"]; ok {
json.Unmarshal(v, &clip.Views)
}
if v, ok := raw["likes"]; ok {
json.Unmarshal(v, &clip.Likes)
}
if v, ok := raw["created_at"]; ok {
json.Unmarshal(v, &clip.CreatedAt)
}
return clip, nil
}
// DeList deserializes a list of ClipShort
func (c *ClipShort) DeList(data []byte, client 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 {
clip := &ClipShort{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, clip); err != nil {
continue
}
result = append(result, clip)
}
return result, nil
}
+78
View File
@@ -0,0 +1,78 @@
package models
import (
"encoding/json"
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils"
)
// Model is the base interface for all models
type Model interface {
// DeJSON deserializes from JSON
DeJSON(data []byte, c ClientInterface) (Model, error)
// DeList deserializes a list of models
DeList(data []byte, c ClientInterface) ([]Model, error)
// ToJSON serializes to JSON
ToJSON() ([]byte, error)
// ToMap converts to map
ToMap() (map[string]any, error)
}
// BaseModel provides common functionality for models
type BaseModel struct {
Client ClientInterface `json:"-"`
}
// NewBaseModel creates a new BaseModel
func NewBaseModel(c ClientInterface) *BaseModel {
return &BaseModel{Client: c}
}
// DeJSON implements Model.DeJSON for basic models
func (b *BaseModel) DeJSON(data []byte, c ClientInterface) (Model, error) {
// This should be overridden by concrete types
return nil, nil
}
// DeList implements Model.DeList for basic models
func (b *BaseModel) 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 range rawList {
// This should be overridden by concrete types
result = append(result, nil)
}
return result, nil
}
// ToJSON serializes the model to JSON
func (b *BaseModel) ToJSON() ([]byte, error) {
return utils.Marshal(b)
}
// ToMap converts the model to a map
func (b *BaseModel) ToMap() (map[string]any, error) {
data, err := b.ToJSON()
if err != nil {
return nil, err
}
var result map[string]any
if err := json.Unmarshal(data, &result); err != nil {
return nil, err
}
return result, nil
}
// SetClient sets the client on the model
func (b *BaseModel) SetClient(c ClientInterface) {
b.Client = c
}
// GetClient returns the client
func (b *BaseModel) GetClient() ClientInterface {
return b.Client
}
+19
View File
@@ -0,0 +1,19 @@
package models
import (
"context"
)
// ClientInterface defines the minimal client interface needed by models
type ClientInterface interface {
GetRequest() RequestInterface
}
// RequestInterface defines the minimal request interface needed by models
type RequestInterface interface {
Get(ctx context.Context, path string, params map[string]string, result any) error
Post(ctx context.Context, path string, body any, result any) error
Put(ctx context.Context, path string, body any, result any) error
Delete(ctx context.Context, path string, body any, result any) error
Download(ctx context.Context, url string) ([]byte, error)
}
+264
View File
@@ -0,0 +1,264 @@
package models
import (
"encoding/json"
)
// Clip represents a clip (short video)
type Clip struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
Duration int `json:"duration"`
CoverURI string `json:"cover_uri"`
PreviewURI string `json:"preview_uri"`
Available bool `json:"available"`
Views int `json:"views"`
Likes int `json:"likes"`
CreatedAt string `json:"created_at"`
Artist *Artist `json:"artist"`
Track *Track `json:"track"`
Playlist *Playlist `json:"playlist"`
ClipType string `json:"clip_type"`
IsVertical bool `json:"is_vertical"`
AspectRatio string `json:"aspect_ratio"`
}
// DeJSON deserializes a Clip from JSON
func (c *Clip) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
clip := &Clip{BaseModel: *NewBaseModel(client)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &clip.ID)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &clip.Title)
}
if v, ok := raw["duration"]; ok {
json.Unmarshal(v, &clip.Duration)
}
if v, ok := raw["cover_uri"]; ok {
json.Unmarshal(v, &clip.CoverURI)
}
if v, ok := raw["preview_uri"]; ok {
json.Unmarshal(v, &clip.PreviewURI)
}
if v, ok := raw["available"]; ok {
json.Unmarshal(v, &clip.Available)
}
if v, ok := raw["views"]; ok {
json.Unmarshal(v, &clip.Views)
}
if v, ok := raw["likes"]; ok {
json.Unmarshal(v, &clip.Likes)
}
if v, ok := raw["created_at"]; ok {
json.Unmarshal(v, &clip.CreatedAt)
}
if v, ok := raw["clip_type"]; ok {
json.Unmarshal(v, &clip.ClipType)
}
if v, ok := raw["is_vertical"]; ok {
json.Unmarshal(v, &clip.IsVertical)
}
if v, ok := raw["aspect_ratio"]; ok {
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
}
// DeList deserializes a list of Clips
func (c *Clip) DeList(data []byte, client 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 {
clip := &Clip{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, clip); err != nil {
continue
}
result = append(result, clip)
}
return result, nil
}
// ClipsWillLike represents clips the user might like
type ClipsWillLike struct {
BaseModel
Clips []Clip `json:"clips"`
Total int `json:"total"`
}
// ArtistClipData represents artist clip data
type ArtistClipData struct {
BaseModel
Clips []Clip `json:"clips"`
Total int `json:"total"`
}
// ArtistClipItem represents an artist clip item
type ArtistClipItem struct {
BaseModel
Clip *Clip `json:"clip"`
Index int `json:"index"`
}
// Shot represents a shot (short video)
type Shot struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Duration int `json:"duration"`
CoverURI string `json:"cover_uri"`
PreviewURI string `json:"preview_uri"`
Available bool `json:"available"`
Views int `json:"views"`
Likes int `json:"likes"`
Comments int `json:"comments"`
CreatedAt string `json:"created_at"`
Author *User `json:"author"`
Track *Track `json:"track"`
ShotType string `json:"shot_type"`
Tags []string `json:"tags"`
IsVertical bool `json:"is_vertical"`
AspectRatio string `json:"aspect_ratio"`
}
// DeJSON deserializes a Shot from JSON
func (s *Shot) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
shot := &Shot{BaseModel: *NewBaseModel(client)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &shot.ID)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &shot.Title)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &shot.Description)
}
if v, ok := raw["duration"]; ok {
json.Unmarshal(v, &shot.Duration)
}
if v, ok := raw["cover_uri"]; ok {
json.Unmarshal(v, &shot.CoverURI)
}
if v, ok := raw["preview_uri"]; ok {
json.Unmarshal(v, &shot.PreviewURI)
}
if v, ok := raw["available"]; ok {
json.Unmarshal(v, &shot.Available)
}
if v, ok := raw["views"]; ok {
json.Unmarshal(v, &shot.Views)
}
if v, ok := raw["likes"]; ok {
json.Unmarshal(v, &shot.Likes)
}
if v, ok := raw["comments"]; ok {
json.Unmarshal(v, &shot.Comments)
}
if v, ok := raw["created_at"]; ok {
json.Unmarshal(v, &shot.CreatedAt)
}
if v, ok := raw["shot_type"]; ok {
json.Unmarshal(v, &shot.ShotType)
}
if v, ok := raw["tags"]; ok {
json.Unmarshal(v, &shot.Tags)
}
if v, ok := raw["is_vertical"]; ok {
json.Unmarshal(v, &shot.IsVertical)
}
if v, ok := raw["aspect_ratio"]; ok {
json.Unmarshal(v, &shot.AspectRatio)
}
if v, ok := raw["author"]; ok {
var user User
json.Unmarshal(v, &user)
shot.Author = &user
}
if v, ok := raw["track"]; ok {
var track Track
json.Unmarshal(v, &track)
shot.Track = &track
}
return shot, nil
}
// DeList deserializes a list of Shots
func (s *Shot) DeList(data []byte, client 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 {
shot := &Shot{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, shot); err != nil {
continue
}
result = append(result, shot)
}
return result, nil
}
// ShotData represents shot data
type ShotData struct {
BaseModel
Shots []Shot `json:"shots"`
Total int `json:"total"`
}
// ShotEvent represents a shot event
type ShotEvent struct {
BaseModel
EventID string `json:"event_id"`
EventType string `json:"event_type"`
Timestamp string `json:"timestamp"`
ShotID string `json:"shot_id"`
Action string `json:"action"`
}
// ShotType represents a shot type
type ShotType struct {
BaseModel
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Icon string `json:"icon"`
}
+243
View File
@@ -0,0 +1,243 @@
package models
import (
"encoding/json"
)
// Concert represents a concert
type Concert struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Date string `json:"date"`
Time string `json:"time"`
Venue *ConcertVenue `json:"venue"`
Location *ConcertLocation `json:"location"`
MinPrice int `json:"min_price"`
MaxPrice int `json:"max_price"`
Currency string `json:"currency"`
TicketURI string `json:"ticket_uri"`
ImageURI string `json:"image_uri"`
Available bool `json:"available"`
SoldOut bool `json:"sold_out"`
Artists []Artist `json:"artists"`
Genres []string `json:"genres"`
Cashback *ConcertCashback `json:"cashback"`
Tabs []ConcertTabConfig `json:"tabs"`
Skeleton *ConcertSkeleton `json:"skeleton"`
}
// DeJSON deserializes a Concert from JSON
func (c *Concert) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
concert := &Concert{BaseModel: *NewBaseModel(client)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &concert.ID)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &concert.Title)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &concert.Description)
}
if v, ok := raw["date"]; ok {
json.Unmarshal(v, &concert.Date)
}
if v, ok := raw["time"]; ok {
json.Unmarshal(v, &concert.Time)
}
if v, ok := raw["min_price"]; ok {
json.Unmarshal(v, &concert.MinPrice)
}
if v, ok := raw["max_price"]; ok {
json.Unmarshal(v, &concert.MaxPrice)
}
if v, ok := raw["currency"]; ok {
json.Unmarshal(v, &concert.Currency)
}
if v, ok := raw["ticket_uri"]; ok {
json.Unmarshal(v, &concert.TicketURI)
}
if v, ok := raw["image_uri"]; ok {
json.Unmarshal(v, &concert.ImageURI)
}
if v, ok := raw["available"]; ok {
json.Unmarshal(v, &concert.Available)
}
if v, ok := raw["sold_out"]; ok {
json.Unmarshal(v, &concert.SoldOut)
}
if v, ok := raw["genres"]; ok {
json.Unmarshal(v, &concert.Genres)
}
if v, ok := raw["venue"]; ok {
var venue ConcertVenue
json.Unmarshal(v, &venue)
concert.Venue = &venue
}
if v, ok := raw["location"]; ok {
var location ConcertLocation
json.Unmarshal(v, &location)
concert.Location = &location
}
if v, ok := raw["artists"]; ok {
var artists []Artist
json.Unmarshal(v, &artists)
concert.Artists = artists
}
if v, ok := raw["cashback"]; ok {
var cashback ConcertCashback
json.Unmarshal(v, &cashback)
concert.Cashback = &cashback
}
if v, ok := raw["tabs"]; ok {
var tabs []ConcertTabConfig
json.Unmarshal(v, &tabs)
concert.Tabs = tabs
}
if v, ok := raw["skeleton"]; ok {
var skeleton ConcertSkeleton
json.Unmarshal(v, &skeleton)
concert.Skeleton = &skeleton
}
return concert, nil
}
// DeList deserializes a list of Concerts
func (c *Concert) DeList(data []byte, client 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 {
concert := &Concert{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, concert); err != nil {
continue
}
result = append(result, concert)
}
return result, nil
}
// ConcertVenue represents a concert venue
type ConcertVenue struct {
BaseModel
ID string `json:"id"`
Name string `json:"name"`
Address string `json:"address"`
City string `json:"city"`
Country string `json:"country"`
Capacity int `json:"capacity"`
ImageURI string `json:"image_uri"`
}
// ConcertLocation represents a concert location
type ConcertLocation struct {
BaseModel
ID string `json:"id"`
Name string `json:"name"`
City string `json:"city"`
Country string `json:"country"`
Timezone string `json:"timezone"`
Venues []ConcertVenue `json:"venues"`
}
// ConcertLocations represents concert locations
type ConcertLocations struct {
BaseModel
Locations []ConcertLocation `json:"locations"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// ConcertFeed represents a concert feed
type ConcertFeed struct {
BaseModel
Concerts []Concert `json:"concerts"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// ConcertInfo represents concert info
type ConcertInfo struct {
BaseModel
Concert *Concert `json:"concert"`
Related []Concert `json:"related"`
}
// ConcertEventInfo represents concert event info
type ConcertEventInfo struct {
BaseModel
EventID string `json:"event_id"`
EventType string `json:"event_type"`
Timestamp string `json:"timestamp"`
Data json.RawMessage `json:"data"`
}
// ConcertDescription represents concert description
type ConcertDescription struct {
BaseModel
Text string `json:"text"`
HTML string `json:"html"`
ShortText string `json:"short_text"`
}
// ConcertCashback represents concert cashback
type ConcertCashback struct {
BaseModel
Amount int `json:"amount"`
Currency string `json:"currency"`
Percent int `json:"percent"`
Expires string `json:"expires"`
}
// ConcertMinPrice represents concert min price
type ConcertMinPrice struct {
BaseModel
Price int `json:"price"`
Currency string `json:"currency"`
}
// ConcertTabConfig represents concert tab config
type ConcertTabConfig struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
Type string `json:"type"`
Active bool `json:"active"`
Range *ConcertTabRange `json:"range"`
}
// ConcertTabRange represents concert tab range
type ConcertTabRange struct {
BaseModel
From string `json:"from"`
To string `json:"to"`
}
// ConcertSkeleton represents concert skeleton
type ConcertSkeleton struct {
BaseModel
Blocks []ConcertSkeletonBlock `json:"blocks"`
}
// ConcertSkeletonBlock represents a concert skeleton block
type ConcertSkeletonBlock struct {
BaseModel
ID string `json:"id"`
Type string `json:"type"`
Title string `json:"title"`
Data json.RawMessage `json:"data"`
}
+171
View File
@@ -0,0 +1,171 @@
package models
import (
"encoding/json"
)
// Credits represents track credits
type Credits struct {
BaseModel
Items []Credit `json:"items"`
Total int `json:"total"`
}
// DeJSON deserializes Credits from JSON
func (c *Credits) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
credits := &Credits{BaseModel: *NewBaseModel(client)}
if v, ok := raw["items"]; ok {
var items []Credit
json.Unmarshal(v, &items)
credits.Items = items
}
if v, ok := raw["total"]; ok {
json.Unmarshal(v, &credits.Total)
}
return credits, nil
}
// DeList deserializes a list of Credits
func (c *Credits) DeList(data []byte, client 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 {
credits := &Credits{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, credits); err != nil {
continue
}
result = append(result, credits)
}
return result, nil
}
// Credit represents a credit
type Credit struct {
BaseModel
Role string `json:"role"`
Name string `json:"name"`
ArtistID string `json:"artist_id"`
ArtistName string `json:"artist_name"`
IsMain bool `json:"is_main"`
TrackID string `json:"track_id"`
AlbumID string `json:"album_id"`
}
// DeJSON deserializes a Credit from JSON
func (c *Credit) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
credit := &Credit{BaseModel: *NewBaseModel(client)}
if v, ok := raw["role"]; ok {
json.Unmarshal(v, &credit.Role)
}
if v, ok := raw["name"]; ok {
json.Unmarshal(v, &credit.Name)
}
if v, ok := raw["artist_id"]; ok {
json.Unmarshal(v, &credit.ArtistID)
}
if v, ok := raw["artist_name"]; ok {
json.Unmarshal(v, &credit.ArtistName)
}
if v, ok := raw["is_main"]; ok {
json.Unmarshal(v, &credit.IsMain)
}
if v, ok := raw["track_id"]; ok {
json.Unmarshal(v, &credit.TrackID)
}
if v, ok := raw["album_id"]; ok {
json.Unmarshal(v, &credit.AlbumID)
}
return credit, nil
}
// DeList deserializes a list of Credits
func (c *Credit) DeList(data []byte, client 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 {
credit := &Credit{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, credit); err != nil {
continue
}
result = append(result, credit)
}
return result, nil
}
// LicenceTextPart represents a licence text part
type LicenceTextPart struct {
BaseModel
Text string `json:"text"`
Language string `json:"language"`
URL string `json:"url"`
Title string `json:"title"`
IsMain bool `json:"is_main"`
}
// DeJSON deserializes a LicenceTextPart from JSON
func (ltp *LicenceTextPart) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
part := &LicenceTextPart{BaseModel: *NewBaseModel(client)}
if v, ok := raw["text"]; ok {
json.Unmarshal(v, &part.Text)
}
if v, ok := raw["language"]; ok {
json.Unmarshal(v, &part.Language)
}
if v, ok := raw["url"]; ok {
json.Unmarshal(v, &part.URL)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &part.Title)
}
if v, ok := raw["is_main"]; ok {
json.Unmarshal(v, &part.IsMain)
}
return part, nil
}
// DeList deserializes a list of LicenceTextPart
func (ltp *LicenceTextPart) DeList(data []byte, client 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 {
part := &LicenceTextPart{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, part); err != nil {
continue
}
result = append(result, part)
}
return result, nil
}
+278
View File
@@ -0,0 +1,278 @@
package models
import (
"encoding/json"
)
// Feed represents the main feed
type Feed struct {
BaseModel
Days []Day `json:"days"`
GeneratedPlaylists []GeneratedPlaylist `json:"generated_playlists"`
PlayContexts []PlayContext `json:"play_contexts"`
}
// DeJSON deserializes a Feed from JSON
func (f *Feed) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
feed := &Feed{BaseModel: *NewBaseModel(c)}
if v, ok := raw["days"]; ok {
var days []Day
json.Unmarshal(v, &days)
feed.Days = days
}
if v, ok := raw["generated_playlists"]; ok {
var playlists []GeneratedPlaylist
json.Unmarshal(v, &playlists)
feed.GeneratedPlaylists = playlists
}
if v, ok := raw["play_contexts"]; ok {
var contexts []PlayContext
json.Unmarshal(v, &contexts)
feed.PlayContexts = contexts
}
return feed, nil
}
// DeList deserializes a list of Feeds
func (f *Feed) 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 {
feed := &Feed{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, feed); err != nil {
continue
}
result = append(result, feed)
}
return result, nil
}
// Day represents a day in the feed
type Day struct {
BaseModel
Date string `json:"date"`
Title string `json:"title"`
Description string `json:"description"`
Blocks []DayBlock `json:"blocks"`
GeneratedPlaylists []GeneratedPlaylist `json:"generated_playlists"`
}
// DeJSON deserializes a Day from JSON
func (d *Day) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
day := &Day{BaseModel: *NewBaseModel(c)}
if v, ok := raw["date"]; ok {
json.Unmarshal(v, &day.Date)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &day.Title)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &day.Description)
}
if v, ok := raw["blocks"]; ok {
var blocks []DayBlock
json.Unmarshal(v, &blocks)
day.Blocks = blocks
}
if v, ok := raw["generated_playlists"]; ok {
var playlists []GeneratedPlaylist
json.Unmarshal(v, &playlists)
day.GeneratedPlaylists = playlists
}
return day, nil
}
// DeList deserializes a list of Days
func (d *Day) 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 {
day := &Day{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, day); err != nil {
continue
}
result = append(result, day)
}
return result, nil
}
// DayBlock represents a block in a day
type DayBlock struct {
BaseModel
ID string `json:"id"`
Type string `json:"type"`
Title string `json:"title"`
Description string `json:"description"`
Data *DayBlockData `json:"data"`
}
// DayBlockData represents data in a day block
type DayBlockData struct {
Tracks []Track `json:"tracks"`
Playlists []Playlist `json:"playlists"`
Albums []Album `json:"albums"`
Artists []Artist `json:"artists"`
GeneratedPlaylist *GeneratedPlaylist `json:"generated_playlist"`
}
// GeneratedPlaylist represents a generated playlist
type GeneratedPlaylist struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
CoverURI string `json:"cover_uri"`
TrackCount int `json:"track_count"`
DurationMs int `json:"duration_ms"`
Kind int `json:"kind"`
Revision int `json:"revision"`
Type string `json:"type"`
Available bool `json:"available"`
Tracks []Track `json:"tracks"`
}
// DeJSON deserializes a GeneratedPlaylist from JSON
func (gp *GeneratedPlaylist) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
playlist := &GeneratedPlaylist{BaseModel: *NewBaseModel(c)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &playlist.ID)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &playlist.Title)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &playlist.Description)
}
if v, ok := raw["cover_uri"]; ok {
json.Unmarshal(v, &playlist.CoverURI)
}
if v, ok := raw["track_count"]; ok {
json.Unmarshal(v, &playlist.TrackCount)
}
if v, ok := raw["duration_ms"]; ok {
json.Unmarshal(v, &playlist.DurationMs)
}
if v, ok := raw["kind"]; ok {
json.Unmarshal(v, &playlist.Kind)
}
if v, ok := raw["revision"]; ok {
json.Unmarshal(v, &playlist.Revision)
}
if v, ok := raw["type"]; ok {
json.Unmarshal(v, &playlist.Type)
}
if v, ok := raw["available"]; ok {
json.Unmarshal(v, &playlist.Available)
}
if v, ok := raw["tracks"]; ok {
var tracks []Track
json.Unmarshal(v, &tracks)
playlist.Tracks = tracks
}
return playlist, nil
}
// DeList deserializes a list of GeneratedPlaylists
func (gp *GeneratedPlaylist) 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 {
playlist := &GeneratedPlaylist{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, playlist); err != nil {
continue
}
result = append(result, playlist)
}
return result, nil
}
// Event represents a feed event
type Event struct {
BaseModel
Timestamp string `json:"timestamp"`
Type string `json:"type"`
TrackID string `json:"track_id"`
AlbumID string `json:"album_id"`
ArtistID string `json:"artist_id"`
PlaylistID string `json:"playlist_id"`
Action string `json:"action"`
}
// AlbumEvent represents an album event
type AlbumEvent struct {
BaseModel
Album *Album `json:"album"`
Timestamp string `json:"timestamp"`
Type string `json:"type"`
Action string `json:"action"`
}
// ArtistEvent represents an artist event
type ArtistEvent struct {
BaseModel
Artist *Artist `json:"artist"`
Timestamp string `json:"timestamp"`
Type string `json:"type"`
Action string `json:"action"`
}
// TrackWithAds represents a track with ads
type TrackWithAds struct {
BaseModel
Track *Track `json:"track"`
Ads []Ad `json:"ads"`
AdParams *AdParams `json:"ad_params"`
}
// Ad represents an ad
type Ad struct {
BaseModel
ID string `json:"id"`
Type string `json:"type"`
Title string `json:"title"`
Description string `json:"description"`
Image string `json:"image"`
URL string `json:"url"`
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"`
}
+95
View File
@@ -0,0 +1,95 @@
package models
import (
"encoding/json"
)
// Label represents a label
type Label struct {
BaseModel
ID string `json:"id"`
Name string `json:"name"`
CoverURI string `json:"cover_uri"`
Description string `json:"description"`
Website string `json:"website"`
SocialLinks []SocialLink `json:"social_links"`
Available bool `json:"available"`
Owner *User `json:"owner"`
}
// DeJSON deserializes a Label from JSON
func (l *Label) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
label := &Label{BaseModel: *NewBaseModel(client)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &label.ID)
}
if v, ok := raw["name"]; ok {
json.Unmarshal(v, &label.Name)
}
if v, ok := raw["cover_uri"]; ok {
json.Unmarshal(v, &label.CoverURI)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &label.Description)
}
if v, ok := raw["website"]; ok {
json.Unmarshal(v, &label.Website)
}
if v, ok := raw["available"]; ok {
json.Unmarshal(v, &label.Available)
}
if v, ok := raw["social_links"]; ok {
var links []SocialLink
json.Unmarshal(v, &links)
label.SocialLinks = links
}
if v, ok := raw["owner"]; ok {
var user User
json.Unmarshal(v, &user)
label.Owner = &user
}
return label, nil
}
// DeList deserializes a list of Labels
func (l *Label) DeList(data []byte, client 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 {
label := &Label{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, label); err != nil {
continue
}
result = append(result, label)
}
return result, nil
}
// LabelAlbums represents label albums
type LabelAlbums struct {
BaseModel
Albums []Album `json:"albums"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// LabelArtists represents label artists
type LabelArtists struct {
BaseModel
Artists []Artist `json:"artists"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
+145
View File
@@ -0,0 +1,145 @@
package models
import (
"encoding/json"
)
// Landing represents a landing page
type Landing struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
CoverURI string `json:"cover_uri"`
Type string `json:"type"`
Blocks []LandingBlock `json:"blocks"`
Available bool `json:"available"`
}
// DeJSON deserializes a Landing from JSON
func (l *Landing) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
landing := &Landing{BaseModel: *NewBaseModel(c)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &landing.ID)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &landing.Title)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &landing.Description)
}
if v, ok := raw["cover_uri"]; ok {
json.Unmarshal(v, &landing.CoverURI)
}
if v, ok := raw["type"]; ok {
json.Unmarshal(v, &landing.Type)
}
if v, ok := raw["available"]; ok {
json.Unmarshal(v, &landing.Available)
}
if v, ok := raw["blocks"]; ok {
var blocks []LandingBlock
json.Unmarshal(v, &blocks)
landing.Blocks = blocks
}
return landing, nil
}
// DeList deserializes a list of Landings
func (l *Landing) 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 {
landing := &Landing{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, landing); err != nil {
continue
}
result = append(result, landing)
}
return result, nil
}
// LandingBlock represents a block in a landing
type LandingBlock struct {
BaseModel
ID string `json:"id"`
Type string `json:"type"`
Title string `json:"title"`
Description string `json:"description"`
Data *LandingBlockData `json:"data"`
}
// LandingBlockData represents data in a landing block
type LandingBlockData struct {
Tracks []Track `json:"tracks"`
Playlists []Playlist `json:"playlists"`
Albums []Album `json:"albums"`
Artists []Artist `json:"artists"`
GeneratedPlaylist *GeneratedPlaylist `json:"generated_playlist"`
}
// LandingList represents a list of landings
type LandingList struct {
BaseModel
Landings []Landing `json:"landings"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// DeJSON deserializes a LandingList from JSON
func (ll *LandingList) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
list := &LandingList{BaseModel: *NewBaseModel(c)}
if v, ok := raw["landings"]; ok {
var landings []Landing
json.Unmarshal(v, &landings)
list.Landings = landings
}
if v, ok := raw["total"]; ok {
json.Unmarshal(v, &list.Total)
}
if v, ok := raw["page"]; ok {
json.Unmarshal(v, &list.Page)
}
if v, ok := raw["page_size"]; ok {
json.Unmarshal(v, &list.PageSize)
}
return list, nil
}
// DeList deserializes a list of LandingLists
func (ll *LandingList) 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 {
list := &LandingList{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, list); err != nil {
continue
}
result = append(result, list)
}
return result, nil
}
+310
View File
@@ -0,0 +1,310 @@
package models
import (
"encoding/json"
)
// Metatag represents a metatag
type Metatag struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Image string `json:"image"`
Type string `json:"type"`
Position int `json:"position"`
Children []Metatag `json:"children"`
}
// DeJSON deserializes a Metatag from JSON
func (m *Metatag) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
metatag := &Metatag{BaseModel: *NewBaseModel(client)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &metatag.ID)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &metatag.Title)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &metatag.Description)
}
if v, ok := raw["image"]; ok {
json.Unmarshal(v, &metatag.Image)
}
if v, ok := raw["type"]; ok {
json.Unmarshal(v, &metatag.Type)
}
if v, ok := raw["position"]; ok {
json.Unmarshal(v, &metatag.Position)
}
if v, ok := raw["children"]; ok {
var children []Metatag
json.Unmarshal(v, &children)
metatag.Children = children
}
return metatag, nil
}
// DeList deserializes a list of Metatags
func (m *Metatag) DeList(data []byte, client 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 {
metatag := &Metatag{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, metatag); err != nil {
continue
}
result = append(result, metatag)
}
return result, nil
}
// MetatagTree represents a metatag tree
type MetatagTree struct {
BaseModel
Root *Metatag `json:"root"`
Leaves []Metatag `json:"leaves"`
}
// MetatagLeaf represents a metatag leaf
type MetatagLeaf struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Image string `json:"image"`
Type string `json:"type"`
Position int `json:"position"`
}
// MetatagArtists represents metatag artists
type MetatagArtists struct {
BaseModel
Artists []Artist `json:"artists"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// MetatagAlbums represents metatag albums
type MetatagAlbums struct {
BaseModel
Albums []Album `json:"albums"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// MetatagPlaylists represents metatag playlists
type MetatagPlaylists struct {
BaseModel
Playlists []Playlist `json:"playlists"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// MetatagTitle represents a metatag title
type MetatagTitle struct {
BaseModel
Title string `json:"title"`
}
// MetatagSortByValue represents metatag sort by value
type MetatagSortByValue struct {
BaseModel
Value string `json:"value"`
Label string `json:"label"`
}
// MetatagArtistEntry represents a metatag artist entry
type MetatagArtistEntry struct {
BaseModel
Artist *Artist `json:"artist"`
Score float64 `json:"score"`
}
// Genre represents a genre
type Genre struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Image string `json:"image"`
Position int `json:"position"`
ParentID string `json:"parent_id"`
Children []Genre `json:"children"`
}
// DeJSON deserializes a Genre from JSON
func (g *Genre) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
genre := &Genre{BaseModel: *NewBaseModel(client)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &genre.ID)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &genre.Title)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &genre.Description)
}
if v, ok := raw["image"]; ok {
json.Unmarshal(v, &genre.Image)
}
if v, ok := raw["position"]; ok {
json.Unmarshal(v, &genre.Position)
}
if v, ok := raw["parent_id"]; ok {
json.Unmarshal(v, &genre.ParentID)
}
if v, ok := raw["children"]; ok {
var children []Genre
json.Unmarshal(v, &children)
genre.Children = children
}
return genre, nil
}
// DeList deserializes a list of Genres
func (g *Genre) DeList(data []byte, client 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 {
genre := &Genre{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, genre); err != nil {
continue
}
result = append(result, genre)
}
return result, nil
}
// Chart represents a chart
type Chart struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
CoverURI string `json:"cover_uri"`
Type string `json:"type"`
Items []ChartItem `json:"items"`
}
// DeJSON deserializes a Chart from JSON
func (c *Chart) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
chart := &Chart{BaseModel: *NewBaseModel(client)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &chart.ID)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &chart.Title)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &chart.Description)
}
if v, ok := raw["cover_uri"]; ok {
json.Unmarshal(v, &chart.CoverURI)
}
if v, ok := raw["type"]; ok {
json.Unmarshal(v, &chart.Type)
}
if v, ok := raw["items"]; ok {
var items []ChartItem
json.Unmarshal(v, &items)
chart.Items = items
}
return chart, nil
}
// DeList deserializes a list of Charts
func (c *Chart) DeList(data []byte, client 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 {
chart := &Chart{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, chart); err != nil {
continue
}
result = append(result, chart)
}
return result, nil
}
// ChartInfo represents chart info
type ChartInfo struct {
BaseModel
Chart *Chart `json:"chart"`
Menu []ChartInfoMenu `json:"menu"`
}
// ChartItem represents a chart item
type ChartItem struct {
BaseModel
Position int `json:"position"`
Track *Track `json:"track"`
Artist *Artist `json:"artist"`
Album *Album `json:"album"`
Playlist *Playlist `json:"playlist"`
Score float64 `json:"score"`
Trend string `json:"trend"`
}
// ChartInfoMenu represents a chart info menu
type ChartInfoMenu struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Items []ChartInfoMenuItem `json:"items"`
}
// ChartInfoMenuItem represents a chart info menu item
type ChartInfoMenuItem struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Action string `json:"action"`
}
// ChartItems represents chart items response
type ChartItems struct {
BaseModel
Items []ChartItem `json:"items"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
+138
View File
@@ -0,0 +1,138 @@
package models
import (
"encoding/json"
)
// MusicHistory represents music history
type MusicHistory struct {
BaseModel
Tabs []MusicHistoryTab `json:"tabs"`
Groups []MusicHistoryGroup `json:"groups"`
Items []MusicHistoryItem `json:"items"`
Context *MusicHistoryContextFullModel `json:"context"`
}
// DeJSON deserializes a MusicHistory from JSON
func (mh *MusicHistory) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
history := &MusicHistory{BaseModel: *NewBaseModel(client)}
if v, ok := raw["tabs"]; ok {
var tabs []MusicHistoryTab
json.Unmarshal(v, &tabs)
history.Tabs = tabs
}
if v, ok := raw["groups"]; ok {
var groups []MusicHistoryGroup
json.Unmarshal(v, &groups)
history.Groups = groups
}
if v, ok := raw["items"]; ok {
var items []MusicHistoryItem
json.Unmarshal(v, &items)
history.Items = items
}
if v, ok := raw["context"]; ok {
var context MusicHistoryContextFullModel
json.Unmarshal(v, &context)
history.Context = &context
}
return history, nil
}
// DeList deserializes a list of MusicHistory
func (mh *MusicHistory) DeList(data []byte, client 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 {
history := &MusicHistory{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, history); err != nil {
continue
}
result = append(result, history)
}
return result, nil
}
// MusicHistoryTab represents a music history tab
type MusicHistoryTab struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Icon string `json:"icon"`
Active bool `json:"active"`
Position int `json:"position"`
}
// MusicHistoryGroup represents a music history group
type MusicHistoryGroup struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Items []MusicHistoryItem `json:"items"`
Total int `json:"total"`
}
// MusicHistoryItem represents a music history item
type MusicHistoryItem struct {
BaseModel
ID string `json:"id"`
Type string `json:"type"` // "track", "album", "artist", "playlist"
Track *Track `json:"track"`
Album *Album `json:"album"`
Artist *Artist `json:"artist"`
Playlist *Playlist `json:"playlist"`
PlayedAt string `json:"played_at"`
PlayCount int `json:"play_count"`
Duration int `json:"duration"`
Context *MusicHistoryItemData `json:"context"`
}
// MusicHistoryItemData represents music history item data
type MusicHistoryItemData struct {
BaseModel
Source string `json:"source"`
SourceID string `json:"source_id"`
SourceType string `json:"source_type"`
PlayContext *PlayContext `json:"play_context"`
Position int `json:"position"`
Duration int `json:"duration"`
Progress float64 `json:"progress"`
}
// MusicHistoryItemID represents music history item ID
type MusicHistoryItemID struct {
BaseModel
ID string `json:"id"`
Type string `json:"type"`
}
// MusicHistoryItems represents music history items
type MusicHistoryItems struct {
BaseModel
Items []MusicHistoryItem `json:"items"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// MusicHistoryContextFullModel represents music history context full model
type MusicHistoryContextFullModel struct {
BaseModel
DeviceID string `json:"device_id"`
SessionID string `json:"session_id"`
Timestamp string `json:"timestamp"`
ClientInfo string `json:"client_info"`
}
+214
View File
@@ -0,0 +1,214 @@
package models
import (
"encoding/json"
)
// Pin represents a pin
type Pin struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Image string `json:"image"`
Type string `json:"type"` // "track", "album", "artist", "playlist"
Track *Track `json:"track"`
Album *Album `json:"album"`
Artist *Artist `json:"artist"`
Playlist *Playlist `json:"playlist"`
Position int `json:"position"`
CreatedAt string `json:"created_at"`
ExpiresAt string `json:"expires_at"`
Active bool `json:"active"`
}
// DeJSON deserializes a Pin from JSON
func (p *Pin) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
pin := &Pin{BaseModel: *NewBaseModel(client)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &pin.ID)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &pin.Title)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &pin.Description)
}
if v, ok := raw["image"]; ok {
json.Unmarshal(v, &pin.Image)
}
if v, ok := raw["type"]; ok {
json.Unmarshal(v, &pin.Type)
}
if v, ok := raw["position"]; ok {
json.Unmarshal(v, &pin.Position)
}
if v, ok := raw["created_at"]; ok {
json.Unmarshal(v, &pin.CreatedAt)
}
if v, ok := raw["expires_at"]; ok {
json.Unmarshal(v, &pin.ExpiresAt)
}
if v, ok := raw["active"]; ok {
json.Unmarshal(v, &pin.Active)
}
if v, ok := raw["track"]; ok {
var track Track
json.Unmarshal(v, &track)
pin.Track = &track
}
if v, ok := raw["album"]; ok {
var album Album
json.Unmarshal(v, &album)
pin.Album = &album
}
if v, ok := raw["artist"]; ok {
var artist Artist
json.Unmarshal(v, &artist)
pin.Artist = &artist
}
if v, ok := raw["playlist"]; ok {
var playlist Playlist
json.Unmarshal(v, &playlist)
pin.Playlist = &playlist
}
return pin, nil
}
// DeList deserializes a list of Pins
func (p *Pin) DeList(data []byte, client 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 {
pin := &Pin{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, pin); err != nil {
continue
}
result = append(result, pin)
}
return result, nil
}
// PinData represents pin data
type PinData struct {
BaseModel
Pins []Pin `json:"pins"`
Total int `json:"total"`
}
// PinsList represents a list of pins
type PinsList struct {
BaseModel
Pins []Pin `json:"pins"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// Presaves represents presaves
type Presaves struct {
BaseModel
Presaves []Presave `json:"presaves"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// Presave represents a presave
type Presave struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Image string `json:"image"`
ReleaseDate string `json:"release_date"`
Track *Track `json:"track"`
Album *Album `json:"album"`
Artist *Artist `json:"artist"`
Status string `json:"status"` // "pending", "released", "cancelled"
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
// DeJSON deserializes a Presave from JSON
func (p *Presave) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
presave := &Presave{BaseModel: *NewBaseModel(client)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &presave.ID)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &presave.Title)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &presave.Description)
}
if v, ok := raw["image"]; ok {
json.Unmarshal(v, &presave.Image)
}
if v, ok := raw["release_date"]; ok {
json.Unmarshal(v, &presave.ReleaseDate)
}
if v, ok := raw["status"]; ok {
json.Unmarshal(v, &presave.Status)
}
if v, ok := raw["created_at"]; ok {
json.Unmarshal(v, &presave.CreatedAt)
}
if v, ok := raw["updated_at"]; ok {
json.Unmarshal(v, &presave.UpdatedAt)
}
if v, ok := raw["track"]; ok {
var track Track
json.Unmarshal(v, &track)
presave.Track = &track
}
if v, ok := raw["album"]; ok {
var album Album
json.Unmarshal(v, &album)
presave.Album = &album
}
if v, ok := raw["artist"]; ok {
var artist Artist
json.Unmarshal(v, &artist)
presave.Artist = &artist
}
return presave, nil
}
// DeList deserializes a list of Presaves
func (p *Presave) DeList(data []byte, client 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 {
presave := &Presave{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, presave); err != nil {
continue
}
result = append(result, presave)
}
return result, nil
}
+407
View File
@@ -0,0 +1,407 @@
package models
import (
"encoding/json"
)
// Playlist represents a playlist
type Playlist struct {
BaseModel
Kind int `json:"kind"`
Title string `json:"title"`
Description string `json:"description"`
CoverURI string `json:"cover_uri"`
Created string `json:"created"`
Modified string `json:"modified"`
TrackCount int `json:"track_count"`
DurationMs int `json:"duration_ms"`
Availability *Availability `json:"availability"`
Owner *User `json:"owner"`
Tracks []Track `json:"tracks"`
LikesCount int `json:"likes_count"`
IsPublic bool `json:"is_public"`
Revision int `json:"revision"`
UUID string `json:"uuid"`
Tags []string `json:"tags"`
SimilarPlaylists []Playlist `json:"similar_playlists"`
// Additional fields
AccessType string `json:"access_type"`
Available bool `json:"available"`
ContentWarning string `json:"content_warning"`
Genre string `json:"genre"`
Image []Image `json:"image"`
OwnerUID int64 `json:"owner_uid"`
Shared bool `json:"shared"`
SnapshotID string `json:"snapshot_id"`
Trailer *TrailerInfo `json:"trailer"`
}
// DeJSON deserializes a Playlist from JSON
func (p *Playlist) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
playlist := &Playlist{BaseModel: *NewBaseModel(c)}
// Simple fields
if v, ok := raw["kind"]; ok {
json.Unmarshal(v, &playlist.Kind)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &playlist.Title)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &playlist.Description)
}
if v, ok := raw["cover_uri"]; ok {
json.Unmarshal(v, &playlist.CoverURI)
}
if v, ok := raw["created"]; ok {
json.Unmarshal(v, &playlist.Created)
}
if v, ok := raw["modified"]; ok {
json.Unmarshal(v, &playlist.Modified)
}
if v, ok := raw["track_count"]; ok {
json.Unmarshal(v, &playlist.TrackCount)
}
if v, ok := raw["duration_ms"]; ok {
json.Unmarshal(v, &playlist.DurationMs)
}
if v, ok := raw["likes_count"]; ok {
json.Unmarshal(v, &playlist.LikesCount)
}
if v, ok := raw["is_public"]; ok {
json.Unmarshal(v, &playlist.IsPublic)
}
if v, ok := raw["revision"]; ok {
json.Unmarshal(v, &playlist.Revision)
}
if v, ok := raw["uuid"]; ok {
json.Unmarshal(v, &playlist.UUID)
}
if v, ok := raw["tags"]; ok {
json.Unmarshal(v, &playlist.Tags)
}
if v, ok := raw["access_type"]; ok {
json.Unmarshal(v, &playlist.AccessType)
}
if v, ok := raw["available"]; ok {
json.Unmarshal(v, &playlist.Available)
}
if v, ok := raw["content_warning"]; ok {
json.Unmarshal(v, &playlist.ContentWarning)
}
if v, ok := raw["genre"]; ok {
json.Unmarshal(v, &playlist.Genre)
}
if v, ok := raw["owner_uid"]; ok {
json.Unmarshal(v, &playlist.OwnerUID)
}
if v, ok := raw["shared"]; ok {
json.Unmarshal(v, &playlist.Shared)
}
if v, ok := raw["snapshot_id"]; ok {
json.Unmarshal(v, &playlist.SnapshotID)
}
// Nested objects
if v, ok := raw["availability"]; ok {
var avail Availability
json.Unmarshal(v, &avail)
playlist.Availability = &avail
}
if v, ok := raw["owner"]; ok {
var user User
json.Unmarshal(v, &user)
playlist.Owner = &user
}
if v, ok := raw["tracks"]; ok {
var tracks []Track
json.Unmarshal(v, &tracks)
playlist.Tracks = tracks
}
if v, ok := raw["similar_playlists"]; ok {
var similar []Playlist
json.Unmarshal(v, &similar)
playlist.SimilarPlaylists = similar
}
if v, ok := raw["image"]; ok {
var images []Image
json.Unmarshal(v, &images)
playlist.Image = images
}
if v, ok := raw["trailer"]; ok {
var trailer TrailerInfo
json.Unmarshal(v, &trailer)
playlist.Trailer = &trailer
}
return playlist, nil
}
// DeList deserializes a list of Playlists
func (p *Playlist) 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 {
playlist := &Playlist{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, playlist); err != nil {
continue
}
result = append(result, playlist)
}
return result, nil
}
// User represents a user (playlist owner)
type User struct {
BaseModel
UID int64 `json:"uid"`
Login string `json:"login"`
DisplayName string `json:"display_name"`
CoverURI string `json:"cover_uri"`
Sex string `json:"sex"`
PublicPlaylists int `json:"public_playlists"`
FollowersCount int `json:"followers_count"`
FollowingsCount int `json:"followings_count"`
HasPlaylists bool `json:"has_playlists"`
Available bool `json:"available"`
// Additional fields
Description string `json:"description"`
IsVerified bool `json:"is_verified"`
City string `json:"city"`
Country string `json:"country"`
Birthday string `json:"birthday"`
RegistrationDate string `json:"registration_date"`
Settings *UserSettings `json:"settings"`
Counters *UserCounters `json:"counters"`
}
// DeJSON deserializes a User from JSON
func (u *User) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
user := &User{BaseModel: *NewBaseModel(c)}
if v, ok := raw["uid"]; ok {
json.Unmarshal(v, &user.UID)
}
if v, ok := raw["login"]; ok {
json.Unmarshal(v, &user.Login)
}
if v, ok := raw["display_name"]; ok {
json.Unmarshal(v, &user.DisplayName)
}
if v, ok := raw["cover_uri"]; ok {
json.Unmarshal(v, &user.CoverURI)
}
if v, ok := raw["sex"]; ok {
json.Unmarshal(v, &user.Sex)
}
if v, ok := raw["public_playlists"]; ok {
json.Unmarshal(v, &user.PublicPlaylists)
}
if v, ok := raw["followers_count"]; ok {
json.Unmarshal(v, &user.FollowersCount)
}
if v, ok := raw["followings_count"]; ok {
json.Unmarshal(v, &user.FollowingsCount)
}
if v, ok := raw["has_playlists"]; ok {
json.Unmarshal(v, &user.HasPlaylists)
}
if v, ok := raw["available"]; ok {
json.Unmarshal(v, &user.Available)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &user.Description)
}
if v, ok := raw["is_verified"]; ok {
json.Unmarshal(v, &user.IsVerified)
}
if v, ok := raw["city"]; ok {
json.Unmarshal(v, &user.City)
}
if v, ok := raw["country"]; ok {
json.Unmarshal(v, &user.Country)
}
if v, ok := raw["birthday"]; ok {
json.Unmarshal(v, &user.Birthday)
}
if v, ok := raw["registration_date"]; ok {
json.Unmarshal(v, &user.RegistrationDate)
}
if v, ok := raw["settings"]; ok {
var settings UserSettings
json.Unmarshal(v, &settings)
user.Settings = &settings
}
if v, ok := raw["counters"]; ok {
var counters UserCounters
json.Unmarshal(v, &counters)
user.Counters = &counters
}
return user, nil
}
// DeList deserializes a list of Users
func (u *User) 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 {
user := &User{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, user); err != nil {
continue
}
result = append(result, user)
}
return result, nil
}
// UserSettings represents user settings (from /account/settings)
type UserSettings struct {
BaseModel
UID int64 `json:"uid"`
LastFMScrobblingEnabled bool `json:"last_fm_scrobbling_enabled"`
ShuffleEnabled bool `json:"shuffle_enabled"`
VolumePercents int `json:"volume_percents"`
Modified string `json:"modified"`
FacebookScrobblingEnabled bool `json:"facebook_scrobbling_enabled"`
AddNewTrackOnPlaylistTop bool `json:"add_new_track_on_playlist_top"`
UserMusicVisibility string `json:"user_music_visibility"`
UserSocialVisibility string `json:"user_social_visibility"`
RBTDisabled bool `json:"rbt_disabled"`
Theme string `json:"theme"`
PromosDisabled bool `json:"promos_disabled"`
AutoPlayRadio bool `json:"auto_play_radio"`
SyncQueueEnabled bool `json:"sync_queue_enabled"`
AdsDisabled *bool `json:"ads_disabled,omitempty"`
DiskEnabled *bool `json:"disk_enabled,omitempty"`
ShowDiskTracksInLibrary *bool `json:"show_disk_tracks_in_library,omitempty"`
}
// DeJSON deserializes UserSettings from JSON
func (u *UserSettings) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
settings := &UserSettings{BaseModel: *NewBaseModel(c)}
if v, ok := raw["uid"]; ok {
json.Unmarshal(v, &settings.UID)
}
if v, ok := raw["last_fm_scrobbling_enabled"]; ok {
json.Unmarshal(v, &settings.LastFMScrobblingEnabled)
}
if v, ok := raw["shuffle_enabled"]; ok {
json.Unmarshal(v, &settings.ShuffleEnabled)
}
if v, ok := raw["volume_percents"]; ok {
json.Unmarshal(v, &settings.VolumePercents)
}
if v, ok := raw["modified"]; ok {
json.Unmarshal(v, &settings.Modified)
}
if v, ok := raw["facebook_scrobbling_enabled"]; ok {
json.Unmarshal(v, &settings.FacebookScrobblingEnabled)
}
if v, ok := raw["add_new_track_on_playlist_top"]; ok {
json.Unmarshal(v, &settings.AddNewTrackOnPlaylistTop)
}
if v, ok := raw["user_music_visibility"]; ok {
json.Unmarshal(v, &settings.UserMusicVisibility)
}
if v, ok := raw["user_social_visibility"]; ok {
json.Unmarshal(v, &settings.UserSocialVisibility)
}
if v, ok := raw["rbt_disabled"]; ok {
json.Unmarshal(v, &settings.RBTDisabled)
}
if v, ok := raw["theme"]; ok {
json.Unmarshal(v, &settings.Theme)
}
if v, ok := raw["promos_disabled"]; ok {
json.Unmarshal(v, &settings.PromosDisabled)
}
if v, ok := raw["auto_play_radio"]; ok {
json.Unmarshal(v, &settings.AutoPlayRadio)
}
if v, ok := raw["sync_queue_enabled"]; ok {
json.Unmarshal(v, &settings.SyncQueueEnabled)
}
if v, ok := raw["ads_disabled"]; ok {
var val bool
json.Unmarshal(v, &val)
settings.AdsDisabled = &val
}
if v, ok := raw["disk_enabled"]; ok {
var val bool
json.Unmarshal(v, &val)
settings.DiskEnabled = &val
}
if v, ok := raw["show_disk_tracks_in_library"]; ok {
var val bool
json.Unmarshal(v, &val)
settings.ShowDiskTracksInLibrary = &val
}
return settings, nil
}
// DeList deserializes a list of UserSettings
func (u *UserSettings) 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 {
settings := &UserSettings{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, settings); err != nil {
continue
}
result = append(result, settings)
}
return result, nil
}
// UserCounters represents user counters
type UserCounters struct {
Tracks int `json:"tracks"`
Albums int `json:"albums"`
Playlists int `json:"playlists"`
Followers int `json:"followers"`
Followings int `json:"followings"`
Likes int `json:"likes"`
}
// Image represents an image with multiple sizes
type Image struct {
URI string `json:"uri"`
Type string `json:"type"`
Width int `json:"width"`
Height int `json:"height"`
Size int64 `json:"size"`
Extension string `json:"extension"`
}
// Re-export types from other files for convenience
// Availability, TrailerInfo are defined in album.go
+292
View File
@@ -0,0 +1,292 @@
package models
import (
"encoding/json"
)
// Queue represents a playback queue
type Queue struct {
BaseModel
ID string `json:"id"`
TrackIDs []string `json:"track_ids"`
CurrentIndex int `json:"current_index"`
QueueType string `json:"queue_type"`
RepeatMode string `json:"repeat_mode"`
Shuffle bool `json:"shuffle"`
Device string `json:"device"`
Items []QueueItem `json:"items"`
Context *PlayContext `json:"context"`
AvailableTracks int `json:"available_tracks"`
TotalDuration int `json:"total_duration_ms"`
}
// DeJSON deserializes a Queue from JSON
func (q *Queue) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
queue := &Queue{BaseModel: *NewBaseModel(c)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &queue.ID)
}
if v, ok := raw["track_ids"]; ok {
json.Unmarshal(v, &queue.TrackIDs)
}
if v, ok := raw["current_index"]; ok {
json.Unmarshal(v, &queue.CurrentIndex)
}
if v, ok := raw["queue_type"]; ok {
json.Unmarshal(v, &queue.QueueType)
}
if v, ok := raw["repeat_mode"]; ok {
json.Unmarshal(v, &queue.RepeatMode)
}
if v, ok := raw["shuffle"]; ok {
json.Unmarshal(v, &queue.Shuffle)
}
if v, ok := raw["device"]; ok {
json.Unmarshal(v, &queue.Device)
}
if v, ok := raw["available_tracks"]; ok {
json.Unmarshal(v, &queue.AvailableTracks)
}
if v, ok := raw["total_duration_ms"]; ok {
json.Unmarshal(v, &queue.TotalDuration)
}
if v, ok := raw["items"]; ok {
var items []QueueItem
json.Unmarshal(v, &items)
queue.Items = items
}
if v, ok := raw["context"]; ok {
var ctx PlayContext
json.Unmarshal(v, &ctx)
queue.Context = &ctx
}
return queue, nil
}
// DeList deserializes a list of Queues
func (q *Queue) 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 {
queue := &Queue{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, queue); err != nil {
continue
}
result = append(result, queue)
}
return result, nil
}
// QueueItem represents a queue item
type QueueItem struct {
BaseModel
TrackID string `json:"track_id"`
Track *Track `json:"track"`
Position int `json:"position"`
AddedAt string `json:"added_at"`
AddedBy string `json:"added_by"`
Playable bool `json:"playable"`
PlayContext *PlayContext `json:"play_context"`
}
// DeJSON deserializes a QueueItem from JSON
func (qi *QueueItem) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
item := &QueueItem{BaseModel: *NewBaseModel(c)}
if v, ok := raw["track_id"]; ok {
json.Unmarshal(v, &item.TrackID)
}
if v, ok := raw["position"]; ok {
json.Unmarshal(v, &item.Position)
}
if v, ok := raw["added_at"]; ok {
json.Unmarshal(v, &item.AddedAt)
}
if v, ok := raw["added_by"]; ok {
json.Unmarshal(v, &item.AddedBy)
}
if v, ok := raw["playable"]; ok {
json.Unmarshal(v, &item.Playable)
}
if v, ok := raw["track"]; ok {
var track Track
json.Unmarshal(v, &track)
item.Track = &track
}
if v, ok := raw["play_context"]; ok {
var ctx PlayContext
json.Unmarshal(v, &ctx)
item.PlayContext = &ctx
}
return item, nil
}
// DeList deserializes a list of QueueItems
func (qi *QueueItem) 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 {
qi := &QueueItem{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, qi); err != nil {
continue
}
result = append(result, qi)
}
return result, nil
}
// PlayContext represents a play context
type PlayContext struct {
BaseModel
ID string `json:"id"`
Type string `json:"type"` // "playlist", "album", "artist", "radio", "search"
SourceID string `json:"source_id"`
SourceKind string `json:"source_kind"`
Title string `json:"title"`
Description string `json:"description"`
CoverURI string `json:"cover_uri"`
TrackCount int `json:"track_count"`
Owner *User `json:"owner"`
Available bool `json:"available"`
}
// DeJSON deserializes a PlayContext from JSON
func (pc *PlayContext) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
ctx := &PlayContext{BaseModel: *NewBaseModel(c)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &ctx.ID)
}
if v, ok := raw["type"]; ok {
json.Unmarshal(v, &ctx.Type)
}
if v, ok := raw["source_id"]; ok {
json.Unmarshal(v, &ctx.SourceID)
}
if v, ok := raw["source_kind"]; ok {
json.Unmarshal(v, &ctx.SourceKind)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &ctx.Title)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &ctx.Description)
}
if v, ok := raw["cover_uri"]; ok {
json.Unmarshal(v, &ctx.CoverURI)
}
if v, ok := raw["track_count"]; ok {
json.Unmarshal(v, &ctx.TrackCount)
}
if v, ok := raw["available"]; ok {
json.Unmarshal(v, &ctx.Available)
}
if v, ok := raw["owner"]; ok {
var user User
json.Unmarshal(v, &user)
ctx.Owner = &user
}
return ctx, nil
}
// DeList deserializes a list of PlayContexts
func (pc *PlayContext) 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 {
ctx := &PlayContext{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, ctx); err != nil {
continue
}
result = append(result, ctx)
}
return result, nil
}
// PlayContextsData represents play contexts data
type PlayContextsData struct {
BaseModel
Contexts []PlayContext `json:"contexts"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// DeJSON deserializes PlayContextsData from JSON
func (pcd *PlayContextsData) 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 := &PlayContextsData{BaseModel: *NewBaseModel(c)}
if v, ok := raw["contexts"]; ok {
var contexts []PlayContext
json.Unmarshal(v, &contexts)
result.Contexts = contexts
}
if v, ok := raw["total"]; ok {
json.Unmarshal(v, &result.Total)
}
if v, ok := raw["page"]; ok {
json.Unmarshal(v, &result.Page)
}
if v, ok := raw["page_size"]; ok {
json.Unmarshal(v, &result.PageSize)
}
return result, nil
}
// DeList deserializes a list of PlayContextsData
func (pcd *PlayContextsData) 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 {
d := &PlayContextsData{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, d); err != nil {
continue
}
result = append(result, d)
}
return result, nil
}
+328
View File
@@ -0,0 +1,328 @@
package models
import (
"encoding/json"
)
// RadioStation represents a radio station
type RadioStation struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
CoverURI string `json:"cover_uri"`
Genre string `json:"genre"`
Available bool `json:"available"`
IsPersonal bool `json:"is_personal"`
}
// DeJSON deserializes a RadioStation from JSON
func (r *RadioStation) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
station := &RadioStation{BaseModel: *NewBaseModel(client)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &station.ID)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &station.Title)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &station.Description)
}
if v, ok := raw["cover_uri"]; ok {
json.Unmarshal(v, &station.CoverURI)
}
if v, ok := raw["genre"]; ok {
json.Unmarshal(v, &station.Genre)
}
if v, ok := raw["available"]; ok {
json.Unmarshal(v, &station.Available)
}
if v, ok := raw["is_personal"]; ok {
json.Unmarshal(v, &station.IsPersonal)
}
return station, nil
}
// DeList deserializes a list of RadioStations
func (r *RadioStation) DeList(data []byte, client 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 := &RadioStation{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, station); err != nil {
continue
}
result = append(result, station)
}
return result, nil
}
// StationTracksResult represents station tracks result
type StationTracksResult struct {
BaseModel
Tracks []Track `json:"tracks"`
Total int `json:"total"`
Sequence string `json:"sequence"`
}
// DeJSON deserializes StationTracksResult from JSON
func (s *StationTracksResult) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
result := &StationTracksResult{BaseModel: *NewBaseModel(client)}
if v, ok := raw["tracks"]; ok {
var tracks []Track
json.Unmarshal(v, &tracks)
result.Tracks = tracks
}
if v, ok := raw["total"]; ok {
json.Unmarshal(v, &result.Total)
}
if v, ok := raw["sequence"]; ok {
json.Unmarshal(v, &result.Sequence)
}
return result, nil
}
// DeList deserializes a list of StationTracksResult
func (s *StationTracksResult) DeList(data []byte, client 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 {
r := &StationTracksResult{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, r); err != nil {
continue
}
result = append(result, r)
}
return result, nil
}
// RotorStation represents a rotor (generative) station
type RotorStation struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
CoverURI string `json:"cover_uri"`
Genre string `json:"genre"`
StationType string `json:"station_type"`
Available bool `json:"available"`
Settings *RotorSettings `json:"settings"`
}
// DeJSON deserializes a RotorStation from JSON
func (r *RotorStation) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
station := &RotorStation{BaseModel: *NewBaseModel(client)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &station.ID)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &station.Title)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &station.Description)
}
if v, ok := raw["cover_uri"]; ok {
json.Unmarshal(v, &station.CoverURI)
}
if v, ok := raw["genre"]; ok {
json.Unmarshal(v, &station.Genre)
}
if v, ok := raw["station_type"]; ok {
json.Unmarshal(v, &station.StationType)
}
if v, ok := raw["available"]; ok {
json.Unmarshal(v, &station.Available)
}
if v, ok := raw["settings"]; ok {
var settings RotorSettings
json.Unmarshal(v, &settings)
station.Settings = &settings
}
return station, nil
}
// DeList deserializes a list of RotorStations
func (r *RotorStation) DeList(data []byte, client 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 := &RotorStation{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, station); err != nil {
continue
}
result = append(result, station)
}
return result, nil
}
// RotorSettings represents rotor station settings
type RotorSettings struct {
BaseModel
Diversity float64 `json:"diversity"`
Energy float64 `json:"energy"`
Acousticness float64 `json:"acousticness"`
Instrumentalness float64 `json:"instrumentalness"`
Live float64 `json:"live"`
Speechiness float64 `json:"speechiness"`
Tempo float64 `json:"tempo"`
Valence float64 `json:"valence"`
}
// RotorStationTracksResult represents rotor station tracks result
type RotorStationTracksResult struct {
BaseModel
Tracks []Track `json:"tracks"`
Total int `json:"total"`
Sequence string `json:"sequence"`
}
// DeJSON deserializes RotorStationTracksResult from JSON
func (r *RotorStationTracksResult) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
result := &RotorStationTracksResult{BaseModel: *NewBaseModel(client)}
if v, ok := raw["tracks"]; ok {
var tracks []Track
json.Unmarshal(v, &tracks)
result.Tracks = tracks
}
if v, ok := raw["total"]; ok {
json.Unmarshal(v, &result.Total)
}
if v, ok := raw["sequence"]; ok {
json.Unmarshal(v, &result.Sequence)
}
return result, nil
}
// DeList deserializes a list of RotorStationTracksResult
func (r *RotorStationTracksResult) DeList(data []byte, client 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 {
r := &RotorStationTracksResult{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, r); err != nil {
continue
}
result = append(result, r)
}
return result, nil
}
// RotorSession represents a rotor session
type RotorSession struct {
BaseModel
SessionID string `json:"session_id"`
StationID string `json:"station_id"`
Tracks []Track `json:"tracks"`
BatchID string `json:"batch_id"`
ExpiresAt string `json:"expires_at"`
CreatedAt string `json:"created_at"`
}
// DeJSON deserializes a RotorSession from JSON
func (r *RotorSession) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
session := &RotorSession{BaseModel: *NewBaseModel(client)}
if v, ok := raw["session_id"]; ok {
json.Unmarshal(v, &session.SessionID)
}
if v, ok := raw["station_id"]; ok {
json.Unmarshal(v, &session.StationID)
}
if v, ok := raw["tracks"]; ok {
var tracks []Track
json.Unmarshal(v, &tracks)
session.Tracks = tracks
}
if v, ok := raw["batch_id"]; ok {
json.Unmarshal(v, &session.BatchID)
}
if v, ok := raw["expires_at"]; ok {
json.Unmarshal(v, &session.ExpiresAt)
}
if v, ok := raw["created_at"]; ok {
json.Unmarshal(v, &session.CreatedAt)
}
return session, nil
}
// DeList deserializes a list of RotorSessions
func (r *RotorSession) DeList(data []byte, client 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 {
session := &RotorSession{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, session); err != nil {
continue
}
result = append(result, session)
}
return result, nil
}
// SessionFeedback represents session feedback
type SessionFeedback struct {
BaseModel
TrackID string `json:"track_id"`
Feedback string `json:"feedback"` // "like", "dislike", "skip"
Timestamp string `json:"timestamp"`
}
// SessionFeedbacks represents multiple session feedbacks
type SessionFeedbacks struct {
BaseModel
Feedbacks []SessionFeedback `json:"feedbacks"`
}
+82
View File
@@ -0,0 +1,82 @@
package models
// SearchResult represents search results
type SearchResult struct {
Tracks *SearchTracksResult `json:"tracks"`
Albums *SearchAlbumsResult `json:"albums"`
Artists *SearchArtistsResult `json:"artists"`
Playlists *SearchPlaylistsResult `json:"playlists"`
Videos *SearchVideosResult `json:"videos"`
Users *SearchUsersResult `json:"users"`
}
// SearchTracksResult represents track search results
type SearchTracksResult struct {
Total int `json:"total"`
Results []Track `json:"results"`
}
// SearchAlbumsResult represents album search results
type SearchAlbumsResult struct {
Total int `json:"total"`
Results []Album `json:"results"`
}
// SearchArtistsResult represents artist search results
type SearchArtistsResult struct {
Total int `json:"total"`
Results []Artist `json:"results"`
}
// SearchPlaylistsResult represents playlist search results
type SearchPlaylistsResult struct {
Total int `json:"total"`
Results []Playlist `json:"results"`
}
// SearchVideosResult represents video search results
type SearchVideosResult struct {
Total int `json:"total"`
Results []Video `json:"results"`
}
// SearchUsersResult represents user search results
type SearchUsersResult struct {
Total int `json:"total"`
Results []User `json:"results"`
}
// SearchBanner represents a search banner
type SearchBanner struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Image string `json:"image"`
URL string `json:"url"`
Action string `json:"action"`
Type string `json:"type"`
}
// Tag represents a search tag
type Tag struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Count int `json:"count"`
Type string `json:"type"`
}
// Video represents a video (minimal)
type Video struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
Duration int `json:"duration"`
CoverURI string `json:"cover_uri"`
PreviewURI string `json:"preview_uri"`
Available bool `json:"available"`
Views int `json:"views"`
Likes int `json:"likes"`
CreatedAt string `json:"created_at"`
Owner *User `json:"owner"`
}
+361
View File
@@ -0,0 +1,361 @@
package models
import (
"encoding/json"
)
// Experiments represents experiments
type Experiments struct {
BaseModel
Experiments []ExperimentDetail `json:"experiments"`
Total int `json:"total"`
}
// DeJSON deserializes Experiments from JSON
func (e *Experiments) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
experiments := &Experiments{BaseModel: *NewBaseModel(client)}
if v, ok := raw["experiments"]; ok {
var items []ExperimentDetail
json.Unmarshal(v, &items)
experiments.Experiments = items
}
if v, ok := raw["total"]; ok {
json.Unmarshal(v, &experiments.Total)
}
return experiments, nil
}
// DeList deserializes a list of Experiments
func (e *Experiments) DeList(data []byte, client 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 {
e := &Experiments{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, e); err != nil {
continue
}
result = append(result, e)
}
return result, nil
}
// ExperimentDetail represents experiment details
type ExperimentDetail struct {
BaseModel
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Status string `json:"status"`
Variant string `json:"variant"`
StartDate string `json:"start_date"`
EndDate string `json:"end_date"`
}
// DeJSON deserializes ExperimentDetail from JSON
func (e *ExperimentDetail) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
detail := &ExperimentDetail{BaseModel: *NewBaseModel(client)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &detail.ID)
}
if v, ok := raw["name"]; ok {
json.Unmarshal(v, &detail.Name)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &detail.Description)
}
if v, ok := raw["status"]; ok {
json.Unmarshal(v, &detail.Status)
}
if v, ok := raw["variant"]; ok {
json.Unmarshal(v, &detail.Variant)
}
if v, ok := raw["start_date"]; ok {
json.Unmarshal(v, &detail.StartDate)
}
if v, ok := raw["end_date"]; ok {
json.Unmarshal(v, &detail.EndDate)
}
return detail, nil
}
// DeList deserializes a list of ExperimentDetails
func (e *ExperimentDetail) DeList(data []byte, client 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 {
detail := &ExperimentDetail{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, detail); err != nil {
continue
}
result = append(result, detail)
}
return result, nil
}
// Permissions represents user permissions
type Permissions struct {
BaseModel
CanPlay bool `json:"can_play"`
CanDownload bool `json:"can_download"`
CanLike bool `json:"can_like"`
CanCreatePlaylist bool `json:"can_create_playlist"`
CanSubscribe bool `json:"can_subscribe"`
CanUsePlus bool `json:"can_use_plus"`
CanUseRadio bool `json:"can_use_radio"`
CanUseYnison bool `json:"can_use_ynison"`
CanUseHighQuality bool `json:"can_use_high_quality"`
CanUseLossless bool `json:"can_use_lossless"`
}
// DeJSON deserializes Permissions from JSON
func (p *Permissions) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
perms := &Permissions{BaseModel: *NewBaseModel(client)}
if v, ok := raw["can_play"]; ok {
json.Unmarshal(v, &perms.CanPlay)
}
if v, ok := raw["can_download"]; ok {
json.Unmarshal(v, &perms.CanDownload)
}
if v, ok := raw["can_like"]; ok {
json.Unmarshal(v, &perms.CanLike)
}
if v, ok := raw["can_create_playlist"]; ok {
json.Unmarshal(v, &perms.CanCreatePlaylist)
}
if v, ok := raw["can_subscribe"]; ok {
json.Unmarshal(v, &perms.CanSubscribe)
}
if v, ok := raw["can_use_plus"]; ok {
json.Unmarshal(v, &perms.CanUsePlus)
}
if v, ok := raw["can_use_radio"]; ok {
json.Unmarshal(v, &perms.CanUseRadio)
}
if v, ok := raw["can_use_ynison"]; ok {
json.Unmarshal(v, &perms.CanUseYnison)
}
if v, ok := raw["can_use_high_quality"]; ok {
json.Unmarshal(v, &perms.CanUseHighQuality)
}
if v, ok := raw["can_use_lossless"]; ok {
json.Unmarshal(v, &perms.CanUseLossless)
}
return perms, nil
}
// DeList deserializes a list of Permissions
func (p *Permissions) DeList(data []byte, client 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 {
perms := &Permissions{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, perms); err != nil {
continue
}
result = append(result, perms)
}
return result, nil
}
// PermissionAlerts represents permission alerts
type PermissionAlerts struct {
BaseModel
Alerts []PermissionAlert `json:"alerts"`
Total int `json:"total"`
}
// DeJSON deserializes PermissionAlerts from JSON
func (p *PermissionAlerts) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
alerts := &PermissionAlerts{BaseModel: *NewBaseModel(client)}
if v, ok := raw["alerts"]; ok {
var items []PermissionAlert
json.Unmarshal(v, &items)
alerts.Alerts = items
}
if v, ok := raw["total"]; ok {
json.Unmarshal(v, &alerts.Total)
}
return alerts, nil
}
// DeList deserializes a list of PermissionAlerts
func (p *PermissionAlerts) DeList(data []byte, client 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 {
alerts := &PermissionAlerts{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, alerts); err != nil {
continue
}
result = append(result, alerts)
}
return result, nil
}
// PermissionAlert represents a permission alert
type PermissionAlert struct {
BaseModel
ID string `json:"id"`
Type string `json:"type"`
Title string `json:"title"`
Description string `json:"description"`
Action string `json:"action"`
IsRead bool `json:"is_read"`
CreatedAt string `json:"created_at"`
}
// DeJSON deserializes PermissionAlert from JSON
func (p *PermissionAlert) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
alert := &PermissionAlert{BaseModel: *NewBaseModel(client)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &alert.ID)
}
if v, ok := raw["type"]; ok {
json.Unmarshal(v, &alert.Type)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &alert.Title)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &alert.Description)
}
if v, ok := raw["action"]; ok {
json.Unmarshal(v, &alert.Action)
}
if v, ok := raw["is_read"]; ok {
json.Unmarshal(v, &alert.IsRead)
}
if v, ok := raw["created_at"]; ok {
json.Unmarshal(v, &alert.CreatedAt)
}
return alert, nil
}
// DeList deserializes a list of PermissionAlerts
func (p *PermissionAlert) DeList(data []byte, client 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 {
alert := &PermissionAlert{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, alert); err != nil {
continue
}
result = append(result, alert)
}
return result, nil
}
// ContentRestrictions represents content restrictions
type ContentRestrictions struct {
BaseModel
HasExplicitContent bool `json:"has_explicit_content"`
RestrictedRegions []string `json:"restricted_regions"`
AgeRating int `json:"age_rating"`
BlockedTracks []string `json:"blocked_tracks"`
BlockedAlbums []string `json:"blocked_albums"`
BlockedArtists []string `json:"blocked_artists"`
}
// DeJSON deserializes ContentRestrictions from JSON
func (c *ContentRestrictions) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
restrictions := &ContentRestrictions{BaseModel: *NewBaseModel(client)}
if v, ok := raw["has_explicit_content"]; ok {
json.Unmarshal(v, &restrictions.HasExplicitContent)
}
if v, ok := raw["restricted_regions"]; ok {
json.Unmarshal(v, &restrictions.RestrictedRegions)
}
if v, ok := raw["age_rating"]; ok {
json.Unmarshal(v, &restrictions.AgeRating)
}
if v, ok := raw["blocked_tracks"]; ok {
json.Unmarshal(v, &restrictions.BlockedTracks)
}
if v, ok := raw["blocked_albums"]; ok {
json.Unmarshal(v, &restrictions.BlockedAlbums)
}
if v, ok := raw["blocked_artists"]; ok {
json.Unmarshal(v, &restrictions.BlockedArtists)
}
return restrictions, nil
}
// DeList deserializes a list of ContentRestrictions
func (c *ContentRestrictions) DeList(data []byte, client 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 {
restrictions := &ContentRestrictions{BaseModel: *NewBaseModel(client)}
if err := json.Unmarshal(item, restrictions); err != nil {
continue
}
result = append(result, restrictions)
}
return result, nil
}
+395
View File
@@ -0,0 +1,395 @@
package models
import (
"encoding/json"
)
// LyricsMajor represents a lyrics provider (e.g., Genius, Musixmatch)
type LyricsMajor struct {
BaseModel
ID int `json:"id"`
Name string `json:"name"`
PrettyName string `json:"pretty_name"`
}
// DeJSON deserializes LyricsMajor from JSON
func (lm *LyricsMajor) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
major := &LyricsMajor{BaseModel: *NewBaseModel(c)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &major.ID)
}
if v, ok := raw["name"]; ok {
json.Unmarshal(v, &major.Name)
}
if v, ok := raw["pretty_name"]; ok {
json.Unmarshal(v, &major.PrettyName)
}
return major, nil
}
// DeList deserializes a list of LyricsMajor
func (lm *LyricsMajor) 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 {
major := &LyricsMajor{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, major); err != nil {
continue
}
result = append(result, major)
}
return result, nil
}
// TrackLyrics represents lyrics information from /tracks/{id}/lyrics endpoint
type TrackLyrics struct {
BaseModel
DownloadURL string `json:"download_url"`
LyricID int `json:"lyric_id"`
ExternalLyricID string `json:"external_lyric_id"`
Writers []string `json:"writers"`
Major *LyricsMajor `json:"major"`
}
// DeJSON deserializes TrackLyrics from JSON
func (tl *TrackLyrics) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
lyrics := &TrackLyrics{BaseModel: *NewBaseModel(c)}
if v, ok := raw["download_url"]; ok {
json.Unmarshal(v, &lyrics.DownloadURL)
}
if v, ok := raw["lyric_id"]; ok {
json.Unmarshal(v, &lyrics.LyricID)
}
if v, ok := raw["external_lyric_id"]; ok {
json.Unmarshal(v, &lyrics.ExternalLyricID)
}
if v, ok := raw["writers"]; ok {
json.Unmarshal(v, &lyrics.Writers)
}
if v, ok := raw["major"]; ok {
var major LyricsMajor
json.Unmarshal(v, &major)
lyrics.Major = &major
}
return lyrics, nil
}
// DeList deserializes a list of TrackLyrics
func (tl *TrackLyrics) 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 {
lyrics := &TrackLyrics{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, lyrics); err != nil {
continue
}
result = append(result, lyrics)
}
return result, nil
}
// SupplementLyrics represents the actual lyrics content (from supplement)
type SupplementLyrics struct {
BaseModel
ID int `json:"id"`
Text string `json:"lyrics"` // First lines of lyrics
FullLyrics string `json:"full_lyrics"` // Full lyrics text
HasRights bool `json:"has_rights"`
ShowTranslation bool `json:"show_translation"`
TextLanguage string `json:"text_language"`
URL string `json:"url"`
Major *LyricsMajor `json:"major"`
}
// DeJSON deserializes SupplementLyrics from JSON
func (l *SupplementLyrics) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
lyrics := &SupplementLyrics{BaseModel: *NewBaseModel(c)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &lyrics.ID)
}
if v, ok := raw["lyrics"]; ok {
json.Unmarshal(v, &lyrics.Text)
}
if v, ok := raw["full_lyrics"]; ok {
json.Unmarshal(v, &lyrics.FullLyrics)
}
if v, ok := raw["has_rights"]; ok {
json.Unmarshal(v, &lyrics.HasRights)
}
if v, ok := raw["show_translation"]; ok {
json.Unmarshal(v, &lyrics.ShowTranslation)
}
if v, ok := raw["text_language"]; ok {
json.Unmarshal(v, &lyrics.TextLanguage)
}
if v, ok := raw["url"]; ok {
json.Unmarshal(v, &lyrics.URL)
}
if v, ok := raw["major"]; ok {
var major LyricsMajor
json.Unmarshal(v, &major)
lyrics.Major = &major
}
return lyrics, nil
}
// DeList deserializes a list of SupplementLyrics
func (l *SupplementLyrics) 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 {
lyrics := &SupplementLyrics{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, lyrics); err != nil {
continue
}
result = append(result, lyrics)
}
return result, nil
}
// LyricsInfo represents lyrics availability information
type LyricsInfo struct {
BaseModel
TrackID string `json:"track_id"`
Lyrics *SupplementLyrics `json:"lyrics"`
Text string `json:"text"`
Language string `json:"language"`
HasText bool `json:"has_text"`
HasSync bool `json:"has_sync"`
SyncType string `json:"sync_type"`
Major *LyricsMajor `json:"major"`
Available bool `json:"available"`
HasAvailableSyncLyrics bool `json:"has_available_sync_lyrics"`
HasAvailableTextLyrics bool `json:"has_available_text_lyrics"`
}
// DeJSON deserializes LyricsInfo from JSON
func (li *LyricsInfo) 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 := &LyricsInfo{BaseModel: *NewBaseModel(c)}
if v, ok := raw["track_id"]; ok {
json.Unmarshal(v, &info.TrackID)
}
if v, ok := raw["text"]; ok {
json.Unmarshal(v, &info.Text)
}
if v, ok := raw["language"]; ok {
json.Unmarshal(v, &info.Language)
}
if v, ok := raw["has_text"]; ok {
json.Unmarshal(v, &info.HasText)
}
if v, ok := raw["has_sync"]; ok {
json.Unmarshal(v, &info.HasSync)
}
if v, ok := raw["sync_type"]; ok {
json.Unmarshal(v, &info.SyncType)
}
if v, ok := raw["available"]; ok {
json.Unmarshal(v, &info.Available)
}
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)
}
if v, ok := raw["lyrics"]; ok {
var lyrics SupplementLyrics
json.Unmarshal(v, &lyrics)
info.Lyrics = &lyrics
}
if v, ok := raw["major"]; ok {
var major LyricsMajor
json.Unmarshal(v, &major)
info.Major = &major
}
return info, nil
}
// DeList deserializes a list of LyricsInfo
func (li *LyricsInfo) 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 := &LyricsInfo{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, info); err != nil {
continue
}
result = append(result, info)
}
return result, nil
}
// VideoSupplement represents a video supplement (from supplement endpoint)
type VideoSupplement struct {
BaseModel
Cover string `json:"cover"`
Provider string `json:"provider"`
Title string `json:"title"`
ProviderVideoID string `json:"provider_video_id"`
URL string `json:"url"`
EmbedURL string `json:"embed_url"`
Embed string `json:"embed"`
}
// DeJSON deserializes VideoSupplement from JSON
func (vs *VideoSupplement) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
supp := &VideoSupplement{BaseModel: *NewBaseModel(c)}
if v, ok := raw["cover"]; ok {
json.Unmarshal(v, &supp.Cover)
}
if v, ok := raw["provider"]; ok {
json.Unmarshal(v, &supp.Provider)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &supp.Title)
}
if v, ok := raw["provider_video_id"]; ok {
json.Unmarshal(v, &supp.ProviderVideoID)
}
if v, ok := raw["url"]; ok {
json.Unmarshal(v, &supp.URL)
}
if v, ok := raw["embed_url"]; ok {
json.Unmarshal(v, &supp.EmbedURL)
}
if v, ok := raw["embed"]; ok {
json.Unmarshal(v, &supp.Embed)
}
return supp, nil
}
// DeList deserializes a list of VideoSupplements
func (vs *VideoSupplement) 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 {
supp := &VideoSupplement{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, supp); err != nil {
continue
}
result = append(result, supp)
}
return result, nil
}
// Supplement represents a track supplement
type Supplement struct {
BaseModel
ID string `json:"id"`
Lyrics *LyricsInfo `json:"lyrics"`
Videos []VideoSupplement `json:"videos"`
RadioIsAvailable bool `json:"radio_is_available"`
Description string `json:"description"`
Available bool `json:"available"`
}
// DeJSON deserializes Supplement from JSON
func (s *Supplement) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
supp := &Supplement{BaseModel: *NewBaseModel(c)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &supp.ID)
}
if v, ok := raw["radio_is_available"]; ok {
json.Unmarshal(v, &supp.RadioIsAvailable)
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &supp.Description)
}
if v, ok := raw["available"]; ok {
json.Unmarshal(v, &supp.Available)
}
if v, ok := raw["lyrics"]; ok {
var lyrics LyricsInfo
json.Unmarshal(v, &lyrics)
supp.Lyrics = &lyrics
}
if v, ok := raw["videos"]; ok {
var videos []VideoSupplement
json.Unmarshal(v, &videos)
supp.Videos = videos
}
return supp, nil
}
// DeList deserializes a list of Supplements
func (s *Supplement) 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 {
supp := &Supplement{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, supp); err != nil {
continue
}
result = append(result, supp)
}
return result, nil
}
+173
View File
@@ -0,0 +1,173 @@
package models
import (
"encoding/json"
)
// Track represents a full track
type Track struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
DurationMs int `json:"duration_ms"`
StorageID string `json:"storage_id"`
CoverURI string `json:"cover_uri"`
Artists []Artist `json:"artists"`
Albums []Album `json:"albums"`
Genre string `json:"genre"`
Version string `json:"version"`
Explicit bool `json:"explicit"`
Available bool `json:"available"`
AvailableForPremiumUsers bool `json:"available_for_premium_users"`
R128 float64 `json:"r128"`
TrackSharingFlag string `json:"track_sharing_flag"`
Major *Major `json:"major"`
Lyrics *TrackLyricsInfo `json:"lyrics"`
SimilarTracks []Track `json:"similar_tracks"`
DownloadInfo []DownloadInfo `json:"download_info"`
}
// DeJSON deserializes a Track from JSON
func (t *Track) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
track := &Track{BaseModel: *NewBaseModel(c)}
// Simple fields
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &track.ID)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &track.Title)
}
if v, ok := raw["duration_ms"]; ok {
json.Unmarshal(v, &track.DurationMs)
}
if v, ok := raw["storage_id"]; ok {
json.Unmarshal(v, &track.StorageID)
}
if v, ok := raw["cover_uri"]; ok {
json.Unmarshal(v, &track.CoverURI)
}
if v, ok := raw["genre"]; ok {
json.Unmarshal(v, &track.Genre)
}
if v, ok := raw["version"]; ok {
json.Unmarshal(v, &track.Version)
}
if v, ok := raw["explicit"]; ok {
json.Unmarshal(v, &track.Explicit)
}
if v, ok := raw["available"]; ok {
json.Unmarshal(v, &track.Available)
}
if v, ok := raw["available_for_premium_users"]; ok {
json.Unmarshal(v, &track.AvailableForPremiumUsers)
}
if v, ok := raw["r128"]; ok {
json.Unmarshal(v, &track.R128)
}
if v, ok := raw["track_sharing_flag"]; ok {
json.Unmarshal(v, &track.TrackSharingFlag)
}
// Nested objects
if v, ok := raw["artists"]; ok {
var artists []Artist
if err := json.Unmarshal(v, &artists); err == nil {
track.Artists = artists
}
}
if v, ok := raw["albums"]; ok {
var albums []Album
if err := json.Unmarshal(v, &albums); err == nil {
track.Albums = albums
}
}
if v, ok := raw["major"]; ok {
var major Major
if err := json.Unmarshal(v, &major); err == nil {
track.Major = &major
}
}
if v, ok := raw["lyrics"]; ok {
var lyrics TrackLyricsInfo
if err := json.Unmarshal(v, &lyrics); err == nil {
track.Lyrics = &lyrics
}
}
if v, ok := raw["similar_tracks"]; ok {
var similar []Track
if err := json.Unmarshal(v, &similar); err == nil {
track.SimilarTracks = similar
}
}
if v, ok := raw["download_info"]; ok {
var downloadInfo []DownloadInfo
if err := json.Unmarshal(v, &downloadInfo); err == nil {
track.DownloadInfo = downloadInfo
}
}
return track, nil
}
// DeList deserializes a list of Tracks
func (t *Track) 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 {
track := &Track{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, track); err != nil {
continue
}
result = append(result, track)
}
return result, nil
}
// GetDownloadInfo returns download info for a specific quality
func (t *Track) GetDownloadInfo(quality string) *DownloadInfo {
for _, info := range t.DownloadInfo {
if info.Quality == quality {
return &info
}
}
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 info
type Major struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
}
// TrackLyricsInfo represents minimal lyrics info for a track
type TrackLyricsInfo struct {
ID string `json:"id"`
Text string `json:"text"`
Major *Major `json:"major"`
Available bool `json:"available"`
HasText bool `json:"has_text"`
}
// DownloadInfo represents file download info
type DownloadInfo struct {
Quality string `json:"quality"`
Codec string `json:"codec"`
Bitrate int `json:"bitrate"`
Size int64 `json:"size"`
URL string `json:"url"`
DirectLink bool `json:"direct_link"`
}
+342
View File
@@ -0,0 +1,342 @@
package models
import (
"encoding/json"
)
// TrackShort represents a lightweight track (used in lists)
type TrackShort struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
DurationMs int `json:"duration_ms"`
StorageID string `json:"storage_id"`
CoverURI string `json:"cover_uri"`
Artists []Artist `json:"artists"`
Albums []Album `json:"albums"`
Genre string `json:"genre"`
Version string `json:"version"`
Explicit bool `json:"explicit"`
Available bool `json:"available"`
AvailableForPremiumUsers bool `json:"available_for_premium_users"`
R128 float64 `json:"r128"`
TrackSharingFlag string `json:"track_sharing_flag"`
Major *Major `json:"major"`
Lyrics *SupplementLyrics `json:"lyrics"`
SimilarTracks []TrackShort `json:"similar_tracks"`
DownloadInfo []DownloadInfo `json:"download_info"`
// Additional fields from track_short.py
Position *TrackPosition `json:"position"`
AvailableAsRingtone bool `json:"available_as_ringtone"`
RingtoneID string `json:"ringtone_id"`
}
// DeJSON deserializes a TrackShort from JSON
func (t *TrackShort) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
track := &TrackShort{BaseModel: *NewBaseModel(c)}
// Simple fields
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &track.ID)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &track.Title)
}
if v, ok := raw["duration_ms"]; ok {
json.Unmarshal(v, &track.DurationMs)
}
if v, ok := raw["storage_id"]; ok {
json.Unmarshal(v, &track.StorageID)
}
if v, ok := raw["cover_uri"]; ok {
json.Unmarshal(v, &track.CoverURI)
}
if v, ok := raw["genre"]; ok {
json.Unmarshal(v, &track.Genre)
}
if v, ok := raw["version"]; ok {
json.Unmarshal(v, &track.Version)
}
if v, ok := raw["explicit"]; ok {
json.Unmarshal(v, &track.Explicit)
}
if v, ok := raw["available"]; ok {
json.Unmarshal(v, &track.Available)
}
if v, ok := raw["available_for_premium_users"]; ok {
json.Unmarshal(v, &track.AvailableForPremiumUsers)
}
if v, ok := raw["r128"]; ok {
json.Unmarshal(v, &track.R128)
}
if v, ok := raw["track_sharing_flag"]; ok {
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
if v, ok := raw["artists"]; ok {
var artists []Artist
if err := json.Unmarshal(v, &artists); err == nil {
track.Artists = artists
}
}
if v, ok := raw["albums"]; ok {
var albums []Album
if err := json.Unmarshal(v, &albums); err == nil {
track.Albums = albums
}
}
if v, ok := raw["major"]; ok {
var major Major
if err := json.Unmarshal(v, &major); err == nil {
track.Major = &major
}
}
if v, ok := raw["lyrics"]; ok {
var lyrics SupplementLyrics
if err := json.Unmarshal(v, &lyrics); err == nil {
track.Lyrics = &lyrics
}
}
if v, ok := raw["similar_tracks"]; ok {
var similar []TrackShort
if err := json.Unmarshal(v, &similar); err == nil {
track.SimilarTracks = similar
}
}
if v, ok := raw["download_info"]; ok {
var downloadInfo []DownloadInfo
if err := json.Unmarshal(v, &downloadInfo); err == nil {
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
}
// DeList deserializes a list of TrackShort
func (t *TrackShort) 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 {
track := &TrackShort{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, track); err != nil {
continue
}
result = append(result, track)
}
return result, nil
}
// TracksList represents a list of tracks with metadata
type TracksList struct {
BaseModel
Tracks []TrackShort `json:"tracks"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
Pager *Pager `json:"pager"`
}
// DeJSON deserializes a TracksList from JSON
func (t *TracksList) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
list := &TracksList{BaseModel: *NewBaseModel(c)}
if v, ok := raw["tracks"]; ok {
var tracks []TrackShort
if err := json.Unmarshal(v, &tracks); err == nil {
list.Tracks = tracks
}
}
if v, ok := raw["total"]; ok {
json.Unmarshal(v, &list.Total)
}
if v, ok := raw["page"]; ok {
json.Unmarshal(v, &list.Page)
}
if v, ok := raw["page_size"]; ok {
json.Unmarshal(v, &list.PageSize)
}
if v, ok := raw["pager"]; ok {
var pager Pager
if err := json.Unmarshal(v, &pager); err == nil {
list.Pager = &pager
}
}
return list, nil
}
// DeList deserializes a list of TracksList
func (t *TracksList) 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 {
list := &TracksList{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, list); err != nil {
continue
}
result = append(result, list)
}
return result, nil
}
// Pager represents pagination info
type Pager struct {
BaseModel
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
Pages int `json:"pages"`
PerPage int `json:"per_page"`
PrevPage *int `json:"prev_page"`
NextPage *int `json:"next_page"`
HasPrev bool `json:"has_prev"`
HasNext bool `json:"has_next"`
}
// DeJSON deserializes a Pager from JSON
func (p *Pager) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
pager := &Pager{BaseModel: *NewBaseModel(c)}
if v, ok := raw["total"]; ok {
json.Unmarshal(v, &pager.Total)
}
if v, ok := raw["page"]; ok {
json.Unmarshal(v, &pager.Page)
}
if v, ok := raw["page_size"]; ok {
json.Unmarshal(v, &pager.PageSize)
}
if v, ok := raw["pages"]; ok {
json.Unmarshal(v, &pager.Pages)
}
if v, ok := raw["per_page"]; ok {
json.Unmarshal(v, &pager.PerPage)
}
if v, ok := raw["prev_page"]; ok {
var prev int
if err := json.Unmarshal(v, &prev); err == nil {
pager.PrevPage = &prev
}
}
if v, ok := raw["next_page"]; ok {
var next int
if err := json.Unmarshal(v, &next); err == nil {
pager.NextPage = &next
}
}
if v, ok := raw["has_prev"]; ok {
json.Unmarshal(v, &pager.HasPrev)
}
if v, ok := raw["has_next"]; ok {
json.Unmarshal(v, &pager.HasNext)
}
return pager, nil
}
// DeList deserializes a list of Pager
func (p *Pager) 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 {
pager := &Pager{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, pager); err != nil {
continue
}
result = append(result, pager)
}
return result, nil
}
// TrackPosition represents track position in a playlist/queue
type TrackPosition struct {
BaseModel
Revision int `json:"revision"`
Timestamp string `json:"timestamp"`
Position int `json:"position"`
FromCache bool `json:"from_cache"`
}
// DeJSON deserializes a TrackPosition from JSON
func (tp *TrackPosition) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
pos := &TrackPosition{BaseModel: *NewBaseModel(c)}
if v, ok := raw["revision"]; ok {
json.Unmarshal(v, &pos.Revision)
}
if v, ok := raw["timestamp"]; ok {
json.Unmarshal(v, &pos.Timestamp)
}
if v, ok := raw["position"]; ok {
json.Unmarshal(v, &pos.Position)
}
if v, ok := raw["from_cache"]; ok {
json.Unmarshal(v, &pos.FromCache)
}
return pos, nil
}
// DeList deserializes a list of TrackPosition
func (tp *TrackPosition) 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 {
pos := &TrackPosition{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, pos); err != nil {
continue
}
result = append(result, pos)
}
return result, nil
}
+136
View File
@@ -0,0 +1,136 @@
package models
import (
"context"
"testing"
)
// mockRequest implements RequestInterface for testing
type mockRequest struct{}
func (m *mockRequest) Get(ctx context.Context, path string, params map[string]string, result any) error {
return nil
}
func (m *mockRequest) Post(ctx context.Context, path string, body any, result any) error {
return nil
}
func (m *mockRequest) Put(ctx context.Context, path string, body any, result any) error {
return nil
}
func (m *mockRequest) Delete(ctx context.Context, path string, body any, result any) error {
return nil
}
func (m *mockRequest) Download(ctx context.Context, url string) ([]byte, error) {
return nil, nil
}
// mockClient implements ClientInterface for testing
type mockClient struct {
request *mockRequest
}
func (m *mockClient) GetRequest() RequestInterface {
return m.request
}
func newMockClient() *mockClient {
return &mockClient{request: &mockRequest{}}
}
func TestTrack_DeJSON(t *testing.T) {
client := newMockClient()
data := []byte(`{
"id": "123",
"title": "Test Track",
"duration_ms": 180000,
"storage_id": "456",
"cover_uri": "https://example.com/cover.jpg",
"genre": "rock",
"version": "album",
"explicit": false,
"available": true,
"available_for_premium_users": true,
"r128": -12.5,
"track_sharing_flag": "allowed",
"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}],
"major": {"id": "major1", "name": "Major Label", "description": "Big label"},
"lyrics": {"id": "lyr1", "text": "Test lyrics", "available": true, "has_text": true}
}`)
track := &Track{BaseModel: *NewBaseModel(client)}
result, err := track.DeJSON(data, client)
if err != nil {
t.Fatalf("DeJSON failed: %v", err)
}
resultTrack := result.(*Track)
if resultTrack.ID != "123" {
t.Errorf("Expected ID '123', got '%s'", resultTrack.ID)
}
if resultTrack.Title != "Test Track" {
t.Errorf("Expected Title 'Test Track', got '%s'", resultTrack.Title)
}
if resultTrack.DurationMs != 180000 {
t.Errorf("Expected DurationMs 180000, got %d", resultTrack.DurationMs)
}
if len(resultTrack.Artists) != 1 {
t.Errorf("Expected 1 artist, got %d", len(resultTrack.Artists))
}
if resultTrack.Major == nil || resultTrack.Major.Name != "Major Label" {
t.Errorf("Expected Major 'Major Label'")
}
if resultTrack.Lyrics == nil || resultTrack.Lyrics.Text != "Test lyrics" {
t.Errorf("Expected Lyrics 'Test lyrics'")
}
}
func TestTrack_DeList(t *testing.T) {
client := newMockClient()
data := []byte(`[
{"id": "1", "title": "Track 1", "duration_ms": 100000},
{"id": "2", "title": "Track 2", "duration_ms": 200000}
]`)
track := &Track{BaseModel: *NewBaseModel(client)}
result, err := track.DeList(data, client)
if err != nil {
t.Fatalf("DeList failed: %v", err)
}
if len(result) != 2 {
t.Errorf("Expected 2 tracks, got %d", len(result))
}
}
func TestTrack_GetDownloadInfo(t *testing.T) {
client := newMockClient()
track := &Track{
BaseModel: *NewBaseModel(client),
DownloadInfo: []DownloadInfo{
{Quality: "mp3", Codec: "mp3", Bitrate: 320, Size: 5000000, URL: "https://example.com/track.mp3"},
{Quality: "flac", Codec: "flac", Bitrate: 1411, Size: 25000000, URL: "https://example.com/track.flac"},
},
}
info := track.GetDownloadInfo("mp3")
if info == nil {
t.Fatal("Expected mp3 download info")
}
if info.Bitrate != 320 {
t.Errorf("Expected bitrate 320, got %d", info.Bitrate)
}
info = track.GetDownloadInfo("flac")
if info == nil {
t.Fatal("Expected flac download info")
}
if info.Bitrate != 1411 {
t.Errorf("Expected bitrate 1411, got %d", info.Bitrate)
}
info = track.GetDownloadInfo("ogg")
if info != nil {
t.Errorf("Expected nil for ogg, got %v", info)
}
}