Compare commits

..
18 Commits
Author SHA1 Message Date
Mrixs dbf9a46bb2 Fix TrackShort.Lyrics type mismatch
TrackShort.Lyrics was SupplementLyrics but Python reference shows it should be TrackLyricsInfo
(has_available_sync_lyrics, has_available_text_lyrics fields)
2026-10-18 15:07:46 +03:00
Mrixs 73775a8526 Add remaining model types and client methods
Models added:
- Disclaimer, ForeignAgent (credits.go)
- ArtistConcerts (concert.go)
- Dashboard, StationResult, Station, AdParams, GenerativeStream,
  GenerativeStreamData, GenerativeStreamInfo (radio.go)
- FileInfo, FileDownloadInfo, TrackFullInfo, SimilarTracks, TrackTrailer (track.go)
- Removed duplicate AdParams from feed.go

Client methods added:
- artists.go: GetArtistBriefInfo, GetArtistSimilar, GetArtistLinks,
  GetArtistAlsoAlbums, GetArtistDiscographyAlbums, GetArtistSafeDirectAlbums,
  GetArtistTrackIds, GetArtistAbout, GetArtistDonation, GetArtistInfo,
  GetArtistSkeleton, GetArtistTrailer
- albums.go: GetAlbumSimilarEntities, GetAlbumTrailer
- clips.go: GetClipsDisclaimer
- concerts.go: GetArtistConcerts, GetConcertSkeleton
- radio.go: GetRotorStationsDashboard, GetRotorStationsList,
  GetRotorStationInfo, GetRotorStationStream, GetRotorStationSettings
- supplements.go: GetTracksCredits, GetClipsCredits
- queue.go: GetQueuesList
- tracks.go: GetTracksFileInfo, GetTracksSimilar, GetTrackTrailer, GetTrackFullInfo
2026-10-18 13:32:45 +03:00
Mrixs 49920d8908 Add missing Track, Album, Artist model types and client methods
Track: Added FileInfo, FileDownloadInfo, TrackFullInfo, SimilarTracks types
Album: Added AlbumSimilarEntities, AlbumTrailer, SimilarEntityItem types
Artist: Added ArtistSimilar, ArtistLinks, ArtistAbout, ArtistInfo, ArtistSkeleton,
       ArtistTrailerStatus, ArtistLink, Stats, SkeletonBlock types

Client methods added:
- artists.go: GetArtistBriefInfo, GetArtistSimilar, GetArtistLinks, GetArtistAlsoAlbums,
  GetArtistDiscographyAlbums, GetArtistSafeDirectAlbums, GetArtistTrackIds, GetArtistAbout,
  GetArtistDonation, GetArtistInfo, GetArtistSkeleton, GetArtistTrailer
- albums.go: GetAlbumSimilarEntities, GetAlbumTrailer
- tracks.go: GetTracksFileInfo, GetTracksSimilar, GetTrackTrailer, GetTrackFullInfo
2026-10-18 13:24:41 +03:00
Mrixs 9d3bcc4a9b Add missing model fields from Python audit
LandingList: Added type, type_for_from, title, id, new_releases, new_playlists, podcasts
New type: PlaylistID
Presaves: Added upcoming_albums, released_albums
Presaves: Added DeJSON method
QueueItem: Added id, context, modified fields (fixed duplicate context)
Clip: Fixed PreviewURI → PreviewURL field name
2026-10-18 13:18:24 +03:00
Mrixs 463e0bcd1a Add missing model fields from Python reference
Feed: Added can_get_more_events, pumpkin, is_wizard_passed, headlines, today, next_revision
Label: Added description_formatted, image, links ([]Link), type
Landing: Added pumpkin, content_id
Pin: Added content_restrictions
Search: Added search_request_id, text, best, podcasts, podcast_episodes, clips, banner, page, per_page, misspell fields

Clip: Rewrote with all missing fields (version, player_id, uuid, thumbnail, track_ids, disclaimers, explicit, cover, content_restrictions)
Concert: Added datetime, afisha_url, city, place, address, content_rating, data_session_id, event_info
2026-10-18 13:14:15 +03:00
Mrixs bc026d82b4 Add missing Track and Album model fields from Python reference
Track: Added 50+ missing fields (storage_dir, file_size, real_id, og_image, type,
normalization, error, can_publish, state, filename, user_info, meta_data, regions,
content_warning, preview_duration_ms, remember_position, background_video_uri,
short_description, is_suitable_for_children, track_source, available_for_options,
derived_colors, fade, smart_preview_params, special_audio_resources, disclaimers,
background_video_id, player_id, mix_fade, chart, clip_ids, isrc, ugc_artist_name,
podcast_episode_type, pub_date, and more)

New types: Normalization, MetaData, Fade, SmartPreviewParams, CoverDerivedColors,
CustomWave, TrackId, TrackFullInfo, SimilarTracks, TrackTrailer

Album: Added 40+ missing fields (error, version, original_release_year, text_color,
short_description, is_premiere, is_banner, meta_type, storage_dir, og_image, buy,
recent, very_important, bests, duplicates, prerolls, regions, available_as_rbt,
lyrics_available, remember_position, duration_ms, explicit, start_date, likes_count,
deprecation, available_regions, available_for_options, disclaimers, action_button,
cover, derived_colors, meta_tag_id, child_content, background_image_url, duration_sec,
has_trailer, pager, sort_order)

New types: Deprecation, AlbumActionButton
2026-10-18 13:12:13 +03:00
Mrixs a9be10b5e5 Fix remaining type mismatches vs Python reference
- RotorSettings: Diversity float64→string, Energy float64→int, add Mood int and MoodEnergy string
- Ratings: Rating float64→int, add Week int and Day int
- MetatagArtistEntry: Remove Score float64 (not in Python), add PopularTracks []Track
- MusicHistoryItemData: Remove Progress float64 (not in Python)
2026-10-18 12:57:21 +03:00
Mrixs 577c4a418c Fix Artist model to match Python reference
- Link field removed (not in Python Artist model)
- Description: string → *Description object (text, uri fields)
- SocialLinks []SocialLink → Links []Link (title, href, type, social_network)
- Donations *ArtistDonations → DonationInfo *ArtistDonationInfo (title, url)
- Decomposed []Artist → []Decomposed (accepts string or Artist object)
- Added Decomposed, Link, Description, ArtistDonationInfo types
2026-10-18 12:44:17 +03:00
Mrixs 277a3e6924 Fix Album model to match Python reference
- Labels: changed from []Label to []LabelOrString (API returns both objects and strings)
- Trailer: changed from *TrailerInfo to *AlbumTrailerStatus (Python has AlbumTrailerStatus with only 'available' field)
- Added LabelOrString type with custom UnmarshalJSON
- Added AlbumTrailerStatus type with Available field
2026-10-18 12:39:45 +03:00
Mrixs 6312c24b0f Fix Track model to match Python reference
- R128: changed from JSONR128 float64 to R128 struct with i, tp, important_secs fields
  (Python R128 is an object, not a number)
- StorageID -> StorageDir with json tag storage_dir (Python API uses storage_dir)
- Lyrics: replaced TrackLyricsInfo with Lyrics (from supplement/lyrics.py) and
  TrackLyricsInfo (from track/lyrics_info.py with has_available_sync_lyrics,
  has_available_text_lyrics fields)
- Track.Lyrics field now maps to lyrics_info JSON key with TrackLyricsInfo type
2026-10-18 12:34:58 +03:00
Mrixs ad83d84531 Fix GetTrackDownloadInfo to return signed download URLs
- Pass quality parameter to /tracks/{track_id}/download-info API request
- Add GetTrackDownloadURL method that resolves signed download URLs by
  downloading XML from download_info_url, parsing host/path/ts/s,
  computing MD5 signature (salt: XGRlBW9FXlekgbPrRHuSiA), and
  returning https://{host}/get-mp3/{sign}/{ts}{path}
- Add DownloadInfoURL field to DownloadInfo model for the XML URL
- Update DownloadTrack to use GetDirectLink resolution when URL is empty
- Port signing logic from yandex-music-api-python download_info.py
2026-10-18 12:13:54 +03:00
Mrixs dbb823ade3 Fix R128 and remaining ID fields to accept both JSON types
- Track.R128 and TrackShort.R128: float64 → JSONR128 (accepts both JSON number and object)
- All remaining primary ID fields: string → JSONID (accepts both JSON string and number)
  Fixed in: account, artist, clip, concert, feed, label, landing, metatag,
  music_history, pin, queue, radio, search, settings, supplement
2026-10-18 11:01:26 +03:00
Mrixs d0173f795d Remove .serena from git tracking
- Remove .serena/.gitignore and .serena/project.yml from version control
- Add .serena/ to .gitignore to prevent future tracking
- Files remain on disk, only removed from git tracking
2026-10-18 10:25:17 +03:00
MrixsandClaude Code 5535d2a941 Fix ID fields to accept both JSON strings and numbers
The Yandex Music API returns IDs as both JSON strings and numbers depending
on the endpoint. Added JSONID custom type in internal/models/base.go that
implements json.Unmarshaler to accept both formats and store as string.

Changed ID fields from string to JSONID:
- Track.ID, Major.ID, TrackLyricsInfo.ID (track.go)
- Album.ID, TrailerInfo.TrackID, TrailerInfo.AlbumID (album.go)

Updated example to convert JSONID to string when passing to client methods.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-18 10:21:58 +03:00
MrixsandClaude Code 0456ca0665 Add comprehensive godoc documentation to all exported symbols
- Document all exported types, functions, methods, and constants across 52 files
- Add package-level doc comments to client, models, utils, and ynison packages
- Document HTTP method and endpoint path for each API client method
- Document model struct fields and DeJSON/DeList/ToJSON/ToMap methods
- Fix duplicate package doc comment in internal/models/base.go

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-18 08:57:43 +03:00
MrixsandClaude Code 7a68ad86cb Add package-level godoc documentation
- models: Package doc comment describing data models with DeJSON/DeList
- utils: Package doc comment for utility functions (JSON, signing, schema mismatch, errors)
- ynison: Package doc comment for Ynison WebSocket client protocol

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-18 08:39:50 +03:00
MrixsandClaude Code 5786f0df1e Add godoc documentation to client package
- Add package-level doc comment
- Add doc comments to ClientConfig, Client, Status, Account, Track types
- Document all exported methods and fields

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-18 08:37:39 +03:00
MrixsandClaude Code 70c2a5cd01 Fix API endpoints: resource IDs in URL path, not query params
- artists/{artist_id}/tracks and /direct-albums: artistID in path, return ArtistTracks/ArtistAlbums with pager
- likes: /users/me/likes/{type}s/{add-multiple|remove}, IDs as body params
- queue: /queues/{queue_id}/... instead of /queue/...?queue_id=...
- radio: /rotor/station/{station_id}/info and /tracks
- concerts: /concerts/{concert_id}/info, /concerts/tab-config
- playlists: /users/{user_id}/playlists
- labels: /labels/{label_id}, /albums, /artists
- clips: /clips/will/like, /artists/{artist_id}/blocks/artist-clips
- feed: /landing3/* endpoints

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-18 08:32:29 +03:00
58 changed files with 3958 additions and 1143 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
.serena/
+182
View File
@@ -0,0 +1,182 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
This is a Go port of the [yandex-music-api](https://github.com/MarshalX/yandex-music-api) Python library. The original Python library is in `yandex-music-api-python/` and serves as the reference implementation.
The Python library provides:
- **Sync and async HTTP clients** for Yandex Music API
- **Ynison protocol support** for player synchronization across devices
- **Radio sessions** (My Wave, generative stations)
- **High-level wrapper objects** (Track, Album, Artist, Playlist, etc.)
- **Schema mismatch handling** for API evolution
- **Multiple JSON backend support** (orjson, pydantic-core, ujson, stdlib json)
## Python Library Architecture (Reference)
### Core Structure
```
yandex_music/
├── client.py # Sync Client (main entry point)
├── client_async.py # Async ClientAsync
├── _client_base.py # Shared base class with common attributes
├── base.py # YandexMusicObject base model with de_json/de_list
├── exceptions.py # Custom exceptions
├── utils/
│ ├── request.py # Sync HTTP request handling
│ ├── request_async.py # Async HTTP request handling
│ ├── json_backend.py # Pluggable JSON backends
│ └── schema_mismatch.py # Schema mismatch reporting
├── _client/ # Generated sync method mixins (20+ files)
├── _client_async/ # Generated async method mixins
├── track/, album/, artist/, playlist/, etc. # Model packages
├── ynison/ # Ynison protocol (WebSocket)
│ ├── client.py # Sync Ynison client
│ ├── client_async.py # Async Ynison client
│ └── models/ # Protobuf-generated models
└── supplement/ # Lyrics, video supplements
```
### Key Patterns
- **Mixin-based Client**: `Client` inherits from 20+ mixins (AccountMixin, AlbumsMixin, TracksMixin, etc.)
- **Model deserialization**: Each model has `de_json(data, client)` and `de_list(list_data, client)` classmethods
- **Request signing**: Requests to `api.music.yandex.net` are signed with a custom algorithm
- **Device auth**: OAuth Device Flow for token acquisition
- **Generated code**: Sync/async clients and models are partially code-generated from OpenAPI spec
## Go Project Structure (Target)
```
yandex-music-api-go/
├── go.mod
├── go.sum
├── cmd/
│ └── examples/ # Example applications
├── internal/
│ ├── client/ # Core HTTP client
│ │ ├── client.go # Main Client struct
│ │ ├── request.go # Request building/signing
│ │ └── auth.go # Device auth, token handling
│ ├── models/ # Data models (Track, Album, Artist, etc.)
│ │ ├── base.go # BaseModel interface
│ │ ├── track.go
│ │ ├── album.go
│ │ └── ...
│ ├── ynison/ # Ynison WebSocket client
│ │ ├── client.go
│ │ └── proto/ # Protobuf-generated Go code
│ └── utils/
│ ├── json.go # JSON handling
│ └── schema.go # Schema mismatch handling
├── yandexmusic/ # Public API package (re-exports)
│ ├── client.go # Public Client interface
│ └── models.go # Public model types
└── tests/ # Integration tests
```
## Development Commands
### Go Toolchain
```bash
# Initialize module (run once)
go mod init github.com/your-org/yandex-music-api-go
# Download dependencies
go mod tidy
# Build
go build ./...
# Run tests
go test ./...
# Run tests with coverage
go test -cover ./...
# Run single test
go test -v -run TestName ./path/to/package
# Lint (requires golangci-lint)
golangci-lint run
# Format
go fmt ./...
# Vet
go vet ./...
# Generate code (if using code generation)
go generate ./...
```
### Protobuf (for Ynison)
```bash
# Install protoc and plugins
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
go install github.com/bufbuild/buf/cmd/buf@latest # optional, for buf.yaml workflow
# Generate from .proto files
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
internal/ynison/proto/*.proto
# Or with buf
buf generate
```
## Key Implementation Notes
### Request Signing
The Python library signs requests with a custom algorithm. Port this exactly from `yandex_music/utils/request.py`:
- Request parameters are sorted and concatenated
- Signed with a secret key
- Signature added as `sign` parameter
### JSON Handling
Go's standard `encoding/json` is fast enough. No need for multiple backends. Use `json.RawMessage` for flexible fields.
### Schema Mismatch Handling
Port the Python `SchemaMismatch` logic:
- Track unknown fields in API responses
- Configurable: ignore, warn, or strict (error)
- Callback support for custom handling (e.g., Sentry)
### Ynison Protocol
- Uses WebSocket with protobuf messages
- Python uses `betterproto` for protobuf; Go uses standard `protobuf` module
- Port the message types from `yandex_music/ynison/models/`
### Model Structure
Follow Python patterns:
```go
// Base interface for all models
type Model interface {
// DeJSON deserializes from JSON
DeJSON(data []byte, client *Client) (Model, error)
// DeList deserializes a list
DeList(data []byte, client *Client) ([]Model, error)
}
```
### Client Design
- Single `Client` struct with methods (not mixins - Go doesn't have them)
- Group methods by feature using separate files: `client_tracks.go`, `client_albums.go`, etc.
- Support both sync and async (context-based) patterns
## Testing Strategy
- Unit tests for models (de/serialization)
- Integration tests against mock server (use `httptest`)
- Test schema mismatch handling
- Test request signing
## License
LGPL-3.0-only (same as Python library)
## References
- Python library: `yandex-music-api-python/`
- OpenAPI spec: https://ym.marshal.dev/api/openapi.json
- Documentation: https://ym.marshal.dev/
- Telegram: https://t.me/yandex_music_api
+1 -1
View File
@@ -57,7 +57,7 @@ func main() {
// Example: Get track by ID (if we have results) // Example: Get track by ID (if we have results)
if len(tracks) > 0 { if len(tracks) > 0 {
fmt.Println("\n--- Getting track details ---") fmt.Println("\n--- Getting track details ---")
track, err := client.GetTrack(ctx, tracks[0].ID) track, err := client.GetTrack(ctx, string(tracks[0].ID))
if err != nil { if err != nil {
log.Printf("Get track failed: %v", err) log.Printf("Get track failed: %v", err)
} else { } else {
+10 -8
View File
@@ -1,3 +1,4 @@
// Package client provides methods for interacting with Yandex Music API endpoints.
package client package client
import ( import (
@@ -6,7 +7,7 @@ import (
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models"
) )
// GetAccountSettings gets account settings (current user's settings from /account/settings) // GetAccountSettings gets account settings (current user's settings from /account/settings).
func (c *Client) GetAccountSettings(ctx context.Context) (*models.UserSettings, error) { func (c *Client) GetAccountSettings(ctx context.Context) (*models.UserSettings, error) {
var result models.UserSettings var result models.UserSettings
if err := c.request.Get(ctx, "/account/settings", nil, &result); err != nil { if err := c.request.Get(ctx, "/account/settings", nil, &result); err != nil {
@@ -15,7 +16,7 @@ func (c *Client) GetAccountSettings(ctx context.Context) (*models.UserSettings,
return &result, nil return &result, nil
} }
// UpdateAccountSettings updates account settings // UpdateAccountSettings updates account settings (PUT /account/settings).
func (c *Client) UpdateAccountSettings(ctx context.Context, settings *models.AccountSettings) (*models.AccountSettings, error) { func (c *Client) UpdateAccountSettings(ctx context.Context, settings *models.AccountSettings) (*models.AccountSettings, error) {
var result models.AccountSettings var result models.AccountSettings
if err := c.request.Put(ctx, "/account/settings", settings, &result); err != nil { if err := c.request.Put(ctx, "/account/settings", settings, &result); err != nil {
@@ -24,7 +25,7 @@ func (c *Client) UpdateAccountSettings(ctx context.Context, settings *models.Acc
return &result, nil return &result, nil
} }
// GetSubscription gets user subscription info // GetSubscription gets user subscription info (GET /account/subscription).
func (c *Client) GetSubscription(ctx context.Context) (*models.Subscription, error) { func (c *Client) GetSubscription(ctx context.Context) (*models.Subscription, error) {
var result models.Subscription var result models.Subscription
if err := c.request.Get(ctx, "/account/subscription", nil, &result); err != nil { if err := c.request.Get(ctx, "/account/subscription", nil, &result); err != nil {
@@ -33,7 +34,7 @@ func (c *Client) GetSubscription(ctx context.Context) (*models.Subscription, err
return &result, nil return &result, nil
} }
// GetPlusInfo gets Plus subscription info // GetPlusInfo gets Plus subscription info (GET /account/plus).
func (c *Client) GetPlusInfo(ctx context.Context) (*models.PlusInfo, error) { func (c *Client) GetPlusInfo(ctx context.Context) (*models.PlusInfo, error) {
var result models.PlusInfo var result models.PlusInfo
if err := c.request.Get(ctx, "/account/plus", nil, &result); err != nil { if err := c.request.Get(ctx, "/account/plus", nil, &result); err != nil {
@@ -42,7 +43,7 @@ func (c *Client) GetPlusInfo(ctx context.Context) (*models.PlusInfo, error) {
return &result, nil return &result, nil
} }
// GetPromoCodeStatus gets promo code status // GetPromoCodeStatus gets promo code status (GET /promo-code-status).
func (c *Client) GetPromoCodeStatus(ctx context.Context, code string) (*models.PromoCodeStatus, error) { func (c *Client) GetPromoCodeStatus(ctx context.Context, code string) (*models.PromoCodeStatus, error) {
params := map[string]string{ params := map[string]string{
"code": code, "code": code,
@@ -54,7 +55,7 @@ func (c *Client) GetPromoCodeStatus(ctx context.Context, code string) (*models.P
return &result, nil return &result, nil
} }
// ActivatePromoCode activates a promo code // ActivatePromoCode activates a promo code (POST /promo-code/activate).
func (c *Client) ActivatePromoCode(ctx context.Context, code string) (*models.PromoCodeStatus, error) { func (c *Client) ActivatePromoCode(ctx context.Context, code string) (*models.PromoCodeStatus, error) {
params := map[string]string{ params := map[string]string{
"code": code, "code": code,
@@ -66,7 +67,7 @@ func (c *Client) ActivatePromoCode(ctx context.Context, code string) (*models.Pr
return &result, nil return &result, nil
} }
// GetUserSettings gets user settings // GetUserSettings gets user settings for a specific user (GET /user/settings).
func (c *Client) GetUserSettings(ctx context.Context, userID string) (*models.UserSettings, error) { func (c *Client) GetUserSettings(ctx context.Context, userID string) (*models.UserSettings, error) {
params := map[string]string{ params := map[string]string{
"user_id": userID, "user_id": userID,
@@ -78,7 +79,8 @@ func (c *Client) GetUserSettings(ctx context.Context, userID string) (*models.Us
return &result, nil return &result, nil
} }
// GetAccountStatus fetches account status // GetAccountStatus fetches account status (GET /account/status).
// It returns the account status including account info and Plus subscription status.
func (c *Client) GetAccountStatus(ctx context.Context) (*Status, error) { func (c *Client) GetAccountStatus(ctx context.Context) (*Status, error) {
var result Status var result Status
if err := c.request.Get(ctx, "/account/status", nil, &result); err != nil { if err := c.request.Get(ctx, "/account/status", nil, &result); err != nil {
+28 -3
View File
@@ -1,13 +1,16 @@
// Package client provides methods for interacting with Yandex Music API endpoints.
package client package client
import ( import (
"context" "context"
"fmt"
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models"
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils"
) )
// GetAlbums gets albums by IDs // GetAlbums gets albums by their IDs (GET /tracks?album-ids=...).
// Returns a slice of Album pointers, or nil if no IDs are provided.
func (c *Client) GetAlbums(ctx context.Context, albumIDs []string) ([]*models.Album, error) { func (c *Client) GetAlbums(ctx context.Context, albumIDs []string) ([]*models.Album, error) {
if len(albumIDs) == 0 { if len(albumIDs) == 0 {
return nil, nil return nil, nil
@@ -30,7 +33,8 @@ func (c *Client) GetAlbums(ctx context.Context, albumIDs []string) ([]*models.Al
return albums, nil return albums, nil
} }
// GetAlbum gets a single album by ID // GetAlbum gets a single album by ID using GetAlbums.
// Returns an error if no album is found with the given ID.
func (c *Client) GetAlbum(ctx context.Context, albumID string) (*models.Album, error) { func (c *Client) GetAlbum(ctx context.Context, albumID string) (*models.Album, error) {
albums, err := c.GetAlbums(ctx, []string{albumID}) albums, err := c.GetAlbums(ctx, []string{albumID})
if err != nil { if err != nil {
@@ -42,7 +46,8 @@ func (c *Client) GetAlbum(ctx context.Context, albumID string) (*models.Album, e
return albums[0], nil return albums[0], nil
} }
// GetAlbumsWithTracks gets albums with tracks expanded // GetAlbumsWithTracks gets albums with tracks expanded (GET /tracks?with-tracks=true).
// Returns a slice of Album pointers with their tracks populated.
func (c *Client) GetAlbumsWithTracks(ctx context.Context, albumIDs []string) ([]*models.Album, error) { func (c *Client) GetAlbumsWithTracks(ctx context.Context, albumIDs []string) ([]*models.Album, error) {
if len(albumIDs) == 0 { if len(albumIDs) == 0 {
return nil, nil return nil, nil
@@ -65,3 +70,23 @@ func (c *Client) GetAlbumsWithTracks(ctx context.Context, albumIDs []string) ([]
} }
return albums, nil return albums, nil
} }
// GetAlbumSimilarEntities gets similar entities for an album (GET /albums/{id}/similar-entities).
func (c *Client) GetAlbumSimilarEntities(ctx context.Context, albumID string) (*models.AlbumSimilarEntities, error) {
var result models.AlbumSimilarEntities
if err := c.request.Get(ctx, fmt.Sprintf("/albums/%s/similar-entities", albumID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetAlbumTrailer gets album trailer (GET /albums/{id}/trailer).
func (c *Client) GetAlbumTrailer(ctx context.Context, albumID string) (*models.AlbumTrailer, error) {
var result models.AlbumTrailer
if err := c.request.Get(ctx, fmt.Sprintf("/albums/%s/trailer", albumID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
+156 -28
View File
@@ -1,3 +1,4 @@
// Package client provides methods for interacting with Yandex Music API endpoints.
package client package client
import ( import (
@@ -8,7 +9,8 @@ import (
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils"
) )
// GetArtists gets artists by IDs // GetArtists gets artists by their IDs (GET /tracks?artist-ids=...).
// Returns a slice of Artist pointers, or nil if no IDs are provided.
func (c *Client) GetArtists(ctx context.Context, artistIDs []string) ([]*models.Artist, error) { func (c *Client) GetArtists(ctx context.Context, artistIDs []string) ([]*models.Artist, error) {
if len(artistIDs) == 0 { if len(artistIDs) == 0 {
return nil, nil return nil, nil
@@ -31,7 +33,8 @@ func (c *Client) GetArtists(ctx context.Context, artistIDs []string) ([]*models.
return artists, nil return artists, nil
} }
// GetArtist gets a single artist by ID // GetArtist gets a single artist by ID using GetArtists.
// Returns an error if no artist is found with the given ID.
func (c *Client) GetArtist(ctx context.Context, artistID string) (*models.Artist, error) { func (c *Client) GetArtist(ctx context.Context, artistID string) (*models.Artist, error) {
artists, err := c.GetArtists(ctx, []string{artistID}) artists, err := c.GetArtists(ctx, []string{artistID})
if err != nil { if err != nil {
@@ -43,47 +46,172 @@ func (c *Client) GetArtist(ctx context.Context, artistID string) (*models.Artist
return artists[0], nil return artists[0], nil
} }
// GetArtistAlbums gets albums by an artist // GetArtistAlbums gets albums by an artist (GET /artists/{artist_id}/direct-albums).
func (c *Client) GetArtistAlbums(ctx context.Context, artistID string, page, pageSize int) ([]*models.Album, error) { // The page and pageSize parameters control pagination.
func (c *Client) GetArtistAlbums(ctx context.Context, artistID string, page, pageSize int) (*models.ArtistAlbums, error) {
params := map[string]string{ params := map[string]string{
"artist-id": artistID,
"page": fmt.Sprintf("%d", page), "page": fmt.Sprintf("%d", page),
"page-size": fmt.Sprintf("%d", pageSize), "page-size": fmt.Sprintf("%d", pageSize),
} }
var result []models.Album var result models.ArtistAlbums
if err := c.request.Get(ctx, "/artists/albums", params, &result); err != nil { if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/direct-albums", artistID), params, &result); err != nil {
return nil, err return nil, err
} }
result.SetClient(c)
albums := make([]*models.Album, len(result)) return &result, nil
for i := range result {
result[i].SetClient(c)
albums[i] = &result[i]
}
return albums, nil
} }
// GetArtistTracks gets tracks by an artist // GetArtistTracks gets tracks by an artist (GET /artists/{artist_id}/tracks).
func (c *Client) GetArtistTracks(ctx context.Context, artistID string, page, pageSize int) ([]*models.Track, error) { // The page and pageSize parameters control pagination.
func (c *Client) GetArtistTracks(ctx context.Context, artistID string, page, pageSize int) (*models.ArtistTracks, error) {
params := map[string]string{ params := map[string]string{
"artist-id": artistID,
"page": fmt.Sprintf("%d", page), "page": fmt.Sprintf("%d", page),
"page-size": fmt.Sprintf("%d", pageSize), "page-size": fmt.Sprintf("%d", pageSize),
} }
var result []models.Track var result models.ArtistTracks
if err := c.request.Get(ctx, "/artists/tracks", params, &result); err != nil { if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/tracks", artistID), params, &result); err != nil {
return nil, err return nil, err
} }
result.SetClient(c)
tracks := make([]*models.Track, len(result)) return &result, nil
for i := range result {
result[i].SetClient(c)
tracks[i] = &result[i]
}
return tracks, nil
} }
// Need to add fmt import // GetArtistBriefInfo gets brief info about an artist (GET /artists/{artist_id}/brief-info).
// "fmt" func (c *Client) GetArtistBriefInfo(ctx context.Context, artistID string) (*models.ArtistBriefInfo, error) {
var result models.ArtistBriefInfo
if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/brief-info", artistID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetArtistSimilar gets similar artists (GET /artists/{artist_id}/similar).
func (c *Client) GetArtistSimilar(ctx context.Context, artistID string) (*models.ArtistSimilar, error) {
var result models.ArtistSimilar
if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/similar", artistID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetArtistLinks gets links to artist's pages (GET /artists/{artist_id}/artist-links).
func (c *Client) GetArtistLinks(ctx context.Context, artistID string) (*models.ArtistLinks, error) {
var result models.ArtistLinks
if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/artist-links", artistID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetArtistAlsoAlbums gets albums where artist participated (GET /artists/{artist_id}/also-albums).
func (c *Client) GetArtistAlsoAlbums(ctx context.Context, artistID string, page, pageSize int, sortBy string) (*models.ArtistAlbums, error) {
params := map[string]string{
"page": fmt.Sprintf("%d", page),
"page-size": fmt.Sprintf("%d", pageSize),
"sort-by": sortBy,
}
var result models.ArtistAlbums
if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/also-albums", artistID), params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetArtistDiscographyAlbums gets artist discography (GET /artists/{artist_id}/discography-albums).
func (c *Client) GetArtistDiscographyAlbums(ctx context.Context, artistID string, page, pageSize int, sortBy string) (*models.ArtistAlbums, error) {
params := map[string]string{
"page": fmt.Sprintf("%d", page),
"page-size": fmt.Sprintf("%d", pageSize),
"sort-by": sortBy,
}
var result models.ArtistAlbums
if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/discography-albums", artistID), params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetArtistSafeDirectAlbums gets safe direct albums (GET /artists/{artist_id}/safe-direct-albums).
func (c *Client) GetArtistSafeDirectAlbums(ctx context.Context, artistID string, sortBy, sortOrder string, limit int) (*models.ArtistAlbums, error) {
params := map[string]string{
"sort-by": sortBy,
"sort-order": sortOrder,
"limit": fmt.Sprintf("%d", limit),
}
var result models.ArtistAlbums
if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/safe-direct-albums", artistID), params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetArtistTrackIds gets track IDs by artist (GET /artists/{artist_id}/track-ids).
func (c *Client) GetArtistTrackIds(ctx context.Context, artistID string, page, pageSize int) ([]string, error) {
params := map[string]string{
"page": fmt.Sprintf("%d", page),
"page-size": fmt.Sprintf("%d", pageSize),
}
var result []string
if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/track-ids", artistID), params, &result); err != nil {
return nil, err
}
return result, nil
}
// GetArtistAbout gets "About artist" info (GET /artists/{artist_id}/about-artist).
func (c *Client) GetArtistAbout(ctx context.Context, artistID string) (*models.ArtistAbout, error) {
var result models.ArtistAbout
if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/about-artist", artistID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetArtistDonation gets artist donation info (GET /artists/{artist_id}/blocks/artist-donation).
func (c *Client) GetArtistDonation(ctx context.Context, artistID string) (*models.ArtistDonations, error) {
var result models.ArtistDonations
if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/blocks/artist-donation", artistID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetArtistInfo gets detailed artist info (GET /artists/{artist_id}/info).
func (c *Client) GetArtistInfo(ctx context.Context, artistID string) (*models.ArtistInfo, error) {
var result models.ArtistInfo
if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/info", artistID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetArtistSkeleton gets artist page skeleton (GET /artists/{artist_id}/skeletons/{skeleton_id}).
func (c *Client) GetArtistSkeleton(ctx context.Context, artistID, skeletonID string) (*models.ArtistSkeleton, error) {
var result models.ArtistSkeleton
if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/skeletons/%s", artistID, skeletonID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetArtistTrailer gets artist trailer (GET /artists/{artist_id}/trailer).
func (c *Client) GetArtistTrailer(ctx context.Context, artistID string) (*models.ArtistTrailerStatus, error) {
var result models.ArtistTrailerStatus
if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/trailer", artistID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
+10 -6
View File
@@ -13,7 +13,7 @@ import (
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils"
) )
// DeviceCode represents the device authorization response // DeviceCode represents the device authorization response from the Yandex OAuth device flow.
type DeviceCode struct { type DeviceCode struct {
DeviceCode string `json:"device_code"` DeviceCode string `json:"device_code"`
UserCode string `json:"user_code"` UserCode string `json:"user_code"`
@@ -22,7 +22,7 @@ type DeviceCode struct {
Interval int `json:"interval"` Interval int `json:"interval"`
} }
// OAuthToken represents an OAuth token response // OAuthToken represents an OAuth token response from the Yandex OAuth token endpoint.
type OAuthToken struct { type OAuthToken struct {
AccessToken string `json:"access_token"` AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"` RefreshToken string `json:"refresh_token"`
@@ -30,7 +30,9 @@ type OAuthToken struct {
ExpiresIn int `json:"expires_in"` ExpiresIn int `json:"expires_in"`
} }
// DeviceAuth performs OAuth Device Flow authentication // DeviceAuth performs OAuth Device Flow authentication.
// It requests a device code, invokes the onCode callback with it, then polls for the token.
// The onCode callback receives the DeviceCode which contains the user_code and verification URL.
func (r *Request) DeviceAuth(ctx context.Context, onCode func(*DeviceCode)) (*OAuthToken, error) { func (r *Request) DeviceAuth(ctx context.Context, onCode func(*DeviceCode)) (*OAuthToken, error) {
// Step 1: Request device code // Step 1: Request device code
deviceCode, err := r.requestDeviceCode(ctx) deviceCode, err := r.requestDeviceCode(ctx)
@@ -47,7 +49,7 @@ func (r *Request) DeviceAuth(ctx context.Context, onCode func(*DeviceCode)) (*OA
return r.pollForToken(ctx, deviceCode) return r.pollForToken(ctx, deviceCode)
} }
// requestDeviceCode requests a device code from Yandex OAuth // requestDeviceCode requests a device code from the Yandex OAuth device code endpoint.
func (r *Request) requestDeviceCode(ctx context.Context) (*DeviceCode, error) { func (r *Request) requestDeviceCode(ctx context.Context) (*DeviceCode, error) {
// Use Yandex OAuth endpoint for device auth // Use Yandex OAuth endpoint for device auth
data := url.Values{} data := url.Values{}
@@ -85,7 +87,8 @@ func (r *Request) requestDeviceCode(ctx context.Context) (*DeviceCode, error) {
return &deviceCode, nil return &deviceCode, nil
} }
// pollForToken polls for the OAuth token // pollForToken polls the Yandex OAuth token endpoint until the user authorizes the device code.
// It returns the OAuthToken once authorization is complete, or an error if the code expires or is denied.
func (r *Request) pollForToken(ctx context.Context, deviceCode *DeviceCode) (*OAuthToken, error) { func (r *Request) pollForToken(ctx context.Context, deviceCode *DeviceCode) (*OAuthToken, error) {
interval := deviceCode.Interval interval := deviceCode.Interval
if interval == 0 { if interval == 0 {
@@ -155,7 +158,8 @@ func (r *Request) pollForToken(ctx context.Context, deviceCode *DeviceCode) (*OA
} }
} }
// RefreshToken refreshes an access token using a refresh token // RefreshToken refreshes an access token using the provided refresh token.
// It makes a POST request to the Yandex OAuth token endpoint with grant_type=refresh_token.
func (r *Request) RefreshToken(ctx context.Context, refreshToken string) (*OAuthToken, error) { func (r *Request) RefreshToken(ctx context.Context, refreshToken string) (*OAuthToken, error) {
data := url.Values{} data := url.Values{}
data.Set("grant_type", "refresh_token") data.Set("grant_type", "refresh_token")
+28 -20
View File
@@ -1,3 +1,4 @@
// Package client provides the core Yandex Music API client and related types.
package client package client
import ( import (
@@ -9,7 +10,7 @@ import (
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils"
) )
// ClientConfig holds configuration for the Client // ClientConfig holds configuration for the Client.
type ClientConfig struct { type ClientConfig struct {
Token string Token string
BaseURL string BaseURL string
@@ -22,7 +23,9 @@ type ClientConfig struct {
JSONBackend utils.JSONBackend JSONBackend utils.JSONBackend
} }
// Client is the main Yandex Music API client // Client is the main Yandex Music API client.
// It provides methods for interacting with the Yandex Music API,
// including tracks, albums, artists, playlists, radio, and more.
type Client struct { type Client struct {
config ClientConfig config ClientConfig
request *Request request *Request
@@ -31,7 +34,8 @@ type Client struct {
accountUID *int64 accountUID *int64
} }
// NewClient creates a new Yandex Music client // NewClient creates a new Yandex Music client with the given configuration.
// If BaseURL, Language, or Timeout are not set in the config, sensible defaults are used.
func NewClient(config ClientConfig) *Client { func NewClient(config ClientConfig) *Client {
if config.BaseURL == "" { if config.BaseURL == "" {
config.BaseURL = DefaultBaseURL config.BaseURL = DefaultBaseURL
@@ -66,7 +70,8 @@ func NewClient(config ClientConfig) *Client {
} }
} }
// Init initializes the client by fetching account info // Init initializes the client by fetching account info from the /account/status endpoint.
// It populates the client's internal account state (GetMe and GetAccountUID).
func (c *Client) Init(ctx context.Context) error { func (c *Client) Init(ctx context.Context) error {
status, err := c.GetAccountStatus(ctx) status, err := c.GetAccountStatus(ctx)
if err != nil { if err != nil {
@@ -79,76 +84,79 @@ func (c *Client) Init(ctx context.Context) error {
return nil return nil
} }
// GetRequest returns the underlying request handler // GetRequest returns the underlying request handler used by this client.
// The returned RequestInterface can be used to make raw API requests.
func (c *Client) GetRequest() models.RequestInterface { func (c *Client) GetRequest() models.RequestInterface {
return c.request return c.request
} }
// GetToken returns the current token // GetToken returns the current OAuth token used for authentication.
func (c *Client) GetToken() string { func (c *Client) GetToken() string {
return c.config.Token return c.config.Token
} }
// SetToken updates the token // SetToken updates the OAuth token used for authentication.
// It also updates the underlying request handler's authorization header.
func (c *Client) SetToken(token string) { func (c *Client) SetToken(token string) {
c.config.Token = token c.config.Token = token
c.request.SetToken(token) c.request.SetToken(token)
} }
// GetDevice returns the device string // GetDevice returns the device identifier string used in API requests.
func (c *Client) GetDevice() string { func (c *Client) GetDevice() string {
return c.device return c.device
} }
// GetMe returns the account status // GetMe returns the account status fetched during Init, or nil if Init has not been called.
func (c *Client) GetMe() *Status { func (c *Client) GetMe() *Status {
return c.me return c.me
} }
// GetAccountUID returns the account UID // GetAccountUID returns the authenticated user's account UID, or nil if not available.
func (c *Client) GetAccountUID() *int64 { func (c *Client) GetAccountUID() *int64 {
return c.accountUID return c.accountUID
} }
// Context returns a context for requests (can be overridden for timeouts) // Context returns a context for requests (can be overridden for timeouts).
// By default it returns context.Background().
func (c *Client) Context() context.Context { func (c *Client) Context() context.Context {
return context.Background() return context.Background()
} }
// WithContext returns a new client with a custom context // WithContext returns a new client with a custom context.
// In Go, context is typically passed to each method rather than stored, so this returns the same client.
func (c *Client) WithContext(ctx context.Context) *Client { func (c *Client) WithContext(ctx context.Context) *Client {
// In Go, we typically pass context to each method rather than storing it // In Go, we typically pass context to each method rather than storing it
return c return c
} }
// DeviceAuth performs OAuth Device Flow authentication // DeviceAuth performs OAuth Device Flow authentication.
// The onCode callback is invoked with the device code so the user can authorize on a separate device.
func (c *Client) DeviceAuth(ctx context.Context, onCode func(*DeviceCode)) (*OAuthToken, error) { func (c *Client) DeviceAuth(ctx context.Context, onCode func(*DeviceCode)) (*OAuthToken, error) {
return c.request.DeviceAuth(ctx, onCode) return c.request.DeviceAuth(ctx, onCode)
} }
// RefreshToken refreshes the access token // RefreshToken refreshes the access token using the provided refresh token.
// It delegates to the underlying request handler.
func (c *Client) RefreshToken(ctx context.Context, refreshToken string) (*OAuthToken, error) { func (c *Client) RefreshToken(ctx context.Context, refreshToken string) (*OAuthToken, error) {
return c.request.RefreshToken(ctx, refreshToken) return c.request.RefreshToken(ctx, refreshToken)
} }
// Status represents account status // Status represents account status returned by the /account/status endpoint.
type Status struct { type Status struct {
Account *Account `json:"account"` Account *Account `json:"account"`
// Other fields...
} }
// Account represents a user account // Account represents a user account.
type Account struct { type Account struct {
UID int64 `json:"uid"` UID int64 `json:"uid"`
Login string `json:"login"` Login string `json:"login"`
DisplayName string `json:"display_name"` DisplayName string `json:"display_name"`
// Other fields...
} }
// Track represents a music track (minimal for now) // Track represents a music track (minimal subset of fields).
type Track struct { type Track struct {
ID string `json:"id"` ID string `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Duration int `json:"duration_ms"` Duration int `json:"duration_ms"`
// Other fields...
} }
+17 -10
View File
@@ -1,3 +1,4 @@
// Package client provides methods for interacting with Yandex Music API endpoints.
package client package client
import ( import (
@@ -7,7 +8,7 @@ import (
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models"
) )
// GetClips gets clips // GetClips gets clips with pagination (GET /clips).
func (c *Client) GetClips(ctx context.Context, page, pageSize int) ([]*models.Clip, error) { func (c *Client) GetClips(ctx context.Context, page, pageSize int) ([]*models.Clip, error) {
params := map[string]string{ params := map[string]string{
"page": "1", "page": "1",
@@ -33,29 +34,35 @@ func (c *Client) GetClips(ctx context.Context, page, pageSize int) ([]*models.Cl
return clips, nil return clips, nil
} }
// GetClipsWillLike gets clips the user might like // GetClipsWillLike gets clips the user might like (GET /clips/will/like).
func (c *Client) GetClipsWillLike(ctx context.Context) (*models.ClipsWillLike, error) { func (c *Client) GetClipsWillLike(ctx context.Context) (*models.ClipsWillLike, error) {
var result models.ClipsWillLike var result models.ClipsWillLike
if err := c.request.Get(ctx, "/clips/will-like", nil, &result); err != nil { if err := c.request.Get(ctx, "/clips/will/like", nil, &result); err != nil {
return nil, err return nil, err
} }
return &result, nil return &result, nil
} }
// GetArtistClips gets clips by an artist // GetArtistClips gets clips by an artist (GET /artists/{artistID}/blocks/artist-clips).
func (c *Client) GetArtistClips(ctx context.Context, artistID string) (*models.ArtistClipData, error) { func (c *Client) GetArtistClips(ctx context.Context, artistID string) (*models.ArtistClipData, error) {
params := map[string]string{
"artist_id": artistID,
}
var result models.ArtistClipData var result models.ArtistClipData
if err := c.request.Get(ctx, "/artists/clips", params, &result); err != nil { if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/blocks/artist-clips", artistID), nil, &result); err != nil {
return nil, err return nil, err
} }
return &result, nil return &result, nil
} }
// GetShots gets shots // GetClipsDisclaimer gets clips disclaimer (GET /clips/{id}/disclaimer).
func (c *Client) GetClipsDisclaimer(ctx context.Context, clipID string) (*models.Disclaimer, error) {
var result models.Disclaimer
if err := c.request.Get(ctx, fmt.Sprintf("/clips/%s/disclaimer", clipID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetShots gets shots with pagination (GET /shots).
func (c *Client) GetShots(ctx context.Context, page, pageSize int) ([]*models.Shot, error) { func (c *Client) GetShots(ctx context.Context, page, pageSize int) ([]*models.Shot, error) {
params := map[string]string{ params := map[string]string{
"page": "1", "page": "1",
+31 -24
View File
@@ -1,3 +1,4 @@
// Package client provides methods for interacting with Yandex Music API endpoints.
package client package client
import ( import (
@@ -7,17 +8,17 @@ import (
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models"
) )
// GetConcerts gets concerts // GetConcerts gets concerts with pagination (GET /concerts).
func (c *Client) GetConcerts(ctx context.Context, page, pageSize int) (*models.ConcertFeed, error) { func (c *Client) GetConcerts(ctx context.Context, page, pageSize int) (*models.ConcertFeed, error) {
params := map[string]string{ params := map[string]string{
"page": "1", "page": "1",
"page_size": "20", "page_size": "20",
} }
if page > 0 { if page > 0 {
params["page"] = fmt.Sprintf("%d", page) params["page"] = fmt.Sprint(page)
} }
if pageSize > 0 { if pageSize > 0 {
params["page_size"] = fmt.Sprintf("%d", pageSize) params["page_size"] = fmt.Sprint(pageSize)
} }
var result models.ConcertFeed var result models.ConcertFeed
@@ -27,7 +28,7 @@ func (c *Client) GetConcerts(ctx context.Context, page, pageSize int) (*models.C
return &result, nil return &result, nil
} }
// GetConcertFeed gets concert feed // GetConcertFeed gets the concert feed (GET /concerts/feed).
func (c *Client) GetConcertFeed(ctx context.Context) (*models.ConcertFeed, error) { func (c *Client) GetConcertFeed(ctx context.Context) (*models.ConcertFeed, error) {
var result models.ConcertFeed var result models.ConcertFeed
if err := c.request.Get(ctx, "/concerts/feed", nil, &result); err != nil { if err := c.request.Get(ctx, "/concerts/feed", nil, &result); err != nil {
@@ -36,20 +37,16 @@ func (c *Client) GetConcertFeed(ctx context.Context) (*models.ConcertFeed, error
return &result, nil return &result, nil
} }
// GetConcertInfo gets concert info // GetConcertInfo gets concert info by ID (GET /concerts/{concertID}/info).
func (c *Client) GetConcertInfo(ctx context.Context, concertID string) (*models.ConcertInfo, error) { func (c *Client) GetConcertInfo(ctx context.Context, concertID string) (*models.ConcertInfo, error) {
params := map[string]string{
"concert_id": concertID,
}
var result models.ConcertInfo var result models.ConcertInfo
if err := c.request.Get(ctx, "/concerts/info", params, &result); err != nil { if err := c.request.Get(ctx, fmt.Sprintf("/concerts/%s/info", concertID), nil, &result); err != nil {
return nil, err return nil, err
} }
return &result, nil return &result, nil
} }
// GetConcertLocations gets concert locations // GetConcertLocations gets concert locations (GET /concerts/locations).
func (c *Client) GetConcertLocations(ctx context.Context) (*models.ConcertLocations, error) { func (c *Client) GetConcertLocations(ctx context.Context) (*models.ConcertLocations, error) {
var result models.ConcertLocations var result models.ConcertLocations
if err := c.request.Get(ctx, "/concerts/locations", nil, &result); err != nil { if err := c.request.Get(ctx, "/concerts/locations", nil, &result); err != nil {
@@ -58,21 +55,31 @@ func (c *Client) GetConcertLocations(ctx context.Context) (*models.ConcertLocati
return &result, nil return &result, nil
} }
// GetConcertTabs gets concert tabs // GetConcertTabs gets concert tab configuration (GET /concerts/tab-config).
func (c *Client) GetConcertTabs(ctx context.Context, concertID string) ([]*models.ConcertTabConfig, error) { func (c *Client) GetConcertTabs(ctx context.Context) (*models.ConcertTabConfig, error) {
params := map[string]string{ var result models.ConcertTabConfig
"concert_id": concertID, if err := c.request.Get(ctx, "/concerts/tab-config", nil, &result); err != nil {
}
var result []models.ConcertTabConfig
if err := c.request.Get(ctx, "/concerts/tabs", params, &result); err != nil {
return nil, err return nil, err
} }
return &result, nil
}
tabs := make([]*models.ConcertTabConfig, len(result)) // GetArtistConcerts gets concerts by artist (GET /artists/{id}/concerts).
for i := range result { func (c *Client) GetArtistConcerts(ctx context.Context, artistID string) (*models.ArtistConcerts, error) {
result[i].SetClient(c) var result models.ArtistConcerts
tabs[i] = &result[i] if err := c.request.Get(ctx, fmt.Sprintf("/artists/%s/concerts", artistID), nil, &result); err != nil {
return nil, err
} }
return tabs, nil result.SetClient(c)
return &result, nil
}
// GetConcertSkeleton gets concert skeleton (GET /concerts/{id}/skeletons/{skeleton_id}).
func (c *Client) GetConcertSkeleton(ctx context.Context, concertID, skeletonID string) (*models.ConcertSkeleton, error) {
var result models.ConcertSkeleton
if err := c.request.Get(ctx, fmt.Sprintf("/concerts/%s/skeletons/%s", concertID, skeletonID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
} }
+13 -11
View File
@@ -1,12 +1,14 @@
// Package client provides methods for interacting with Yandex Music API endpoints.
package client package client
import ( import (
"context" "context"
"fmt"
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models"
) )
// GetFeed gets the main feed // GetFeed gets the main feed (GET /feed).
func (c *Client) GetFeed(ctx context.Context) (*models.Feed, error) { func (c *Client) GetFeed(ctx context.Context) (*models.Feed, error) {
var result models.Feed var result models.Feed
if err := c.request.Get(ctx, "/feed", nil, &result); err != nil { if err := c.request.Get(ctx, "/feed", nil, &result); err != nil {
@@ -15,7 +17,7 @@ func (c *Client) GetFeed(ctx context.Context) (*models.Feed, error) {
return &result, nil return &result, nil
} }
// GetFeedBlocks gets feed blocks // GetFeedBlocks gets feed blocks (GET /feed/blocks).
func (c *Client) GetFeedBlocks(ctx context.Context) ([]*models.DayBlock, error) { func (c *Client) GetFeedBlocks(ctx context.Context) ([]*models.DayBlock, error) {
var result []models.DayBlock var result []models.DayBlock
if err := c.request.Get(ctx, "/feed/blocks", nil, &result); err != nil { if err := c.request.Get(ctx, "/feed/blocks", nil, &result); err != nil {
@@ -30,7 +32,7 @@ func (c *Client) GetFeedBlocks(ctx context.Context) ([]*models.DayBlock, error)
return blocks, nil return blocks, nil
} }
// GetFeedGeneratedPlaylist gets a generated playlist by ID from feed // GetFeedGeneratedPlaylist gets a generated playlist by ID from the feed (GET /feed/generated-playlist).
func (c *Client) GetFeedGeneratedPlaylist(ctx context.Context, playlistID string) (*models.GeneratedPlaylist, error) { func (c *Client) GetFeedGeneratedPlaylist(ctx context.Context, playlistID string) (*models.GeneratedPlaylist, error) {
params := map[string]string{ params := map[string]string{
"playlist_id": playlistID, "playlist_id": playlistID,
@@ -44,7 +46,7 @@ func (c *Client) GetFeedGeneratedPlaylist(ctx context.Context, playlistID string
return &result, nil return &result, nil
} }
// GetFeedDay gets a specific day from the feed // GetFeedDay gets a specific day from the feed by date (GET /feed/day).
func (c *Client) GetFeedDay(ctx context.Context, date string) (*models.Day, error) { func (c *Client) GetFeedDay(ctx context.Context, date string) (*models.Day, error) {
params := map[string]string{ params := map[string]string{
"date": date, "date": date,
@@ -57,7 +59,7 @@ func (c *Client) GetFeedDay(ctx context.Context, date string) (*models.Day, erro
return &result, nil return &result, nil
} }
// GetGeneratedPlaylists gets generated playlists // GetGeneratedPlaylists gets generated playlists (GET /generated-playlists).
func (c *Client) GetGeneratedPlaylists(ctx context.Context) ([]*models.GeneratedPlaylist, error) { func (c *Client) GetGeneratedPlaylists(ctx context.Context) ([]*models.GeneratedPlaylist, error) {
var result []models.GeneratedPlaylist var result []models.GeneratedPlaylist
if err := c.request.Get(ctx, "/generated-playlists", nil, &result); err != nil { if err := c.request.Get(ctx, "/generated-playlists", nil, &result); err != nil {
@@ -72,36 +74,36 @@ func (c *Client) GetGeneratedPlaylists(ctx context.Context) ([]*models.Generated
return playlists, nil return playlists, nil
} }
// GetLanding gets a landing page // GetLanding gets a landing page by ID (GET /landing3/{landingID}).
func (c *Client) GetLanding(ctx context.Context, landingID string) (*models.Landing, error) { func (c *Client) GetLanding(ctx context.Context, landingID string) (*models.Landing, error) {
params := map[string]string{ params := map[string]string{
"landing_id": landingID, "landing_id": landingID,
} }
var result models.Landing var result models.Landing
if err := c.request.Get(ctx, "/landing", params, &result); err != nil { if err := c.request.Get(ctx, fmt.Sprintf("/landing3/%s", landingID), params, &result); err != nil {
return nil, err return nil, err
} }
return &result, nil return &result, nil
} }
// GetLandingList gets a list of landings // GetLandingList gets a list of landings (GET /landing3/list).
func (c *Client) GetLandingList(ctx context.Context) (*models.LandingList, error) { func (c *Client) GetLandingList(ctx context.Context) (*models.LandingList, error) {
var result models.LandingList var result models.LandingList
if err := c.request.Get(ctx, "/landing/list", nil, &result); err != nil { if err := c.request.Get(ctx, "/landing3/list", nil, &result); err != nil {
return nil, err return nil, err
} }
return &result, nil return &result, nil
} }
// GetLandingBlocks gets landing blocks by block IDs // GetLandingBlocks gets landing blocks by block IDs (GET /landing3/blocks).
func (c *Client) GetLandingBlocks(ctx context.Context, blockIDs []string) ([]*models.LandingBlock, error) { func (c *Client) GetLandingBlocks(ctx context.Context, blockIDs []string) ([]*models.LandingBlock, error) {
params := map[string]string{ params := map[string]string{
"blocks": joinStrings(blockIDs, ","), "blocks": joinStrings(blockIDs, ","),
} }
var result []models.LandingBlock var result []models.LandingBlock
if err := c.request.Get(ctx, "/landing/blocks", params, &result); err != nil { if err := c.request.Get(ctx, "/landing3/blocks", params, &result); err != nil {
return nil, err return nil, err
} }
+4 -3
View File
@@ -1,3 +1,4 @@
// Package client provides methods for interacting with Yandex Music API endpoints.
package client package client
import ( import (
@@ -7,7 +8,7 @@ import (
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models"
) )
// GetMusicHistory gets music history // GetMusicHistory gets the user's music history (GET /music-history).
func (c *Client) GetMusicHistory(ctx context.Context) (*models.MusicHistory, error) { func (c *Client) GetMusicHistory(ctx context.Context) (*models.MusicHistory, error) {
var result models.MusicHistory var result models.MusicHistory
if err := c.request.Get(ctx, "/music-history", nil, &result); err != nil { if err := c.request.Get(ctx, "/music-history", nil, &result); err != nil {
@@ -16,7 +17,7 @@ func (c *Client) GetMusicHistory(ctx context.Context) (*models.MusicHistory, err
return &result, nil return &result, nil
} }
// GetMusicHistoryTabs gets music history tabs // GetMusicHistoryTabs gets music history tabs (GET /music-history/tabs).
func (c *Client) GetMusicHistoryTabs(ctx context.Context) ([]*models.MusicHistoryTab, error) { func (c *Client) GetMusicHistoryTabs(ctx context.Context) ([]*models.MusicHistoryTab, error) {
var result []models.MusicHistoryTab var result []models.MusicHistoryTab
if err := c.request.Get(ctx, "/music-history/tabs", nil, &result); err != nil { if err := c.request.Get(ctx, "/music-history/tabs", nil, &result); err != nil {
@@ -31,7 +32,7 @@ func (c *Client) GetMusicHistoryTabs(ctx context.Context) ([]*models.MusicHistor
return tabs, nil return tabs, nil
} }
// GetMusicHistoryItems gets music history items // GetMusicHistoryItems gets music history items for a tab (GET /music-history/items).
func (c *Client) GetMusicHistoryItems(ctx context.Context, tabID string, page, pageSize int) (*models.MusicHistoryItems, error) { func (c *Client) GetMusicHistoryItems(ctx context.Context, tabID string, page, pageSize int) (*models.MusicHistoryItems, error) {
params := map[string]string{} params := map[string]string{}
if tabID != "" { if tabID != "" {
+10 -17
View File
@@ -1,3 +1,4 @@
// Package client provides methods for interacting with Yandex Music API endpoints.
package client package client
import ( import (
@@ -7,7 +8,7 @@ import (
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models"
) )
// GetLabels gets all labels // GetLabels gets all labels (GET /labels).
func (c *Client) GetLabels(ctx context.Context) ([]*models.Label, error) { func (c *Client) GetLabels(ctx context.Context) ([]*models.Label, error) {
var result []models.Label var result []models.Label
if err := c.request.Get(ctx, "/labels", nil, &result); err != nil { if err := c.request.Get(ctx, "/labels", nil, &result); err != nil {
@@ -22,25 +23,19 @@ func (c *Client) GetLabels(ctx context.Context) ([]*models.Label, error) {
return labels, nil return labels, nil
} }
// GetLabel gets a label by ID // GetLabel gets a label by ID (GET /labels/{labelID}).
func (c *Client) GetLabel(ctx context.Context, labelID string) (*models.Label, error) { func (c *Client) GetLabel(ctx context.Context, labelID string) (*models.Label, error) {
params := map[string]string{
"label_id": labelID,
}
var result models.Label var result models.Label
if err := c.request.Get(ctx, "/labels", params, &result); err != nil { if err := c.request.Get(ctx, fmt.Sprintf("/labels/%s", labelID), nil, &result); err != nil {
return nil, err return nil, err
} }
result.SetClient(c) result.SetClient(c)
return &result, nil return &result, nil
} }
// GetLabelAlbums gets albums for a label // GetLabelAlbums gets albums for a label (GET /labels/{labelID}/albums).
func (c *Client) GetLabelAlbums(ctx context.Context, labelID string, page, pageSize int) (*models.LabelAlbums, error) { func (c *Client) GetLabelAlbums(ctx context.Context, labelID string, page, pageSize int) (*models.LabelAlbums, error) {
params := map[string]string{ params := map[string]string{}
"label_id": labelID,
}
if page > 0 { if page > 0 {
params["page"] = fmt.Sprintf("%d", page) params["page"] = fmt.Sprintf("%d", page)
} }
@@ -49,17 +44,15 @@ func (c *Client) GetLabelAlbums(ctx context.Context, labelID string, page, pageS
} }
var result models.LabelAlbums var result models.LabelAlbums
if err := c.request.Get(ctx, "/labels/albums", params, &result); err != nil { if err := c.request.Get(ctx, fmt.Sprintf("/labels/%s/albums", labelID), params, &result); err != nil {
return nil, err return nil, err
} }
return &result, nil return &result, nil
} }
// GetLabelArtists gets artists for a label // GetLabelArtists gets artists for a label (GET /labels/{labelID}/artists).
func (c *Client) GetLabelArtists(ctx context.Context, labelID string, page, pageSize int) (*models.LabelArtists, error) { func (c *Client) GetLabelArtists(ctx context.Context, labelID string, page, pageSize int) (*models.LabelArtists, error) {
params := map[string]string{ params := map[string]string{}
"label_id": labelID,
}
if page > 0 { if page > 0 {
params["page"] = fmt.Sprintf("%d", page) params["page"] = fmt.Sprintf("%d", page)
} }
@@ -68,7 +61,7 @@ func (c *Client) GetLabelArtists(ctx context.Context, labelID string, page, page
} }
var result models.LabelArtists var result models.LabelArtists
if err := c.request.Get(ctx, "/labels/artists", params, &result); err != nil { if err := c.request.Get(ctx, fmt.Sprintf("/labels/%s/artists", labelID), params, &result); err != nil {
return nil, err return nil, err
} }
return &result, nil return &result, nil
+33 -32
View File
@@ -1,3 +1,4 @@
// Package client provides methods for interacting with Yandex Music API endpoints.
package client package client
import ( import (
@@ -7,82 +8,82 @@ import (
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models"
) )
// LikeTrack likes a track // LikeTrack likes a track (POST /users/me/likes/tracks/add-multiple).
func (c *Client) LikeTrack(ctx context.Context, trackID string) error { func (c *Client) LikeTrack(ctx context.Context, trackID string) error {
params := map[string]string{ params := map[string]string{
"track_id": trackID, "track-ids": trackID,
} }
var result map[string]any var result map[string]any
return c.request.Post(ctx, "/users/likes/tracks/add", params, &result) return c.request.Post(ctx, "/users/me/likes/tracks/add-multiple", params, &result)
} }
// UnlikeTrack unlikes a track // UnlikeTrack unlikes a track (POST /users/me/likes/tracks/remove).
func (c *Client) UnlikeTrack(ctx context.Context, trackID string) error { func (c *Client) UnlikeTrack(ctx context.Context, trackID string) error {
params := map[string]string{ params := map[string]string{
"track_id": trackID, "track-ids": trackID,
} }
var result map[string]any var result map[string]any
return c.request.Post(ctx, "/users/likes/tracks/remove", params, &result) return c.request.Post(ctx, "/users/me/likes/tracks/remove", params, &result)
} }
// LikeAlbum likes an album // LikeAlbum likes an album (POST /users/me/likes/albums/add-multiple).
func (c *Client) LikeAlbum(ctx context.Context, albumID string) error { func (c *Client) LikeAlbum(ctx context.Context, albumID string) error {
params := map[string]string{ params := map[string]string{
"album_id": albumID, "album-ids": albumID,
} }
var result map[string]any var result map[string]any
return c.request.Post(ctx, "/users/likes/albums/add", params, &result) return c.request.Post(ctx, "/users/me/likes/albums/add-multiple", params, &result)
} }
// UnlikeAlbum unlikes an album // UnlikeAlbum unlikes an album (POST /users/me/likes/albums/remove).
func (c *Client) UnlikeAlbum(ctx context.Context, albumID string) error { func (c *Client) UnlikeAlbum(ctx context.Context, albumID string) error {
params := map[string]string{ params := map[string]string{
"album_id": albumID, "album-ids": albumID,
} }
var result map[string]any var result map[string]any
return c.request.Post(ctx, "/users/likes/albums/remove", params, &result) return c.request.Post(ctx, "/users/me/likes/albums/remove", params, &result)
} }
// LikeArtist likes an artist // LikeArtist likes an artist (POST /users/me/likes/artists/add-multiple).
func (c *Client) LikeArtist(ctx context.Context, artistID string) error { func (c *Client) LikeArtist(ctx context.Context, artistID string) error {
params := map[string]string{ params := map[string]string{
"artist_id": artistID, "artist-ids": artistID,
} }
var result map[string]any var result map[string]any
return c.request.Post(ctx, "/users/likes/artists/add", params, &result) return c.request.Post(ctx, "/users/me/likes/artists/add-multiple", params, &result)
} }
// UnlikeArtist unlikes an artist // UnlikeArtist unlikes an artist (POST /users/me/likes/artists/remove).
func (c *Client) UnlikeArtist(ctx context.Context, artistID string) error { func (c *Client) UnlikeArtist(ctx context.Context, artistID string) error {
params := map[string]string{ params := map[string]string{
"artist_id": artistID, "artist-ids": artistID,
} }
var result map[string]any var result map[string]any
return c.request.Post(ctx, "/users/likes/artists/remove", params, &result) return c.request.Post(ctx, "/users/me/likes/artists/remove", params, &result)
} }
// LikePlaylist likes a playlist // LikePlaylist likes a playlist (POST /users/me/likes/playlists/add-multiple).
func (c *Client) LikePlaylist(ctx context.Context, kind int) error { func (c *Client) LikePlaylist(ctx context.Context, kind int) error {
params := map[string]string{ params := map[string]string{
"kind": fmt.Sprintf("%d", kind), "playlist-ids": fmt.Sprintf("%d", kind),
} }
var result map[string]any var result map[string]any
return c.request.Post(ctx, "/users/likes/playlists/add", params, &result) return c.request.Post(ctx, "/users/me/likes/playlists/add-multiple", params, &result)
} }
// UnlikePlaylist unlikes a playlist // UnlikePlaylist unlikes a playlist (POST /users/me/likes/playlists/remove).
func (c *Client) UnlikePlaylist(ctx context.Context, kind int) error { func (c *Client) UnlikePlaylist(ctx context.Context, kind int) error {
params := map[string]string{ params := map[string]string{
"kind": fmt.Sprintf("%d", kind), "playlist-ids": fmt.Sprintf("%d", kind),
} }
var result map[string]any var result map[string]any
return c.request.Post(ctx, "/users/likes/playlists/remove", params, &result) return c.request.Post(ctx, "/users/me/likes/playlists/remove", params, &result)
} }
// GetLikedTracks gets user's liked tracks // GetLikedTracks gets the user's liked tracks (GET /users/me/likes/tracks).
func (c *Client) GetLikedTracks(ctx context.Context) ([]*models.Track, error) { func (c *Client) GetLikedTracks(ctx context.Context) ([]*models.Track, error) {
var result []models.Track var result []models.Track
if err := c.request.Get(ctx, "/users/likes/tracks", nil, &result); err != nil { if err := c.request.Get(ctx, "/users/me/likes/tracks", nil, &result); err != nil {
return nil, err return nil, err
} }
@@ -94,10 +95,10 @@ func (c *Client) GetLikedTracks(ctx context.Context) ([]*models.Track, error) {
return tracks, nil return tracks, nil
} }
// GetLikedAlbums gets user's liked albums // GetLikedAlbums gets the user's liked albums (GET /users/me/likes/albums).
func (c *Client) GetLikedAlbums(ctx context.Context) ([]*models.Album, error) { func (c *Client) GetLikedAlbums(ctx context.Context) ([]*models.Album, error) {
var result []models.Album var result []models.Album
if err := c.request.Get(ctx, "/users/likes/albums", nil, &result); err != nil { if err := c.request.Get(ctx, "/users/me/likes/albums", nil, &result); err != nil {
return nil, err return nil, err
} }
@@ -109,10 +110,10 @@ func (c *Client) GetLikedAlbums(ctx context.Context) ([]*models.Album, error) {
return albums, nil return albums, nil
} }
// GetLikedArtists gets user's liked artists // GetLikedArtists gets the user's liked artists (GET /users/me/likes/artists).
func (c *Client) GetLikedArtists(ctx context.Context) ([]*models.Artist, error) { func (c *Client) GetLikedArtists(ctx context.Context) ([]*models.Artist, error) {
var result []models.Artist var result []models.Artist
if err := c.request.Get(ctx, "/users/likes/artists", nil, &result); err != nil { if err := c.request.Get(ctx, "/users/me/likes/artists", nil, &result); err != nil {
return nil, err return nil, err
} }
@@ -124,10 +125,10 @@ func (c *Client) GetLikedArtists(ctx context.Context) ([]*models.Artist, error)
return artists, nil return artists, nil
} }
// GetLikedPlaylists gets user's liked playlists // GetLikedPlaylists gets the user's liked playlists (GET /users/me/likes/playlists).
func (c *Client) GetLikedPlaylists(ctx context.Context) ([]*models.Playlist, error) { func (c *Client) GetLikedPlaylists(ctx context.Context) ([]*models.Playlist, error) {
var result []models.Playlist var result []models.Playlist
if err := c.request.Get(ctx, "/users/likes/playlists", nil, &result); err != nil { if err := c.request.Get(ctx, "/users/me/likes/playlists", nil, &result); err != nil {
return nil, err return nil, err
} }
+11 -10
View File
@@ -1,3 +1,4 @@
// Package client provides methods for interacting with Yandex Music API endpoints.
package client package client
import ( import (
@@ -7,7 +8,7 @@ import (
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models"
) )
// GetMetatags gets all metatags // GetMetatags gets all metatags (GET /metatags).
func (c *Client) GetMetatags(ctx context.Context) ([]*models.Metatag, error) { func (c *Client) GetMetatags(ctx context.Context) ([]*models.Metatag, error) {
var result []models.Metatag var result []models.Metatag
if err := c.request.Get(ctx, "/metatags", nil, &result); err != nil { if err := c.request.Get(ctx, "/metatags", nil, &result); err != nil {
@@ -22,7 +23,7 @@ func (c *Client) GetMetatags(ctx context.Context) ([]*models.Metatag, error) {
return metatags, nil return metatags, nil
} }
// GetMetatagTree gets metatag tree // GetMetatagTree gets the metatag tree (GET /metatags/tree).
func (c *Client) GetMetatagTree(ctx context.Context) (*models.MetatagTree, error) { func (c *Client) GetMetatagTree(ctx context.Context) (*models.MetatagTree, error) {
var result models.MetatagTree var result models.MetatagTree
if err := c.request.Get(ctx, "/metatags/tree", nil, &result); err != nil { if err := c.request.Get(ctx, "/metatags/tree", nil, &result); err != nil {
@@ -31,7 +32,7 @@ func (c *Client) GetMetatagTree(ctx context.Context) (*models.MetatagTree, error
return &result, nil return &result, nil
} }
// GetMetatagArtists gets artists for a metatag // GetMetatagArtists gets artists for a metatag (GET /metatags/artists).
func (c *Client) GetMetatagArtists(ctx context.Context, metatagID string, page, pageSize int) (*models.MetatagArtists, error) { func (c *Client) GetMetatagArtists(ctx context.Context, metatagID string, page, pageSize int) (*models.MetatagArtists, error) {
params := map[string]string{ params := map[string]string{
"metatag_id": metatagID, "metatag_id": metatagID,
@@ -50,7 +51,7 @@ func (c *Client) GetMetatagArtists(ctx context.Context, metatagID string, page,
return &result, nil return &result, nil
} }
// GetMetatagAlbums gets albums for a metatag // GetMetatagAlbums gets albums for a metatag (GET /metatags/albums).
func (c *Client) GetMetatagAlbums(ctx context.Context, metatagID string, page, pageSize int) (*models.MetatagAlbums, error) { func (c *Client) GetMetatagAlbums(ctx context.Context, metatagID string, page, pageSize int) (*models.MetatagAlbums, error) {
params := map[string]string{ params := map[string]string{
"metatag_id": metatagID, "metatag_id": metatagID,
@@ -69,7 +70,7 @@ func (c *Client) GetMetatagAlbums(ctx context.Context, metatagID string, page, p
return &result, nil return &result, nil
} }
// GetMetatagPlaylists gets playlists for a metatag // GetMetatagPlaylists gets playlists for a metatag (GET /metatags/playlists).
func (c *Client) GetMetatagPlaylists(ctx context.Context, metatagID string, page, pageSize int) (*models.MetatagPlaylists, error) { func (c *Client) GetMetatagPlaylists(ctx context.Context, metatagID string, page, pageSize int) (*models.MetatagPlaylists, error) {
params := map[string]string{ params := map[string]string{
"metatag_id": metatagID, "metatag_id": metatagID,
@@ -88,7 +89,7 @@ func (c *Client) GetMetatagPlaylists(ctx context.Context, metatagID string, page
return &result, nil return &result, nil
} }
// GetGenres gets all genres // GetGenres gets all genres (GET /genres).
func (c *Client) GetGenres(ctx context.Context) ([]*models.Genre, error) { func (c *Client) GetGenres(ctx context.Context) ([]*models.Genre, error) {
var result []models.Genre var result []models.Genre
if err := c.request.Get(ctx, "/genres", nil, &result); err != nil { if err := c.request.Get(ctx, "/genres", nil, &result); err != nil {
@@ -103,7 +104,7 @@ func (c *Client) GetGenres(ctx context.Context) ([]*models.Genre, error) {
return genres, nil return genres, nil
} }
// GetCharts gets charts // GetCharts gets all charts (GET /charts).
func (c *Client) GetCharts(ctx context.Context) ([]*models.Chart, error) { func (c *Client) GetCharts(ctx context.Context) ([]*models.Chart, error) {
var result []models.Chart var result []models.Chart
if err := c.request.Get(ctx, "/charts", nil, &result); err != nil { if err := c.request.Get(ctx, "/charts", nil, &result); err != nil {
@@ -118,7 +119,7 @@ func (c *Client) GetCharts(ctx context.Context) ([]*models.Chart, error) {
return charts, nil return charts, nil
} }
// GetChartInfo gets chart info // GetChartInfo gets chart info for a chart ID (GET /charts/info).
func (c *Client) GetChartInfo(ctx context.Context, chartID string) (*models.ChartInfo, error) { func (c *Client) GetChartInfo(ctx context.Context, chartID string) (*models.ChartInfo, error) {
params := map[string]string{ params := map[string]string{
"chart_id": chartID, "chart_id": chartID,
@@ -131,7 +132,7 @@ func (c *Client) GetChartInfo(ctx context.Context, chartID string) (*models.Char
return &result, nil return &result, nil
} }
// GetChartItems gets chart items // GetChartItems gets chart items for a chart ID (GET /charts/items).
func (c *Client) GetChartItems(ctx context.Context, chartID string, page, pageSize int) (*models.ChartItems, error) { func (c *Client) GetChartItems(ctx context.Context, chartID string, page, pageSize int) (*models.ChartItems, error) {
params := map[string]string{ params := map[string]string{
"chart_id": chartID, "chart_id": chartID,
@@ -150,7 +151,7 @@ func (c *Client) GetChartItems(ctx context.Context, chartID string, page, pageSi
return &result, nil return &result, nil
} }
// GetChartMenu gets chart menu // GetChartMenu gets chart menu for a chart ID (GET /charts/menu).
func (c *Client) GetChartMenu(ctx context.Context, chartID string) (*models.ChartInfoMenu, error) { func (c *Client) GetChartMenu(ctx context.Context, chartID string) (*models.ChartInfoMenu, error) {
params := map[string]string{ params := map[string]string{
"chart_id": chartID, "chart_id": chartID,
+5 -4
View File
@@ -1,3 +1,4 @@
// Package client provides methods for interacting with Yandex Music API endpoints.
package client package client
import ( import (
@@ -6,7 +7,7 @@ import (
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models"
) )
// GetPins gets user's pins // GetPins gets user's pins (GET /pins).
func (c *Client) GetPins(ctx context.Context) (*models.PinsList, error) { func (c *Client) GetPins(ctx context.Context) (*models.PinsList, error) {
var result models.PinsList var result models.PinsList
if err := c.request.Get(ctx, "/pins", nil, &result); err != nil { if err := c.request.Get(ctx, "/pins", nil, &result); err != nil {
@@ -15,7 +16,7 @@ func (c *Client) GetPins(ctx context.Context) (*models.PinsList, error) {
return &result, nil return &result, nil
} }
// AddPin adds a pin // AddPin adds a pin (POST /pins).
func (c *Client) AddPin(ctx context.Context, pinType, entityID string) (*models.Pin, error) { func (c *Client) AddPin(ctx context.Context, pinType, entityID string) (*models.Pin, error) {
params := map[string]string{ params := map[string]string{
"type": pinType, "type": pinType,
@@ -29,7 +30,7 @@ func (c *Client) AddPin(ctx context.Context, pinType, entityID string) (*models.
return &result, nil return &result, nil
} }
// RemovePin removes a pin // RemovePin removes a pin by ID (DELETE /pins).
func (c *Client) RemovePin(ctx context.Context, pinID string) error { func (c *Client) RemovePin(ctx context.Context, pinID string) error {
params := map[string]string{ params := map[string]string{
"pin_id": pinID, "pin_id": pinID,
@@ -38,7 +39,7 @@ func (c *Client) RemovePin(ctx context.Context, pinID string) error {
return c.request.Delete(ctx, "/pins", params, &result) return c.request.Delete(ctx, "/pins", params, &result)
} }
// GetPresaves gets user's presaves // GetPresaves gets user's presaves (GET /presaves).
func (c *Client) GetPresaves(ctx context.Context) (*models.Presaves, error) { func (c *Client) GetPresaves(ctx context.Context) (*models.Presaves, error) {
var result models.Presaves var result models.Presaves
if err := c.request.Get(ctx, "/presaves", nil, &result); err != nil { if err := c.request.Get(ctx, "/presaves", nil, &result); err != nil {
+19 -15
View File
@@ -1,3 +1,4 @@
// Package client provides methods for interacting with Yandex Music API endpoints.
package client package client
import ( import (
@@ -8,7 +9,8 @@ import (
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils"
) )
// GetPlaylists gets playlists by IDs (kinds) // GetPlaylists gets playlists by their kind IDs (GET /playlists).
// Returns a slice of Playlist pointers, or nil if no kinds are provided.
func (c *Client) GetPlaylists(ctx context.Context, kinds []int) ([]*models.Playlist, error) { func (c *Client) GetPlaylists(ctx context.Context, kinds []int) ([]*models.Playlist, error) {
if len(kinds) == 0 { if len(kinds) == 0 {
return nil, nil return nil, nil
@@ -40,7 +42,8 @@ func (c *Client) GetPlaylists(ctx context.Context, kinds []int) ([]*models.Playl
return playlists, nil return playlists, nil
} }
// GetPlaylist gets a single playlist by kind // GetPlaylist gets a single playlist by kind using GetPlaylists.
// Returns an error if no playlist is found with the given kind.
func (c *Client) GetPlaylist(ctx context.Context, kind int) (*models.Playlist, error) { func (c *Client) GetPlaylist(ctx context.Context, kind int) (*models.Playlist, error) {
playlists, err := c.GetPlaylists(ctx, []int{kind}) playlists, err := c.GetPlaylists(ctx, []int{kind})
if err != nil { if err != nil {
@@ -52,14 +55,10 @@ func (c *Client) GetPlaylist(ctx context.Context, kind int) (*models.Playlist, e
return playlists[0], nil return playlists[0], nil
} }
// GetUserPlaylists gets playlists for a user // GetUserPlaylists gets playlists for a user (GET /users/{userID}/playlists).
func (c *Client) GetUserPlaylists(ctx context.Context, userID string) ([]*models.Playlist, error) { func (c *Client) GetUserPlaylists(ctx context.Context, userID string) ([]*models.Playlist, error) {
params := map[string]string{
"user-id": userID,
}
var result []models.Playlist var result []models.Playlist
if err := c.request.Get(ctx, "/users/playlists", params, &result); err != nil { if err := c.request.Get(ctx, fmt.Sprintf("/users/%s/playlists", userID), nil, &result); err != nil {
return nil, err return nil, err
} }
@@ -71,7 +70,8 @@ func (c *Client) GetUserPlaylists(ctx context.Context, userID string) ([]*models
return playlists, nil return playlists, nil
} }
// CreatePlaylist creates a new playlist // CreatePlaylist creates a new playlist (POST /users/me/playlists).
// The visibility parameter should be "public" or "private".
func (c *Client) CreatePlaylist(ctx context.Context, title string, visibility string) (*models.Playlist, error) { func (c *Client) CreatePlaylist(ctx context.Context, title string, visibility string) (*models.Playlist, error) {
params := map[string]string{ params := map[string]string{
"title": title, "title": title,
@@ -86,7 +86,8 @@ func (c *Client) CreatePlaylist(ctx context.Context, title string, visibility st
return &result, nil return &result, nil
} }
// EditPlaylist edits an existing playlist // EditPlaylist edits an existing playlist (PUT /users/me/playlists).
// Only non-empty parameters are updated.
func (c *Client) EditPlaylist(ctx context.Context, kind int, title, description, visibility string) (*models.Playlist, error) { func (c *Client) EditPlaylist(ctx context.Context, kind int, title, description, visibility string) (*models.Playlist, error) {
params := map[string]string{ params := map[string]string{
"kind": fmt.Sprintf("%d", kind), "kind": fmt.Sprintf("%d", kind),
@@ -109,7 +110,7 @@ func (c *Client) EditPlaylist(ctx context.Context, kind int, title, description,
return &result, nil return &result, nil
} }
// DeletePlaylist deletes a playlist // DeletePlaylist deletes a playlist (DELETE /users/me/playlists).
func (c *Client) DeletePlaylist(ctx context.Context, kind int) error { func (c *Client) DeletePlaylist(ctx context.Context, kind int) error {
params := map[string]string{ params := map[string]string{
"kind": fmt.Sprintf("%d", kind), "kind": fmt.Sprintf("%d", kind),
@@ -118,7 +119,8 @@ func (c *Client) DeletePlaylist(ctx context.Context, kind int) error {
return c.request.Delete(ctx, "/users/me/playlists", params, &result) return c.request.Delete(ctx, "/users/me/playlists", params, &result)
} }
// AddTracksToPlaylist adds tracks to a playlist // AddTracksToPlaylist adds tracks to a playlist (POST /users/me/playlists/tracks).
// The revision parameter is the current playlist revision for optimistic concurrency.
func (c *Client) AddTracksToPlaylist(ctx context.Context, kind int, trackIDs []string, revision int) (*models.Playlist, error) { func (c *Client) AddTracksToPlaylist(ctx context.Context, kind int, trackIDs []string, revision int) (*models.Playlist, error) {
if len(trackIDs) == 0 { if len(trackIDs) == 0 {
return nil, utils.NewYandexMusicError("no tracks provided", utils.ErrBadRequest) return nil, utils.NewYandexMusicError("no tracks provided", utils.ErrBadRequest)
@@ -146,7 +148,8 @@ func (c *Client) AddTracksToPlaylist(ctx context.Context, kind int, trackIDs []s
return &result, nil return &result, nil
} }
// RemoveTracksFromPlaylist removes tracks from a playlist // RemoveTracksFromPlaylist removes tracks from a playlist (DELETE /users/me/playlists/tracks).
// The revision parameter is the current playlist revision for optimistic concurrency.
func (c *Client) RemoveTracksFromPlaylist(ctx context.Context, kind int, trackIDs []string, revision int) (*models.Playlist, error) { func (c *Client) RemoveTracksFromPlaylist(ctx context.Context, kind int, trackIDs []string, revision int) (*models.Playlist, error) {
if len(trackIDs) == 0 { if len(trackIDs) == 0 {
return nil, utils.NewYandexMusicError("no tracks provided", utils.ErrBadRequest) return nil, utils.NewYandexMusicError("no tracks provided", utils.ErrBadRequest)
@@ -174,7 +177,8 @@ func (c *Client) RemoveTracksFromPlaylist(ctx context.Context, kind int, trackID
return &result, nil return &result, nil
} }
// ReorderPlaylistTracks reorders tracks in a playlist // ReorderPlaylistTracks reorders tracks in a playlist (PUT /users/me/playlists/tracks/reorder).
// The from and to parameters specify the position range to move.
func (c *Client) ReorderPlaylistTracks(ctx context.Context, kind int, from, to, revision int) (*models.Playlist, error) { func (c *Client) ReorderPlaylistTracks(ctx context.Context, kind int, from, to, revision int) (*models.Playlist, error) {
params := map[string]string{ params := map[string]string{
"kind": fmt.Sprintf("%d", kind), "kind": fmt.Sprintf("%d", kind),
@@ -191,7 +195,7 @@ func (c *Client) ReorderPlaylistTracks(ctx context.Context, kind int, from, to,
return &result, nil return &result, nil
} }
// GetPlaylistRecommendations gets playlist recommendations // GetPlaylistRecommendations gets playlist recommendations based on a playlist kind (GET /playlists/recommendations).
func (c *Client) GetPlaylistRecommendations(ctx context.Context, kind int) ([]*models.Playlist, error) { func (c *Client) GetPlaylistRecommendations(ctx context.Context, kind int) ([]*models.Playlist, error) {
params := map[string]string{ params := map[string]string{
"kind": fmt.Sprintf("%d", kind), "kind": fmt.Sprintf("%d", kind),
+199 -208
View File
@@ -7,213 +7,10 @@ import (
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models"
) )
// GetQueue gets the current queue // GetQueuesList gets the queues list (GET /queues).
func (c *Client) GetQueue(ctx context.Context) (*models.Queue, error) { func (c *Client) GetQueuesList(ctx context.Context) ([]*models.QueueItem, error) {
var result models.Queue
if err := c.request.Get(ctx, "/queue", nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// CreateQueue creates a new queue
func (c *Client) CreateQueue(ctx context.Context, trackID string, queueType string) (*models.Queue, error) {
params := map[string]string{
"track_id": trackID,
"queue_type": queueType, // "radio", "album", "playlist", etc.
}
var result models.Queue
if err := c.request.Post(ctx, "/queue", params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetQueueItem gets a queue item
func (c *Client) GetQueueItem(ctx context.Context, queueID string) (*models.QueueItem, error) {
params := map[string]string{
"queue_id": queueID,
}
var result models.QueueItem
if err := c.request.Get(ctx, "/queue/item", params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// AddToQueue adds a track to the queue
func (c *Client) AddToQueue(ctx context.Context, queueID, trackID string, position int) (*models.Queue, error) {
params := map[string]string{
"queue_id": queueID,
"track_id": trackID,
"position": fmt.Sprintf("%d", position),
}
var result models.Queue
if err := c.request.Post(ctx, "/queue/add", params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// RemoveFromQueue removes a track from the queue
func (c *Client) RemoveFromQueue(ctx context.Context, queueID, trackID string) (*models.Queue, error) {
params := map[string]string{
"queue_id": queueID,
"track_id": trackID,
}
var result models.Queue
if err := c.request.Delete(ctx, "/queue/remove", params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// ReorderQueue reorders the queue
func (c *Client) ReorderQueue(ctx context.Context, queueID string, from, to int) (*models.Queue, error) {
params := map[string]string{
"queue_id": queueID,
"from": fmt.Sprintf("%d", from),
"to": fmt.Sprintf("%d", to),
}
var result models.Queue
if err := c.request.Put(ctx, "/queue/reorder", params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// ClearQueue clears the queue
func (c *Client) ClearQueue(ctx context.Context, queueID string) error {
params := map[string]string{
"queue_id": queueID,
}
var result map[string]any
return c.request.Delete(ctx, "/queue/clear", params, &result)
}
// NextTrack moves to the next track in the queue
func (c *Client) NextTrack(ctx context.Context, queueID string) (*models.Queue, error) {
params := map[string]string{
"queue_id": queueID,
}
var result models.Queue
if err := c.request.Post(ctx, "/queue/next", params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// PreviousTrack moves to the previous track in the queue
func (c *Client) PreviousTrack(ctx context.Context, queueID string) (*models.Queue, error) {
params := map[string]string{
"queue_id": queueID,
}
var result models.Queue
if err := c.request.Post(ctx, "/queue/prev", params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// Seek seeks to a specific position in the current track (in milliseconds)
func (c *Client) Seek(ctx context.Context, queueID string, positionMs int) (*models.Queue, error) {
params := map[string]string{
"queue_id": queueID,
"position": fmt.Sprintf("%d", positionMs),
}
var result models.Queue
if err := c.request.Post(ctx, "/queue/seek", params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// SetRepeatMode sets the repeat mode for the queue
// mode can be "off", "one", or "all"
func (c *Client) SetRepeatMode(ctx context.Context, queueID, mode string) (*models.Queue, error) {
params := map[string]string{
"queue_id": queueID,
"mode": mode,
}
var result models.Queue
if err := c.request.Post(ctx, "/queue/repeat", params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// SetShuffle enables or disables shuffle for the queue
func (c *Client) SetShuffle(ctx context.Context, queueID string, enabled bool) (*models.Queue, error) {
params := map[string]string{
"queue_id": queueID,
"enabled": fmt.Sprintf("%t", enabled),
}
var result models.Queue
if err := c.request.Post(ctx, "/queue/shuffle", params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// SetVolume sets the volume for the queue (0-100)
func (c *Client) SetVolume(ctx context.Context, queueID string, volume int) (*models.Queue, error) {
params := map[string]string{
"queue_id": queueID,
"volume": fmt.Sprintf("%d", volume),
}
var result models.Queue
if err := c.request.Post(ctx, "/queue/volume", params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetCurrentTrack gets the currently playing track from the queue
func (c *Client) GetCurrentTrack(ctx context.Context, queueID string) (*models.QueueItem, error) {
params := map[string]string{
"queue_id": queueID,
}
var result models.QueueItem
if err := c.request.Get(ctx, "/queue/current", params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetQueueHistory gets the queue history (recently played tracks)
func (c *Client) GetQueueHistory(ctx context.Context, queueID string, limit int) ([]*models.QueueItem, error) {
params := map[string]string{
"queue_id": queueID,
"limit": fmt.Sprintf("%d", limit),
}
var result []models.QueueItem var result []models.QueueItem
if err := c.request.Get(ctx, "/queue/history", params, &result); err != nil { if err := c.request.Get(ctx, "/queues", nil, &result); err != nil {
return nil, err return nil, err
} }
@@ -225,7 +22,201 @@ func (c *Client) GetQueueHistory(ctx context.Context, queueID string, limit int)
return items, nil return items, nil
} }
// GetPlayContexts gets play contexts // GetQueue gets the current queue
func (c *Client) GetQueue(ctx context.Context) (*models.Queue, error) {
var result models.Queue
if err := c.request.Get(ctx, "/queues", nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// CreateQueue creates a new playback queue (POST /queues).
// The queueType parameter specifies the type of queue (e.g., "radio", "album", "playlist").
func (c *Client) CreateQueue(ctx context.Context, trackID string, queueType string) (*models.Queue, error) {
params := map[string]string{
"track_id": trackID,
"queue_type": queueType, // "radio", "album", "playlist", etc.
}
var result models.Queue
if err := c.request.Post(ctx, "/queues", params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetQueueItem gets a queue item by queue ID (GET /queues/{queue_id}).
func (c *Client) GetQueueItem(ctx context.Context, queueID string) (*models.QueueItem, error) {
var result models.QueueItem
if err := c.request.Get(ctx, fmt.Sprintf("/queues/%s", queueID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// AddToQueue adds a track to the queue at the given position (POST /queues/{queue_id}/add).
func (c *Client) AddToQueue(ctx context.Context, queueID, trackID string, position int) (*models.Queue, error) {
params := map[string]string{
"track_id": trackID,
"position": fmt.Sprintf("%d", position),
}
var result models.Queue
if err := c.request.Post(ctx, fmt.Sprintf("/queues/%s/add", queueID), params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// RemoveFromQueue removes a track from the queue (DELETE /queues/{queue_id}/remove).
func (c *Client) RemoveFromQueue(ctx context.Context, queueID, trackID string) (*models.Queue, error) {
params := map[string]string{
"track_id": trackID,
}
var result models.Queue
if err := c.request.Delete(ctx, fmt.Sprintf("/queues/%s/remove", queueID), params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// ReorderQueue reorders items in the queue from one position to another (PUT /queues/{queue_id}/reorder).
func (c *Client) ReorderQueue(ctx context.Context, queueID string, from, to int) (*models.Queue, error) {
params := map[string]string{
"from": fmt.Sprintf("%d", from),
"to": fmt.Sprintf("%d", to),
}
var result models.Queue
if err := c.request.Put(ctx, fmt.Sprintf("/queues/%s/reorder", queueID), params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// ClearQueue clears the queue (DELETE /queues/{queue_id}/clear).
func (c *Client) ClearQueue(ctx context.Context, queueID string) error {
var result map[string]any
return c.request.Delete(ctx, fmt.Sprintf("/queues/%s/clear", queueID), nil, &result)
}
// NextTrack moves to the next track in the queue (POST /queues/{queue_id}/next).
func (c *Client) NextTrack(ctx context.Context, queueID string) (*models.Queue, error) {
var result models.Queue
if err := c.request.Post(ctx, fmt.Sprintf("/queues/%s/next", queueID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// PreviousTrack moves to the previous track in the queue (POST /queues/{queue_id}/prev).
func (c *Client) PreviousTrack(ctx context.Context, queueID string) (*models.Queue, error) {
var result models.Queue
if err := c.request.Post(ctx, fmt.Sprintf("/queues/%s/prev", queueID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// Seek seeks to a specific position in the current track (POST /queues/{queue_id}/seek).
// The positionMs parameter is the position in milliseconds.
func (c *Client) Seek(ctx context.Context, queueID string, positionMs int) (*models.Queue, error) {
params := map[string]string{
"position": fmt.Sprintf("%d", positionMs),
}
var result models.Queue
if err := c.request.Post(ctx, fmt.Sprintf("/queues/%s/seek", queueID), params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// SetRepeatMode sets the repeat mode for the queue (POST /queues/{queue_id}/repeat).
// The mode parameter can be "off", "one", or "all".
func (c *Client) SetRepeatMode(ctx context.Context, queueID, mode string) (*models.Queue, error) {
params := map[string]string{
"mode": mode,
}
var result models.Queue
if err := c.request.Post(ctx, fmt.Sprintf("/queues/%s/repeat", queueID), params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// SetShuffle enables or disables shuffle for the queue (POST /queues/{queue_id}/shuffle).
func (c *Client) SetShuffle(ctx context.Context, queueID string, enabled bool) (*models.Queue, error) {
params := map[string]string{
"enabled": fmt.Sprintf("%t", enabled),
}
var result models.Queue
if err := c.request.Post(ctx, fmt.Sprintf("/queues/%s/shuffle", queueID), params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// SetVolume sets the volume for the queue (POST /queues/{queue_id}/volume).
// The volume parameter should be between 0 and 100.
func (c *Client) SetVolume(ctx context.Context, queueID string, volume int) (*models.Queue, error) {
params := map[string]string{
"volume": fmt.Sprintf("%d", volume),
}
var result models.Queue
if err := c.request.Post(ctx, fmt.Sprintf("/queues/%s/volume", queueID), params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetCurrentTrack gets the currently playing track from the queue (GET /queues/{queue_id}/current).
func (c *Client) GetCurrentTrack(ctx context.Context, queueID string) (*models.QueueItem, error) {
var result models.QueueItem
if err := c.request.Get(ctx, fmt.Sprintf("/queues/%s/current", queueID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetQueueHistory gets the queue history (recently played tracks) (GET /queues/{queue_id}/history).
func (c *Client) GetQueueHistory(ctx context.Context, queueID string, limit int) ([]*models.QueueItem, error) {
params := map[string]string{
"limit": fmt.Sprintf("%d", limit),
}
var result []models.QueueItem
if err := c.request.Get(ctx, fmt.Sprintf("/queues/%s/history", queueID), params, &result); err != nil {
return nil, err
}
items := make([]*models.QueueItem, len(result))
for i := range result {
result[i].SetClient(c)
items[i] = &result[i]
}
return items, nil
}
// GetPlayContexts gets play contexts (GET /play-contexts).
func (c *Client) GetPlayContexts(ctx context.Context) ([]*models.PlayContext, error) { func (c *Client) GetPlayContexts(ctx context.Context) ([]*models.PlayContext, error) {
var result []models.PlayContext var result []models.PlayContext
if err := c.request.Get(ctx, "/play-contexts", nil, &result); err != nil { if err := c.request.Get(ctx, "/play-contexts", nil, &result); err != nil {
@@ -240,7 +231,7 @@ func (c *Client) GetPlayContexts(ctx context.Context) ([]*models.PlayContext, er
return contexts, nil return contexts, nil
} }
// GetPlayContextsData gets play contexts data // GetPlayContextsData gets play contexts data (GET /play-contexts/data).
func (c *Client) GetPlayContextsData(ctx context.Context) (*models.PlayContextsData, error) { func (c *Client) GetPlayContextsData(ctx context.Context) (*models.PlayContextsData, error) {
var result models.PlayContextsData var result models.PlayContextsData
if err := c.request.Get(ctx, "/play-contexts/data", nil, &result); err != nil { if err := c.request.Get(ctx, "/play-contexts/data", nil, &result); err != nil {
+77 -34
View File
@@ -1,3 +1,4 @@
// Package client provides methods for interacting with Yandex Music API endpoints.
package client package client
import ( import (
@@ -7,7 +8,7 @@ import (
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models"
) )
// GetRadioStations gets radio stations // GetRadioStations gets radio stations (GET /radio/stations).
func (c *Client) GetRadioStations(ctx context.Context) ([]*models.RadioStation, error) { func (c *Client) GetRadioStations(ctx context.Context) ([]*models.RadioStation, error) {
var result []models.RadioStation var result []models.RadioStation
if err := c.request.Get(ctx, "/radio/stations", nil, &result); err != nil { if err := c.request.Get(ctx, "/radio/stations", nil, &result); err != nil {
@@ -22,37 +23,31 @@ func (c *Client) GetRadioStations(ctx context.Context) ([]*models.RadioStation,
return stations, nil return stations, nil
} }
// GetRadioStation gets a radio station by ID // GetRadioStation gets a radio station by ID (GET /rotor/station/{stationID}/info).
func (c *Client) GetRadioStation(ctx context.Context, stationID string) (*models.RadioStation, error) { func (c *Client) GetRadioStation(ctx context.Context, stationID string) (*models.RadioStation, error) {
params := map[string]string{
"station_id": stationID,
}
var result models.RadioStation var result models.RadioStation
if err := c.request.Get(ctx, "/radio/station", params, &result); err != nil { if err := c.request.Get(ctx, fmt.Sprintf("/rotor/station/%s/info", stationID), nil, &result); err != nil {
return nil, err return nil, err
} }
result.SetClient(c) result.SetClient(c)
return &result, nil return &result, nil
} }
// GetStationTracks gets tracks for a radio station // GetStationTracks gets tracks for a radio station (GET /rotor/station/{stationID}/tracks).
func (c *Client) GetStationTracks(ctx context.Context, stationID string, limit int) (*models.StationTracksResult, error) { func (c *Client) GetStationTracks(ctx context.Context, stationID string, limit int) (*models.StationTracksResult, error) {
params := map[string]string{ params := map[string]string{}
"station_id": stationID,
}
if limit > 0 { if limit > 0 {
params["limit"] = fmt.Sprintf("%d", limit) params["limit"] = fmt.Sprintf("%d", limit)
} }
var result models.StationTracksResult var result models.StationTracksResult
if err := c.request.Get(ctx, "/radio/station/tracks", params, &result); err != nil { if err := c.request.Get(ctx, fmt.Sprintf("/rotor/station/%s/tracks", stationID), params, &result); err != nil {
return nil, err return nil, err
} }
return &result, nil return &result, nil
} }
// GetRotorStations gets rotor stations (generative) // GetRotorStations gets rotor stations (generative stations) (GET /rotor/stations).
func (c *Client) GetRotorStations(ctx context.Context) ([]*models.RotorStation, error) { func (c *Client) GetRotorStations(ctx context.Context) ([]*models.RotorStation, error) {
var result []models.RotorStation var result []models.RotorStation
if err := c.request.Get(ctx, "/rotor/stations", nil, &result); err != nil { if err := c.request.Get(ctx, "/rotor/stations", nil, &result); err != nil {
@@ -67,51 +62,41 @@ func (c *Client) GetRotorStations(ctx context.Context) ([]*models.RotorStation,
return stations, nil return stations, nil
} }
// GetRotorStation gets a rotor station by ID // GetRotorStation gets a rotor station by ID (GET /rotor/station/{stationID}/info).
func (c *Client) GetRotorStation(ctx context.Context, stationID string) (*models.RotorStation, error) { func (c *Client) GetRotorStation(ctx context.Context, stationID string) (*models.RotorStation, error) {
params := map[string]string{
"station_id": stationID,
}
var result models.RotorStation var result models.RotorStation
if err := c.request.Get(ctx, "/rotor/station", params, &result); err != nil { if err := c.request.Get(ctx, fmt.Sprintf("/rotor/station/%s/info", stationID), nil, &result); err != nil {
return nil, err return nil, err
} }
result.SetClient(c) result.SetClient(c)
return &result, nil return &result, nil
} }
// GetRotorStationTracks gets tracks for a rotor station // GetRotorStationTracks gets tracks for a rotor station (GET /rotor/station/{stationID}/tracks).
func (c *Client) GetRotorStationTracks(ctx context.Context, stationID string, limit int) (*models.RotorStationTracksResult, error) { func (c *Client) GetRotorStationTracks(ctx context.Context, stationID string, limit int) (*models.RotorStationTracksResult, error) {
params := map[string]string{ params := map[string]string{}
"station_id": stationID,
}
if limit > 0 { if limit > 0 {
params["limit"] = fmt.Sprintf("%d", limit) params["limit"] = fmt.Sprintf("%d", limit)
} }
var result models.RotorStationTracksResult var result models.RotorStationTracksResult
if err := c.request.Get(ctx, "/rotor/station/tracks", params, &result); err != nil { if err := c.request.Get(ctx, fmt.Sprintf("/rotor/station/%s/tracks", stationID), params, &result); err != nil {
return nil, err return nil, err
} }
return &result, nil return &result, nil
} }
// StartRotorSession starts a rotor session // StartRotorSession starts a rotor session (POST /rotor/station/{stationID}/session/start).
func (c *Client) StartRotorSession(ctx context.Context, stationID string) (*models.RotorSession, error) { func (c *Client) StartRotorSession(ctx context.Context, stationID string) (*models.RotorSession, error) {
params := map[string]string{
"station_id": stationID,
}
var result models.RotorSession var result models.RotorSession
if err := c.request.Post(ctx, "/rotor/session/start", params, &result); err != nil { if err := c.request.Post(ctx, fmt.Sprintf("/rotor/station/%s/session/start", stationID), nil, &result); err != nil {
return nil, err return nil, err
} }
result.SetClient(c) result.SetClient(c)
return &result, nil return &result, nil
} }
// GetRotorSession gets rotor session info // GetRotorSession gets rotor session info by session ID (GET /rotor/session).
func (c *Client) GetRotorSession(ctx context.Context, sessionID string) (*models.RotorSession, error) { func (c *Client) GetRotorSession(ctx context.Context, sessionID string) (*models.RotorSession, error) {
params := map[string]string{ params := map[string]string{
"session_id": sessionID, "session_id": sessionID,
@@ -125,14 +110,72 @@ func (c *Client) GetRotorSession(ctx context.Context, sessionID string) (*models
return &result, nil return &result, nil
} }
// RotorSessionFeedback sends feedback for a rotor session track // RotorSessionFeedback sends feedback for a rotor session track (POST /rotor/station/{stationID}/feedback).
// The feedbackType parameter can be "like", "dislike", or "skip".
func (c *Client) RotorSessionFeedback(ctx context.Context, sessionID, trackID, feedbackType string) error { func (c *Client) RotorSessionFeedback(ctx context.Context, sessionID, trackID, feedbackType string) error {
params := map[string]string{ params := map[string]string{
"session_id": sessionID,
"track_id": trackID, "track_id": trackID,
"feedback": feedbackType, // "like", "dislike", "skip" "feedback": feedbackType, // "like", "dislike", "skip"
} }
var result map[string]any var result map[string]any
return c.request.Post(ctx, "/rotor/session/feedback", params, &result) return c.request.Post(ctx, fmt.Sprintf("/rotor/station/%s/feedback", sessionID), params, &result)
}
// GetRotorStationsDashboard gets rotor stations dashboard (GET /rotor/stations/dashboard).
func (c *Client) GetRotorStationsDashboard(ctx context.Context) (*models.Dashboard, error) {
var result models.Dashboard
if err := c.request.Get(ctx, "/rotor/stations/dashboard", nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetRotorStationsList gets rotor stations list (GET /rotor/stations/list).
func (c *Client) GetRotorStationsList(ctx context.Context) ([]*models.StationResult, error) {
var result []models.StationResult
if err := c.request.Get(ctx, "/rotor/stations/list", nil, &result); err != nil {
return nil, err
}
stations := make([]*models.StationResult, len(result))
for i := range result {
result[i].SetClient(c)
stations[i] = &result[i]
}
return stations, nil
}
// GetRotorStationInfo gets rotor station info (GET /rotor/station/{id}/info).
func (c *Client) GetRotorStationInfo(ctx context.Context, stationID string) (*models.RotorStation, error) {
var result models.RotorStation
if err := c.request.Get(ctx, fmt.Sprintf("/rotor/station/%s/info", stationID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetRotorStationStream gets rotor station stream (GET /rotor/station/{id}/stream).
func (c *Client) GetRotorStationStream(ctx context.Context, stationID string) (*models.GenerativeStream, error) {
var result models.GenerativeStream
if err := c.request.Get(ctx, fmt.Sprintf("/rotor/station/%s/stream", stationID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetRotorStationSettings gets rotor station settings (POST /rotor/station/{id}/settings3).
func (c *Client) GetRotorStationSettings(ctx context.Context, stationID string, moodEnergy, diversity, language string) (bool, error) {
params := map[string]string{
"mood-energy": moodEnergy,
"diversity": diversity,
"language": language,
}
var result bool
if err := c.request.Post(ctx, fmt.Sprintf("/rotor/station/%s/settings3", stationID), params, &result); err != nil {
return false, err
}
return result, nil
} }
+22 -13
View File
@@ -13,7 +13,7 @@ import (
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils"
) )
// Default values // Default values for the Yandex Music API client.
const ( const (
DefaultBaseURL = "https://api.music.yandex.net" DefaultBaseURL = "https://api.music.yandex.net"
DefaultTimeout = 5 * time.Second DefaultTimeout = 5 * time.Second
@@ -21,7 +21,7 @@ const (
DefaultClientHeader = "YandexMusicAndroid/24023621" DefaultClientHeader = "YandexMusicAndroid/24023621"
) )
// RequestConfig holds configuration for the request client // RequestConfig holds configuration for the request client.
type RequestConfig struct { type RequestConfig struct {
BaseURL string BaseURL string
Token string Token string
@@ -36,14 +36,16 @@ type RequestConfig struct {
OnSchemaMismatch utils.SchemaMismatchHandler OnSchemaMismatch utils.SchemaMismatchHandler
} }
// Request handles HTTP requests to Yandex Music API // Request handles HTTP requests to the Yandex Music API.
// It manages the HTTP client, authentication headers, and request signing.
type Request struct { type Request struct {
config RequestConfig config RequestConfig
client *http.Client client *http.Client
schemaTracker *utils.SchemaMismatchTracker schemaTracker *utils.SchemaMismatchTracker
} }
// NewRequest creates a new Request instance // NewRequest creates a new Request instance with the given configuration.
// It sets up the HTTP client, default headers, and authentication if a token is provided.
func NewRequest(config RequestConfig) *Request { func NewRequest(config RequestConfig) *Request {
if config.BaseURL == "" { if config.BaseURL == "" {
config.BaseURL = DefaultBaseURL config.BaseURL = DefaultBaseURL
@@ -92,7 +94,8 @@ func NewRequest(config RequestConfig) *Request {
return r return r
} }
// SetToken updates the authorization token // SetToken updates the authorization token used for API requests.
// When a token is provided, it sets the Authorization header to "OAuth <token>".
func (r *Request) SetToken(token string) { func (r *Request) SetToken(token string) {
r.config.Token = token r.config.Token = token
if token != "" { if token != "" {
@@ -102,33 +105,38 @@ func (r *Request) SetToken(token string) {
} }
} }
// SetLanguage sets the Accept-Language header // SetLanguage sets the Accept-Language header for API requests.
func (r *Request) SetLanguage(lang string) { func (r *Request) SetLanguage(lang string) {
r.config.Language = lang r.config.Language = lang
r.config.Headers["Accept-Language"] = lang r.config.Headers["Accept-Language"] = lang
} }
// Get performs a GET request // Get performs a GET request to the given path with query parameters.
// The response is unmarshaled into the result pointer.
func (r *Request) Get(ctx context.Context, path string, params map[string]string, result any) error { func (r *Request) Get(ctx context.Context, path string, params map[string]string, result any) error {
return r.doRequest(ctx, "GET", path, params, nil, result) return r.doRequest(ctx, "GET", path, params, nil, result)
} }
// Post performs a POST request // Post performs a POST request to the given path with a JSON body.
// The response is unmarshaled into the result pointer.
func (r *Request) Post(ctx context.Context, path string, body any, result any) error { func (r *Request) Post(ctx context.Context, path string, body any, result any) error {
return r.doRequest(ctx, "POST", path, nil, body, result) return r.doRequest(ctx, "POST", path, nil, body, result)
} }
// Put performs a PUT request // Put performs a PUT request to the given path with a JSON body.
// The response is unmarshaled into the result pointer.
func (r *Request) Put(ctx context.Context, path string, body any, result any) error { func (r *Request) Put(ctx context.Context, path string, body any, result any) error {
return r.doRequest(ctx, "PUT", path, nil, body, result) return r.doRequest(ctx, "PUT", path, nil, body, result)
} }
// Delete performs a DELETE request // Delete performs a DELETE request to the given path with an optional JSON body.
// The response is unmarshaled into the result pointer.
func (r *Request) Delete(ctx context.Context, path string, body any, result any) error { func (r *Request) Delete(ctx context.Context, path string, body any, result any) error {
return r.doRequest(ctx, "DELETE", path, nil, body, result) return r.doRequest(ctx, "DELETE", path, nil, body, result)
} }
// doRequest performs the actual HTTP request // doRequest performs the actual HTTP request to the Yandex Music API.
// It handles URL building, request signing, response parsing, and error handling.
func (r *Request) doRequest(ctx context.Context, method, path string, params map[string]string, body any, result any) error { func (r *Request) doRequest(ctx context.Context, method, path string, params map[string]string, body any, result any) error {
// Build URL // Build URL
fullURL := r.config.BaseURL + path fullURL := r.config.BaseURL + path
@@ -209,7 +217,7 @@ func (r *Request) doRequest(ctx context.Context, method, path string, params map
return nil return nil
} }
// handleErrorResponse handles HTTP error responses // handleErrorResponse handles HTTP error responses by mapping status codes to typed errors.
func (r *Request) handleErrorResponse(statusCode int, body []byte) error { func (r *Request) handleErrorResponse(statusCode int, body []byte) error {
var msg string var msg string
apiResp, err := utils.DeJSON(body) apiResp, err := utils.DeJSON(body)
@@ -235,7 +243,8 @@ func (r *Request) handleErrorResponse(statusCode int, body []byte) error {
} }
} }
// Download downloads a file from a URL // Download downloads a file from the given URL and returns the raw response body.
// It is used for downloading track files and lyrics text.
func (r *Request) Download(ctx context.Context, urlStr string) ([]byte, error) { func (r *Request) Download(ctx context.Context, urlStr string) ([]byte, error) {
req, err := http.NewRequestWithContext(ctx, "GET", urlStr, nil) req, err := http.NewRequestWithContext(ctx, "GET", urlStr, nil)
if err != nil { if err != nil {
+12 -10
View File
@@ -1,3 +1,4 @@
// Package client provides methods for interacting with Yandex Music API endpoints.
package client package client
import ( import (
@@ -7,7 +8,8 @@ import (
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models"
) )
// Search performs a multi-type search // Search performs a multi-type search (GET /search).
// The searchType parameter specifies what to search for (e.g., "track", "album", "artist").
func (c *Client) Search(ctx context.Context, query string, searchType string, page, pageSize int) (*models.SearchResult, error) { func (c *Client) Search(ctx context.Context, query string, searchType string, page, pageSize int) (*models.SearchResult, error) {
params := map[string]string{ params := map[string]string{
"text": query, "text": query,
@@ -46,7 +48,7 @@ func (c *Client) Search(ctx context.Context, query string, searchType string, pa
return &result, nil return &result, nil
} }
// SearchTracks searches for tracks // SearchTracks searches for tracks using Search with type "track".
func (c *Client) SearchTracks(ctx context.Context, query string, page, pageSize int) (*models.SearchTracksResult, error) { func (c *Client) SearchTracks(ctx context.Context, query string, page, pageSize int) (*models.SearchTracksResult, error) {
result, err := c.Search(ctx, query, "track", page, pageSize) result, err := c.Search(ctx, query, "track", page, pageSize)
if err != nil { if err != nil {
@@ -55,7 +57,7 @@ func (c *Client) SearchTracks(ctx context.Context, query string, page, pageSize
return result.Tracks, nil return result.Tracks, nil
} }
// SearchAlbums searches for albums // SearchAlbums searches for albums using Search with type "album".
func (c *Client) SearchAlbums(ctx context.Context, query string, page, pageSize int) (*models.SearchAlbumsResult, error) { func (c *Client) SearchAlbums(ctx context.Context, query string, page, pageSize int) (*models.SearchAlbumsResult, error) {
result, err := c.Search(ctx, query, "album", page, pageSize) result, err := c.Search(ctx, query, "album", page, pageSize)
if err != nil { if err != nil {
@@ -64,7 +66,7 @@ func (c *Client) SearchAlbums(ctx context.Context, query string, page, pageSize
return result.Albums, nil return result.Albums, nil
} }
// SearchArtists searches for artists // SearchArtists searches for artists using Search with type "artist".
func (c *Client) SearchArtists(ctx context.Context, query string, page, pageSize int) (*models.SearchArtistsResult, error) { func (c *Client) SearchArtists(ctx context.Context, query string, page, pageSize int) (*models.SearchArtistsResult, error) {
result, err := c.Search(ctx, query, "artist", page, pageSize) result, err := c.Search(ctx, query, "artist", page, pageSize)
if err != nil { if err != nil {
@@ -73,7 +75,7 @@ func (c *Client) SearchArtists(ctx context.Context, query string, page, pageSize
return result.Artists, nil return result.Artists, nil
} }
// SearchPlaylists searches for playlists // SearchPlaylists searches for playlists using Search with type "playlist".
func (c *Client) SearchPlaylists(ctx context.Context, query string, page, pageSize int) (*models.SearchPlaylistsResult, error) { func (c *Client) SearchPlaylists(ctx context.Context, query string, page, pageSize int) (*models.SearchPlaylistsResult, error) {
result, err := c.Search(ctx, query, "playlist", page, pageSize) result, err := c.Search(ctx, query, "playlist", page, pageSize)
if err != nil { if err != nil {
@@ -82,7 +84,7 @@ func (c *Client) SearchPlaylists(ctx context.Context, query string, page, pageSi
return result.Playlists, nil return result.Playlists, nil
} }
// SearchVideos searches for videos // SearchVideos searches for videos using Search with type "video".
func (c *Client) SearchVideos(ctx context.Context, query string, page, pageSize int) (*models.SearchVideosResult, error) { func (c *Client) SearchVideos(ctx context.Context, query string, page, pageSize int) (*models.SearchVideosResult, error) {
result, err := c.Search(ctx, query, "video", page, pageSize) result, err := c.Search(ctx, query, "video", page, pageSize)
if err != nil { if err != nil {
@@ -91,7 +93,7 @@ func (c *Client) SearchVideos(ctx context.Context, query string, page, pageSize
return result.Videos, nil return result.Videos, nil
} }
// SearchUsers searches for users // SearchUsers searches for users using Search with type "user".
func (c *Client) SearchUsers(ctx context.Context, query string, page, pageSize int) (*models.SearchUsersResult, error) { func (c *Client) SearchUsers(ctx context.Context, query string, page, pageSize int) (*models.SearchUsersResult, error) {
result, err := c.Search(ctx, query, "user", page, pageSize) result, err := c.Search(ctx, query, "user", page, pageSize)
if err != nil { if err != nil {
@@ -100,7 +102,7 @@ func (c *Client) SearchUsers(ctx context.Context, query string, page, pageSize i
return result.Users, nil return result.Users, nil
} }
// SearchSuggestions gets search suggestions // SearchSuggestions gets search suggestions for a query (GET /search/suggest?type=suggest).
func (c *Client) SearchSuggestions(ctx context.Context, query string) ([]string, error) { func (c *Client) SearchSuggestions(ctx context.Context, query string) ([]string, error) {
params := map[string]string{ params := map[string]string{
"text": query, "text": query,
@@ -116,7 +118,7 @@ func (c *Client) SearchSuggestions(ctx context.Context, query string) ([]string,
return result.Suggestions, nil return result.Suggestions, nil
} }
// SearchBanners gets search banners // SearchBanners gets search banners for a query (GET /search/banner?type=banner).
func (c *Client) SearchBanners(ctx context.Context, query string) ([]models.SearchBanner, error) { func (c *Client) SearchBanners(ctx context.Context, query string) ([]models.SearchBanner, error) {
params := map[string]string{ params := map[string]string{
"text": query, "text": query,
@@ -132,7 +134,7 @@ func (c *Client) SearchBanners(ctx context.Context, query string) ([]models.Sear
return result.Banners, nil return result.Banners, nil
} }
// SearchTags gets search tags // SearchTags gets search tags for a query (GET /search/tag?type=tag).
func (c *Client) SearchTags(ctx context.Context, query string) ([]models.Tag, error) { func (c *Client) SearchTags(ctx context.Context, query string) ([]models.Tag, error) {
params := map[string]string{ params := map[string]string{
"text": query, "text": query,
+8 -7
View File
@@ -1,3 +1,4 @@
// Package client provides methods for interacting with Yandex Music API endpoints.
package client package client
import ( import (
@@ -6,7 +7,7 @@ import (
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models"
) )
// GetSettings gets user settings // GetSettings gets user settings (GET /account/settings).
func (c *Client) GetSettings(ctx context.Context) (*models.AccountSettings, error) { func (c *Client) GetSettings(ctx context.Context) (*models.AccountSettings, error) {
var result models.AccountSettings var result models.AccountSettings
if err := c.request.Get(ctx, "/account/settings", nil, &result); err != nil { if err := c.request.Get(ctx, "/account/settings", nil, &result); err != nil {
@@ -15,7 +16,7 @@ func (c *Client) GetSettings(ctx context.Context) (*models.AccountSettings, erro
return &result, nil return &result, nil
} }
// UpdateSettings updates user settings // UpdateSettings updates user settings (PUT /account/settings).
func (c *Client) UpdateSettings(ctx context.Context, settings *models.AccountSettings) (*models.AccountSettings, error) { func (c *Client) UpdateSettings(ctx context.Context, settings *models.AccountSettings) (*models.AccountSettings, error) {
var result models.AccountSettings var result models.AccountSettings
if err := c.request.Put(ctx, "/account/settings", settings, &result); err != nil { if err := c.request.Put(ctx, "/account/settings", settings, &result); err != nil {
@@ -24,7 +25,7 @@ func (c *Client) UpdateSettings(ctx context.Context, settings *models.AccountSet
return &result, nil return &result, nil
} }
// GetExperiments gets experiments // GetExperiments gets experiments (GET /experiments).
func (c *Client) GetExperiments(ctx context.Context) (*models.Experiments, error) { func (c *Client) GetExperiments(ctx context.Context) (*models.Experiments, error) {
var result models.Experiments var result models.Experiments
if err := c.request.Get(ctx, "/experiments", nil, &result); err != nil { if err := c.request.Get(ctx, "/experiments", nil, &result); err != nil {
@@ -33,7 +34,7 @@ func (c *Client) GetExperiments(ctx context.Context) (*models.Experiments, error
return &result, nil return &result, nil
} }
// GetExperimentDetails gets experiment details // GetExperimentDetails gets experiment details for the given experiment ID (GET /experiments/details).
func (c *Client) GetExperimentDetails(ctx context.Context, experimentID string) (*models.ExperimentDetail, error) { func (c *Client) GetExperimentDetails(ctx context.Context, experimentID string) (*models.ExperimentDetail, error) {
params := map[string]string{ params := map[string]string{
"experiment_id": experimentID, "experiment_id": experimentID,
@@ -45,7 +46,7 @@ func (c *Client) GetExperimentDetails(ctx context.Context, experimentID string)
return &result, nil return &result, nil
} }
// GetPermissions gets user permissions // GetPermissions gets user permissions (GET /permissions).
func (c *Client) GetPermissions(ctx context.Context) (*models.Permissions, error) { func (c *Client) GetPermissions(ctx context.Context) (*models.Permissions, error) {
var result models.Permissions var result models.Permissions
if err := c.request.Get(ctx, "/permissions", nil, &result); err != nil { if err := c.request.Get(ctx, "/permissions", nil, &result); err != nil {
@@ -54,7 +55,7 @@ func (c *Client) GetPermissions(ctx context.Context) (*models.Permissions, error
return &result, nil return &result, nil
} }
// GetPermissionAlerts gets permission alerts // GetPermissionAlerts gets permission alerts (GET /permission-alerts).
func (c *Client) GetPermissionAlerts(ctx context.Context) (*models.PermissionAlerts, error) { func (c *Client) GetPermissionAlerts(ctx context.Context) (*models.PermissionAlerts, error) {
var result models.PermissionAlerts var result models.PermissionAlerts
if err := c.request.Get(ctx, "/permission-alerts", nil, &result); err != nil { if err := c.request.Get(ctx, "/permission-alerts", nil, &result); err != nil {
@@ -63,7 +64,7 @@ func (c *Client) GetPermissionAlerts(ctx context.Context) (*models.PermissionAle
return &result, nil return &result, nil
} }
// GetContentRestrictions gets content restrictions // GetContentRestrictions gets content restrictions (GET /content-restrictions).
func (c *Client) GetContentRestrictions(ctx context.Context) (*models.ContentRestrictions, error) { func (c *Client) GetContentRestrictions(ctx context.Context) (*models.ContentRestrictions, error) {
var result models.ContentRestrictions var result models.ContentRestrictions
if err := c.request.Get(ctx, "/content-restrictions", nil, &result); err != nil { if err := c.request.Get(ctx, "/content-restrictions", nil, &result); err != nil {
+40 -21
View File
@@ -1,3 +1,4 @@
// Package client provides methods for interacting with Yandex Music API endpoints.
package client package client
import ( import (
@@ -9,8 +10,7 @@ import (
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils"
) )
// GetTrackSupplement gets supplement for a track // GetTrackSupplement gets supplement for a track (GET /tracks/{track_id}/supplement).
// GET /tracks/{track_id}/supplement
func (c *Client) GetTrackSupplement(ctx context.Context, trackID string) (*models.Supplement, error) { func (c *Client) GetTrackSupplement(ctx context.Context, trackID string) (*models.Supplement, error) {
path := fmt.Sprintf("/tracks/%s/supplement", trackID) path := fmt.Sprintf("/tracks/%s/supplement", trackID)
@@ -21,8 +21,9 @@ func (c *Client) GetTrackSupplement(ctx context.Context, trackID string) (*model
return &result, nil return &result, nil
} }
// GetTrackLyrics gets lyrics for a track (new endpoint with signing) // GetTrackLyrics gets lyrics for a track (GET /tracks/{track_id}/lyrics).
// GET /tracks/{track_id}/lyrics // The format parameter specifies the lyrics format (e.g., "TEXT" or "LRC").
// The request is signed using the default signing key.
func (c *Client) GetTrackLyrics(ctx context.Context, trackID string, format string) (*models.TrackLyrics, error) { func (c *Client) GetTrackLyrics(ctx context.Context, trackID string, format string) (*models.TrackLyrics, error) {
path := fmt.Sprintf("/tracks/%s/lyrics", trackID) path := fmt.Sprintf("/tracks/%s/lyrics", trackID)
@@ -42,7 +43,7 @@ func (c *Client) GetTrackLyrics(ctx context.Context, trackID string, format stri
return &result, nil return &result, nil
} }
// GetTrackLyricsText fetches the full lyrics text from the download URL // GetTrackLyricsText fetches the full lyrics text from the download URL provided in TrackLyrics.
func (c *Client) GetTrackLyricsText(ctx context.Context, trackLyrics *models.TrackLyrics) (string, error) { func (c *Client) GetTrackLyricsText(ctx context.Context, trackLyrics *models.TrackLyrics) (string, error) {
data, err := c.request.Download(ctx, trackLyrics.DownloadURL) data, err := c.request.Download(ctx, trackLyrics.DownloadURL)
if err != nil { if err != nil {
@@ -51,8 +52,7 @@ func (c *Client) GetTrackLyricsText(ctx context.Context, trackLyrics *models.Tra
return string(data), nil return string(data), nil
} }
// GetLyrics gets lyrics info for a track (legacy endpoint) // GetLyrics gets lyrics info for a track using the legacy endpoint (GET /tracks/lyrics).
// GET /tracks/lyrics?track_id={track_id}
func (c *Client) GetLyrics(ctx context.Context, trackID string) (*models.LyricsInfo, error) { func (c *Client) GetLyrics(ctx context.Context, trackID string) (*models.LyricsInfo, error) {
params := map[string]string{ params := map[string]string{
"track_id": trackID, "track_id": trackID,
@@ -65,8 +65,7 @@ func (c *Client) GetLyrics(ctx context.Context, trackID string) (*models.LyricsI
return &result, nil return &result, nil
} }
// GetVideoSupplement gets video supplement for a track (legacy endpoint) // GetVideoSupplement gets video supplement for a track using the legacy endpoint (GET /tracks/video-supplement).
// GET /tracks/video-supplement?track_id={track_id}
func (c *Client) GetVideoSupplement(ctx context.Context, trackID string) (*models.VideoSupplement, error) { func (c *Client) GetVideoSupplement(ctx context.Context, trackID string) (*models.VideoSupplement, error) {
params := map[string]string{ params := map[string]string{
"track_id": trackID, "track_id": trackID,
@@ -79,44 +78,43 @@ func (c *Client) GetVideoSupplement(ctx context.Context, trackID string) (*model
return &result, nil return &result, nil
} }
// GetLyricsByTrackID is an alias for GetTrackLyrics for backward compatibility // GetLyricsByTrackID is an alias for GetTrackLyrics for backward compatibility (GET /tracks/{track_id}/lyrics).
// GET /tracks/{track_id}/lyrics
func (c *Client) GetLyricsByTrackID(ctx context.Context, trackID string, format string) (*models.TrackLyrics, error) { func (c *Client) GetLyricsByTrackID(ctx context.Context, trackID string, format string) (*models.TrackLyrics, error) {
return c.GetTrackLyrics(ctx, trackID, format) return c.GetTrackLyrics(ctx, trackID, format)
} }
// LyricsFormat represents the lyrics format // LyricsFormat represents the lyrics format.
type LyricsFormat string type LyricsFormat string
const ( const (
// LyricsFormatText is plain text format // LyricsFormatText is plain text lyrics format.
LyricsFormatText LyricsFormat = "TEXT" LyricsFormatText LyricsFormat = "TEXT"
// LyricsFormatLRC is LRC format with timestamps // LyricsFormatLRC is LRC format lyrics with timestamps.
LyricsFormatLRC LyricsFormat = "LRC" LyricsFormatLRC LyricsFormat = "LRC"
) )
// GetTrackLyricsWithOptions gets lyrics with options // GetTrackLyricsWithOptions gets lyrics with options using a LyricsFormat value.
func (c *Client) GetTrackLyricsWithOptions(ctx context.Context, trackID string, format LyricsFormat) (*models.TrackLyrics, error) { func (c *Client) GetTrackLyricsWithOptions(ctx context.Context, trackID string, format LyricsFormat) (*models.TrackLyrics, error) {
return c.GetTrackLyrics(ctx, trackID, string(format)) return c.GetTrackLyrics(ctx, trackID, string(format))
} }
// SupplementOptions holds options for supplement requests // SupplementOptions holds options for supplement requests.
type SupplementOptions struct { type SupplementOptions struct {
TrackID string TrackID string
} }
// GetSupplement gets track supplement with options // GetSupplement gets track supplement with options.
func (c *Client) GetSupplement(ctx context.Context, opts SupplementOptions) (*models.Supplement, error) { func (c *Client) GetSupplement(ctx context.Context, opts SupplementOptions) (*models.Supplement, error) {
return c.GetTrackSupplement(ctx, opts.TrackID) return c.GetTrackSupplement(ctx, opts.TrackID)
} }
// LyricsOptions holds options for lyrics requests // LyricsOptions holds options for lyrics requests.
type LyricsOptions struct { type LyricsOptions struct {
TrackID string TrackID string
Format LyricsFormat Format LyricsFormat
} }
// GetLyricsWithOptions gets track lyrics with options // GetLyricsWithOptions gets track lyrics with options.
func (c *Client) GetLyricsWithOptions(ctx context.Context, opts LyricsOptions) (*models.TrackLyrics, error) { func (c *Client) GetLyricsWithOptions(ctx context.Context, opts LyricsOptions) (*models.TrackLyrics, error) {
format := string(opts.Format) format := string(opts.Format)
if format == "" { if format == "" {
@@ -125,13 +123,34 @@ func (c *Client) GetLyricsWithOptions(ctx context.Context, opts LyricsOptions) (
return c.GetTrackLyrics(ctx, opts.TrackID, format) return c.GetTrackLyrics(ctx, opts.TrackID, format)
} }
// Sign represents a request signature // Sign represents a request signature for signed API requests.
type Sign struct { type Sign struct {
Timestamp int64 Timestamp int64
Value string Value string
} }
// GetSignRequest creates a signature for a track request (exported for external use) // GetTracksCredits gets track credits (GET /tracks/{id}/credits).
func (c *Client) GetTracksCredits(ctx context.Context, trackID string) (*models.Credits, error) {
var result models.Credits
if err := c.request.Get(ctx, fmt.Sprintf("/tracks/%s/credits", trackID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetClipsCredits gets clip credits (GET /clips/{id}/credits).
func (c *Client) GetClipsCredits(ctx context.Context, clipID string) (*models.Credits, error) {
var result models.Credits
if err := c.request.Get(ctx, fmt.Sprintf("/clips/%s/credits", clipID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetSignRequest creates a signature for a track request (exported for external use).
// It delegates to utils.GetSignRequest with the default signing key.
func GetSignRequest(trackID string) Sign { func GetSignRequest(trackID string) Sign {
h := utils.GetSignRequest(trackID, utils.DefaultSignKey) h := utils.GetSignRequest(trackID, utils.DefaultSignKey)
+154 -15
View File
@@ -1,14 +1,52 @@
// Package client provides methods for interacting with Yandex Music API endpoints.
package client package client
import ( import (
"context" "context"
"crypto/md5"
"encoding/xml"
"fmt" "fmt"
"strings"
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models"
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils"
) )
// GetTracks gets tracks by IDs const downloadSignSalt = "XGRlBW9FXlekgbPrRHuSiA"
// downloadInfoXML represents the XML response from download_info_url.
type downloadInfoXML struct {
Host string `xml:"host"`
Path string `xml:"path"`
Ts string `xml:"ts"`
S string `xml:"s"`
}
// resolveDownloadURL downloads the XML from downloadInfoURL, parses host/path/ts/s,
// computes the MD5 signature, and returns the signed download URL:
// https://{host}/get-mp3/{sign}/{ts}{path}
func resolveDownloadURL(ctx context.Context, downloadInfoURL string, ri models.RequestInterface) (string, error) {
data, err := ri.Download(ctx, downloadInfoURL)
if err != nil {
return "", err
}
var xmlData downloadInfoXML
if err := xml.Unmarshal(data, &xmlData); err != nil {
return "", fmt.Errorf("failed to parse download info XML: %w", err)
}
if xmlData.Path == "" || xmlData.S == "" {
return "", utils.NewYandexMusicError("invalid download info XML: missing path or s", utils.ErrBadRequest)
}
sign := fmt.Sprintf("%x", md5.Sum([]byte(downloadSignSalt+xmlData.Path[1:]+xmlData.S)))
return fmt.Sprintf("https://%s/get-mp3/%s/%s%s", xmlData.Host, sign, xmlData.Ts, xmlData.Path), nil
}
// GetTracks gets tracks by their IDs (GET /tracks).
// Returns a slice of Track pointers, or nil if no IDs are provided.
func (c *Client) GetTracks(ctx context.Context, trackIDs []string) ([]*models.Track, error) { func (c *Client) GetTracks(ctx context.Context, trackIDs []string) ([]*models.Track, error) {
if len(trackIDs) == 0 { if len(trackIDs) == 0 {
return nil, nil return nil, nil
@@ -31,7 +69,8 @@ func (c *Client) GetTracks(ctx context.Context, trackIDs []string) ([]*models.Tr
return tracks, nil return tracks, nil
} }
// GetTrack gets a single track by ID // GetTrack gets a single track by ID using GetTracks.
// Returns an error if no track is found with the given ID.
func (c *Client) GetTrack(ctx context.Context, trackID string) (*models.Track, error) { func (c *Client) GetTrack(ctx context.Context, trackID string) (*models.Track, error) {
tracks, err := c.GetTracks(ctx, []string{trackID}) tracks, err := c.GetTracks(ctx, []string{trackID})
if err != nil { if err != nil {
@@ -43,13 +82,15 @@ func (c *Client) GetTrack(ctx context.Context, trackID string) (*models.Track, e
return tracks[0], nil return tracks[0], nil
} }
// GetTrackDownloadInfo gets download info for a track // GetTrackDownloadInfo gets download info for a track (GET /tracks/{track_id}/download-info).
// The quality parameter specifies the desired audio quality (e.g., "mp3").
func (c *Client) GetTrackDownloadInfo(ctx context.Context, trackID, quality string) ([]models.DownloadInfo, error) { func (c *Client) GetTrackDownloadInfo(ctx context.Context, trackID, quality string) ([]models.DownloadInfo, error) {
// This requires signing - use the sign utility
// For now, return basic implementation
params := map[string]string{ params := map[string]string{
"track_id": trackID, "track_id": trackID,
} }
if quality != "" {
params["quality"] = quality
}
var result []models.DownloadInfo var result []models.DownloadInfo
if err := c.request.Get(ctx, fmt.Sprintf("/tracks/%s/download-info", trackID), params, &result); err != nil { if err := c.request.Get(ctx, fmt.Sprintf("/tracks/%s/download-info", trackID), params, &result); err != nil {
@@ -58,7 +99,45 @@ func (c *Client) GetTrackDownloadInfo(ctx context.Context, trackID, quality stri
return result, nil return result, nil
} }
// DownloadTrack downloads a track // GetTrackDownloadURL returns a signed direct download URL for a track.
// It fetches download info, finds a matching entry, downloads the XML from its
// download_info_url, parses host/path/ts/s, computes the MD5 signature, and
// returns https://{host}/get-mp3/{sign}/{ts}{path}.
func (c *Client) GetTrackDownloadURL(ctx context.Context, trackID, format string, bitrate int) (string, error) {
info, err := c.GetTrackDownloadInfo(ctx, trackID, format)
if err != nil {
return "", err
}
// Find matching download info
var di *models.DownloadInfo
for i := range info {
if info[i].Codec == format && (bitrate == 0 || info[i].Bitrate == bitrate) {
di = &info[i]
break
}
}
if di == nil && len(info) > 0 {
di = &info[0]
}
if di == nil {
return "", utils.NewYandexMusicError("no download info available", utils.ErrNotFound)
}
if di.URL != "" {
return di.URL, nil
}
if di.DownloadInfoURL == "" {
return "", utils.NewYandexMusicError("no download URL available", utils.ErrNotFound)
}
return resolveDownloadURL(ctx, di.DownloadInfoURL, c.request)
}
// DownloadTrack downloads a track by ID.
// It first fetches download info, finds a matching format/bitrate, resolves the
// signed URL if needed, then downloads the file content.
func (c *Client) DownloadTrack(ctx context.Context, trackID, format string, bitrate int) ([]byte, error) { func (c *Client) DownloadTrack(ctx context.Context, trackID, format string, bitrate int) ([]byte, error) {
info, err := c.GetTrackDownloadInfo(ctx, trackID, format) info, err := c.GetTrackDownloadInfo(ctx, trackID, format)
if err != nil { if err != nil {
@@ -66,31 +145,91 @@ func (c *Client) DownloadTrack(ctx context.Context, trackID, format string, bitr
} }
// Find matching download info // Find matching download info
var url string var di *models.DownloadInfo
for _, di := range info { for i := range info {
if di.Codec == format && (bitrate == 0 || di.Bitrate == bitrate) { if info[i].Codec == format && (bitrate == 0 || info[i].Bitrate == bitrate) {
url = di.URL di = &info[i]
break break
} }
} }
if di == nil && len(info) > 0 {
if url == "" && len(info) > 0 { di = &info[0]
url = info[0].URL }
if di == nil {
return nil, utils.NewYandexMusicError("no download info available", utils.ErrNotFound)
} }
url := di.URL
if url == "" { if url == "" {
if di.DownloadInfoURL == "" {
return nil, utils.NewYandexMusicError("no download URL available", utils.ErrNotFound) return nil, utils.NewYandexMusicError("no download URL available", utils.ErrNotFound)
} }
url, err = resolveDownloadURL(ctx, di.DownloadInfoURL, c.request)
if err != nil {
return nil, err
}
}
return c.request.Download(ctx, url) return c.request.Download(ctx, url)
} }
// SearchResult represents search results // GetTracksFileInfo gets file info for a track (GET /get-file-info).
func (c *Client) GetTracksFileInfo(ctx context.Context, trackID, quality string, codecs []string, transport string) (*models.FileInfo, error) {
params := map[string]string{
"track_id": trackID,
"quality": quality,
}
if len(codecs) > 0 {
params["codecs"] = strings.Join(codecs, ",")
}
if transport != "" {
params["transport"] = transport
}
var result models.FileInfo
if err := c.request.Get(ctx, "/get-file-info", params, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetTracksSimilar gets similar tracks (GET /tracks/{track_id}/similar).
func (c *Client) GetTracksSimilar(ctx context.Context, trackID string) (*models.SimilarTracks, error) {
var result models.SimilarTracks
if err := c.request.Get(ctx, fmt.Sprintf("/tracks/%s/similar", trackID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetTrackTrailer gets track trailer (GET /tracks/{track_id}/trailer).
func (c *Client) GetTrackTrailer(ctx context.Context, trackID string) (*models.TrackTrailer, error) {
var result models.TrackTrailer
if err := c.request.Get(ctx, fmt.Sprintf("/tracks/%s/trailer", trackID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// GetTrackFullInfo gets full track info (GET /tracks/{track_id}/full-info).
func (c *Client) GetTrackFullInfo(ctx context.Context, trackID string) (*models.TrackFullInfo, error) {
var result models.TrackFullInfo
if err := c.request.Get(ctx, fmt.Sprintf("/tracks/%s/full-info", trackID), nil, &result); err != nil {
return nil, err
}
result.SetClient(c)
return &result, nil
}
// SearchResult represents search results containing tracks.
type SearchResult struct { type SearchResult struct {
Tracks *SearchTracksResult `json:"tracks"` Tracks *SearchTracksResult `json:"tracks"`
} }
// SearchTracksResult represents track search results // SearchTracksResult represents track search results with total count and results list.
type SearchTracksResult struct { type SearchTracksResult struct {
Total int `json:"total"` Total int `json:"total"`
Results []models.Track `json:"results"` Results []models.Track `json:"results"`
+12 -12
View File
@@ -30,7 +30,7 @@ func TestClient_GetTracks(t *testing.T) {
"id": "12345", "id": "12345",
"title": "Test Track", "title": "Test Track",
"duration_ms": 180000, "duration_ms": 180000,
"storage_id": "67890", "storage_dir": "67890",
"cover_uri": "https://example.com/cover.jpg", "cover_uri": "https://example.com/cover.jpg",
"genre": "rock", "genre": "rock",
"version": "album", "version": "album",
@@ -42,7 +42,7 @@ func TestClient_GetTracks(t *testing.T) {
"artists": [{"id": "1", "name": "Artist 1", "cover_uri": "https://example.com/artist.jpg"}], "artists": [{"id": "1", "name": "Artist 1", "cover_uri": "https://example.com/artist.jpg"}],
"albums": [{"id": "789", "title": "Test Album", "cover_uri": "https://example.com/album.jpg", "year": 2023}], "albums": [{"id": "789", "title": "Test Album", "cover_uri": "https://example.com/album.jpg", "year": 2023}],
"major": {"id": "major1", "name": "Major Label", "description": "Big label"}, "major": {"id": "major1", "name": "Major Label", "description": "Big label"},
"lyrics": {"id": "lyr1", "text": "Test lyrics", "available": true, "has_text": true} "lyrics_info": {"has_available_sync_lyrics": true, "has_available_text_lyrics": true}
} }
] ]
}`), }`),
@@ -63,8 +63,8 @@ func TestClient_GetTracks(t *testing.T) {
if tr.Major == nil || tr.Major.Name != "Major Label" { if tr.Major == nil || tr.Major.Name != "Major Label" {
t.Errorf("Expected Major 'Major Label'") t.Errorf("Expected Major 'Major Label'")
} }
if tr.Lyrics == nil || tr.Lyrics.Text != "Test lyrics" { if tr.LyricsInfo == nil || tr.LyricsInfo.HasAvailableSyncLyrics != true {
t.Errorf("Expected Lyrics 'Test lyrics'") t.Errorf("Expected LyricsInfo.HasAvailableSyncLyrics true")
} }
}, },
}, },
@@ -73,8 +73,8 @@ func TestClient_GetTracks(t *testing.T) {
trackIDs: []string{"12345", "67890"}, trackIDs: []string{"12345", "67890"},
mockResponse: []byte(`{ mockResponse: []byte(`{
"result": [ "result": [
{"id": "12345", "title": "Track 1", "duration_ms": 100000, "storage_id": "1", "cover_uri": "", "genre": "", "version": "", "explicit": false, "available": true, "available_for_premium_users": true, "r128": 0, "track_sharing_flag": "", "artists": [], "albums": []}, {"id": "12345", "title": "Track 1", "duration_ms": 100000, "storage_dir": "1", "cover_uri": "", "genre": "", "version": "", "explicit": false, "available": true, "available_for_premium_users": true, "r128": 0, "track_sharing_flag": "", "artists": [], "albums": []},
{"id": "67890", "title": "Track 2", "duration_ms": 200000, "storage_id": "2", "cover_uri": "", "genre": "", "version": "", "explicit": false, "available": true, "available_for_premium_users": true, "r128": 0, "track_sharing_flag": "", "artists": [], "albums": []} {"id": "67890", "title": "Track 2", "duration_ms": 200000, "storage_dir": "2", "cover_uri": "", "genre": "", "version": "", "explicit": false, "available": true, "available_for_premium_users": true, "r128": 0, "track_sharing_flag": "", "artists": [], "albums": []}
] ]
}`), }`),
expectedTracks: 2, expectedTracks: 2,
@@ -189,7 +189,7 @@ func TestClient_GetTrack(t *testing.T) {
"id": "12345", "id": "12345",
"title": "Test Track", "title": "Test Track",
"duration_ms": 180000, "duration_ms": 180000,
"storage_id": "67890", "storage_dir": "67890",
"cover_uri": "https://example.com/cover.jpg", "cover_uri": "https://example.com/cover.jpg",
"genre": "rock", "genre": "rock",
"version": "album", "version": "album",
@@ -201,7 +201,7 @@ func TestClient_GetTrack(t *testing.T) {
"artists": [{"id": "1", "name": "Artist 1", "cover_uri": "https://example.com/artist.jpg"}], "artists": [{"id": "1", "name": "Artist 1", "cover_uri": "https://example.com/artist.jpg"}],
"albums": [{"id": "789", "title": "Test Album", "cover_uri": "https://example.com/album.jpg", "year": 2023}], "albums": [{"id": "789", "title": "Test Album", "cover_uri": "https://example.com/album.jpg", "year": 2023}],
"major": {"id": "major1", "name": "Major Label", "description": "Big label"}, "major": {"id": "major1", "name": "Major Label", "description": "Big label"},
"lyrics": {"id": "lyr1", "text": "Test lyrics", "available": true, "has_text": true} "lyrics_info": {"has_available_sync_lyrics": true, "has_available_text_lyrics": true}
} }
] ]
}`), }`),
@@ -311,7 +311,7 @@ func TestClient_SearchTracks(t *testing.T) {
"id": "1", "id": "1",
"title": "Track 1", "title": "Track 1",
"duration_ms": 100000, "duration_ms": 100000,
"storage_id": "1", "storage_dir": "1",
"cover_uri": "", "cover_uri": "",
"genre": "", "genre": "",
"version": "", "version": "",
@@ -327,7 +327,7 @@ func TestClient_SearchTracks(t *testing.T) {
"id": "2", "id": "2",
"title": "Track 2", "title": "Track 2",
"duration_ms": 200000, "duration_ms": 200000,
"storage_id": "2", "storage_dir": "2",
"cover_uri": "", "cover_uri": "",
"genre": "", "genre": "",
"version": "", "version": "",
@@ -408,7 +408,7 @@ func TestClient_SearchTracks_Mocked(t *testing.T) {
"id": "1", "id": "1",
"title": "Track 1", "title": "Track 1",
"duration_ms": 100000, "duration_ms": 100000,
"storage_id": "1", "storage_dir": "1",
"cover_uri": "", "cover_uri": "",
"genre": "", "genre": "",
"version": "", "version": "",
@@ -424,7 +424,7 @@ func TestClient_SearchTracks_Mocked(t *testing.T) {
"id": "2", "id": "2",
"title": "Track 2", "title": "Track 2",
"duration_ms": 200000, "duration_ms": 200000,
"storage_id": "2", "storage_dir": "2",
"cover_uri": "", "cover_uri": "",
"genre": "", "genre": "",
"version": "", "version": "",
+14 -13
View File
@@ -1,10 +1,11 @@
// Package models provides data models for the Yandex Music API.
package models package models
import ( import (
"encoding/json" "encoding/json"
) )
// AccountSettings represents account settings // AccountSettings represents account settings from the Yandex Music API.
type AccountSettings struct { type AccountSettings struct {
BaseModel BaseModel
Language string `json:"language"` Language string `json:"language"`
@@ -26,7 +27,7 @@ type AccountSettings struct {
DownloadOnWifiOnly bool `json:"download_on_wifi_only"` DownloadOnWifiOnly bool `json:"download_on_wifi_only"`
} }
// DeJSON deserializes AccountSettings from JSON // DeJSON deserializes AccountSettings from JSON data.
func (a *AccountSettings) DeJSON(data []byte, c ClientInterface) (Model, error) { func (a *AccountSettings) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -90,7 +91,7 @@ func (a *AccountSettings) DeJSON(data []byte, c ClientInterface) (Model, error)
return settings, nil return settings, nil
} }
// DeList deserializes a list of AccountSettings // DeList deserializes a list of AccountSettings from JSON data.
func (a *AccountSettings) DeList(data []byte, c ClientInterface) ([]Model, error) { func (a *AccountSettings) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -108,10 +109,10 @@ func (a *AccountSettings) DeList(data []byte, c ClientInterface) ([]Model, error
return result, nil return result, nil
} }
// Subscription represents subscription info // Subscription represents subscription info.
type Subscription struct { type Subscription struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Type string `json:"type"` Type string `json:"type"`
Status string `json:"status"` Status string `json:"status"`
StartDate string `json:"start_date"` StartDate string `json:"start_date"`
@@ -127,7 +128,7 @@ type Subscription struct {
TrialUsed bool `json:"trial_used"` TrialUsed bool `json:"trial_used"`
} }
// DeJSON deserializes Subscription from JSON // DeJSON deserializes Subscription from JSON data.
func (s *Subscription) DeJSON(data []byte, c ClientInterface) (Model, error) { func (s *Subscription) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -182,7 +183,7 @@ func (s *Subscription) DeJSON(data []byte, c ClientInterface) (Model, error) {
return sub, nil return sub, nil
} }
// DeList deserializes a list of Subscriptions // DeList deserializes a list of Subscriptions from JSON data.
func (s *Subscription) DeList(data []byte, c ClientInterface) ([]Model, error) { func (s *Subscription) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -200,7 +201,7 @@ func (s *Subscription) DeList(data []byte, c ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// PlusInfo represents Plus subscription info (from /account/plus or account status) // PlusInfo represents Plus subscription info.
type PlusInfo struct { type PlusInfo struct {
BaseModel BaseModel
HasPlus bool `json:"has_plus"` HasPlus bool `json:"has_plus"`
@@ -208,7 +209,7 @@ type PlusInfo struct {
Migrated *bool `json:"migrated,omitempty"` Migrated *bool `json:"migrated,omitempty"`
} }
// DeJSON deserializes PlusInfo from JSON // DeJSON deserializes PlusInfo from JSON data.
func (p *PlusInfo) DeJSON(data []byte, c ClientInterface) (Model, error) { func (p *PlusInfo) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -232,7 +233,7 @@ func (p *PlusInfo) DeJSON(data []byte, c ClientInterface) (Model, error) {
return plus, nil return plus, nil
} }
// DeList deserializes a list of PlusInfo // DeList deserializes a list of PlusInfo from JSON data.
func (p *PlusInfo) DeList(data []byte, c ClientInterface) ([]Model, error) { func (p *PlusInfo) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -250,7 +251,7 @@ func (p *PlusInfo) DeList(data []byte, c ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// PromoCodeStatus represents promo code status // PromoCodeStatus represents promo code status.
type PromoCodeStatus struct { type PromoCodeStatus struct {
BaseModel BaseModel
Code string `json:"code"` Code string `json:"code"`
@@ -262,7 +263,7 @@ type PromoCodeStatus struct {
Activated bool `json:"activated"` Activated bool `json:"activated"`
} }
// DeJSON deserializes PromoCodeStatus from JSON // DeJSON deserializes PromoCodeStatus from JSON data.
func (p *PromoCodeStatus) DeJSON(data []byte, c ClientInterface) (Model, error) { func (p *PromoCodeStatus) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -296,7 +297,7 @@ func (p *PromoCodeStatus) DeJSON(data []byte, c ClientInterface) (Model, error)
return promo, nil return promo, nil
} }
// DeList deserializes a list of PromoCodeStatus // DeList deserializes a list of PromoCodeStatus from JSON data.
func (p *PromoCodeStatus) DeList(data []byte, c ClientInterface) ([]Model, error) { func (p *PromoCodeStatus) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
+283 -29
View File
@@ -1,40 +1,128 @@
// Package models provides data models for the Yandex Music API.
package models package models
import ( import (
"encoding/json" "encoding/json"
) )
// Album represents a full album // Album represents a full album from the Yandex Music API.
type Album struct { type Album struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Error string `json:"error"`
Title string `json:"title"` Title string `json:"title"`
CoverURI string `json:"cover_uri"`
Genre string `json:"genre"`
Year int `json:"year"`
TrackCount int `json:"track_count"` TrackCount int `json:"track_count"`
Available bool `json:"available"`
ReleaseDate string `json:"release_date"`
Artists []Artist `json:"artists"` Artists []Artist `json:"artists"`
Labels []Label `json:"labels"` Labels []LabelOrString `json:"labels"`
Volumes [][]Track `json:"volumes"` Available bool `json:"available"`
AvailableForPremiumUsers bool `json:"available_for_premium_users"`
Version string `json:"version"`
CoverURI string `json:"cover_uri"`
ContentWarning string `json:"content_warning"`
OriginalReleaseYear int `json:"original_release_year"`
Genre string `json:"genre"`
TextColor string `json:"text_color"`
ShortDescription string `json:"short_description"`
Description string `json:"description"` Description string `json:"description"`
IsPremiere bool `json:"is_premiere"`
IsBanner bool `json:"is_banner"`
MetaType string `json:"meta_type"`
StorageDir string `json:"storage_dir"`
OgImage string `json:"og_image"`
Buy []json.RawMessage `json:"buy"`
Recent bool `json:"recent"`
VeryImportant bool `json:"very_important"`
AvailableForMobile bool `json:"available_for_mobile"`
AvailablePartially bool `json:"available_partially"`
Bests []int `json:"bests"`
Duplicates []Album `json:"duplicates"`
Prerolls []json.RawMessage `json:"prerolls"`
Volumes [][]Track `json:"volumes"`
Year int `json:"year"`
ReleaseDate string `json:"release_date"`
Type string `json:"type"`
TrackPosition *TrackPosition `json:"track_position"`
Regions []string `json:"regions"`
AvailableAsRbt bool `json:"available_as_rbt"`
LyricsAvailable bool `json:"lyrics_available"`
RememberPosition bool `json:"remember_position"`
Albums []Album `json:"albums"`
DurationMs int `json:"duration_ms"`
Explicit bool `json:"explicit"`
StartDate string `json:"start_date"`
LikesCount int `json:"likes_count"`
Deprecation *Deprecation `json:"deprecation"`
AvailableRegions []string `json:"available_regions"`
AvailableForOptions []string `json:"available_for_options"`
ListeningFinished bool `json:"listening_finished"`
Disclaimers []string `json:"disclaimers"`
ActionButton *AlbumActionButton `json:"action_button"`
Cover *Cover `json:"cover"`
DerivedColors *CoverDerivedColors `json:"derived_colors"`
MetaTagID string `json:"meta_tag_id"`
ChildContent bool `json:"child_content"`
BackgroundImageURL string `json:"background_image_url"`
CustomWave *CustomWave `json:"custom_wave"`
DurationSec int `json:"duration_sec"`
HasTrailer bool `json:"has_trailer"`
Trailer *AlbumTrailerStatus `json:"trailer"`
Pager *Pager `json:"pager"`
SortOrder string `json:"sort_order"`
Meta *AlbumMeta `json:"meta"` Meta *AlbumMeta `json:"meta"`
Best *Best `json:"best"` Best *Best `json:"best"`
// Additional fields
Region string `json:"region"` Region string `json:"region"`
AvailableForPremiumUsers bool `json:"available_for_premium_users"`
AvailableForPremiumUsersOnly bool `json:"available_for_premium_users_only"` AvailableForPremiumUsersOnly bool `json:"available_for_premium_users_only"`
ContentWarning string `json:"content_warning"`
Label string `json:"label"` Label string `json:"label"`
CatalogNumber string `json:"catalog_number"` CatalogNumber string `json:"catalog_number"`
Options []string `json:"options"` Options []string `json:"options"`
TrackPositions []TrackPosition `json:"track_positions"` TrackPositions []TrackPosition `json:"track_positions"`
SimilarAlbums []Album `json:"similar_albums"` SimilarAlbums []Album `json:"similar_albums"`
Trailer *TrailerInfo `json:"trailer"`
} }
// DeJSON deserializes an Album from JSON // LabelOrString represents a label that can be either a Label object or a string.
type LabelOrString struct {
Label *Label `json:"-"`
Str string `json:"-"`
IsStr bool `json:"-"`
}
// UnmarshalJSON implements json.Unmarshaler, accepting both Label objects and strings.
func (l *LabelOrString) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err == nil {
l.Str = s
l.IsStr = true
return nil
}
var label Label
if err := json.Unmarshal(data, &label); err != nil {
return err
}
l.Label = &label
l.IsStr = false
return nil
}
// MarshalJSON implements json.Marshaler.
func (l LabelOrString) MarshalJSON() ([]byte, error) {
if l.IsStr {
return json.Marshal(l.Str)
}
return json.Marshal(l.Label)
}
// String returns the label as a string.
func (l LabelOrString) String() string {
if l.IsStr {
return l.Str
}
if l.Label != nil {
return l.Label.Name
}
return ""
}
// DeJSON deserializes an Album from JSON data.
func (a *Album) DeJSON(data []byte, c ClientInterface) (Model, error) { func (a *Album) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -47,6 +135,9 @@ func (a *Album) DeJSON(data []byte, c ClientInterface) (Model, error) {
if v, ok := raw["id"]; ok { if v, ok := raw["id"]; ok {
json.Unmarshal(v, &album.ID) json.Unmarshal(v, &album.ID)
} }
if v, ok := raw["error"]; ok {
json.Unmarshal(v, &album.Error)
}
if v, ok := raw["title"]; ok { if v, ok := raw["title"]; ok {
json.Unmarshal(v, &album.Title) json.Unmarshal(v, &album.Title)
} }
@@ -100,10 +191,11 @@ func (a *Album) DeJSON(data []byte, c ClientInterface) (Model, error) {
album.Artists = artists album.Artists = artists
} }
if v, ok := raw["labels"]; ok { if v, ok := raw["labels"]; ok {
var labels []Label var labels []LabelOrString
json.Unmarshal(v, &labels) if err := json.Unmarshal(v, &labels); err == nil {
album.Labels = labels album.Labels = labels
} }
}
if v, ok := raw["volumes"]; ok { if v, ok := raw["volumes"]; ok {
var volumes [][]Track var volumes [][]Track
json.Unmarshal(v, &volumes) json.Unmarshal(v, &volumes)
@@ -130,7 +222,7 @@ func (a *Album) DeJSON(data []byte, c ClientInterface) (Model, error) {
album.SimilarAlbums = similar album.SimilarAlbums = similar
} }
if v, ok := raw["trailer"]; ok { if v, ok := raw["trailer"]; ok {
var trailer TrailerInfo var trailer AlbumTrailerStatus
json.Unmarshal(v, &trailer) json.Unmarshal(v, &trailer)
album.Trailer = &trailer album.Trailer = &trailer
} }
@@ -138,7 +230,7 @@ func (a *Album) DeJSON(data []byte, c ClientInterface) (Model, error) {
return album, nil return album, nil
} }
// DeList deserializes a list of Albums // DeList deserializes a list of Albums from JSON data.
func (a *Album) DeList(data []byte, c ClientInterface) ([]Model, error) { func (a *Album) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -156,14 +248,14 @@ func (a *Album) DeList(data []byte, c ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// AlbumMeta represents album metadata // AlbumMeta represents album metadata.
type AlbumMeta struct { type AlbumMeta struct {
BaseModel BaseModel
Availability *Availability `json:"availability"` Availability *Availability `json:"availability"`
Upsell *Upsell `json:"upsell"` Upsell *Upsell `json:"upsell"`
} }
// DeJSON deserializes an AlbumMeta from JSON // DeJSON deserializes an AlbumMeta from JSON data.
func (am *AlbumMeta) DeJSON(data []byte, c ClientInterface) (Model, error) { func (am *AlbumMeta) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -186,7 +278,7 @@ func (am *AlbumMeta) DeJSON(data []byte, c ClientInterface) (Model, error) {
return meta, nil return meta, nil
} }
// DeList deserializes a list of AlbumMeta // DeList deserializes a list of AlbumMeta from JSON data.
func (am *AlbumMeta) DeList(data []byte, c ClientInterface) ([]Model, error) { func (am *AlbumMeta) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -204,33 +296,33 @@ func (am *AlbumMeta) DeList(data []byte, c ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// Availability represents availability info // Availability represents availability info for an album.
type Availability struct { type Availability struct {
AvailableForPremiumUsers bool `json:"available_for_premium_users"` AvailableForPremiumUsers bool `json:"available_for_premium_users"`
Territories []string `json:"territories"` Territories []string `json:"territories"`
} }
// Upsell represents upsell info // Upsell represents upsell info for an album.
type Upsell struct { type Upsell struct {
Type string `json:"type"` Type string `json:"type"`
Description string `json:"description"` Description string `json:"description"`
} }
// Best represents best tracks info // Best represents best tracks info for an album.
type Best struct { type Best struct {
Tracks []Track `json:"tracks"` Tracks []Track `json:"tracks"`
} }
// TrailerInfo represents album/track trailer info // TrailerInfo represents album/track trailer info.
type TrailerInfo struct { type TrailerInfo struct {
BaseModel BaseModel
PreviewURI string `json:"preview_uri"` PreviewURI string `json:"preview_uri"`
TrackID string `json:"track_id"` TrackID JSONID `json:"track_id"`
AlbumID string `json:"album_id"` AlbumID JSONID `json:"album_id"`
Duration int `json:"duration"` Duration int `json:"duration"`
} }
// DeJSON deserializes a TrailerInfo from JSON // DeJSON deserializes a TrailerInfo from JSON data.
func (ti *TrailerInfo) DeJSON(data []byte, c ClientInterface) (Model, error) { func (ti *TrailerInfo) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -255,7 +347,7 @@ func (ti *TrailerInfo) DeJSON(data []byte, c ClientInterface) (Model, error) {
return trailer, nil return trailer, nil
} }
// DeList deserializes a list of TrailerInfo // DeList deserializes a list of TrailerInfo from JSON data.
func (ti *TrailerInfo) DeList(data []byte, c ClientInterface) ([]Model, error) { func (ti *TrailerInfo) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -272,3 +364,165 @@ func (ti *TrailerInfo) DeList(data []byte, c ClientInterface) ([]Model, error) {
} }
return result, nil return result, nil
} }
// AlbumTrailerStatus represents trailer availability status for an album.
type AlbumTrailerStatus struct {
BaseModel
Available bool `json:"available"`
}
// DeJSON deserializes AlbumTrailerStatus from JSON data.
func (ats *AlbumTrailerStatus) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
status := &AlbumTrailerStatus{BaseModel: *NewBaseModel(c)}
if v, ok := raw["available"]; ok {
json.Unmarshal(v, &status.Available)
}
return status, nil
}
// DeList deserializes a list of AlbumTrailerStatus from JSON data.
func (ats *AlbumTrailerStatus) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
status := &AlbumTrailerStatus{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, status); err != nil {
continue
}
result = append(result, status)
}
return result, nil
}
// Deprecation represents album deprecation info.
type Deprecation struct {
BaseModel
TargetAlbumID JSONID `json:"target_album_id"`
Status string `json:"status"`
Done bool `json:"done"`
}
// AlbumActionButton represents an action button for an album.
type AlbumActionButton struct {
BaseModel
Text string `json:"text"`
URL string `json:"url"`
Color string `json:"color"`
ViewBrowser bool `json:"view_browser"`
}
// AlbumSimilarEntities represents similar entities for an album.
type AlbumSimilarEntities struct {
BaseModel
Items []SimilarEntityItem `json:"items"`
}
// DeJSON deserializes AlbumSimilarEntities from JSON data.
func (ae *AlbumSimilarEntities) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
entities := &AlbumSimilarEntities{BaseModel: *NewBaseModel(c)}
if v, ok := raw["items"]; ok {
var items []SimilarEntityItem
json.Unmarshal(v, &items)
entities.Items = items
}
return entities, nil
}
// DeList deserializes a list of AlbumSimilarEntities from JSON data.
func (ae *AlbumSimilarEntities) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
entities := &AlbumSimilarEntities{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, entities); err != nil {
continue
}
result = append(result, entities)
}
return result, nil
}
// SimilarEntityItem represents a similar entity item.
type SimilarEntityItem struct {
BaseModel
ID JSONID `json:"id"`
Type string `json:"type"`
Data json.RawMessage `json:"data"`
}
// AlbumTrailer represents album trailer info.
type AlbumTrailer struct {
BaseModel
Album *Album `json:"album"`
Artists []Artist `json:"artists"`
Trailer *TrailerInfo `json:"trailer"`
}
// DeJSON deserializes AlbumTrailer from JSON data.
func (at *AlbumTrailer) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
trailer := &AlbumTrailer{BaseModel: *NewBaseModel(c)}
if v, ok := raw["album"]; ok {
var album Album
json.Unmarshal(v, &album)
trailer.Album = &album
}
if v, ok := raw["artists"]; ok {
var artists []Artist
json.Unmarshal(v, &artists)
trailer.Artists = artists
}
if v, ok := raw["trailer"]; ok {
var trailerInfo TrailerInfo
json.Unmarshal(v, &trailerInfo)
trailer.Trailer = &trailerInfo
}
return trailer, nil
}
// DeList deserializes a list of AlbumTrailer from JSON data.
func (at *AlbumTrailer) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
trailer := &AlbumTrailer{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, trailer); err != nil {
continue
}
result = append(result, trailer)
}
return result, nil
}
+18 -39
View File
@@ -30,7 +30,7 @@ func TestAlbum_DeJSON(t *testing.T) {
"best": {"tracks": [{"id": "bt1", "title": "Best Track", "duration_ms": 200000}]}, "best": {"tracks": [{"id": "bt1", "title": "Best Track", "duration_ms": 200000}]},
"track_positions": [{"revision": 1, "timestamp": "2023-01-01T00:00:00Z", "position": 1, "from_cache": false}], "track_positions": [{"revision": 1, "timestamp": "2023-01-01T00:00:00Z", "position": 1, "from_cache": false}],
"similar_albums": [{"id": "sim1", "title": "Similar Album", "cover_uri": "https://example.com/sim.jpg", "year": 2022}], "similar_albums": [{"id": "sim1", "title": "Similar Album", "cover_uri": "https://example.com/sim.jpg", "year": 2022}],
"trailer": {"preview_uri": "https://example.com/preview.mp3", "track_id": "t1", "album_id": "123", "duration": 30} "trailer": {"available": true}
}`) }`)
album := &Album{BaseModel: *NewBaseModel(client)} album := &Album{BaseModel: *NewBaseModel(client)}
@@ -101,8 +101,8 @@ func TestAlbum_DeJSON(t *testing.T) {
if len(resultAlbum.Labels) != 1 { if len(resultAlbum.Labels) != 1 {
t.Errorf("Expected 1 label, got %d", len(resultAlbum.Labels)) t.Errorf("Expected 1 label, got %d", len(resultAlbum.Labels))
} }
if resultAlbum.Labels[0].Name != "Label 1" { if resultAlbum.Labels[0].String() != "Label 1" {
t.Errorf("Expected label name 'Label 1', got '%s'", resultAlbum.Labels[0].Name) t.Errorf("Expected label name 'Label 1', got '%s'", resultAlbum.Labels[0].String())
} }
// Test volumes // Test volumes
@@ -164,17 +164,8 @@ func TestAlbum_DeJSON(t *testing.T) {
if resultAlbum.Trailer == nil { if resultAlbum.Trailer == nil {
t.Fatal("Expected Trailer to be non-nil") t.Fatal("Expected Trailer to be non-nil")
} }
if resultAlbum.Trailer.PreviewURI != "https://example.com/preview.mp3" { if !resultAlbum.Trailer.Available {
t.Errorf("Expected PreviewURI 'https://example.com/preview.mp3', got '%s'", resultAlbum.Trailer.PreviewURI) t.Errorf("Expected Trailer.Available true, got false")
}
if resultAlbum.Trailer.TrackID != "t1" {
t.Errorf("Expected TrackID 't1', got '%s'", resultAlbum.Trailer.TrackID)
}
if resultAlbum.Trailer.AlbumID != "123" {
t.Errorf("Expected AlbumID '123', got '%s'", resultAlbum.Trailer.AlbumID)
}
if resultAlbum.Trailer.Duration != 30 {
t.Errorf("Expected Duration 30, got %d", resultAlbum.Trailer.Duration)
} }
} }
@@ -308,50 +299,38 @@ func TestAlbumMeta_DeList(t *testing.T) {
} }
} }
func TestTrailerInfo_DeJSON(t *testing.T) { func TestAlbumTrailerStatus_DeJSON(t *testing.T) {
client := newMockClient() client := newMockClient()
data := []byte(`{ data := []byte(`{
"preview_uri": "https://example.com/preview.mp3", "available": true
"track_id": "track123",
"album_id": "album456",
"duration": 45
}`) }`)
trailer := &TrailerInfo{BaseModel: *NewBaseModel(client)} status := &AlbumTrailerStatus{BaseModel: *NewBaseModel(client)}
result, err := trailer.DeJSON(data, client) result, err := status.DeJSON(data, client)
if err != nil { if err != nil {
t.Fatalf("DeJSON failed: %v", err) t.Fatalf("DeJSON failed: %v", err)
} }
resultTrailer := result.(*TrailerInfo) resultStatus := result.(*AlbumTrailerStatus)
if resultTrailer.PreviewURI != "https://example.com/preview.mp3" { if !resultStatus.Available {
t.Errorf("Expected PreviewURI 'https://example.com/preview.mp3', got '%s'", resultTrailer.PreviewURI) t.Errorf("Expected Available true, got false")
}
if resultTrailer.TrackID != "track123" {
t.Errorf("Expected TrackID 'track123', got '%s'", resultTrailer.TrackID)
}
if resultTrailer.AlbumID != "album456" {
t.Errorf("Expected AlbumID 'album456', got '%s'", resultTrailer.AlbumID)
}
if resultTrailer.Duration != 45 {
t.Errorf("Expected Duration 45, got %d", resultTrailer.Duration)
} }
} }
func TestTrailerInfo_DeList(t *testing.T) { func TestAlbumTrailerStatus_DeList(t *testing.T) {
client := newMockClient() client := newMockClient()
data := []byte(`[ data := []byte(`[
{"preview_uri": "https://example.com/preview1.mp3", "track_id": "t1", "album_id": "a1", "duration": 30}, {"available": true},
{"preview_uri": "https://example.com/preview2.mp3", "track_id": "t2", "album_id": "a2", "duration": 45} {"available": false}
]`) ]`)
trailer := &TrailerInfo{BaseModel: *NewBaseModel(client)} status := &AlbumTrailerStatus{BaseModel: *NewBaseModel(client)}
result, err := trailer.DeList(data, client) result, err := status.DeList(data, client)
if err != nil { if err != nil {
t.Fatalf("DeList failed: %v", err) t.Fatalf("DeList failed: %v", err)
} }
if len(result) != 2 { if len(result) != 2 {
t.Errorf("Expected 2 trailers, got %d", len(result)) t.Errorf("Expected 2 statuses, got %d", len(result))
} }
} }
+561 -41
View File
@@ -1,32 +1,32 @@
// Package models provides data models for the Yandex Music API.
package models package models
import ( import (
"encoding/json" "encoding/json"
) )
// Artist represents a full artist // Artist represents a full artist from the Yandex Music API.
type Artist struct { type Artist struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Name string `json:"name"` Name string `json:"name"`
CoverURI string `json:"cover_uri"` CoverURI string `json:"cover_uri"`
Genres []string `json:"genres"` Genres []string `json:"genres"`
Tracks int `json:"tracks"` Tracks int `json:"tracks"`
Albums int `json:"albums"` Albums int `json:"albums"`
Link string `json:"link"`
Available bool `json:"available"` Available bool `json:"available"`
Description string `json:"description"`
Composer bool `json:"composer"` Composer bool `json:"composer"`
Various bool `json:"various"` Various bool `json:"various"`
Decomposed []Artist `json:"decomposed"` Decomposed []Decomposed `json:"decomposed"`
Ratings *Ratings `json:"ratings"` Ratings *Ratings `json:"ratings"`
Counts *Counts `json:"counts"` Counts *Counts `json:"counts"`
SocialLinks []SocialLink `json:"social_links"` Links []Link `json:"social_links"`
Description *Description `json:"description"`
// Additional fields // Additional fields
IDForFrom string `json:"id_for_from"` IDForFrom string `json:"id_for_from"`
Cover *Cover `json:"cover"` Cover *Cover `json:"cover"`
BriefInfo *ArtistBriefInfo `json:"brief_info"` BriefInfo *ArtistBriefInfo `json:"brief_info"`
Donations *ArtistDonations `json:"donations"` DonationInfo *ArtistDonationInfo `json:"donation_info"`
Concerts []Concert `json:"concerts"` Concerts []Concert `json:"concerts"`
Clips []Clip `json:"clips"` Clips []Clip `json:"clips"`
Similar []Artist `json:"similar"` Similar []Artist `json:"similar"`
@@ -34,7 +34,7 @@ type Artist struct {
TracksData []Track `json:"tracks_data"` TracksData []Track `json:"tracks_data"`
} }
// DeJSON deserializes an Artist from JSON // DeJSON deserializes an Artist from JSON data.
func (a *Artist) DeJSON(data []byte, c ClientInterface) (Model, error) { func (a *Artist) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -62,15 +62,9 @@ func (a *Artist) DeJSON(data []byte, c ClientInterface) (Model, error) {
if v, ok := raw["albums"]; ok { if v, ok := raw["albums"]; ok {
json.Unmarshal(v, &artist.Albums) json.Unmarshal(v, &artist.Albums)
} }
if v, ok := raw["link"]; ok {
json.Unmarshal(v, &artist.Link)
}
if v, ok := raw["available"]; ok { if v, ok := raw["available"]; ok {
json.Unmarshal(v, &artist.Available) json.Unmarshal(v, &artist.Available)
} }
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &artist.Description)
}
if v, ok := raw["composer"]; ok { if v, ok := raw["composer"]; ok {
json.Unmarshal(v, &artist.Composer) json.Unmarshal(v, &artist.Composer)
} }
@@ -83,10 +77,11 @@ func (a *Artist) DeJSON(data []byte, c ClientInterface) (Model, error) {
// Nested objects // Nested objects
if v, ok := raw["decomposed"]; ok { if v, ok := raw["decomposed"]; ok {
var decomposed []Artist var decomposed []Decomposed
json.Unmarshal(v, &decomposed) if err := json.Unmarshal(v, &decomposed); err == nil {
artist.Decomposed = decomposed artist.Decomposed = decomposed
} }
}
if v, ok := raw["ratings"]; ok { if v, ok := raw["ratings"]; ok {
var ratings Ratings var ratings Ratings
json.Unmarshal(v, &ratings) json.Unmarshal(v, &ratings)
@@ -98,9 +93,10 @@ func (a *Artist) DeJSON(data []byte, c ClientInterface) (Model, error) {
artist.Counts = &counts artist.Counts = &counts
} }
if v, ok := raw["social_links"]; ok { if v, ok := raw["social_links"]; ok {
var links []SocialLink var links []Link
json.Unmarshal(v, &links) if err := json.Unmarshal(v, &links); err == nil {
artist.SocialLinks = links artist.Links = links
}
} }
if v, ok := raw["cover"]; ok { if v, ok := raw["cover"]; ok {
var cover Cover var cover Cover
@@ -112,10 +108,10 @@ func (a *Artist) DeJSON(data []byte, c ClientInterface) (Model, error) {
json.Unmarshal(v, &brief) json.Unmarshal(v, &brief)
artist.BriefInfo = &brief artist.BriefInfo = &brief
} }
if v, ok := raw["donations"]; ok { if v, ok := raw["donation_info"]; ok {
var donations ArtistDonations var donation ArtistDonationInfo
json.Unmarshal(v, &donations) json.Unmarshal(v, &donation)
artist.Donations = &donations artist.DonationInfo = &donation
} }
if v, ok := raw["concerts"]; ok { if v, ok := raw["concerts"]; ok {
var concerts []Concert var concerts []Concert
@@ -146,7 +142,7 @@ func (a *Artist) DeJSON(data []byte, c ClientInterface) (Model, error) {
return artist, nil return artist, nil
} }
// DeList deserializes a list of Artists // DeList deserializes a list of Artists from JSON data.
func (a *Artist) DeList(data []byte, c ClientInterface) ([]Model, error) { func (a *Artist) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -164,27 +160,29 @@ func (a *Artist) DeList(data []byte, c ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// Ratings represents artist ratings // Ratings represents artist ratings.
type Ratings struct { type Ratings struct {
Rating float64 `json:"rating"` Rating int `json:"rating"`
Votes int `json:"votes"` Votes int `json:"votes"`
Week int `json:"week"`
Day int `json:"day"`
} }
// Counts represents artist counts // Counts represents artist counts.
type Counts struct { type Counts struct {
Tracks int `json:"tracks"` Tracks int `json:"tracks"`
Albums int `json:"albums"` Albums int `json:"albums"`
Folders int `json:"folders"` Folders int `json:"folders"`
} }
// SocialLink represents a social media link // SocialLink represents a social media link.
type SocialLink struct { type SocialLink struct {
URL string `json:"url"` URL string `json:"url"`
Title string `json:"title"` Title string `json:"title"`
Type string `json:"type"` Type string `json:"type"`
} }
// Cover represents cover image info // Cover represents cover image info.
type Cover struct { type Cover struct {
URI string `json:"uri"` URI string `json:"uri"`
Type string `json:"type"` Type string `json:"type"`
@@ -193,23 +191,25 @@ type Cover struct {
Background string `json:"background"` Background string `json:"background"`
} }
// ArtistBriefInfo represents brief artist info // ArtistBriefInfo represents brief artist info.
type ArtistBriefInfo struct { type ArtistBriefInfo struct {
BaseModel
Description string `json:"description"` Description string `json:"description"`
Available bool `json:"available"` Available bool `json:"available"`
} }
// ArtistDonations represents artist donations info // ArtistDonations represents artist donations info.
type ArtistDonations struct { type ArtistDonations struct {
BaseModel
Items []DonationItem `json:"items"` Items []DonationItem `json:"items"`
Total int `json:"total"` Total int `json:"total"`
Goal int `json:"goal"` Goal int `json:"goal"`
Raised int `json:"raised"` Raised int `json:"raised"`
} }
// DonationItem represents a donation item // DonationItem represents a donation item.
type DonationItem struct { type DonationItem struct {
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
Amount int `json:"amount"` Amount int `json:"amount"`
@@ -218,10 +218,182 @@ type DonationItem struct {
Currency string `json:"currency"` Currency string `json:"currency"`
} }
// ConcertShort represents a concert (minimal for artist) // Decomposed represents a decomposed artist that can be either a string or an Artist object.
type Decomposed struct {
Artist *Artist `json:"-"`
Str string `json:"-"`
IsStr bool `json:"-"`
}
// UnmarshalJSON implements json.Unmarshaler, accepting both string and Artist object.
func (d *Decomposed) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err == nil {
d.Str = s
d.IsStr = true
return nil
}
var artist Artist
if err := json.Unmarshal(data, &artist); err != nil {
return err
}
d.Artist = &artist
d.IsStr = false
return nil
}
// MarshalJSON implements json.Marshaler.
func (d Decomposed) MarshalJSON() ([]byte, error) {
if d.IsStr {
return json.Marshal(d.Str)
}
return json.Marshal(d.Artist)
}
// Link represents a link to an artist's official page or social network.
type Link struct {
BaseModel
Title string `json:"title"`
Href string `json:"href"`
Type string `json:"type"`
SocialNetwork string `json:"social_network"`
}
// DeJSON deserializes Link from JSON data.
func (l *Link) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
link := &Link{BaseModel: *NewBaseModel(c)}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &link.Title)
}
if v, ok := raw["href"]; ok {
json.Unmarshal(v, &link.Href)
}
if v, ok := raw["type"]; ok {
json.Unmarshal(v, &link.Type)
}
if v, ok := raw["social_network"]; ok {
json.Unmarshal(v, &link.SocialNetwork)
}
return link, nil
}
// DeList deserializes a list of Links from JSON data.
func (l *Link) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
link := &Link{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, link); err != nil {
continue
}
result = append(result, link)
}
return result, nil
}
// Description represents artist description.
type Description struct {
BaseModel
Text string `json:"text"`
URI string `json:"uri"`
}
// DeJSON deserializes Description from JSON data.
func (d *Description) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
desc := &Description{BaseModel: *NewBaseModel(c)}
if v, ok := raw["text"]; ok {
json.Unmarshal(v, &desc.Text)
}
if v, ok := raw["uri"]; ok {
json.Unmarshal(v, &desc.URI)
}
return desc, nil
}
// DeList deserializes a list of Descriptions from JSON data.
func (d *Description) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
desc := &Description{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, desc); err != nil {
continue
}
result = append(result, desc)
}
return result, nil
}
// ArtistDonationInfo represents artist donation info.
type ArtistDonationInfo struct {
BaseModel
Title string `json:"title"`
URL string `json:"url"`
}
// DeJSON deserializes ArtistDonationInfo from JSON data.
func (adi *ArtistDonationInfo) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
info := &ArtistDonationInfo{BaseModel: *NewBaseModel(c)}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &info.Title)
}
if v, ok := raw["url"]; ok {
json.Unmarshal(v, &info.URL)
}
return info, nil
}
// DeList deserializes a list of ArtistDonationInfo from JSON data.
func (adi *ArtistDonationInfo) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
info := &ArtistDonationInfo{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, info); err != nil {
continue
}
result = append(result, info)
}
return result, nil
}
// ConcertShort represents a concert (minimal for artist).
type ConcertShort struct { type ConcertShort struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Date string `json:"date"` Date string `json:"date"`
Venue string `json:"venue"` Venue string `json:"venue"`
@@ -233,7 +405,7 @@ type ConcertShort struct {
Available bool `json:"available"` Available bool `json:"available"`
} }
// DeJSON deserializes a ConcertShort from JSON // DeJSON deserializes a ConcertShort from JSON data.
func (c *ConcertShort) DeJSON(data []byte, client ClientInterface) (Model, error) { func (c *ConcertShort) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -276,7 +448,7 @@ func (c *ConcertShort) DeJSON(data []byte, client ClientInterface) (Model, error
return concert, nil return concert, nil
} }
// DeList deserializes a list of ConcertShort // DeList deserializes a list of ConcertShort from JSON data.
func (c *ConcertShort) DeList(data []byte, client ClientInterface) ([]Model, error) { func (c *ConcertShort) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -294,10 +466,10 @@ func (c *ConcertShort) DeList(data []byte, client ClientInterface) ([]Model, err
return result, nil return result, nil
} }
// ClipShort represents a clip (minimal for artist) // ClipShort represents a clip (minimal for artist).
type ClipShort struct { type ClipShort struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Duration int `json:"duration"` Duration int `json:"duration"`
CoverURI string `json:"cover_uri"` CoverURI string `json:"cover_uri"`
@@ -308,7 +480,7 @@ type ClipShort struct {
CreatedAt string `json:"created_at"` CreatedAt string `json:"created_at"`
} }
// DeJSON deserializes a ClipShort from JSON // DeJSON deserializes a ClipShort from JSON data.
func (c *ClipShort) DeJSON(data []byte, client ClientInterface) (Model, error) { func (c *ClipShort) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -348,7 +520,7 @@ func (c *ClipShort) DeJSON(data []byte, client ClientInterface) (Model, error) {
return clip, nil return clip, nil
} }
// DeList deserializes a list of ClipShort // DeList deserializes a list of ClipShort from JSON data.
func (c *ClipShort) DeList(data []byte, client ClientInterface) ([]Model, error) { func (c *ClipShort) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -365,3 +537,351 @@ func (c *ClipShort) DeList(data []byte, client ClientInterface) ([]Model, error)
} }
return result, nil return result, nil
} }
// ArtistSimilar represents similar artists.
type ArtistSimilar struct {
BaseModel
Artist *Artist `json:"artist"`
SimilarArtists []Artist `json:"similar_artists"`
}
// DeJSON deserializes ArtistSimilar from JSON data.
func (as *ArtistSimilar) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
similar := &ArtistSimilar{BaseModel: *NewBaseModel(c)}
if v, ok := raw["artist"]; ok {
var artist Artist
json.Unmarshal(v, &artist)
similar.Artist = &artist
}
if v, ok := raw["similar_artists"]; ok {
var artists []Artist
json.Unmarshal(v, &artists)
similar.SimilarArtists = artists
}
return similar, nil
}
// DeList deserializes a list of ArtistSimilar from JSON data.
func (as *ArtistSimilar) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
similar := &ArtistSimilar{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, similar); err != nil {
continue
}
result = append(result, similar)
}
return result, nil
}
// ArtistLinks represents links to artist's official pages or social networks.
type ArtistLinks struct {
BaseModel
Links []Link `json:"links"`
}
// DeJSON deserializes ArtistLinks from JSON data.
func (al *ArtistLinks) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
links := &ArtistLinks{BaseModel: *NewBaseModel(c)}
if v, ok := raw["links"]; ok {
var linkList []Link
json.Unmarshal(v, &linkList)
links.Links = linkList
}
return links, nil
}
// DeList deserializes a list of ArtistLinks from JSON data.
func (al *ArtistLinks) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
links := &ArtistLinks{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, links); err != nil {
continue
}
result = append(result, links)
}
return result, nil
}
// ArtistAbout represents information "About artist".
type ArtistAbout struct {
BaseModel
Artist *Artist `json:"artist"`
Stats *Stats `json:"stats"`
Description string `json:"description"`
Links []ArtistLink `json:"links"`
Covers []Cover `json:"covers"`
ArtistType string `json:"artist_type"`
}
// DeJSON deserializes ArtistAbout from JSON data.
func (aa *ArtistAbout) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
about := &ArtistAbout{BaseModel: *NewBaseModel(c)}
if v, ok := raw["artist"]; ok {
var artist Artist
json.Unmarshal(v, &artist)
about.Artist = &artist
}
if v, ok := raw["stats"]; ok {
var stats Stats
json.Unmarshal(v, &stats)
about.Stats = &stats
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &about.Description)
}
if v, ok := raw["links"]; ok {
var linkList []ArtistLink
json.Unmarshal(v, &linkList)
about.Links = linkList
}
if v, ok := raw["covers"]; ok {
var covers []Cover
json.Unmarshal(v, &covers)
about.Covers = covers
}
if v, ok := raw["artist_type"]; ok {
json.Unmarshal(v, &about.ArtistType)
}
return about, nil
}
// DeList deserializes a list of ArtistAbout from JSON data.
func (aa *ArtistAbout) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
about := &ArtistAbout{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, about); err != nil {
continue
}
result = append(result, about)
}
return result, nil
}
// ArtistInfo represents detailed artist info.
type ArtistInfo struct {
BaseModel
Artist *Artist `json:"artist"`
LikesCount int `json:"likes_count"`
Stats *Stats `json:"stats"`
Trailer *ArtistTrailerStatus `json:"trailer"`
Covers []Cover `json:"covers"`
Description string `json:"description"`
ArtistType string `json:"artist_type"`
}
// DeJSON deserializes ArtistInfo from JSON data.
func (ai *ArtistInfo) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
info := &ArtistInfo{BaseModel: *NewBaseModel(c)}
if v, ok := raw["artist"]; ok {
var artist Artist
json.Unmarshal(v, &artist)
info.Artist = &artist
}
if v, ok := raw["likes_count"]; ok {
json.Unmarshal(v, &info.LikesCount)
}
if v, ok := raw["stats"]; ok {
var stats Stats
json.Unmarshal(v, &stats)
info.Stats = &stats
}
if v, ok := raw["trailer"]; ok {
var trailer ArtistTrailerStatus
json.Unmarshal(v, &trailer)
info.Trailer = &trailer
}
if v, ok := raw["covers"]; ok {
var covers []Cover
json.Unmarshal(v, &covers)
info.Covers = covers
}
if v, ok := raw["description"]; ok {
json.Unmarshal(v, &info.Description)
}
if v, ok := raw["artist_type"]; ok {
json.Unmarshal(v, &info.ArtistType)
}
return info, nil
}
// DeList deserializes a list of ArtistInfo from JSON data.
func (ai *ArtistInfo) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
info := &ArtistInfo{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, info); err != nil {
continue
}
result = append(result, info)
}
return result, nil
}
// ArtistSkeleton represents artist page skeleton.
type ArtistSkeleton struct {
BaseModel
ID string `json:"id"`
Title string `json:"title"`
Blocks []SkeletonBlock `json:"blocks"`
}
// DeJSON deserializes ArtistSkeleton from JSON data.
func (as *ArtistSkeleton) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
skeleton := &ArtistSkeleton{BaseModel: *NewBaseModel(c)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &skeleton.ID)
}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &skeleton.Title)
}
if v, ok := raw["blocks"]; ok {
var blocks []SkeletonBlock
json.Unmarshal(v, &blocks)
skeleton.Blocks = blocks
}
return skeleton, nil
}
// DeList deserializes a list of ArtistSkeleton from JSON data.
func (as *ArtistSkeleton) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
skeleton := &ArtistSkeleton{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, skeleton); err != nil {
continue
}
result = append(result, skeleton)
}
return result, nil
}
// ArtistTrailerStatus represents trailer availability for artist.
type ArtistTrailerStatus struct {
BaseModel
Available bool `json:"available"`
}
// DeJSON deserializes ArtistTrailerStatus from JSON data.
func (ats *ArtistTrailerStatus) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
status := &ArtistTrailerStatus{BaseModel: *NewBaseModel(c)}
if v, ok := raw["available"]; ok {
json.Unmarshal(v, &status.Available)
}
return status, nil
}
// DeList deserializes a list of ArtistTrailerStatus from JSON data.
func (ats *ArtistTrailerStatus) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
status := &ArtistTrailerStatus{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, status); err != nil {
continue
}
result = append(result, status)
}
return result, nil
}
// ArtistLink represents a link for artist about page.
type ArtistLink struct {
BaseModel
Title string `json:"title"`
Subtitle string `json:"subtitle,omitempty"`
URL string `json:"url,omitempty"`
ImgURL string `json:"img_url,omitempty"`
}
// Stats represents artist stats.
type Stats struct {
BaseModel
Rating int `json:"rating"`
Votes int `json:"votes"`
Month int `json:"month"`
Week int `json:"week"`
Day int `json:"day"`
}
// SkeletonBlock represents a skeleton block.
type SkeletonBlock struct {
BaseModel
ID string `json:"id"`
Type string `json:"type"`
Data json.RawMessage `json:"data"`
}
+56
View File
@@ -0,0 +1,56 @@
// Package models provides data models for the Yandex Music API.
package models
import (
"encoding/json"
)
// ArtistAlbums represents a page of albums by an artist.
type ArtistAlbums struct {
BaseModel
Albums []Album `json:"albums"`
Pager *Pager `json:"pager"`
}
// DeJSON deserializes an ArtistAlbums from JSON data.
func (aa *ArtistAlbums) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
albums := &ArtistAlbums{BaseModel: *NewBaseModel(c)}
if v, ok := raw["albums"]; ok {
var albumList []Album
if err := json.Unmarshal(v, &albumList); err == nil {
albums.Albums = albumList
}
}
if v, ok := raw["pager"]; ok {
var pager Pager
if err := json.Unmarshal(v, &pager); err == nil {
albums.Pager = &pager
}
}
return albums, nil
}
// DeList deserializes a list of ArtistAlbums from JSON data.
func (aa *ArtistAlbums) 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 {
albums := &ArtistAlbums{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, albums); err != nil {
continue
}
result = append(result, albums)
}
return result, nil
}
+56
View File
@@ -0,0 +1,56 @@
// Package models provides data models for the Yandex Music API.
package models
import (
"encoding/json"
)
// ArtistTracks represents a page of tracks by an artist.
type ArtistTracks struct {
BaseModel
Tracks []Track `json:"tracks"`
Pager *Pager `json:"pager"`
}
// DeJSON deserializes an ArtistTracks from JSON data.
func (at *ArtistTracks) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
tracks := &ArtistTracks{BaseModel: *NewBaseModel(c)}
if v, ok := raw["tracks"]; ok {
var trackList []Track
if err := json.Unmarshal(v, &trackList); err == nil {
tracks.Tracks = trackList
}
}
if v, ok := raw["pager"]; ok {
var pager Pager
if err := json.Unmarshal(v, &pager); err == nil {
tracks.Pager = &pager
}
}
return tracks, nil
}
// DeList deserializes a list of ArtistTracks from JSON data.
func (at *ArtistTracks) 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 {
tracks := &ArtistTracks{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, tracks); err != nil {
continue
}
result = append(result, tracks)
}
return result, nil
}
+114 -13
View File
@@ -1,40 +1,47 @@
// Package models provides data models for the Yandex Music API.
// All models implement the Model interface with DeJSON/DeList deserialization methods.
package models package models
import ( import (
"encoding/json" "encoding/json"
"fmt"
"strconv"
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils"
) )
// Model is the base interface for all models // Model is the base interface for all models.
// All model types implement this interface for JSON deserialization and serialization.
type Model interface { type Model interface {
// DeJSON deserializes from JSON // DeJSON deserializes a model from JSON data.
DeJSON(data []byte, c ClientInterface) (Model, error) DeJSON(data []byte, c ClientInterface) (Model, error)
// DeList deserializes a list of models // DeList deserializes a list of models from JSON data.
DeList(data []byte, c ClientInterface) ([]Model, error) DeList(data []byte, c ClientInterface) ([]Model, error)
// ToJSON serializes to JSON // ToJSON serializes the model to JSON.
ToJSON() ([]byte, error) ToJSON() ([]byte, error)
// ToMap converts to map // ToMap converts the model to a map.
ToMap() (map[string]any, error) ToMap() (map[string]any, error)
} }
// BaseModel provides common functionality for models // BaseModel provides common functionality for models, including a client reference.
type BaseModel struct { type BaseModel struct {
Client ClientInterface `json:"-"` Client ClientInterface `json:"-"`
} }
// NewBaseModel creates a new BaseModel // NewBaseModel creates a new BaseModel with the given client interface.
func NewBaseModel(c ClientInterface) *BaseModel { func NewBaseModel(c ClientInterface) *BaseModel {
return &BaseModel{Client: c} return &BaseModel{Client: c}
} }
// DeJSON implements Model.DeJSON for basic models // DeJSON implements Model.DeJSON for basic models.
// This should be overridden by concrete types.
func (b *BaseModel) DeJSON(data []byte, c ClientInterface) (Model, error) { func (b *BaseModel) DeJSON(data []byte, c ClientInterface) (Model, error) {
// This should be overridden by concrete types // This should be overridden by concrete types
return nil, nil return nil, nil
} }
// DeList implements Model.DeList for basic models // DeList implements Model.DeList for basic models.
// This should be overridden by concrete types.
func (b *BaseModel) DeList(data []byte, c ClientInterface) ([]Model, error) { func (b *BaseModel) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -49,12 +56,12 @@ func (b *BaseModel) DeList(data []byte, c ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// ToJSON serializes the model to JSON // ToJSON serializes the model to JSON using the default JSON backend.
func (b *BaseModel) ToJSON() ([]byte, error) { func (b *BaseModel) ToJSON() ([]byte, error) {
return utils.Marshal(b) return utils.Marshal(b)
} }
// ToMap converts the model to a map // ToMap converts the model to a map by serializing to JSON and unmarshaling into a map.
func (b *BaseModel) ToMap() (map[string]any, error) { func (b *BaseModel) ToMap() (map[string]any, error) {
data, err := b.ToJSON() data, err := b.ToJSON()
if err != nil { if err != nil {
@@ -67,12 +74,106 @@ func (b *BaseModel) ToMap() (map[string]any, error) {
return result, nil return result, nil
} }
// SetClient sets the client on the model // SetClient sets the client interface on the model.
func (b *BaseModel) SetClient(c ClientInterface) { func (b *BaseModel) SetClient(c ClientInterface) {
b.Client = c b.Client = c
} }
// GetClient returns the client // GetClient returns the client interface associated with the model.
func (b *BaseModel) GetClient() ClientInterface { func (b *BaseModel) GetClient() ClientInterface {
return b.Client return b.Client
} }
// JSONID is an ID that can be represented as either a JSON string or a JSON number.
// The Yandex Music API sometimes returns IDs as numbers (e.g., 12345) and sometimes
// as strings (e.g., "12345"). JSONID accepts both and stores the value as a string.
type JSONID string
// UnmarshalJSON implements json.Unmarshaler, accepting both JSON strings and numbers.
func (id *JSONID) UnmarshalJSON(data []byte) error {
// Try string first
var s string
if err := json.Unmarshal(data, &s); err == nil {
*id = JSONID(s)
return nil
}
// Try number (float64 is the default JSON number decoding)
var f float64
if err := json.Unmarshal(data, &f); err != nil {
return fmt.Errorf("cannot unmarshal %v into JSONID: %w", string(data), err)
}
*id = JSONID(strconv.FormatFloat(f, 'f', -1, 64))
return nil
}
// String returns the ID as a Go string.
func (id JSONID) String() string {
return string(id)
}
// R128 represents EBU R 128 loudness normalization parameters.
// The Yandex Music API returns this either as a number (e.g., -23.5) or as an object
// with fields i, tp, important_secs.
type R128 struct {
BaseModel
I float64 `json:"i"`
Tp float64 `json:"tp"`
ImportantSecs int `json:"important_secs"`
}
// UnmarshalJSON implements json.Unmarshaler, accepting both JSON numbers and objects.
func (r *R128) UnmarshalJSON(data []byte) error {
// Try float64 first (API sometimes returns just a number)
var f float64
if err := json.Unmarshal(data, &f); err == nil {
r.I = f
r.Tp = 0
r.ImportantSecs = 0
return nil
}
// Try object with fields i, tp, important_secs
var obj struct {
I float64 `json:"i"`
Tp float64 `json:"tp"`
ImportantSecs int `json:"important_secs"`
}
if err := json.Unmarshal(data, &obj); err != nil {
return err
}
r.I = obj.I
r.Tp = obj.Tp
r.ImportantSecs = obj.ImportantSecs
return nil
}
// Float64 returns the integrated loudness (i) value as a float64.
func (r R128) Float64() float64 {
return r.I
}
// DeJSON deserializes an R128 from JSON data.
func (r *R128) DeJSON(data []byte, c ClientInterface) (Model, error) {
r128 := &R128{BaseModel: *NewBaseModel(c)}
if err := r128.UnmarshalJSON(data); err != nil {
return nil, err
}
return r128, nil
}
// DeList deserializes a list of R128 from JSON data.
func (r *R128) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
r128 := &R128{BaseModel: *NewBaseModel(c)}
if err := r128.UnmarshalJSON(item); err != nil {
continue
}
result = append(result, r128)
}
return result, nil
}
+11 -2
View File
@@ -1,19 +1,28 @@
// Package models provides data models for the Yandex Music API.
package models package models
import ( import (
"context" "context"
) )
// ClientInterface defines the minimal client interface needed by models // ClientInterface defines the minimal client interface needed by models.
// Models use this to access the underlying request handler.
type ClientInterface interface { type ClientInterface interface {
// GetRequest returns the underlying request handler.
GetRequest() RequestInterface GetRequest() RequestInterface
} }
// RequestInterface defines the minimal request interface needed by models // RequestInterface defines the minimal request interface needed by models.
// It provides methods for making HTTP requests to the Yandex Music API.
type RequestInterface interface { type RequestInterface interface {
// Get performs a GET request to the given path with query parameters.
Get(ctx context.Context, path string, params map[string]string, result any) error Get(ctx context.Context, path string, params map[string]string, result any) error
// Post performs a POST request to the given path with a JSON body.
Post(ctx context.Context, path string, body any, result any) error Post(ctx context.Context, path string, body any, result any) error
// Put performs a PUT request to the given path with a JSON body.
Put(ctx context.Context, path string, body any, result any) error Put(ctx context.Context, path string, body any, result any) error
// Delete performs a DELETE request to the given path with an optional JSON body.
Delete(ctx context.Context, path string, body any, result any) error Delete(ctx context.Context, path string, body any, result any) error
// Download downloads a file from the given URL and returns the raw response body.
Download(ctx context.Context, url string) ([]byte, error) Download(ctx context.Context, url string) ([]byte, error)
} }
+61 -52
View File
@@ -1,30 +1,34 @@
// Package models provides data models for the Yandex Music API.
package models package models
import ( import (
"encoding/json" "encoding/json"
) )
// Clip represents a clip (short video) // Clip represents a clip (short video) from the Yandex Music API.
type Clip struct { type Clip struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Version string `json:"version"`
PlayerID string `json:"player_id"`
UUID string `json:"uuid"`
Thumbnail string `json:"thumbnail"`
PreviewURL string `json:"preview_url"`
Duration int `json:"duration"` Duration int `json:"duration"`
CoverURI string `json:"cover_uri"` TrackIDs []int `json:"track_ids"`
PreviewURI string `json:"preview_uri"` Artists []Artist `json:"artists"`
Available bool `json:"available"` Disclaimers []string `json:"disclaimers"`
Views int `json:"views"` Explicit bool `json:"explicit"`
Likes int `json:"likes"` Cover *Cover `json:"cover"`
CreatedAt string `json:"created_at"` ContentRestrictions *ContentRestrictions `json:"content_restrictions"`
Artist *Artist `json:"artist"` Client *ClientInterface `json:"-"`
Track *Track `json:"track"`
Playlist *Playlist `json:"playlist"`
ClipType string `json:"clip_type"` ClipType string `json:"clip_type"`
IsVertical bool `json:"is_vertical"` IsVertical bool `json:"is_vertical"`
AspectRatio string `json:"aspect_ratio"` AspectRatio string `json:"aspect_ratio"`
} }
// DeJSON deserializes a Clip from JSON // DeJSON deserializes a Clip from JSON data.
func (c *Clip) DeJSON(data []byte, client ClientInterface) (Model, error) { func (c *Clip) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -39,26 +43,47 @@ func (c *Clip) DeJSON(data []byte, client ClientInterface) (Model, error) {
if v, ok := raw["title"]; ok { if v, ok := raw["title"]; ok {
json.Unmarshal(v, &clip.Title) json.Unmarshal(v, &clip.Title)
} }
if v, ok := raw["version"]; ok {
json.Unmarshal(v, &clip.Version)
}
if v, ok := raw["player_id"]; ok {
json.Unmarshal(v, &clip.PlayerID)
}
if v, ok := raw["uuid"]; ok {
json.Unmarshal(v, &clip.UUID)
}
if v, ok := raw["thumbnail"]; ok {
json.Unmarshal(v, &clip.Thumbnail)
}
if v, ok := raw["preview_url"]; ok {
json.Unmarshal(v, &clip.PreviewURL)
}
if v, ok := raw["duration"]; ok { if v, ok := raw["duration"]; ok {
json.Unmarshal(v, &clip.Duration) json.Unmarshal(v, &clip.Duration)
} }
if v, ok := raw["cover_uri"]; ok { if v, ok := raw["track_ids"]; ok {
json.Unmarshal(v, &clip.CoverURI) json.Unmarshal(v, &clip.TrackIDs)
} }
if v, ok := raw["preview_uri"]; ok { if v, ok := raw["artists"]; ok {
json.Unmarshal(v, &clip.PreviewURI) var artists []Artist
json.Unmarshal(v, &artists)
clip.Artists = artists
} }
if v, ok := raw["available"]; ok { if v, ok := raw["disclaimers"]; ok {
json.Unmarshal(v, &clip.Available) json.Unmarshal(v, &clip.Disclaimers)
} }
if v, ok := raw["views"]; ok { if v, ok := raw["explicit"]; ok {
json.Unmarshal(v, &clip.Views) json.Unmarshal(v, &clip.Explicit)
} }
if v, ok := raw["likes"]; ok { if v, ok := raw["cover"]; ok {
json.Unmarshal(v, &clip.Likes) var cover Cover
json.Unmarshal(v, &cover)
clip.Cover = &cover
} }
if v, ok := raw["created_at"]; ok { if v, ok := raw["content_restrictions"]; ok {
json.Unmarshal(v, &clip.CreatedAt) var cr ContentRestrictions
json.Unmarshal(v, &cr)
clip.ContentRestrictions = &cr
} }
if v, ok := raw["clip_type"]; ok { if v, ok := raw["clip_type"]; ok {
json.Unmarshal(v, &clip.ClipType) json.Unmarshal(v, &clip.ClipType)
@@ -70,26 +95,10 @@ func (c *Clip) DeJSON(data []byte, client ClientInterface) (Model, error) {
json.Unmarshal(v, &clip.AspectRatio) json.Unmarshal(v, &clip.AspectRatio)
} }
if v, ok := raw["artist"]; ok {
var artist Artist
json.Unmarshal(v, &artist)
clip.Artist = &artist
}
if v, ok := raw["track"]; ok {
var track Track
json.Unmarshal(v, &track)
clip.Track = &track
}
if v, ok := raw["playlist"]; ok {
var playlist Playlist
json.Unmarshal(v, &playlist)
clip.Playlist = &playlist
}
return clip, nil return clip, nil
} }
// DeList deserializes a list of Clips // DeList deserializes a list of Clips from JSON data.
func (c *Clip) DeList(data []byte, client ClientInterface) ([]Model, error) { func (c *Clip) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -107,31 +116,31 @@ func (c *Clip) DeList(data []byte, client ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// ClipsWillLike represents clips the user might like // ClipsWillLike represents clips the user might like.
type ClipsWillLike struct { type ClipsWillLike struct {
BaseModel BaseModel
Clips []Clip `json:"clips"` Clips []Clip `json:"clips"`
Total int `json:"total"` Total int `json:"total"`
} }
// ArtistClipData represents artist clip data // ArtistClipData represents artist clip data.
type ArtistClipData struct { type ArtistClipData struct {
BaseModel BaseModel
Clips []Clip `json:"clips"` Clips []Clip `json:"clips"`
Total int `json:"total"` Total int `json:"total"`
} }
// ArtistClipItem represents an artist clip item // ArtistClipItem represents an artist clip item.
type ArtistClipItem struct { type ArtistClipItem struct {
BaseModel BaseModel
Clip *Clip `json:"clip"` Clip *Clip `json:"clip"`
Index int `json:"index"` Index int `json:"index"`
} }
// Shot represents a shot (short video) // Shot represents a shot (short video).
type Shot struct { type Shot struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
Duration int `json:"duration"` Duration int `json:"duration"`
@@ -150,7 +159,7 @@ type Shot struct {
AspectRatio string `json:"aspect_ratio"` AspectRatio string `json:"aspect_ratio"`
} }
// DeJSON deserializes a Shot from JSON // DeJSON deserializes a Shot from JSON data.
func (s *Shot) DeJSON(data []byte, client ClientInterface) (Model, error) { func (s *Shot) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -219,7 +228,7 @@ func (s *Shot) DeJSON(data []byte, client ClientInterface) (Model, error) {
return shot, nil return shot, nil
} }
// DeList deserializes a list of Shots // DeList deserializes a list of Shots from JSON data.
func (s *Shot) DeList(data []byte, client ClientInterface) ([]Model, error) { func (s *Shot) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -237,14 +246,14 @@ func (s *Shot) DeList(data []byte, client ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// ShotData represents shot data // ShotData represents shot data.
type ShotData struct { type ShotData struct {
BaseModel BaseModel
Shots []Shot `json:"shots"` Shots []Shot `json:"shots"`
Total int `json:"total"` Total int `json:"total"`
} }
// ShotEvent represents a shot event // ShotEvent represents a shot event.
type ShotEvent struct { type ShotEvent struct {
BaseModel BaseModel
EventID string `json:"event_id"` EventID string `json:"event_id"`
@@ -254,10 +263,10 @@ type ShotEvent struct {
Action string `json:"action"` Action string `json:"action"`
} }
// ShotType represents a shot type // ShotType represents a shot type.
type ShotType struct { type ShotType struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Description string `json:"description"` Description string `json:"description"`
Icon string `json:"icon"` Icon string `json:"icon"`
+100 -21
View File
@@ -1,17 +1,19 @@
// Package models provides data models for the Yandex Music API.
package models package models
import ( import (
"encoding/json" "encoding/json"
) )
// Concert represents a concert // Concert represents a concert from the Yandex Music API.
type Concert struct { type Concert struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
Date string `json:"date"` Date string `json:"date"`
Time string `json:"time"` Time string `json:"time"`
Datetime string `json:"datetime"`
Venue *ConcertVenue `json:"venue"` Venue *ConcertVenue `json:"venue"`
Location *ConcertLocation `json:"location"` Location *ConcertLocation `json:"location"`
MinPrice int `json:"min_price"` MinPrice int `json:"min_price"`
@@ -26,9 +28,16 @@ type Concert struct {
Cashback *ConcertCashback `json:"cashback"` Cashback *ConcertCashback `json:"cashback"`
Tabs []ConcertTabConfig `json:"tabs"` Tabs []ConcertTabConfig `json:"tabs"`
Skeleton *ConcertSkeleton `json:"skeleton"` Skeleton *ConcertSkeleton `json:"skeleton"`
AfishaURL string `json:"afisha_url"`
City string `json:"city"`
Place string `json:"place"`
Address string `json:"address"`
ContentRating string `json:"content_rating"`
DataSessionID string `json:"data_session_id"`
EventInfo *ConcertEventInfo `json:"event_info"`
} }
// DeJSON deserializes a Concert from JSON // DeJSON deserializes a Concert from JSON data.
func (c *Concert) DeJSON(data []byte, client ClientInterface) (Model, error) { func (c *Concert) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -76,6 +85,30 @@ func (c *Concert) DeJSON(data []byte, client ClientInterface) (Model, error) {
if v, ok := raw["genres"]; ok { if v, ok := raw["genres"]; ok {
json.Unmarshal(v, &concert.Genres) json.Unmarshal(v, &concert.Genres)
} }
if v, ok := raw["datetime"]; ok {
json.Unmarshal(v, &concert.Datetime)
}
if v, ok := raw["afisha_url"]; ok {
json.Unmarshal(v, &concert.AfishaURL)
}
if v, ok := raw["city"]; ok {
json.Unmarshal(v, &concert.City)
}
if v, ok := raw["place"]; ok {
json.Unmarshal(v, &concert.Place)
}
if v, ok := raw["address"]; ok {
json.Unmarshal(v, &concert.Address)
}
if v, ok := raw["content_rating"]; ok {
json.Unmarshal(v, &concert.ContentRating)
}
if v, ok := raw["data_session_id"]; ok {
json.Unmarshal(v, &concert.DataSessionID)
}
if v, ok := raw["event_info"]; ok {
json.Unmarshal(v, &concert.EventInfo)
}
if v, ok := raw["venue"]; ok { if v, ok := raw["venue"]; ok {
var venue ConcertVenue var venue ConcertVenue
@@ -111,7 +144,7 @@ func (c *Concert) DeJSON(data []byte, client ClientInterface) (Model, error) {
return concert, nil return concert, nil
} }
// DeList deserializes a list of Concerts // DeList deserializes a list of Concerts from JSON data.
func (c *Concert) DeList(data []byte, client ClientInterface) ([]Model, error) { func (c *Concert) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -129,10 +162,10 @@ func (c *Concert) DeList(data []byte, client ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// ConcertVenue represents a concert venue // ConcertVenue represents a concert venue.
type ConcertVenue struct { type ConcertVenue struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Address string `json:"address"` Address string `json:"address"`
City string `json:"city"` City string `json:"city"`
@@ -141,10 +174,10 @@ type ConcertVenue struct {
ImageURI string `json:"image_uri"` ImageURI string `json:"image_uri"`
} }
// ConcertLocation represents a concert location // ConcertLocation represents a concert location.
type ConcertLocation struct { type ConcertLocation struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Name string `json:"name"` Name string `json:"name"`
City string `json:"city"` City string `json:"city"`
Country string `json:"country"` Country string `json:"country"`
@@ -152,7 +185,7 @@ type ConcertLocation struct {
Venues []ConcertVenue `json:"venues"` Venues []ConcertVenue `json:"venues"`
} }
// ConcertLocations represents concert locations // ConcertLocations represents concert locations.
type ConcertLocations struct { type ConcertLocations struct {
BaseModel BaseModel
Locations []ConcertLocation `json:"locations"` Locations []ConcertLocation `json:"locations"`
@@ -161,7 +194,7 @@ type ConcertLocations struct {
PageSize int `json:"page_size"` PageSize int `json:"page_size"`
} }
// ConcertFeed represents a concert feed // ConcertFeed represents a concert feed.
type ConcertFeed struct { type ConcertFeed struct {
BaseModel BaseModel
Concerts []Concert `json:"concerts"` Concerts []Concert `json:"concerts"`
@@ -170,14 +203,14 @@ type ConcertFeed struct {
PageSize int `json:"page_size"` PageSize int `json:"page_size"`
} }
// ConcertInfo represents concert info // ConcertInfo represents concert info.
type ConcertInfo struct { type ConcertInfo struct {
BaseModel BaseModel
Concert *Concert `json:"concert"` Concert *Concert `json:"concert"`
Related []Concert `json:"related"` Related []Concert `json:"related"`
} }
// ConcertEventInfo represents concert event info // ConcertEventInfo represents concert event info.
type ConcertEventInfo struct { type ConcertEventInfo struct {
BaseModel BaseModel
EventID string `json:"event_id"` EventID string `json:"event_id"`
@@ -186,7 +219,7 @@ type ConcertEventInfo struct {
Data json.RawMessage `json:"data"` Data json.RawMessage `json:"data"`
} }
// ConcertDescription represents concert description // ConcertDescription represents concert description.
type ConcertDescription struct { type ConcertDescription struct {
BaseModel BaseModel
Text string `json:"text"` Text string `json:"text"`
@@ -194,7 +227,7 @@ type ConcertDescription struct {
ShortText string `json:"short_text"` ShortText string `json:"short_text"`
} }
// ConcertCashback represents concert cashback // ConcertCashback represents concert cashback.
type ConcertCashback struct { type ConcertCashback struct {
BaseModel BaseModel
Amount int `json:"amount"` Amount int `json:"amount"`
@@ -203,41 +236,87 @@ type ConcertCashback struct {
Expires string `json:"expires"` Expires string `json:"expires"`
} }
// ConcertMinPrice represents concert min price // ConcertMinPrice represents concert min price.
type ConcertMinPrice struct { type ConcertMinPrice struct {
BaseModel BaseModel
Price int `json:"price"` Price int `json:"price"`
Currency string `json:"currency"` Currency string `json:"currency"`
} }
// ConcertTabConfig represents concert tab config // ConcertTabConfig represents concert tab config.
type ConcertTabConfig struct { type ConcertTabConfig struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Type string `json:"type"` Type string `json:"type"`
Active bool `json:"active"` Active bool `json:"active"`
Range *ConcertTabRange `json:"range"` Range *ConcertTabRange `json:"range"`
} }
// ConcertTabRange represents concert tab range // ConcertTabRange represents concert tab range.
type ConcertTabRange struct { type ConcertTabRange struct {
BaseModel BaseModel
From string `json:"from"` From string `json:"from"`
To string `json:"to"` To string `json:"to"`
} }
// ConcertSkeleton represents concert skeleton // ConcertSkeleton represents concert skeleton.
type ConcertSkeleton struct { type ConcertSkeleton struct {
BaseModel BaseModel
Blocks []ConcertSkeletonBlock `json:"blocks"` Blocks []ConcertSkeletonBlock `json:"blocks"`
} }
// ConcertSkeletonBlock represents a concert skeleton block // ConcertSkeletonBlock represents a concert skeleton block.
type ConcertSkeletonBlock struct { type ConcertSkeletonBlock struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Type string `json:"type"` Type string `json:"type"`
Title string `json:"title"` Title string `json:"title"`
Data json.RawMessage `json:"data"` Data json.RawMessage `json:"data"`
} }
// ArtistConcerts represents concerts by an artist.
type ArtistConcerts struct {
BaseModel
ArtistTitle string `json:"artist_title"`
Concerts []Concert `json:"concerts"`
}
// DeJSON deserializes ArtistConcerts from JSON data.
func (ac *ArtistConcerts) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
acObj := &ArtistConcerts{BaseModel: *NewBaseModel(c)}
if v, ok := raw["artist_title"]; ok {
json.Unmarshal(v, &acObj.ArtistTitle)
}
if v, ok := raw["concerts"]; ok {
var concerts []Concert
json.Unmarshal(v, &concerts)
acObj.Concerts = concerts
}
return acObj, nil
}
// DeList deserializes a list of ArtistConcerts from JSON data.
func (ac *ArtistConcerts) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
ac := &ArtistConcerts{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, ac); err != nil {
continue
}
result = append(result, ac)
}
return result, nil
}
+91 -8
View File
@@ -1,17 +1,18 @@
// Package models provides data models for the Yandex Music API.
package models package models
import ( import (
"encoding/json" "encoding/json"
) )
// Credits represents track credits // Credits represents track credits from the Yandex Music API.
type Credits struct { type Credits struct {
BaseModel BaseModel
Items []Credit `json:"items"` Items []Credit `json:"items"`
Total int `json:"total"` Total int `json:"total"`
} }
// DeJSON deserializes Credits from JSON // DeJSON deserializes Credits from JSON data.
func (c *Credits) DeJSON(data []byte, client ClientInterface) (Model, error) { func (c *Credits) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -32,7 +33,7 @@ func (c *Credits) DeJSON(data []byte, client ClientInterface) (Model, error) {
return credits, nil return credits, nil
} }
// DeList deserializes a list of Credits // DeList deserializes a list of Credits from JSON data. from JSON data.
func (c *Credits) DeList(data []byte, client ClientInterface) ([]Model, error) { func (c *Credits) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -50,7 +51,7 @@ func (c *Credits) DeList(data []byte, client ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// Credit represents a credit // Credit represents a credit.
type Credit struct { type Credit struct {
BaseModel BaseModel
Role string `json:"role"` Role string `json:"role"`
@@ -62,7 +63,7 @@ type Credit struct {
AlbumID string `json:"album_id"` AlbumID string `json:"album_id"`
} }
// DeJSON deserializes a Credit from JSON // DeJSON deserializes a Credit from JSON data.
func (c *Credit) DeJSON(data []byte, client ClientInterface) (Model, error) { func (c *Credit) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -114,7 +115,7 @@ func (c *Credit) DeList(data []byte, client ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// LicenceTextPart represents a licence text part // LicenceTextPart represents a licence text part.
type LicenceTextPart struct { type LicenceTextPart struct {
BaseModel BaseModel
Text string `json:"text"` Text string `json:"text"`
@@ -124,7 +125,7 @@ type LicenceTextPart struct {
IsMain bool `json:"is_main"` IsMain bool `json:"is_main"`
} }
// DeJSON deserializes a LicenceTextPart from JSON // DeJSON deserializes a LicenceTextPart from JSON data.
func (ltp *LicenceTextPart) DeJSON(data []byte, client ClientInterface) (Model, error) { func (ltp *LicenceTextPart) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -152,7 +153,7 @@ func (ltp *LicenceTextPart) DeJSON(data []byte, client ClientInterface) (Model,
return part, nil return part, nil
} }
// DeList deserializes a list of LicenceTextPart // DeList deserializes a list of LicenceTextPart from JSON data.
func (ltp *LicenceTextPart) DeList(data []byte, client ClientInterface) ([]Model, error) { func (ltp *LicenceTextPart) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -169,3 +170,85 @@ func (ltp *LicenceTextPart) DeList(data []byte, client ClientInterface) ([]Model
} }
return result, nil return result, nil
} }
// Disclaimer represents a disclaimer.
type Disclaimer struct {
BaseModel
ForeignAgent *ForeignAgent `json:"foreign_agent"`
}
// DeJSON deserializes Disclaimer from JSON data.
func (d *Disclaimer) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
disclaimer := &Disclaimer{BaseModel: *NewBaseModel(c)}
if v, ok := raw["foreign_agent"]; ok {
var fa ForeignAgent
json.Unmarshal(v, &fa)
disclaimer.ForeignAgent = &fa
}
return disclaimer, nil
}
// DeList deserializes a list of Disclaimers from JSON data.
func (d *Disclaimer) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
disclaimer := &Disclaimer{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, disclaimer); err != nil {
continue
}
result = append(result, disclaimer)
}
return result, nil
}
// ForeignAgent represents a foreign agent.
type ForeignAgent struct {
BaseModel
Text string `json:"text"`
}
// DeJSON deserializes ForeignAgent from JSON data.
func (fa *ForeignAgent) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
agent := &ForeignAgent{BaseModel: *NewBaseModel(c)}
if v, ok := raw["text"]; ok {
json.Unmarshal(v, &agent.Text)
}
return agent, nil
}
// DeList deserializes a list of ForeignAgent from JSON data.
func (fa *ForeignAgent) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
agent := &ForeignAgent{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, agent); err != nil {
continue
}
result = append(result, agent)
}
return result, nil
}
+44 -26
View File
@@ -1,18 +1,25 @@
// Package models provides data models for the Yandex Music API.
package models package models
import ( import (
"encoding/json" "encoding/json"
) )
// Feed represents the main feed // Feed represents the main feed from the Yandex Music API.
type Feed struct { type Feed struct {
BaseModel BaseModel
CanGetMoreEvents bool `json:"can_get_more_events"`
Pumpkin bool `json:"pumpkin"`
IsWizardPassed bool `json:"is_wizard_passed"`
Headlines []string `json:"headlines"`
Today string `json:"today"`
Days []Day `json:"days"` Days []Day `json:"days"`
GeneratedPlaylists []GeneratedPlaylist `json:"generated_playlists"` GeneratedPlaylists []GeneratedPlaylist `json:"generated_playlists"`
PlayContexts []PlayContext `json:"play_contexts"` PlayContexts []PlayContext `json:"play_contexts"`
NextRevision string `json:"next_revision"`
} }
// DeJSON deserializes a Feed from JSON // DeJSON deserializes a Feed from JSON data.
func (f *Feed) DeJSON(data []byte, c ClientInterface) (Model, error) { func (f *Feed) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -21,6 +28,21 @@ func (f *Feed) DeJSON(data []byte, c ClientInterface) (Model, error) {
feed := &Feed{BaseModel: *NewBaseModel(c)} feed := &Feed{BaseModel: *NewBaseModel(c)}
if v, ok := raw["can_get_more_events"]; ok {
json.Unmarshal(v, &feed.CanGetMoreEvents)
}
if v, ok := raw["pumpkin"]; ok {
json.Unmarshal(v, &feed.Pumpkin)
}
if v, ok := raw["is_wizard_passed"]; ok {
json.Unmarshal(v, &feed.IsWizardPassed)
}
if v, ok := raw["headlines"]; ok {
json.Unmarshal(v, &feed.Headlines)
}
if v, ok := raw["today"]; ok {
json.Unmarshal(v, &feed.Today)
}
if v, ok := raw["days"]; ok { if v, ok := raw["days"]; ok {
var days []Day var days []Day
json.Unmarshal(v, &days) json.Unmarshal(v, &days)
@@ -36,11 +58,14 @@ func (f *Feed) DeJSON(data []byte, c ClientInterface) (Model, error) {
json.Unmarshal(v, &contexts) json.Unmarshal(v, &contexts)
feed.PlayContexts = contexts feed.PlayContexts = contexts
} }
if v, ok := raw["next_revision"]; ok {
json.Unmarshal(v, &feed.NextRevision)
}
return feed, nil return feed, nil
} }
// DeList deserializes a list of Feeds // DeList deserializes a list of Feeds from JSON data.
func (f *Feed) DeList(data []byte, c ClientInterface) ([]Model, error) { func (f *Feed) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -58,7 +83,7 @@ func (f *Feed) DeList(data []byte, c ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// Day represents a day in the feed // Day represents a day in the feed.
type Day struct { type Day struct {
BaseModel BaseModel
Date string `json:"date"` Date string `json:"date"`
@@ -68,7 +93,7 @@ type Day struct {
GeneratedPlaylists []GeneratedPlaylist `json:"generated_playlists"` GeneratedPlaylists []GeneratedPlaylist `json:"generated_playlists"`
} }
// DeJSON deserializes a Day from JSON // DeJSON deserializes a Day from JSON data.
func (d *Day) DeJSON(data []byte, c ClientInterface) (Model, error) { func (d *Day) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -100,7 +125,7 @@ func (d *Day) DeJSON(data []byte, c ClientInterface) (Model, error) {
return day, nil return day, nil
} }
// DeList deserializes a list of Days // DeList deserializes a list of Days from JSON data.
func (d *Day) DeList(data []byte, c ClientInterface) ([]Model, error) { func (d *Day) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -118,17 +143,17 @@ func (d *Day) DeList(data []byte, c ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// DayBlock represents a block in a day // DayBlock represents a block in a day.
type DayBlock struct { type DayBlock struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Type string `json:"type"` Type string `json:"type"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
Data *DayBlockData `json:"data"` Data *DayBlockData `json:"data"`
} }
// DayBlockData represents data in a day block // DayBlockData represents data in a day block.
type DayBlockData struct { type DayBlockData struct {
Tracks []Track `json:"tracks"` Tracks []Track `json:"tracks"`
Playlists []Playlist `json:"playlists"` Playlists []Playlist `json:"playlists"`
@@ -137,10 +162,10 @@ type DayBlockData struct {
GeneratedPlaylist *GeneratedPlaylist `json:"generated_playlist"` GeneratedPlaylist *GeneratedPlaylist `json:"generated_playlist"`
} }
// GeneratedPlaylist represents a generated playlist // GeneratedPlaylist represents a generated playlist.
type GeneratedPlaylist struct { type GeneratedPlaylist struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
CoverURI string `json:"cover_uri"` CoverURI string `json:"cover_uri"`
@@ -153,7 +178,7 @@ type GeneratedPlaylist struct {
Tracks []Track `json:"tracks"` Tracks []Track `json:"tracks"`
} }
// DeJSON deserializes a GeneratedPlaylist from JSON // DeJSON deserializes a GeneratedPlaylist from JSON data.
func (gp *GeneratedPlaylist) DeJSON(data []byte, c ClientInterface) (Model, error) { func (gp *GeneratedPlaylist) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -201,7 +226,7 @@ func (gp *GeneratedPlaylist) DeJSON(data []byte, c ClientInterface) (Model, erro
return playlist, nil return playlist, nil
} }
// DeList deserializes a list of GeneratedPlaylists // DeList deserializes a list of GeneratedPlaylists from JSON data.
func (gp *GeneratedPlaylist) DeList(data []byte, c ClientInterface) ([]Model, error) { func (gp *GeneratedPlaylist) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -219,7 +244,7 @@ func (gp *GeneratedPlaylist) DeList(data []byte, c ClientInterface) ([]Model, er
return result, nil return result, nil
} }
// Event represents a feed event // Event represents a feed event.
type Event struct { type Event struct {
BaseModel BaseModel
Timestamp string `json:"timestamp"` Timestamp string `json:"timestamp"`
@@ -231,7 +256,7 @@ type Event struct {
Action string `json:"action"` Action string `json:"action"`
} }
// AlbumEvent represents an album event // AlbumEvent represents an album event.
type AlbumEvent struct { type AlbumEvent struct {
BaseModel BaseModel
Album *Album `json:"album"` Album *Album `json:"album"`
@@ -240,7 +265,7 @@ type AlbumEvent struct {
Action string `json:"action"` Action string `json:"action"`
} }
// ArtistEvent represents an artist event // ArtistEvent represents an artist event.
type ArtistEvent struct { type ArtistEvent struct {
BaseModel BaseModel
Artist *Artist `json:"artist"` Artist *Artist `json:"artist"`
@@ -249,7 +274,7 @@ type ArtistEvent struct {
Action string `json:"action"` Action string `json:"action"`
} }
// TrackWithAds represents a track with ads // TrackWithAds represents a track with ads.
type TrackWithAds struct { type TrackWithAds struct {
BaseModel BaseModel
Track *Track `json:"track"` Track *Track `json:"track"`
@@ -257,10 +282,10 @@ type TrackWithAds struct {
AdParams *AdParams `json:"ad_params"` AdParams *AdParams `json:"ad_params"`
} }
// Ad represents an ad // Ad represents an ad.
type Ad struct { type Ad struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Type string `json:"type"` Type string `json:"type"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
@@ -269,10 +294,3 @@ type Ad struct {
Duration int `json:"duration"` Duration int `json:"duration"`
} }
// AdParams represents ad parameters
type AdParams struct {
BaseModel
Frequency int `json:"frequency"`
MaxPerDay int `json:"max_per_day"`
Targeting string `json:"targeting"`
}
+23 -10
View File
@@ -1,23 +1,27 @@
// Package models provides data models for the Yandex Music API.
package models package models
import ( import (
"encoding/json" "encoding/json"
) )
// Label represents a label // Label represents a label from the Yandex Music API.
type Label struct { type Label struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Name string `json:"name"` Name string `json:"name"`
CoverURI string `json:"cover_uri"` CoverURI string `json:"cover_uri"`
Description string `json:"description"` Description string `json:"description"`
DescriptionFormatted string `json:"description_formatted"`
Image string `json:"image"`
Website string `json:"website"` Website string `json:"website"`
SocialLinks []SocialLink `json:"social_links"` Links []Link `json:"links"`
Type string `json:"type"`
Available bool `json:"available"` Available bool `json:"available"`
Owner *User `json:"owner"` Owner *User `json:"owner"`
} }
// DeJSON deserializes a Label from JSON // DeJSON deserializes a Label from JSON data.
func (l *Label) DeJSON(data []byte, client ClientInterface) (Model, error) { func (l *Label) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -44,10 +48,19 @@ func (l *Label) DeJSON(data []byte, client ClientInterface) (Model, error) {
if v, ok := raw["available"]; ok { if v, ok := raw["available"]; ok {
json.Unmarshal(v, &label.Available) json.Unmarshal(v, &label.Available)
} }
if v, ok := raw["social_links"]; ok { if v, ok := raw["description_formatted"]; ok {
var links []SocialLink json.Unmarshal(v, &label.DescriptionFormatted)
}
if v, ok := raw["image"]; ok {
json.Unmarshal(v, &label.Image)
}
if v, ok := raw["links"]; ok {
var links []Link
json.Unmarshal(v, &links) json.Unmarshal(v, &links)
label.SocialLinks = links label.Links = links
}
if v, ok := raw["type"]; ok {
json.Unmarshal(v, &label.Type)
} }
if v, ok := raw["owner"]; ok { if v, ok := raw["owner"]; ok {
var user User var user User
@@ -58,7 +71,7 @@ func (l *Label) DeJSON(data []byte, client ClientInterface) (Model, error) {
return label, nil return label, nil
} }
// DeList deserializes a list of Labels // DeList deserializes a list of Labels from JSON data.
func (l *Label) DeList(data []byte, client ClientInterface) ([]Model, error) { func (l *Label) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -76,7 +89,7 @@ func (l *Label) DeList(data []byte, client ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// LabelAlbums represents label albums // LabelAlbums represents label albums.
type LabelAlbums struct { type LabelAlbums struct {
BaseModel BaseModel
Albums []Album `json:"albums"` Albums []Album `json:"albums"`
@@ -85,7 +98,7 @@ type LabelAlbums struct {
PageSize int `json:"page_size"` PageSize int `json:"page_size"`
} }
// LabelArtists represents label artists // LabelArtists represents label artists.
type LabelArtists struct { type LabelArtists struct {
BaseModel BaseModel
Artists []Artist `json:"artists"` Artists []Artist `json:"artists"`
+70 -10
View File
@@ -1,22 +1,25 @@
// Package models provides data models for the Yandex Music API.
package models package models
import ( import (
"encoding/json" "encoding/json"
) )
// Landing represents a landing page // Landing represents a landing page from the Yandex Music API.
type Landing struct { type Landing struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
CoverURI string `json:"cover_uri"` CoverURI string `json:"cover_uri"`
Type string `json:"type"` Type string `json:"type"`
Blocks []LandingBlock `json:"blocks"` Blocks []LandingBlock `json:"blocks"`
Available bool `json:"available"` Available bool `json:"available"`
Pumpkin bool `json:"pumpkin"`
ContentID string `json:"content_id"`
} }
// DeJSON deserializes a Landing from JSON // DeJSON deserializes a Landing from JSON data.
func (l *Landing) DeJSON(data []byte, c ClientInterface) (Model, error) { func (l *Landing) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -43,6 +46,12 @@ func (l *Landing) DeJSON(data []byte, c ClientInterface) (Model, error) {
if v, ok := raw["available"]; ok { if v, ok := raw["available"]; ok {
json.Unmarshal(v, &landing.Available) json.Unmarshal(v, &landing.Available)
} }
if v, ok := raw["pumpkin"]; ok {
json.Unmarshal(v, &landing.Pumpkin)
}
if v, ok := raw["content_id"]; ok {
json.Unmarshal(v, &landing.ContentID)
}
if v, ok := raw["blocks"]; ok { if v, ok := raw["blocks"]; ok {
var blocks []LandingBlock var blocks []LandingBlock
@@ -53,7 +62,7 @@ func (l *Landing) DeJSON(data []byte, c ClientInterface) (Model, error) {
return landing, nil return landing, nil
} }
// DeList deserializes a list of Landings // DeList deserializes a list of Landings from JSON data.
func (l *Landing) DeList(data []byte, c ClientInterface) ([]Model, error) { func (l *Landing) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -71,17 +80,17 @@ func (l *Landing) DeList(data []byte, c ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// LandingBlock represents a block in a landing // LandingBlock represents a block in a landing.
type LandingBlock struct { type LandingBlock struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Type string `json:"type"` Type string `json:"type"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
Data *LandingBlockData `json:"data"` Data *LandingBlockData `json:"data"`
} }
// LandingBlockData represents data in a landing block // LandingBlockData represents data in a landing block.
type LandingBlockData struct { type LandingBlockData struct {
Tracks []Track `json:"tracks"` Tracks []Track `json:"tracks"`
Playlists []Playlist `json:"playlists"` Playlists []Playlist `json:"playlists"`
@@ -90,16 +99,67 @@ type LandingBlockData struct {
GeneratedPlaylist *GeneratedPlaylist `json:"generated_playlist"` GeneratedPlaylist *GeneratedPlaylist `json:"generated_playlist"`
} }
// LandingList represents a list of landings // LandingList represents a list of landings.
type LandingList struct { type LandingList struct {
BaseModel BaseModel
Type string `json:"type"`
TypeForFrom string `json:"type_for_from"`
Title string `json:"title"`
ID string `json:"id"`
Landings []Landing `json:"landings"` Landings []Landing `json:"landings"`
NewReleases []int `json:"new_releases"`
NewPlaylists []PlaylistID `json:"new_playlists"`
Podcasts []int `json:"podcasts"`
Total int `json:"total"` Total int `json:"total"`
Page int `json:"page"` Page int `json:"page"`
PageSize int `json:"page_size"` PageSize int `json:"page_size"`
} }
// DeJSON deserializes a LandingList from JSON // PlaylistID represents a playlist ID reference.
type PlaylistID struct {
BaseModel
UID int `json:"uid"`
Kind int `json:"kind"`
}
// DeJSON deserializes a PlaylistID from JSON data.
func (pid *PlaylistID) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
playlistID := &PlaylistID{BaseModel: *NewBaseModel(c)}
if v, ok := raw["uid"]; ok {
json.Unmarshal(v, &playlistID.UID)
}
if v, ok := raw["kind"]; ok {
json.Unmarshal(v, &playlistID.Kind)
}
return playlistID, nil
}
// DeList deserializes a list of PlaylistID from JSON data.
func (pid *PlaylistID) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
pid := &PlaylistID{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, pid); err != nil {
continue
}
result = append(result, pid)
}
return result, nil
}
// DeJSON deserializes a LandingList from JSON data.
func (ll *LandingList) DeJSON(data []byte, c ClientInterface) (Model, error) { func (ll *LandingList) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -126,7 +186,7 @@ func (ll *LandingList) DeJSON(data []byte, c ClientInterface) (Model, error) {
return list, nil return list, nil
} }
// DeList deserializes a list of LandingLists // DeList deserializes a list of LandingLists from JSON data.
func (ll *LandingList) DeList(data []byte, c ClientInterface) ([]Model, error) { func (ll *LandingList) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
+30 -29
View File
@@ -1,13 +1,14 @@
// Package models provides data models for the Yandex Music API.
package models package models
import ( import (
"encoding/json" "encoding/json"
) )
// Metatag represents a metatag // Metatag represents a metatag from the Yandex Music API.
type Metatag struct { type Metatag struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
Image string `json:"image"` Image string `json:"image"`
@@ -16,7 +17,7 @@ type Metatag struct {
Children []Metatag `json:"children"` Children []Metatag `json:"children"`
} }
// DeJSON deserializes a Metatag from JSON // DeJSON deserializes a Metatag from JSON data.
func (m *Metatag) DeJSON(data []byte, client ClientInterface) (Model, error) { func (m *Metatag) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -52,7 +53,7 @@ func (m *Metatag) DeJSON(data []byte, client ClientInterface) (Model, error) {
return metatag, nil return metatag, nil
} }
// DeList deserializes a list of Metatags // DeList deserializes a list of Metatags from JSON data.
func (m *Metatag) DeList(data []byte, client ClientInterface) ([]Model, error) { func (m *Metatag) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -70,17 +71,17 @@ func (m *Metatag) DeList(data []byte, client ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// MetatagTree represents a metatag tree // MetatagTree represents a metatag tree.
type MetatagTree struct { type MetatagTree struct {
BaseModel BaseModel
Root *Metatag `json:"root"` Root *Metatag `json:"root"`
Leaves []Metatag `json:"leaves"` Leaves []Metatag `json:"leaves"`
} }
// MetatagLeaf represents a metatag leaf // MetatagLeaf represents a metatag leaf.
type MetatagLeaf struct { type MetatagLeaf struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
Image string `json:"image"` Image string `json:"image"`
@@ -88,7 +89,7 @@ type MetatagLeaf struct {
Position int `json:"position"` Position int `json:"position"`
} }
// MetatagArtists represents metatag artists // MetatagArtists represents metatag artists.
type MetatagArtists struct { type MetatagArtists struct {
BaseModel BaseModel
Artists []Artist `json:"artists"` Artists []Artist `json:"artists"`
@@ -97,7 +98,7 @@ type MetatagArtists struct {
PageSize int `json:"page_size"` PageSize int `json:"page_size"`
} }
// MetatagAlbums represents metatag albums // MetatagAlbums represents metatag albums.
type MetatagAlbums struct { type MetatagAlbums struct {
BaseModel BaseModel
Albums []Album `json:"albums"` Albums []Album `json:"albums"`
@@ -106,7 +107,7 @@ type MetatagAlbums struct {
PageSize int `json:"page_size"` PageSize int `json:"page_size"`
} }
// MetatagPlaylists represents metatag playlists // MetatagPlaylists represents metatag playlists.
type MetatagPlaylists struct { type MetatagPlaylists struct {
BaseModel BaseModel
Playlists []Playlist `json:"playlists"` Playlists []Playlist `json:"playlists"`
@@ -115,30 +116,30 @@ type MetatagPlaylists struct {
PageSize int `json:"page_size"` PageSize int `json:"page_size"`
} }
// MetatagTitle represents a metatag title // MetatagTitle represents a metatag title.
type MetatagTitle struct { type MetatagTitle struct {
BaseModel BaseModel
Title string `json:"title"` Title string `json:"title"`
} }
// MetatagSortByValue represents metatag sort by value // MetatagSortByValue represents metatag sort by value.
type MetatagSortByValue struct { type MetatagSortByValue struct {
BaseModel BaseModel
Value string `json:"value"` Value string `json:"value"`
Label string `json:"label"` Label string `json:"label"`
} }
// MetatagArtistEntry represents a metatag artist entry // MetatagArtistEntry represents a metatag artist entry.
type MetatagArtistEntry struct { type MetatagArtistEntry struct {
BaseModel BaseModel
Artist *Artist `json:"artist"` Artist *Artist `json:"artist"`
Score float64 `json:"score"` PopularTracks []Track `json:"popular_tracks"`
} }
// Genre represents a genre // Genre represents a genre from the Yandex Music API.
type Genre struct { type Genre struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
Image string `json:"image"` Image string `json:"image"`
@@ -147,7 +148,7 @@ type Genre struct {
Children []Genre `json:"children"` Children []Genre `json:"children"`
} }
// DeJSON deserializes a Genre from JSON // DeJSON deserializes a Genre from JSON data.
func (g *Genre) DeJSON(data []byte, client ClientInterface) (Model, error) { func (g *Genre) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -183,7 +184,7 @@ func (g *Genre) DeJSON(data []byte, client ClientInterface) (Model, error) {
return genre, nil return genre, nil
} }
// DeList deserializes a list of Genres // DeList deserializes a list of Genres from JSON data.
func (g *Genre) DeList(data []byte, client ClientInterface) ([]Model, error) { func (g *Genre) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -201,10 +202,10 @@ func (g *Genre) DeList(data []byte, client ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// Chart represents a chart // Chart represents a chart from the Yandex Music API.
type Chart struct { type Chart struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
CoverURI string `json:"cover_uri"` CoverURI string `json:"cover_uri"`
@@ -212,7 +213,7 @@ type Chart struct {
Items []ChartItem `json:"items"` Items []ChartItem `json:"items"`
} }
// DeJSON deserializes a Chart from JSON // DeJSON deserializes a Chart from JSON data.
func (c *Chart) DeJSON(data []byte, client ClientInterface) (Model, error) { func (c *Chart) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -245,7 +246,7 @@ func (c *Chart) DeJSON(data []byte, client ClientInterface) (Model, error) {
return chart, nil return chart, nil
} }
// DeList deserializes a list of Charts // DeList deserializes a list of Charts from JSON data.
func (c *Chart) DeList(data []byte, client ClientInterface) ([]Model, error) { func (c *Chart) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -263,14 +264,14 @@ func (c *Chart) DeList(data []byte, client ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// ChartInfo represents chart info // ChartInfo represents chart info.
type ChartInfo struct { type ChartInfo struct {
BaseModel BaseModel
Chart *Chart `json:"chart"` Chart *Chart `json:"chart"`
Menu []ChartInfoMenu `json:"menu"` Menu []ChartInfoMenu `json:"menu"`
} }
// ChartItem represents a chart item // ChartItem represents a chart item.
type ChartItem struct { type ChartItem struct {
BaseModel BaseModel
Position int `json:"position"` Position int `json:"position"`
@@ -282,25 +283,25 @@ type ChartItem struct {
Trend string `json:"trend"` Trend string `json:"trend"`
} }
// ChartInfoMenu represents a chart info menu // ChartInfoMenu represents a chart info menu.
type ChartInfoMenu struct { type ChartInfoMenu struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
Items []ChartInfoMenuItem `json:"items"` Items []ChartInfoMenuItem `json:"items"`
} }
// ChartInfoMenuItem represents a chart info menu item // ChartInfoMenuItem represents a chart info menu item.
type ChartInfoMenuItem struct { type ChartInfoMenuItem struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
Action string `json:"action"` Action string `json:"action"`
} }
// ChartItems represents chart items response // ChartItems represents chart items response.
type ChartItems struct { type ChartItems struct {
BaseModel BaseModel
Items []ChartItem `json:"items"` Items []ChartItem `json:"items"`
+15 -15
View File
@@ -1,10 +1,11 @@
// Package models provides data models for the Yandex Music API.
package models package models
import ( import (
"encoding/json" "encoding/json"
) )
// MusicHistory represents music history // MusicHistory represents music history from the Yandex Music API.
type MusicHistory struct { type MusicHistory struct {
BaseModel BaseModel
Tabs []MusicHistoryTab `json:"tabs"` Tabs []MusicHistoryTab `json:"tabs"`
@@ -13,7 +14,7 @@ type MusicHistory struct {
Context *MusicHistoryContextFullModel `json:"context"` Context *MusicHistoryContextFullModel `json:"context"`
} }
// DeJSON deserializes a MusicHistory from JSON // DeJSON deserializes a MusicHistory from JSON data.
func (mh *MusicHistory) DeJSON(data []byte, client ClientInterface) (Model, error) { func (mh *MusicHistory) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -46,7 +47,7 @@ func (mh *MusicHistory) DeJSON(data []byte, client ClientInterface) (Model, erro
return history, nil return history, nil
} }
// DeList deserializes a list of MusicHistory // DeList deserializes a list of MusicHistory from JSON data.
func (mh *MusicHistory) DeList(data []byte, client ClientInterface) ([]Model, error) { func (mh *MusicHistory) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -64,10 +65,10 @@ func (mh *MusicHistory) DeList(data []byte, client ClientInterface) ([]Model, er
return result, nil return result, nil
} }
// MusicHistoryTab represents a music history tab // MusicHistoryTab represents a music history tab.
type MusicHistoryTab struct { type MusicHistoryTab struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
Icon string `json:"icon"` Icon string `json:"icon"`
@@ -75,20 +76,20 @@ type MusicHistoryTab struct {
Position int `json:"position"` Position int `json:"position"`
} }
// MusicHistoryGroup represents a music history group // MusicHistoryGroup represents a music history group.
type MusicHistoryGroup struct { type MusicHistoryGroup struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
Items []MusicHistoryItem `json:"items"` Items []MusicHistoryItem `json:"items"`
Total int `json:"total"` Total int `json:"total"`
} }
// MusicHistoryItem represents a music history item // MusicHistoryItem represents a music history item.
type MusicHistoryItem struct { type MusicHistoryItem struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Type string `json:"type"` // "track", "album", "artist", "playlist" Type string `json:"type"` // "track", "album", "artist", "playlist"
Track *Track `json:"track"` Track *Track `json:"track"`
Album *Album `json:"album"` Album *Album `json:"album"`
@@ -100,7 +101,7 @@ type MusicHistoryItem struct {
Context *MusicHistoryItemData `json:"context"` Context *MusicHistoryItemData `json:"context"`
} }
// MusicHistoryItemData represents music history item data // MusicHistoryItemData represents music history item data.
type MusicHistoryItemData struct { type MusicHistoryItemData struct {
BaseModel BaseModel
Source string `json:"source"` Source string `json:"source"`
@@ -109,17 +110,16 @@ type MusicHistoryItemData struct {
PlayContext *PlayContext `json:"play_context"` PlayContext *PlayContext `json:"play_context"`
Position int `json:"position"` Position int `json:"position"`
Duration int `json:"duration"` Duration int `json:"duration"`
Progress float64 `json:"progress"`
} }
// MusicHistoryItemID represents music history item ID // MusicHistoryItemID represents music history item ID.
type MusicHistoryItemID struct { type MusicHistoryItemID struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Type string `json:"type"` Type string `json:"type"`
} }
// MusicHistoryItems represents music history items // MusicHistoryItems represents music history items.
type MusicHistoryItems struct { type MusicHistoryItems struct {
BaseModel BaseModel
Items []MusicHistoryItem `json:"items"` Items []MusicHistoryItem `json:"items"`
@@ -128,7 +128,7 @@ type MusicHistoryItems struct {
PageSize int `json:"page_size"` PageSize int `json:"page_size"`
} }
// MusicHistoryContextFullModel represents music history context full model // MusicHistoryContextFullModel represents music history context full model.
type MusicHistoryContextFullModel struct { type MusicHistoryContextFullModel struct {
BaseModel BaseModel
DeviceID string `json:"device_id"` DeviceID string `json:"device_id"`
+57 -11
View File
@@ -1,13 +1,14 @@
// Package models provides data models for the Yandex Music API.
package models package models
import ( import (
"encoding/json" "encoding/json"
) )
// Pin represents a pin // Pin represents a pin from the Yandex Music API.
type Pin struct { type Pin struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
Image string `json:"image"` Image string `json:"image"`
@@ -20,9 +21,10 @@ type Pin struct {
CreatedAt string `json:"created_at"` CreatedAt string `json:"created_at"`
ExpiresAt string `json:"expires_at"` ExpiresAt string `json:"expires_at"`
Active bool `json:"active"` Active bool `json:"active"`
ContentRestrictions *ContentRestrictions `json:"content_restrictions"`
} }
// DeJSON deserializes a Pin from JSON // DeJSON deserializes a Pin from JSON data.
func (p *Pin) DeJSON(data []byte, client ClientInterface) (Model, error) { func (p *Pin) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -58,6 +60,11 @@ func (p *Pin) DeJSON(data []byte, client ClientInterface) (Model, error) {
if v, ok := raw["active"]; ok { if v, ok := raw["active"]; ok {
json.Unmarshal(v, &pin.Active) json.Unmarshal(v, &pin.Active)
} }
if v, ok := raw["content_restrictions"]; ok {
var cr ContentRestrictions
json.Unmarshal(v, &cr)
pin.ContentRestrictions = &cr
}
if v, ok := raw["track"]; ok { if v, ok := raw["track"]; ok {
var track Track var track Track
@@ -83,7 +90,7 @@ func (p *Pin) DeJSON(data []byte, client ClientInterface) (Model, error) {
return pin, nil return pin, nil
} }
// DeList deserializes a list of Pins // DeList deserializes a list of Pins from JSON data.
func (p *Pin) DeList(data []byte, client ClientInterface) ([]Model, error) { func (p *Pin) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -101,14 +108,14 @@ func (p *Pin) DeList(data []byte, client ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// PinData represents pin data // PinData represents pin data.
type PinData struct { type PinData struct {
BaseModel BaseModel
Pins []Pin `json:"pins"` Pins []Pin `json:"pins"`
Total int `json:"total"` Total int `json:"total"`
} }
// PinsList represents a list of pins // PinsList represents a list of pins.
type PinsList struct { type PinsList struct {
BaseModel BaseModel
Pins []Pin `json:"pins"` Pins []Pin `json:"pins"`
@@ -117,19 +124,21 @@ type PinsList struct {
PageSize int `json:"page_size"` PageSize int `json:"page_size"`
} }
// Presaves represents presaves // Presaves represents presaves.
type Presaves struct { type Presaves struct {
BaseModel BaseModel
UpcomingAlbums []Album `json:"upcoming_albums"`
ReleasedAlbums []Album `json:"released_albums"`
Presaves []Presave `json:"presaves"` Presaves []Presave `json:"presaves"`
Total int `json:"total"` Total int `json:"total"`
Page int `json:"page"` Page int `json:"page"`
PageSize int `json:"page_size"` PageSize int `json:"page_size"`
} }
// Presave represents a presave // Presave represents a presave.
type Presave struct { type Presave struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
Image string `json:"image"` Image string `json:"image"`
@@ -142,7 +151,7 @@ type Presave struct {
UpdatedAt string `json:"updated_at"` UpdatedAt string `json:"updated_at"`
} }
// DeJSON deserializes a Presave from JSON // DeJSON deserializes a Presave from JSON data.
func (p *Presave) DeJSON(data []byte, client ClientInterface) (Model, error) { func (p *Presave) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -195,7 +204,7 @@ func (p *Presave) DeJSON(data []byte, client ClientInterface) (Model, error) {
return presave, nil return presave, nil
} }
// DeList deserializes a list of Presaves // DeList deserializes a list of Presaves from JSON data.
func (p *Presave) DeList(data []byte, client ClientInterface) ([]Model, error) { func (p *Presave) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -212,3 +221,40 @@ func (p *Presave) DeList(data []byte, client ClientInterface) ([]Model, error) {
} }
return result, nil return result, nil
} }
// DeJSON deserializes Presaves from JSON data.
func (p *Presaves) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
presaves := &Presaves{BaseModel: *NewBaseModel(client)}
if v, ok := raw["upcoming_albums"]; ok {
var albums []Album
json.Unmarshal(v, &albums)
presaves.UpcomingAlbums = albums
}
if v, ok := raw["released_albums"]; ok {
var albums []Album
json.Unmarshal(v, &albums)
presaves.ReleasedAlbums = albums
}
if v, ok := raw["presaves"]; ok {
var presaveList []Presave
json.Unmarshal(v, &presaveList)
presaves.Presaves = presaveList
}
if v, ok := raw["total"]; ok {
json.Unmarshal(v, &presaves.Total)
}
if v, ok := raw["page"]; ok {
json.Unmarshal(v, &presaves.Page)
}
if v, ok := raw["page_size"]; ok {
json.Unmarshal(v, &presaves.PageSize)
}
return presaves, nil
}
+12 -11
View File
@@ -1,10 +1,11 @@
// Package models provides data models for the Yandex Music API.
package models package models
import ( import (
"encoding/json" "encoding/json"
) )
// Playlist represents a playlist // Playlist represents a playlist from the Yandex Music API.
type Playlist struct { type Playlist struct {
BaseModel BaseModel
Kind int `json:"kind"` Kind int `json:"kind"`
@@ -36,7 +37,7 @@ type Playlist struct {
Trailer *TrailerInfo `json:"trailer"` Trailer *TrailerInfo `json:"trailer"`
} }
// DeJSON deserializes a Playlist from JSON // DeJSON deserializes a Playlist from JSON data.
func (p *Playlist) DeJSON(data []byte, c ClientInterface) (Model, error) { func (p *Playlist) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -142,7 +143,7 @@ func (p *Playlist) DeJSON(data []byte, c ClientInterface) (Model, error) {
return playlist, nil return playlist, nil
} }
// DeList deserializes a list of Playlists // DeList deserializes a list of Playlists from JSON data.
func (p *Playlist) DeList(data []byte, c ClientInterface) ([]Model, error) { func (p *Playlist) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -160,7 +161,7 @@ func (p *Playlist) DeList(data []byte, c ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// User represents a user (playlist owner) // User represents a user (playlist owner) from the Yandex Music API.
type User struct { type User struct {
BaseModel BaseModel
UID int64 `json:"uid"` UID int64 `json:"uid"`
@@ -184,7 +185,7 @@ type User struct {
Counters *UserCounters `json:"counters"` Counters *UserCounters `json:"counters"`
} }
// DeJSON deserializes a User from JSON // DeJSON deserializes a User from JSON data.
func (u *User) DeJSON(data []byte, c ClientInterface) (Model, error) { func (u *User) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -255,7 +256,7 @@ func (u *User) DeJSON(data []byte, c ClientInterface) (Model, error) {
return user, nil return user, nil
} }
// DeList deserializes a list of Users // DeList deserializes a list of Users from JSON data.
func (u *User) DeList(data []byte, c ClientInterface) ([]Model, error) { func (u *User) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -273,7 +274,7 @@ func (u *User) DeList(data []byte, c ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// UserSettings represents user settings (from /account/settings) // UserSettings represents user settings from /account/settings.
type UserSettings struct { type UserSettings struct {
BaseModel BaseModel
UID int64 `json:"uid"` UID int64 `json:"uid"`
@@ -295,7 +296,7 @@ type UserSettings struct {
ShowDiskTracksInLibrary *bool `json:"show_disk_tracks_in_library,omitempty"` ShowDiskTracksInLibrary *bool `json:"show_disk_tracks_in_library,omitempty"`
} }
// DeJSON deserializes UserSettings from JSON // DeJSON deserializes UserSettings from JSON data.
func (u *UserSettings) DeJSON(data []byte, c ClientInterface) (Model, error) { func (u *UserSettings) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -365,7 +366,7 @@ func (u *UserSettings) DeJSON(data []byte, c ClientInterface) (Model, error) {
return settings, nil return settings, nil
} }
// DeList deserializes a list of UserSettings // DeList deserializes a list of UserSettings from JSON data.
func (u *UserSettings) DeList(data []byte, c ClientInterface) ([]Model, error) { func (u *UserSettings) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -383,7 +384,7 @@ func (u *UserSettings) DeList(data []byte, c ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// UserCounters represents user counters // UserCounters represents user counters.
type UserCounters struct { type UserCounters struct {
Tracks int `json:"tracks"` Tracks int `json:"tracks"`
Albums int `json:"albums"` Albums int `json:"albums"`
@@ -393,7 +394,7 @@ type UserCounters struct {
Likes int `json:"likes"` Likes int `json:"likes"`
} }
// Image represents an image with multiple sizes // Image represents an image with multiple sizes.
type Image struct { type Image struct {
URI string `json:"uri"` URI string `json:"uri"`
Type string `json:"type"` Type string `json:"type"`
+20 -17
View File
@@ -1,13 +1,14 @@
// Package models provides data models for the Yandex Music API.
package models package models
import ( import (
"encoding/json" "encoding/json"
) )
// Queue represents a playback queue // Queue represents a playback queue from the Yandex Music API.
type Queue struct { type Queue struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
TrackIDs []string `json:"track_ids"` TrackIDs []string `json:"track_ids"`
CurrentIndex int `json:"current_index"` CurrentIndex int `json:"current_index"`
QueueType string `json:"queue_type"` QueueType string `json:"queue_type"`
@@ -20,7 +21,7 @@ type Queue struct {
TotalDuration int `json:"total_duration_ms"` TotalDuration int `json:"total_duration_ms"`
} }
// DeJSON deserializes a Queue from JSON // DeJSON deserializes a Queue from JSON data.
func (q *Queue) DeJSON(data []byte, c ClientInterface) (Model, error) { func (q *Queue) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -71,7 +72,7 @@ func (q *Queue) DeJSON(data []byte, c ClientInterface) (Model, error) {
return queue, nil return queue, nil
} }
// DeList deserializes a list of Queues // DeList deserializes a list of Queues from JSON data.
func (q *Queue) DeList(data []byte, c ClientInterface) ([]Model, error) { func (q *Queue) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -89,19 +90,21 @@ func (q *Queue) DeList(data []byte, c ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// QueueItem represents a queue item // QueueItem represents a queue item.
type QueueItem struct { type QueueItem struct {
BaseModel BaseModel
ID JSONID `json:"id"`
TrackID string `json:"track_id"` TrackID string `json:"track_id"`
Track *Track `json:"track"` Track *Track `json:"track"`
Position int `json:"position"` Position int `json:"position"`
AddedAt string `json:"added_at"` AddedAt string `json:"added_at"`
AddedBy string `json:"added_by"` AddedBy string `json:"added_by"`
Playable bool `json:"playable"` Playable bool `json:"playable"`
PlayContext *PlayContext `json:"play_context"` Modified string `json:"modified"`
Context *PlayContext `json:"context"`
} }
// DeJSON deserializes a QueueItem from JSON // DeJSON deserializes a QueueItem from JSON data.
func (qi *QueueItem) DeJSON(data []byte, c ClientInterface) (Model, error) { func (qi *QueueItem) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -131,16 +134,16 @@ func (qi *QueueItem) DeJSON(data []byte, c ClientInterface) (Model, error) {
json.Unmarshal(v, &track) json.Unmarshal(v, &track)
item.Track = &track item.Track = &track
} }
if v, ok := raw["play_context"]; ok { if v, ok := raw["context"]; ok {
var ctx PlayContext var ctx PlayContext
json.Unmarshal(v, &ctx) json.Unmarshal(v, &ctx)
item.PlayContext = &ctx item.Context = &ctx
} }
return item, nil return item, nil
} }
// DeList deserializes a list of QueueItems // DeList deserializes a list of QueueItems from JSON data.
func (qi *QueueItem) DeList(data []byte, c ClientInterface) ([]Model, error) { func (qi *QueueItem) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -158,10 +161,10 @@ func (qi *QueueItem) DeList(data []byte, c ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// PlayContext represents a play context // PlayContext represents a play context.
type PlayContext struct { type PlayContext struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Type string `json:"type"` // "playlist", "album", "artist", "radio", "search" Type string `json:"type"` // "playlist", "album", "artist", "radio", "search"
SourceID string `json:"source_id"` SourceID string `json:"source_id"`
SourceKind string `json:"source_kind"` SourceKind string `json:"source_kind"`
@@ -173,7 +176,7 @@ type PlayContext struct {
Available bool `json:"available"` Available bool `json:"available"`
} }
// DeJSON deserializes a PlayContext from JSON // DeJSON deserializes a PlayContext from JSON data.
func (pc *PlayContext) DeJSON(data []byte, c ClientInterface) (Model, error) { func (pc *PlayContext) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -219,7 +222,7 @@ func (pc *PlayContext) DeJSON(data []byte, c ClientInterface) (Model, error) {
return ctx, nil return ctx, nil
} }
// DeList deserializes a list of PlayContexts // DeList deserializes a list of PlayContexts from JSON data.
func (pc *PlayContext) DeList(data []byte, c ClientInterface) ([]Model, error) { func (pc *PlayContext) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -237,7 +240,7 @@ func (pc *PlayContext) DeList(data []byte, c ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// PlayContextsData represents play contexts data // PlayContextsData represents play contexts data.
type PlayContextsData struct { type PlayContextsData struct {
BaseModel BaseModel
Contexts []PlayContext `json:"contexts"` Contexts []PlayContext `json:"contexts"`
@@ -246,7 +249,7 @@ type PlayContextsData struct {
PageSize int `json:"page_size"` PageSize int `json:"page_size"`
} }
// DeJSON deserializes PlayContextsData from JSON // DeJSON deserializes PlayContextsData from JSON data.
func (pcd *PlayContextsData) DeJSON(data []byte, c ClientInterface) (Model, error) { func (pcd *PlayContextsData) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -273,7 +276,7 @@ func (pcd *PlayContextsData) DeJSON(data []byte, c ClientInterface) (Model, erro
return result, nil return result, nil
} }
// DeList deserializes a list of PlayContextsData // DeList deserializes a list of PlayContextsData from JSON data.
func (pcd *PlayContextsData) DeList(data []byte, c ClientInterface) ([]Model, error) { func (pcd *PlayContextsData) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
+414 -22
View File
@@ -1,13 +1,14 @@
// Package models provides data models for the Yandex Music API.
package models package models
import ( import (
"encoding/json" "encoding/json"
) )
// RadioStation represents a radio station // RadioStation represents a radio station from the Yandex Music API.
type RadioStation struct { type RadioStation struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
CoverURI string `json:"cover_uri"` CoverURI string `json:"cover_uri"`
@@ -16,7 +17,7 @@ type RadioStation struct {
IsPersonal bool `json:"is_personal"` IsPersonal bool `json:"is_personal"`
} }
// DeJSON deserializes a RadioStation from JSON // DeJSON deserializes a RadioStation from JSON data.
func (r *RadioStation) DeJSON(data []byte, client ClientInterface) (Model, error) { func (r *RadioStation) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -50,7 +51,7 @@ func (r *RadioStation) DeJSON(data []byte, client ClientInterface) (Model, error
return station, nil return station, nil
} }
// DeList deserializes a list of RadioStations // DeList deserializes a list of RadioStations from JSON data.
func (r *RadioStation) DeList(data []byte, client ClientInterface) ([]Model, error) { func (r *RadioStation) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -68,7 +69,7 @@ func (r *RadioStation) DeList(data []byte, client ClientInterface) ([]Model, err
return result, nil return result, nil
} }
// StationTracksResult represents station tracks result // StationTracksResult represents station tracks result.
type StationTracksResult struct { type StationTracksResult struct {
BaseModel BaseModel
Tracks []Track `json:"tracks"` Tracks []Track `json:"tracks"`
@@ -76,7 +77,7 @@ type StationTracksResult struct {
Sequence string `json:"sequence"` Sequence string `json:"sequence"`
} }
// DeJSON deserializes StationTracksResult from JSON // DeJSON deserializes StationTracksResult from JSON data.
func (s *StationTracksResult) DeJSON(data []byte, client ClientInterface) (Model, error) { func (s *StationTracksResult) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -100,7 +101,7 @@ func (s *StationTracksResult) DeJSON(data []byte, client ClientInterface) (Model
return result, nil return result, nil
} }
// DeList deserializes a list of StationTracksResult // DeList deserializes a list of StationTracksResult from JSON data.
func (s *StationTracksResult) DeList(data []byte, client ClientInterface) ([]Model, error) { func (s *StationTracksResult) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -118,10 +119,10 @@ func (s *StationTracksResult) DeList(data []byte, client ClientInterface) ([]Mod
return result, nil return result, nil
} }
// RotorStation represents a rotor (generative) station // RotorStation represents a rotor (generative) station.
type RotorStation struct { type RotorStation struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
CoverURI string `json:"cover_uri"` CoverURI string `json:"cover_uri"`
@@ -131,7 +132,7 @@ type RotorStation struct {
Settings *RotorSettings `json:"settings"` Settings *RotorSettings `json:"settings"`
} }
// DeJSON deserializes a RotorStation from JSON // DeJSON deserializes a RotorStation from JSON data.
func (r *RotorStation) DeJSON(data []byte, client ClientInterface) (Model, error) { func (r *RotorStation) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -170,7 +171,7 @@ func (r *RotorStation) DeJSON(data []byte, client ClientInterface) (Model, error
return station, nil return station, nil
} }
// DeList deserializes a list of RotorStations // DeList deserializes a list of RotorStations from JSON data.
func (r *RotorStation) DeList(data []byte, client ClientInterface) ([]Model, error) { func (r *RotorStation) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -188,11 +189,14 @@ func (r *RotorStation) DeList(data []byte, client ClientInterface) ([]Model, err
return result, nil return result, nil
} }
// RotorSettings represents rotor station settings // RotorSettings represents rotor station settings.
type RotorSettings struct { type RotorSettings struct {
BaseModel BaseModel
Diversity float64 `json:"diversity"` Language string `json:"language"`
Energy float64 `json:"energy"` Diversity string `json:"diversity"`
Mood int `json:"mood"`
Energy int `json:"energy"`
MoodEnergy string `json:"mood_energy"`
Acousticness float64 `json:"acousticness"` Acousticness float64 `json:"acousticness"`
Instrumentalness float64 `json:"instrumentalness"` Instrumentalness float64 `json:"instrumentalness"`
Live float64 `json:"live"` Live float64 `json:"live"`
@@ -201,7 +205,7 @@ type RotorSettings struct {
Valence float64 `json:"valence"` Valence float64 `json:"valence"`
} }
// RotorStationTracksResult represents rotor station tracks result // RotorStationTracksResult represents rotor station tracks result.
type RotorStationTracksResult struct { type RotorStationTracksResult struct {
BaseModel BaseModel
Tracks []Track `json:"tracks"` Tracks []Track `json:"tracks"`
@@ -209,7 +213,7 @@ type RotorStationTracksResult struct {
Sequence string `json:"sequence"` Sequence string `json:"sequence"`
} }
// DeJSON deserializes RotorStationTracksResult from JSON // DeJSON deserializes RotorStationTracksResult from JSON data.
func (r *RotorStationTracksResult) DeJSON(data []byte, client ClientInterface) (Model, error) { func (r *RotorStationTracksResult) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -233,7 +237,7 @@ func (r *RotorStationTracksResult) DeJSON(data []byte, client ClientInterface) (
return result, nil return result, nil
} }
// DeList deserializes a list of RotorStationTracksResult // DeList deserializes a list of RotorStationTracksResult from JSON data.
func (r *RotorStationTracksResult) DeList(data []byte, client ClientInterface) ([]Model, error) { func (r *RotorStationTracksResult) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -251,7 +255,7 @@ func (r *RotorStationTracksResult) DeList(data []byte, client ClientInterface) (
return result, nil return result, nil
} }
// RotorSession represents a rotor session // RotorSession represents a rotor session.
type RotorSession struct { type RotorSession struct {
BaseModel BaseModel
SessionID string `json:"session_id"` SessionID string `json:"session_id"`
@@ -262,7 +266,7 @@ type RotorSession struct {
CreatedAt string `json:"created_at"` CreatedAt string `json:"created_at"`
} }
// DeJSON deserializes a RotorSession from JSON // DeJSON deserializes a RotorSession from JSON data.
func (r *RotorSession) DeJSON(data []byte, client ClientInterface) (Model, error) { func (r *RotorSession) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -295,7 +299,7 @@ func (r *RotorSession) DeJSON(data []byte, client ClientInterface) (Model, error
return session, nil return session, nil
} }
// DeList deserializes a list of RotorSessions // DeList deserializes a list of RotorSessions from JSON data.
func (r *RotorSession) DeList(data []byte, client ClientInterface) ([]Model, error) { func (r *RotorSession) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -313,7 +317,71 @@ func (r *RotorSession) DeList(data []byte, client ClientInterface) ([]Model, err
return result, nil return result, nil
} }
// SessionFeedback represents session feedback // AdParams represents ad parameters.
type AdParams struct {
BaseModel
PartnerID string `json:"partner_id"`
CategoryID string `json:"category_id"`
PageRef string `json:"page_ref"`
TargetRef string `json:"target_ref"`
OtherParams string `json:"other_params"`
GenreID string `json:"genre_id"`
GenreName string `json:"genre_name"`
}
// DeJSON deserializes AdParams from JSON data.
func (ap *AdParams) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
params := &AdParams{BaseModel: *NewBaseModel(c)}
if v, ok := raw["partner_id"]; ok {
json.Unmarshal(v, &params.PartnerID)
}
if v, ok := raw["category_id"]; ok {
json.Unmarshal(v, &params.CategoryID)
}
if v, ok := raw["page_ref"]; ok {
json.Unmarshal(v, &params.PageRef)
}
if v, ok := raw["target_ref"]; ok {
json.Unmarshal(v, &params.TargetRef)
}
if v, ok := raw["other_params"]; ok {
json.Unmarshal(v, &params.OtherParams)
}
if v, ok := raw["genre_id"]; ok {
json.Unmarshal(v, &params.GenreID)
}
if v, ok := raw["genre_name"]; ok {
json.Unmarshal(v, &params.GenreName)
}
return params, nil
}
// DeList deserializes a list of AdParams from JSON data.
func (ap *AdParams) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
params := &AdParams{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, params); err != nil {
continue
}
result = append(result, params)
}
return result, nil
}
// SessionFeedback represents session feedback.
type SessionFeedback struct { type SessionFeedback struct {
BaseModel BaseModel
TrackID string `json:"track_id"` TrackID string `json:"track_id"`
@@ -321,8 +389,332 @@ type SessionFeedback struct {
Timestamp string `json:"timestamp"` Timestamp string `json:"timestamp"`
} }
// SessionFeedbacks represents multiple session feedbacks // SessionFeedbacks represents multiple session feedbacks.
type SessionFeedbacks struct { type SessionFeedbacks struct {
BaseModel BaseModel
Feedbacks []SessionFeedback `json:"feedbacks"` Feedbacks []SessionFeedback `json:"feedbacks"`
} }
// Dashboard represents rotor stations dashboard.
type Dashboard struct {
BaseModel
DashboardID string `json:"dashboard_id"`
Stations []StationResult `json:"stations"`
Pumpkin bool `json:"pumpkin"`
}
// DeJSON deserializes Dashboard from JSON data.
func (d *Dashboard) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
dashboard := &Dashboard{BaseModel: *NewBaseModel(c)}
if v, ok := raw["dashboard_id"]; ok {
json.Unmarshal(v, &dashboard.DashboardID)
}
if v, ok := raw["stations"]; ok {
var stations []StationResult
json.Unmarshal(v, &stations)
dashboard.Stations = stations
}
if v, ok := raw["pumpkin"]; ok {
json.Unmarshal(v, &dashboard.Pumpkin)
}
return dashboard, nil
}
// DeList deserializes a list of Dashboards from JSON data.
func (d *Dashboard) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
dashboard := &Dashboard{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, dashboard); err != nil {
continue
}
result = append(result, dashboard)
}
return result, nil
}
// StationResult represents a station result.
type StationResult struct {
BaseModel
Station *Station `json:"station"`
Settings *RotorSettings `json:"settings"`
Settings2 *RotorSettings `json:"settings2"`
AdParams *AdParams `json:"ad_params"`
}
// DeJSON deserializes StationResult from JSON data.
func (sr *StationResult) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
result := &StationResult{BaseModel: *NewBaseModel(c)}
if v, ok := raw["station"]; ok {
var station Station
json.Unmarshal(v, &station)
result.Station = &station
}
if v, ok := raw["settings"]; ok {
var settings RotorSettings
json.Unmarshal(v, &settings)
result.Settings = &settings
}
if v, ok := raw["settings2"]; ok {
var settings2 RotorSettings
json.Unmarshal(v, &settings2)
result.Settings2 = &settings2
}
if v, ok := raw["ad_params"]; ok {
var adParams AdParams
json.Unmarshal(v, &adParams)
result.AdParams = &adParams
}
return result, nil
}
// DeList deserializes a list of StationResults from JSON data.
func (sr *StationResult) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
sr := &StationResult{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, sr); err != nil {
continue
}
result = append(result, sr)
}
return result, nil
}
// GenerativeStream represents a generative stream.
type GenerativeStream struct {
BaseModel
Data *GenerativeStreamData `json:"data"`
Version string `json:"version"`
Stream *GenerativeStreamInfo `json:"stream"`
}
// DeJSON deserializes GenerativeStream from JSON data.
func (gs *GenerativeStream) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
stream := &GenerativeStream{BaseModel: *NewBaseModel(c)}
if v, ok := raw["data"]; ok {
var data GenerativeStreamData
json.Unmarshal(v, &data)
stream.Data = &data
}
if v, ok := raw["version"]; ok {
json.Unmarshal(v, &stream.Version)
}
if v, ok := raw["stream"]; ok {
var streamInfo GenerativeStreamInfo
json.Unmarshal(v, &streamInfo)
stream.Stream = &streamInfo
}
return stream, nil
}
// DeList deserializes a list of GenerativeStreams from JSON data.
func (gs *GenerativeStream) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
stream := &GenerativeStream{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, stream); err != nil {
continue
}
result = append(result, stream)
}
return result, nil
}
// Station represents a radio station (used in StationResult).
type Station struct {
BaseModel
ID JSONID `json:"id"`
Name string `json:"name"`
Icon string `json:"icon"`
IDForFrom string `json:"id_for_from"`
}
// DeJSON deserializes Station from JSON data.
func (s *Station) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
station := &Station{BaseModel: *NewBaseModel(c)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &station.ID)
}
if v, ok := raw["name"]; ok {
json.Unmarshal(v, &station.Name)
}
if v, ok := raw["icon"]; ok {
json.Unmarshal(v, &station.Icon)
}
if v, ok := raw["id_for_from"]; ok {
json.Unmarshal(v, &station.IDForFrom)
}
return station, nil
}
// DeList deserializes a list of Stations from JSON data.
func (s *Station) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
station := &Station{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, station); err != nil {
continue
}
result = append(result, station)
}
return result, nil
}
// GenerativeStreamData represents generative stream data.
type GenerativeStreamData struct {
BaseModel
Title string `json:"title"`
Subtitle string `json:"subtitle"`
BackgroundColor string `json:"background_color"`
DerivedColors *CoverDerivedColors `json:"derived_colors"`
ImageURL string `json:"image_url"`
VideoCoverURI string `json:"video_cover_uri"`
Explanations string `json:"explanations"`
}
// DeJSON deserializes GenerativeStreamData from JSON data.
func (gsd *GenerativeStreamData) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
gsdData := &GenerativeStreamData{BaseModel: *NewBaseModel(c)}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &gsdData.Title)
}
if v, ok := raw["subtitle"]; ok {
json.Unmarshal(v, &gsdData.Subtitle)
}
if v, ok := raw["background_color"]; ok {
json.Unmarshal(v, &gsdData.BackgroundColor)
}
if v, ok := raw["derived_colors"]; ok {
var dc CoverDerivedColors
json.Unmarshal(v, &dc)
gsdData.DerivedColors = &dc
}
if v, ok := raw["image_url"]; ok {
json.Unmarshal(v, &gsdData.ImageURL)
}
if v, ok := raw["video_cover_uri"]; ok {
json.Unmarshal(v, &gsdData.VideoCoverURI)
}
if v, ok := raw["explanations"]; ok {
json.Unmarshal(v, &gsdData.Explanations)
}
return gsdData, nil
}
// DeList deserializes a list of GenerativeStreamData from JSON data.
func (gsd *GenerativeStreamData) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
data := &GenerativeStreamData{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, data); err != nil {
continue
}
result = append(result, data)
}
return result, nil
}
// GenerativeStreamInfo represents generative stream info.
type GenerativeStreamInfo struct {
BaseModel
ID string `json:"id"`
URL string `json:"url"`
}
// DeJSON deserializes GenerativeStreamInfo from JSON data.
func (gsi *GenerativeStreamInfo) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
info := &GenerativeStreamInfo{BaseModel: *NewBaseModel(c)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &info.ID)
}
if v, ok := raw["url"]; ok {
json.Unmarshal(v, &info.URL)
}
return info, nil
}
// DeList deserializes a list of GenerativeStreamInfo from JSON data.
func (gsi *GenerativeStreamInfo) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
info := &GenerativeStreamInfo{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, info); err != nil {
continue
}
result = append(result, info)
}
return result, nil
}
+27 -13
View File
@@ -1,54 +1,68 @@
// Package models provides data models for the Yandex Music API.
package models package models
// SearchResult represents search results // SearchResult represents search results from the Yandex Music API.
type SearchResult struct { type SearchResult struct {
SearchRequestID string `json:"search_request_id"`
Text string `json:"text"`
Best *Best `json:"best"`
Tracks *SearchTracksResult `json:"tracks"` Tracks *SearchTracksResult `json:"tracks"`
Albums *SearchAlbumsResult `json:"albums"` Albums *SearchAlbumsResult `json:"albums"`
Artists *SearchArtistsResult `json:"artists"` Artists *SearchArtistsResult `json:"artists"`
Playlists *SearchPlaylistsResult `json:"playlists"` Playlists *SearchPlaylistsResult `json:"playlists"`
Videos *SearchVideosResult `json:"videos"` Videos *SearchVideosResult `json:"videos"`
Users *SearchUsersResult `json:"users"` Users *SearchUsersResult `json:"users"`
Podcasts *SearchAlbumsResult `json:"podcasts"`
PodcastEpisodes *SearchTracksResult `json:"podcast_episodes"`
Clips *SearchVideosResult `json:"clips"`
Banner *SearchBanner `json:"banner"`
Page int `json:"page"`
PerPage int `json:"per_page"`
MisspellResult bool `json:"misspell_result"`
MisspellOriginal string `json:"misspell_original"`
MisspellCorrected bool `json:"misspell_corrected"`
Nocorrect bool `json:"nocorrect"`
} }
// SearchTracksResult represents track search results // SearchTracksResult represents track search results.
type SearchTracksResult struct { type SearchTracksResult struct {
Total int `json:"total"` Total int `json:"total"`
Results []Track `json:"results"` Results []Track `json:"results"`
} }
// SearchAlbumsResult represents album search results // SearchAlbumsResult represents album search results.
type SearchAlbumsResult struct { type SearchAlbumsResult struct {
Total int `json:"total"` Total int `json:"total"`
Results []Album `json:"results"` Results []Album `json:"results"`
} }
// SearchArtistsResult represents artist search results // SearchArtistsResult represents artist search results.
type SearchArtistsResult struct { type SearchArtistsResult struct {
Total int `json:"total"` Total int `json:"total"`
Results []Artist `json:"results"` Results []Artist `json:"results"`
} }
// SearchPlaylistsResult represents playlist search results // SearchPlaylistsResult represents playlist search results.
type SearchPlaylistsResult struct { type SearchPlaylistsResult struct {
Total int `json:"total"` Total int `json:"total"`
Results []Playlist `json:"results"` Results []Playlist `json:"results"`
} }
// SearchVideosResult represents video search results // SearchVideosResult represents video search results.
type SearchVideosResult struct { type SearchVideosResult struct {
Total int `json:"total"` Total int `json:"total"`
Results []Video `json:"results"` Results []Video `json:"results"`
} }
// SearchUsersResult represents user search results // SearchUsersResult represents user search results.
type SearchUsersResult struct { type SearchUsersResult struct {
Total int `json:"total"` Total int `json:"total"`
Results []User `json:"results"` Results []User `json:"results"`
} }
// SearchBanner represents a search banner // SearchBanner represents a search banner.
type SearchBanner struct { type SearchBanner struct {
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
Image string `json:"image"` Image string `json:"image"`
@@ -57,19 +71,19 @@ type SearchBanner struct {
Type string `json:"type"` Type string `json:"type"`
} }
// Tag represents a search tag // Tag represents a search tag.
type Tag struct { type Tag struct {
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
Count int `json:"count"` Count int `json:"count"`
Type string `json:"type"` Type string `json:"type"`
} }
// Video represents a video (minimal) // Video represents a video (minimal).
type Video struct { type Video struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Duration int `json:"duration"` Duration int `json:"duration"`
CoverURI string `json:"cover_uri"` CoverURI string `json:"cover_uri"`
+20 -19
View File
@@ -1,17 +1,18 @@
// Package models provides data models for the Yandex Music API.
package models package models
import ( import (
"encoding/json" "encoding/json"
) )
// Experiments represents experiments // Experiments represents experiments from the Yandex Music API.
type Experiments struct { type Experiments struct {
BaseModel BaseModel
Experiments []ExperimentDetail `json:"experiments"` Experiments []ExperimentDetail `json:"experiments"`
Total int `json:"total"` Total int `json:"total"`
} }
// DeJSON deserializes Experiments from JSON // DeJSON deserializes Experiments from JSON data.
func (e *Experiments) DeJSON(data []byte, client ClientInterface) (Model, error) { func (e *Experiments) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -32,7 +33,7 @@ func (e *Experiments) DeJSON(data []byte, client ClientInterface) (Model, error)
return experiments, nil return experiments, nil
} }
// DeList deserializes a list of Experiments // DeList deserializes a list of Experiments from JSON data.
func (e *Experiments) DeList(data []byte, client ClientInterface) ([]Model, error) { func (e *Experiments) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -50,10 +51,10 @@ func (e *Experiments) DeList(data []byte, client ClientInterface) ([]Model, erro
return result, nil return result, nil
} }
// ExperimentDetail represents experiment details // ExperimentDetail represents experiment details.
type ExperimentDetail struct { type ExperimentDetail struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Description string `json:"description"` Description string `json:"description"`
Status string `json:"status"` Status string `json:"status"`
@@ -62,7 +63,7 @@ type ExperimentDetail struct {
EndDate string `json:"end_date"` EndDate string `json:"end_date"`
} }
// DeJSON deserializes ExperimentDetail from JSON // DeJSON deserializes ExperimentDetail from JSON data.
func (e *ExperimentDetail) DeJSON(data []byte, client ClientInterface) (Model, error) { func (e *ExperimentDetail) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -96,7 +97,7 @@ func (e *ExperimentDetail) DeJSON(data []byte, client ClientInterface) (Model, e
return detail, nil return detail, nil
} }
// DeList deserializes a list of ExperimentDetails // DeList deserializes a list of ExperimentDetails from JSON data.
func (e *ExperimentDetail) DeList(data []byte, client ClientInterface) ([]Model, error) { func (e *ExperimentDetail) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -114,7 +115,7 @@ func (e *ExperimentDetail) DeList(data []byte, client ClientInterface) ([]Model,
return result, nil return result, nil
} }
// Permissions represents user permissions // Permissions represents user permissions.
type Permissions struct { type Permissions struct {
BaseModel BaseModel
CanPlay bool `json:"can_play"` CanPlay bool `json:"can_play"`
@@ -129,7 +130,7 @@ type Permissions struct {
CanUseLossless bool `json:"can_use_lossless"` CanUseLossless bool `json:"can_use_lossless"`
} }
// DeJSON deserializes Permissions from JSON // DeJSON deserializes Permissions from JSON data.
func (p *Permissions) DeJSON(data []byte, client ClientInterface) (Model, error) { func (p *Permissions) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -172,7 +173,7 @@ func (p *Permissions) DeJSON(data []byte, client ClientInterface) (Model, error)
return perms, nil return perms, nil
} }
// DeList deserializes a list of Permissions // DeList deserializes a list of Permissions from JSON data.
func (p *Permissions) DeList(data []byte, client ClientInterface) ([]Model, error) { func (p *Permissions) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -190,14 +191,14 @@ func (p *Permissions) DeList(data []byte, client ClientInterface) ([]Model, erro
return result, nil return result, nil
} }
// PermissionAlerts represents permission alerts // PermissionAlerts represents permission alerts.
type PermissionAlerts struct { type PermissionAlerts struct {
BaseModel BaseModel
Alerts []PermissionAlert `json:"alerts"` Alerts []PermissionAlert `json:"alerts"`
Total int `json:"total"` Total int `json:"total"`
} }
// DeJSON deserializes PermissionAlerts from JSON // DeJSON deserializes PermissionAlerts from JSON data.
func (p *PermissionAlerts) DeJSON(data []byte, client ClientInterface) (Model, error) { func (p *PermissionAlerts) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -218,7 +219,7 @@ func (p *PermissionAlerts) DeJSON(data []byte, client ClientInterface) (Model, e
return alerts, nil return alerts, nil
} }
// DeList deserializes a list of PermissionAlerts // DeList deserializes a list of PermissionAlerts from JSON data. from JSON data.
func (p *PermissionAlerts) DeList(data []byte, client ClientInterface) ([]Model, error) { func (p *PermissionAlerts) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -236,10 +237,10 @@ func (p *PermissionAlerts) DeList(data []byte, client ClientInterface) ([]Model,
return result, nil return result, nil
} }
// PermissionAlert represents a permission alert // PermissionAlert represents a permission alert.
type PermissionAlert struct { type PermissionAlert struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Type string `json:"type"` Type string `json:"type"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
@@ -248,7 +249,7 @@ type PermissionAlert struct {
CreatedAt string `json:"created_at"` CreatedAt string `json:"created_at"`
} }
// DeJSON deserializes PermissionAlert from JSON // DeJSON deserializes PermissionAlert from JSON data.
func (p *PermissionAlert) DeJSON(data []byte, client ClientInterface) (Model, error) { func (p *PermissionAlert) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -300,7 +301,7 @@ func (p *PermissionAlert) DeList(data []byte, client ClientInterface) ([]Model,
return result, nil return result, nil
} }
// ContentRestrictions represents content restrictions // ContentRestrictions represents content restrictions.
type ContentRestrictions struct { type ContentRestrictions struct {
BaseModel BaseModel
HasExplicitContent bool `json:"has_explicit_content"` HasExplicitContent bool `json:"has_explicit_content"`
@@ -311,7 +312,7 @@ type ContentRestrictions struct {
BlockedArtists []string `json:"blocked_artists"` BlockedArtists []string `json:"blocked_artists"`
} }
// DeJSON deserializes ContentRestrictions from JSON // DeJSON deserializes ContentRestrictions from JSON data.
func (c *ContentRestrictions) DeJSON(data []byte, client ClientInterface) (Model, error) { func (c *ContentRestrictions) DeJSON(data []byte, client ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -342,7 +343,7 @@ func (c *ContentRestrictions) DeJSON(data []byte, client ClientInterface) (Model
return restrictions, nil return restrictions, nil
} }
// DeList deserializes a list of ContentRestrictions // DeList deserializes a list of ContentRestrictions from JSON data.
func (c *ContentRestrictions) DeList(data []byte, client ClientInterface) ([]Model, error) { func (c *ContentRestrictions) DeList(data []byte, client ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
+20 -19
View File
@@ -1,10 +1,11 @@
// Package models provides data models for the Yandex Music API.
package models package models
import ( import (
"encoding/json" "encoding/json"
) )
// LyricsMajor represents a lyrics provider (e.g., Genius, Musixmatch) // LyricsMajor represents a lyrics provider (e.g., Genius, Musixmatch).
type LyricsMajor struct { type LyricsMajor struct {
BaseModel BaseModel
ID int `json:"id"` ID int `json:"id"`
@@ -12,7 +13,7 @@ type LyricsMajor struct {
PrettyName string `json:"pretty_name"` PrettyName string `json:"pretty_name"`
} }
// DeJSON deserializes LyricsMajor from JSON // DeJSON deserializes LyricsMajor from JSON data.
func (lm *LyricsMajor) DeJSON(data []byte, c ClientInterface) (Model, error) { func (lm *LyricsMajor) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -34,7 +35,7 @@ func (lm *LyricsMajor) DeJSON(data []byte, c ClientInterface) (Model, error) {
return major, nil return major, nil
} }
// DeList deserializes a list of LyricsMajor // DeList deserializes a list of LyricsMajor from JSON data.
func (lm *LyricsMajor) DeList(data []byte, c ClientInterface) ([]Model, error) { func (lm *LyricsMajor) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -52,7 +53,7 @@ func (lm *LyricsMajor) DeList(data []byte, c ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// TrackLyrics represents lyrics information from /tracks/{id}/lyrics endpoint // TrackLyrics represents lyrics information from /tracks/{id}/lyrics endpoint.
type TrackLyrics struct { type TrackLyrics struct {
BaseModel BaseModel
DownloadURL string `json:"download_url"` DownloadURL string `json:"download_url"`
@@ -62,7 +63,7 @@ type TrackLyrics struct {
Major *LyricsMajor `json:"major"` Major *LyricsMajor `json:"major"`
} }
// DeJSON deserializes TrackLyrics from JSON // DeJSON deserializes TrackLyrics from JSON data.
func (tl *TrackLyrics) DeJSON(data []byte, c ClientInterface) (Model, error) { func (tl *TrackLyrics) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -92,7 +93,7 @@ func (tl *TrackLyrics) DeJSON(data []byte, c ClientInterface) (Model, error) {
return lyrics, nil return lyrics, nil
} }
// DeList deserializes a list of TrackLyrics // DeList deserializes a list of TrackLyrics from JSON data.
func (tl *TrackLyrics) DeList(data []byte, c ClientInterface) ([]Model, error) { func (tl *TrackLyrics) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -110,7 +111,7 @@ func (tl *TrackLyrics) DeList(data []byte, c ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// SupplementLyrics represents the actual lyrics content (from supplement) // SupplementLyrics represents the actual lyrics content from supplement.
type SupplementLyrics struct { type SupplementLyrics struct {
BaseModel BaseModel
ID int `json:"id"` ID int `json:"id"`
@@ -123,7 +124,7 @@ type SupplementLyrics struct {
Major *LyricsMajor `json:"major"` Major *LyricsMajor `json:"major"`
} }
// DeJSON deserializes SupplementLyrics from JSON // DeJSON deserializes SupplementLyrics from JSON data.
func (l *SupplementLyrics) DeJSON(data []byte, c ClientInterface) (Model, error) { func (l *SupplementLyrics) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -162,7 +163,7 @@ func (l *SupplementLyrics) DeJSON(data []byte, c ClientInterface) (Model, error)
return lyrics, nil return lyrics, nil
} }
// DeList deserializes a list of SupplementLyrics // DeList deserializes a list of SupplementLyrics from JSON data.
func (l *SupplementLyrics) DeList(data []byte, c ClientInterface) ([]Model, error) { func (l *SupplementLyrics) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -180,7 +181,7 @@ func (l *SupplementLyrics) DeList(data []byte, c ClientInterface) ([]Model, erro
return result, nil return result, nil
} }
// LyricsInfo represents lyrics availability information // LyricsInfo represents lyrics availability information.
type LyricsInfo struct { type LyricsInfo struct {
BaseModel BaseModel
TrackID string `json:"track_id"` TrackID string `json:"track_id"`
@@ -196,7 +197,7 @@ type LyricsInfo struct {
HasAvailableTextLyrics bool `json:"has_available_text_lyrics"` HasAvailableTextLyrics bool `json:"has_available_text_lyrics"`
} }
// DeJSON deserializes LyricsInfo from JSON // DeJSON deserializes LyricsInfo from JSON data.
func (li *LyricsInfo) DeJSON(data []byte, c ClientInterface) (Model, error) { func (li *LyricsInfo) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -247,7 +248,7 @@ func (li *LyricsInfo) DeJSON(data []byte, c ClientInterface) (Model, error) {
return info, nil return info, nil
} }
// DeList deserializes a list of LyricsInfo // DeList deserializes a list of LyricsInfo from JSON data.
func (li *LyricsInfo) DeList(data []byte, c ClientInterface) ([]Model, error) { func (li *LyricsInfo) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -265,7 +266,7 @@ func (li *LyricsInfo) DeList(data []byte, c ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// VideoSupplement represents a video supplement (from supplement endpoint) // VideoSupplement represents a video supplement from supplement endpoint.
type VideoSupplement struct { type VideoSupplement struct {
BaseModel BaseModel
Cover string `json:"cover"` Cover string `json:"cover"`
@@ -277,7 +278,7 @@ type VideoSupplement struct {
Embed string `json:"embed"` Embed string `json:"embed"`
} }
// DeJSON deserializes VideoSupplement from JSON // DeJSON deserializes VideoSupplement from JSON data.
func (vs *VideoSupplement) DeJSON(data []byte, c ClientInterface) (Model, error) { func (vs *VideoSupplement) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -311,7 +312,7 @@ func (vs *VideoSupplement) DeJSON(data []byte, c ClientInterface) (Model, error)
return supp, nil return supp, nil
} }
// DeList deserializes a list of VideoSupplements // DeList deserializes a list of VideoSupplements from JSON data.
func (vs *VideoSupplement) DeList(data []byte, c ClientInterface) ([]Model, error) { func (vs *VideoSupplement) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -329,10 +330,10 @@ func (vs *VideoSupplement) DeList(data []byte, c ClientInterface) ([]Model, erro
return result, nil return result, nil
} }
// Supplement represents a track supplement // Supplement represents a track supplement.
type Supplement struct { type Supplement struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Lyrics *LyricsInfo `json:"lyrics"` Lyrics *LyricsInfo `json:"lyrics"`
Videos []VideoSupplement `json:"videos"` Videos []VideoSupplement `json:"videos"`
RadioIsAvailable bool `json:"radio_is_available"` RadioIsAvailable bool `json:"radio_is_available"`
@@ -340,7 +341,7 @@ type Supplement struct {
Available bool `json:"available"` Available bool `json:"available"`
} }
// DeJSON deserializes Supplement from JSON // DeJSON deserializes Supplement from JSON data.
func (s *Supplement) DeJSON(data []byte, c ClientInterface) (Model, error) { func (s *Supplement) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -376,7 +377,7 @@ func (s *Supplement) DeJSON(data []byte, c ClientInterface) (Model, error) {
return supp, nil return supp, nil
} }
// DeList deserializes a list of Supplements // DeList deserializes a list of Supplements from JSON data.
func (s *Supplement) DeList(data []byte, c ClientInterface) ([]Model, error) { func (s *Supplement) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
+513 -31
View File
@@ -1,33 +1,78 @@
// Package models provides data models for the Yandex Music API.
package models package models
import ( import (
"encoding/json" "encoding/json"
) )
// Track represents a full track // Track represents a full track from the Yandex Music API.
type Track struct { type Track struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
DurationMs int `json:"duration_ms"` Available bool `json:"available"`
StorageID string `json:"storage_id"`
CoverURI string `json:"cover_uri"`
Artists []Artist `json:"artists"` Artists []Artist `json:"artists"`
Albums []Album `json:"albums"` 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"` AvailableForPremiumUsers bool `json:"available_for_premium_users"`
R128 float64 `json:"r128"` LyricsAvailable bool `json:"lyrics_available"`
TrackSharingFlag string `json:"track_sharing_flag"` Best bool `json:"best"`
RealID JSONID `json:"real_id"`
OgImage string `json:"og_image"`
Type string `json:"type"`
CoverURI string `json:"cover_uri"`
Major *Major `json:"major"` Major *Major `json:"major"`
Lyrics *TrackLyricsInfo `json:"lyrics"` DurationMs int `json:"duration_ms"`
StorageDir string `json:"storage_dir"`
FileSize int64 `json:"file_size"`
Genre string `json:"genre"`
AvailableAsRingtone bool `json:"available_as_ringtone"`
RingtoneID string `json:"ringtone_id"`
SimilarTracks []Track `json:"similar_tracks"` SimilarTracks []Track `json:"similar_tracks"`
Position *TrackPosition `json:"position"`
Substituted *Track `json:"substituted"`
MatchedTrack *Track `json:"matched_track"`
Normalization *Normalization `json:"normalization"`
Error string `json:"error"`
CanPublish bool `json:"can_publish"`
State string `json:"state"`
DesiredVisibility string `json:"desired_visibility"`
Filename string `json:"filename"`
UserInfo *User `json:"user_info"`
MetaData *MetaData `json:"meta_data"`
Regions []string `json:"regions"`
AvailableAsRbt bool `json:"available_as_rbt"`
ContentWarning string `json:"content_warning"`
Explicit bool `json:"explicit"`
PreviewDurationMs int `json:"preview_duration_ms"`
AvailableFullWithoutPermission bool `json:"available_full_without_permission"`
Version string `json:"version"`
RememberPosition bool `json:"remember_position"`
BackgroundVideoURI string `json:"background_video_uri"`
ShortDescription string `json:"short_description"`
IsSuitableForChildren bool `json:"is_suitable_for_children"`
TrackSource string `json:"track_source"`
AvailableForOptions []string `json:"available_for_options"`
R128 *R128 `json:"r128"`
LyricsInfo *TrackLyricsInfo `json:"lyrics_info"`
TrackSharingFlag string `json:"track_sharing_flag"`
DerivedColors *CoverDerivedColors `json:"derived_colors"`
Fade *Fade `json:"fade"`
SmartPreviewParams *SmartPreviewParams `json:"smart_preview_params"`
SpecialAudioResources []string `json:"special_audio_resources"`
Disclaimers []string `json:"disclaimers"`
BackgroundVideoID string `json:"background_video_id"`
PlayerID string `json:"player_id"`
MixFade *Fade `json:"mix_fade"`
Chart *Chart `json:"chart"`
ClipIDs []int `json:"clip_ids"`
Isrc string `json:"isrc"`
UgcArtistName string `json:"ugc_artist_name"`
PodcastEpisodeType string `json:"podcast_episode_type"`
PubDate string `json:"pub_date"`
DownloadInfo []DownloadInfo `json:"download_info"` DownloadInfo []DownloadInfo `json:"download_info"`
} }
// DeJSON deserializes a Track from JSON // DeJSON deserializes a Track from JSON data.
func (t *Track) DeJSON(data []byte, c ClientInterface) (Model, error) { func (t *Track) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -46,8 +91,8 @@ func (t *Track) DeJSON(data []byte, c ClientInterface) (Model, error) {
if v, ok := raw["duration_ms"]; ok { if v, ok := raw["duration_ms"]; ok {
json.Unmarshal(v, &track.DurationMs) json.Unmarshal(v, &track.DurationMs)
} }
if v, ok := raw["storage_id"]; ok { if v, ok := raw["storage_dir"]; ok {
json.Unmarshal(v, &track.StorageID) json.Unmarshal(v, &track.StorageDir)
} }
if v, ok := raw["cover_uri"]; ok { if v, ok := raw["cover_uri"]; ok {
json.Unmarshal(v, &track.CoverURI) json.Unmarshal(v, &track.CoverURI)
@@ -73,6 +118,12 @@ func (t *Track) DeJSON(data []byte, c ClientInterface) (Model, error) {
if v, ok := raw["track_sharing_flag"]; ok { if v, ok := raw["track_sharing_flag"]; ok {
json.Unmarshal(v, &track.TrackSharingFlag) json.Unmarshal(v, &track.TrackSharingFlag)
} }
if v, ok := raw["available_as_ringtone"]; ok {
json.Unmarshal(v, &track.AvailableAsRingtone)
}
if v, ok := raw["ringtone_id"]; ok {
json.Unmarshal(v, &track.RingtoneID)
}
// Nested objects // Nested objects
if v, ok := raw["artists"]; ok { if v, ok := raw["artists"]; ok {
@@ -93,10 +144,10 @@ func (t *Track) DeJSON(data []byte, c ClientInterface) (Model, error) {
track.Major = &major track.Major = &major
} }
} }
if v, ok := raw["lyrics"]; ok { if v, ok := raw["lyrics_info"]; ok {
var lyrics TrackLyricsInfo var lyrics TrackLyricsInfo
if err := json.Unmarshal(v, &lyrics); err == nil { if err := json.Unmarshal(v, &lyrics); err == nil {
track.Lyrics = &lyrics track.LyricsInfo = &lyrics
} }
} }
if v, ok := raw["similar_tracks"]; ok { if v, ok := raw["similar_tracks"]; ok {
@@ -111,11 +162,17 @@ func (t *Track) DeJSON(data []byte, c ClientInterface) (Model, error) {
track.DownloadInfo = downloadInfo track.DownloadInfo = downloadInfo
} }
} }
if v, ok := raw["position"]; ok {
var pos TrackPosition
if err := json.Unmarshal(v, &pos); err == nil {
track.Position = &pos
}
}
return track, nil return track, nil
} }
// DeList deserializes a list of Tracks // DeList deserializes a list of Tracks from JSON data.
func (t *Track) DeList(data []byte, c ClientInterface) ([]Model, error) { func (t *Track) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -133,7 +190,7 @@ func (t *Track) DeList(data []byte, c ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// GetDownloadInfo returns download info for a specific quality // GetDownloadInfo returns download info for a specific quality, or nil if not found.
func (t *Track) GetDownloadInfo(quality string) *DownloadInfo { func (t *Track) GetDownloadInfo(quality string) *DownloadInfo {
for _, info := range t.DownloadInfo { for _, info := range t.DownloadInfo {
if info.Quality == quality { if info.Quality == quality {
@@ -143,26 +200,58 @@ func (t *Track) GetDownloadInfo(quality string) *DownloadInfo {
return nil return nil
} }
// Note: Artist and Album types are defined in their respective files // Major represents major label info for a track.
// These are imported automatically since they're in the same package
// Major represents major info
type Major struct { type Major struct {
ID string `json:"id"` ID JSONID `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Description string `json:"description"` Description string `json:"description"`
} }
// TrackLyricsInfo represents minimal lyrics info for a track // TrackLyricsInfo represents lyrics availability info for a track.
type TrackLyricsInfo struct { type TrackLyricsInfo struct {
ID string `json:"id"` BaseModel
Text string `json:"text"` HasAvailableSyncLyrics bool `json:"has_available_sync_lyrics"`
Major *Major `json:"major"` HasAvailableTextLyrics bool `json:"has_available_text_lyrics"`
Available bool `json:"available"`
HasText bool `json:"has_text"`
} }
// DownloadInfo represents file download info // DeJSON deserializes TrackLyricsInfo from JSON data.
func (tli *TrackLyricsInfo) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
info := &TrackLyricsInfo{BaseModel: *NewBaseModel(c)}
if v, ok := raw["has_available_sync_lyrics"]; ok {
json.Unmarshal(v, &info.HasAvailableSyncLyrics)
}
if v, ok := raw["has_available_text_lyrics"]; ok {
json.Unmarshal(v, &info.HasAvailableTextLyrics)
}
return info, nil
}
// DeList deserializes a list of TrackLyricsInfo from JSON data.
func (tli *TrackLyricsInfo) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
info := &TrackLyricsInfo{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, info); err != nil {
continue
}
result = append(result, info)
}
return result, nil
}
// DownloadInfo represents file download info for a track.
type DownloadInfo struct { type DownloadInfo struct {
Quality string `json:"quality"` Quality string `json:"quality"`
Codec string `json:"codec"` Codec string `json:"codec"`
@@ -170,4 +259,397 @@ type DownloadInfo struct {
Size int64 `json:"size"` Size int64 `json:"size"`
URL string `json:"url"` URL string `json:"url"`
DirectLink bool `json:"direct_link"` DirectLink bool `json:"direct_link"`
DownloadInfoURL string `json:"download_info_url"`
DirectLinkURL string `json:"-"` // Computed signed download URL
}
// Normalization represents track normalization values.
type Normalization struct {
BaseModel
Gain float64 `json:"gain"`
Peak int `json:"peak"`
}
// MetaData represents track metadata.
type MetaData struct {
BaseModel
Album string `json:"album"`
Volume int `json:"volume"`
Year int `json:"year"`
Number int `json:"number"`
Genre string `json:"genre"`
Lyricist string `json:"lyricist"`
Version string `json:"version"`
Composer string `json:"composer"`
}
// Fade represents fade parameters for a track.
type Fade struct {
BaseModel
InStart float64 `json:"in_start"`
InStop float64 `json:"in_stop"`
OutStart float64 `json:"out_start"`
OutStop float64 `json:"out_stop"`
}
// SmartPreviewParams represents smart preview parameters.
type SmartPreviewParams struct {
BaseModel
DurationMs int `json:"duration_ms"`
Fade *Fade `json:"fade"`
}
// CoverDerivedColors represents derived colors from cover art.
type CoverDerivedColors struct {
BaseModel
Average string `json:"average"`
WaveText string `json:"wave_text"`
MiniPlayer string `json:"mini_player"`
Accent string `json:"accent"`
}
// CustomWave represents a custom wave for playlists.
type CustomWave struct {
BaseModel
Title string `json:"title"`
AnimationURL string `json:"animation_url"`
Position string `json:"position"`
Header string `json:"header"`
BackgroundImageURL string `json:"background_image_url"`
SquareAgentAnimation string `json:"square_agent_animation"`
}
// TrackId represents a track ID reference.
type TrackId struct {
BaseModel
ID JSONID `json:"id"`
TrackID JSONID `json:"track_id"`
AlbumID JSONID `json:"album_id"`
From string `json:"from"`
}
// DeJSON deserializes a TrackId from JSON data.
func (ti *TrackId) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
tid := &TrackId{BaseModel: *NewBaseModel(c)}
if v, ok := raw["id"]; ok {
json.Unmarshal(v, &tid.ID)
}
if v, ok := raw["track_id"]; ok {
json.Unmarshal(v, &tid.TrackID)
}
if v, ok := raw["album_id"]; ok {
json.Unmarshal(v, &tid.AlbumID)
}
if v, ok := raw["from"]; ok {
json.Unmarshal(v, &tid.From)
}
return tid, nil
}
// DeList deserializes a list of TrackId from JSON data.
func (ti *TrackId) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
tid := &TrackId{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, tid); err != nil {
continue
}
result = append(result, tid)
}
return result, nil
}
// TrackFullInfo represents full track info.
type TrackFullInfo struct {
BaseModel
Track *Track `json:"track"`
SimilarTracks []Track `json:"similar_tracks"`
AlsoInAlbums []Track `json:"also_in_albums"`
Aliases []string `json:"aliases"`
Artists []Artist `json:"artists"`
}
// DeJSON deserializes a TrackFullInfo from JSON data.
func (tfi *TrackFullInfo) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
info := &TrackFullInfo{BaseModel: *NewBaseModel(c)}
if v, ok := raw["track"]; ok {
var track Track
if err := json.Unmarshal(v, &track); err == nil {
info.Track = &track
}
}
if v, ok := raw["similar_tracks"]; ok {
var tracks []Track
if err := json.Unmarshal(v, &tracks); err == nil {
info.SimilarTracks = tracks
}
}
if v, ok := raw["also_in_albums"]; ok {
var tracks []Track
if err := json.Unmarshal(v, &tracks); err == nil {
info.AlsoInAlbums = tracks
}
}
if v, ok := raw["aliases"]; ok {
json.Unmarshal(v, &info.Aliases)
}
if v, ok := raw["artists"]; ok {
var artists []Artist
if err := json.Unmarshal(v, &artists); err == nil {
info.Artists = artists
}
}
return info, nil
}
// DeList deserializes a list of TrackFullInfo from JSON data.
func (tfi *TrackFullInfo) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
info := &TrackFullInfo{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, info); err != nil {
continue
}
result = append(result, info)
}
return result, nil
}
// SimilarTracks represents similar tracks.
type SimilarTracks struct {
BaseModel
Track *Track `json:"track"`
SimilarTracks []Track `json:"similar_tracks"`
}
// DeJSON deserializes SimilarTracks from JSON data.
func (st *SimilarTracks) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
similar := &SimilarTracks{BaseModel: *NewBaseModel(c)}
if v, ok := raw["track"]; ok {
var track Track
if err := json.Unmarshal(v, &track); err == nil {
similar.Track = &track
}
}
if v, ok := raw["similar_tracks"]; ok {
var tracks []Track
if err := json.Unmarshal(v, &tracks); err == nil {
similar.SimilarTracks = tracks
}
}
return similar, nil
}
// DeList deserializes a list of SimilarTracks from JSON data.
func (st *SimilarTracks) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
similar := &SimilarTracks{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, similar); err != nil {
continue
}
result = append(result, similar)
}
return result, nil
}
// TrackTrailer represents track trailer info.
type TrackTrailer struct {
BaseModel
Title string `json:"title"`
Track *Track `json:"track"`
}
// DeJSON deserializes a TrackTrailer from JSON data.
func (tt *TrackTrailer) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
trailer := &TrackTrailer{BaseModel: *NewBaseModel(c)}
if v, ok := raw["title"]; ok {
json.Unmarshal(v, &trailer.Title)
}
if v, ok := raw["track"]; ok {
var track Track
if err := json.Unmarshal(v, &track); err == nil {
trailer.Track = &track
}
}
return trailer, nil
}
// DeList deserializes a list of TrackTrailer from JSON data.
func (tt *TrackTrailer) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
trailer := &TrackTrailer{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, trailer); err != nil {
continue
}
result = append(result, trailer)
}
return result, nil
}
// FileInfo represents file info for a track.
type FileInfo struct {
BaseModel
DownloadInfo *FileDownloadInfo `json:"download_info"`
}
// DeJSON deserializes FileInfo from JSON data.
func (fi *FileInfo) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
info := &FileInfo{BaseModel: *NewBaseModel(c)}
if v, ok := raw["download_info"]; ok {
var di FileDownloadInfo
json.Unmarshal(v, &di)
info.DownloadInfo = &di
}
return info, nil
}
// DeList deserializes a list of FileInfo from JSON data.
func (fi *FileInfo) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
info := &FileInfo{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, info); err != nil {
continue
}
result = append(result, info)
}
return result, nil
}
// FileDownloadInfo represents file download info.
type FileDownloadInfo struct {
BaseModel
TrackID string `json:"track_id"`
Quality string `json:"quality"`
Codec string `json:"codec"`
Bitrate int `json:"bitrate"`
Transport string `json:"transport"`
URL string `json:"url"`
URLs []string `json:"urls"`
RealID string `json:"real_id"`
Gain bool `json:"gain"`
Key string `json:"key"`
}
// DeJSON deserializes FileDownloadInfo from JSON data.
func (fdi *FileDownloadInfo) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
info := &FileDownloadInfo{BaseModel: *NewBaseModel(c)}
if v, ok := raw["track_id"]; ok {
json.Unmarshal(v, &info.TrackID)
}
if v, ok := raw["quality"]; ok {
json.Unmarshal(v, &info.Quality)
}
if v, ok := raw["codec"]; ok {
json.Unmarshal(v, &info.Codec)
}
if v, ok := raw["bitrate"]; ok {
json.Unmarshal(v, &info.Bitrate)
}
if v, ok := raw["transport"]; ok {
json.Unmarshal(v, &info.Transport)
}
if v, ok := raw["url"]; ok {
json.Unmarshal(v, &info.URL)
}
if v, ok := raw["urls"]; ok {
json.Unmarshal(v, &info.URLs)
}
if v, ok := raw["real_id"]; ok {
json.Unmarshal(v, &info.RealID)
}
if v, ok := raw["gain"]; ok {
json.Unmarshal(v, &info.Gain)
}
if v, ok := raw["key"]; ok {
json.Unmarshal(v, &info.Key)
}
return info, nil
}
// DeList deserializes a list of FileDownloadInfo from JSON data.
func (fdi *FileDownloadInfo) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil {
return nil, err
}
result := make([]Model, 0, len(rawList))
for _, item := range rawList {
info := &FileDownloadInfo{BaseModel: *NewBaseModel(c)}
if err := json.Unmarshal(item, info); err != nil {
continue
}
result = append(result, info)
}
return result, nil
} }
+14 -13
View File
@@ -1,16 +1,17 @@
// Package models provides data models for the Yandex Music API.
package models package models
import ( import (
"encoding/json" "encoding/json"
) )
// TrackShort represents a lightweight track (used in lists) // TrackShort represents a lightweight track used in lists.
type TrackShort struct { type TrackShort struct {
BaseModel BaseModel
ID string `json:"id"` ID JSONID `json:"id"`
Title string `json:"title"` Title string `json:"title"`
DurationMs int `json:"duration_ms"` DurationMs int `json:"duration_ms"`
StorageID string `json:"storage_id"` StorageDir string `json:"storage_dir"`
CoverURI string `json:"cover_uri"` CoverURI string `json:"cover_uri"`
Artists []Artist `json:"artists"` Artists []Artist `json:"artists"`
Albums []Album `json:"albums"` Albums []Album `json:"albums"`
@@ -19,10 +20,10 @@ type TrackShort struct {
Explicit bool `json:"explicit"` Explicit bool `json:"explicit"`
Available bool `json:"available"` Available bool `json:"available"`
AvailableForPremiumUsers bool `json:"available_for_premium_users"` AvailableForPremiumUsers bool `json:"available_for_premium_users"`
R128 float64 `json:"r128"` R128 *R128 `json:"r128"`
TrackSharingFlag string `json:"track_sharing_flag"` TrackSharingFlag string `json:"track_sharing_flag"`
Major *Major `json:"major"` Major *Major `json:"major"`
Lyrics *SupplementLyrics `json:"lyrics"` Lyrics *TrackLyricsInfo `json:"lyrics"`
SimilarTracks []TrackShort `json:"similar_tracks"` SimilarTracks []TrackShort `json:"similar_tracks"`
DownloadInfo []DownloadInfo `json:"download_info"` DownloadInfo []DownloadInfo `json:"download_info"`
// Additional fields from track_short.py // Additional fields from track_short.py
@@ -31,7 +32,7 @@ type TrackShort struct {
RingtoneID string `json:"ringtone_id"` RingtoneID string `json:"ringtone_id"`
} }
// DeJSON deserializes a TrackShort from JSON // DeJSON deserializes a TrackShort from JSON data.
func (t *TrackShort) DeJSON(data []byte, c ClientInterface) (Model, error) { func (t *TrackShort) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -50,8 +51,8 @@ func (t *TrackShort) DeJSON(data []byte, c ClientInterface) (Model, error) {
if v, ok := raw["duration_ms"]; ok { if v, ok := raw["duration_ms"]; ok {
json.Unmarshal(v, &track.DurationMs) json.Unmarshal(v, &track.DurationMs)
} }
if v, ok := raw["storage_id"]; ok { if v, ok := raw["storage_dir"]; ok {
json.Unmarshal(v, &track.StorageID) json.Unmarshal(v, &track.StorageDir)
} }
if v, ok := raw["cover_uri"]; ok { if v, ok := raw["cover_uri"]; ok {
json.Unmarshal(v, &track.CoverURI) json.Unmarshal(v, &track.CoverURI)
@@ -104,7 +105,7 @@ func (t *TrackShort) DeJSON(data []byte, c ClientInterface) (Model, error) {
} }
} }
if v, ok := raw["lyrics"]; ok { if v, ok := raw["lyrics"]; ok {
var lyrics SupplementLyrics var lyrics TrackLyricsInfo
if err := json.Unmarshal(v, &lyrics); err == nil { if err := json.Unmarshal(v, &lyrics); err == nil {
track.Lyrics = &lyrics track.Lyrics = &lyrics
} }
@@ -131,7 +132,7 @@ func (t *TrackShort) DeJSON(data []byte, c ClientInterface) (Model, error) {
return track, nil return track, nil
} }
// DeList deserializes a list of TrackShort // DeList deserializes a list of TrackShort from JSON data.
func (t *TrackShort) DeList(data []byte, c ClientInterface) ([]Model, error) { func (t *TrackShort) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
@@ -149,7 +150,7 @@ func (t *TrackShort) DeList(data []byte, c ClientInterface) ([]Model, error) {
return result, nil return result, nil
} }
// TracksList represents a list of tracks with metadata // TracksList represents a list of tracks with metadata.
type TracksList struct { type TracksList struct {
BaseModel BaseModel
Tracks []TrackShort `json:"tracks"` Tracks []TrackShort `json:"tracks"`
@@ -159,7 +160,7 @@ type TracksList struct {
Pager *Pager `json:"pager"` Pager *Pager `json:"pager"`
} }
// DeJSON deserializes a TracksList from JSON // DeJSON deserializes a TracksList from JSON data.
func (t *TracksList) DeJSON(data []byte, c ClientInterface) (Model, error) { func (t *TracksList) DeJSON(data []byte, c ClientInterface) (Model, error) {
var raw map[string]json.RawMessage var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@@ -193,7 +194,7 @@ func (t *TracksList) DeJSON(data []byte, c ClientInterface) (Model, error) {
return list, nil return list, nil
} }
// DeList deserializes a list of TracksList // DeList deserializes a list of TracksList from JSON data.
func (t *TracksList) DeList(data []byte, c ClientInterface) ([]Model, error) { func (t *TracksList) DeList(data []byte, c ClientInterface) ([]Model, error) {
var rawList []json.RawMessage var rawList []json.RawMessage
if err := json.Unmarshal(data, &rawList); err != nil { if err := json.Unmarshal(data, &rawList); err != nil {
+4 -4
View File
@@ -43,7 +43,7 @@ func TestTrack_DeJSON(t *testing.T) {
"id": "123", "id": "123",
"title": "Test Track", "title": "Test Track",
"duration_ms": 180000, "duration_ms": 180000,
"storage_id": "456", "storage_dir": "456",
"cover_uri": "https://example.com/cover.jpg", "cover_uri": "https://example.com/cover.jpg",
"genre": "rock", "genre": "rock",
"version": "album", "version": "album",
@@ -55,7 +55,7 @@ func TestTrack_DeJSON(t *testing.T) {
"artists": [{"id": "1", "name": "Artist 1", "cover_uri": "https://example.com/artist.jpg"}], "artists": [{"id": "1", "name": "Artist 1", "cover_uri": "https://example.com/artist.jpg"}],
"albums": [{"id": "789", "title": "Test Album", "cover_uri": "https://example.com/album.jpg", "year": 2023}], "albums": [{"id": "789", "title": "Test Album", "cover_uri": "https://example.com/album.jpg", "year": 2023}],
"major": {"id": "major1", "name": "Major Label", "description": "Big label"}, "major": {"id": "major1", "name": "Major Label", "description": "Big label"},
"lyrics": {"id": "lyr1", "text": "Test lyrics", "available": true, "has_text": true} "lyrics_info": {"has_available_sync_lyrics": true, "has_available_text_lyrics": true}
}`) }`)
track := &Track{BaseModel: *NewBaseModel(client)} track := &Track{BaseModel: *NewBaseModel(client)}
@@ -80,8 +80,8 @@ func TestTrack_DeJSON(t *testing.T) {
if resultTrack.Major == nil || resultTrack.Major.Name != "Major Label" { if resultTrack.Major == nil || resultTrack.Major.Name != "Major Label" {
t.Errorf("Expected Major 'Major Label'") t.Errorf("Expected Major 'Major Label'")
} }
if resultTrack.Lyrics == nil || resultTrack.Lyrics.Text != "Test lyrics" { if resultTrack.LyricsInfo == nil || resultTrack.LyricsInfo.HasAvailableSyncLyrics != true {
t.Errorf("Expected Lyrics 'Test lyrics'") t.Errorf("Expected LyricsInfo.HasAvailableSyncLyrics true")
} }
} }
+23 -12
View File
@@ -1,3 +1,4 @@
// Package utils provides utility functions for the Yandex Music API client.
package utils package utils
import ( import (
@@ -6,42 +7,49 @@ import (
"strings" "strings"
) )
// JSONBackend defines the interface for JSON encoding/decoding // JSONBackend defines the interface for JSON encoding/decoding.
// Implementations can use encoding/json, json-iterator, or other backends.
type JSONBackend interface { type JSONBackend interface {
// Marshal encodes a Go value to JSON.
Marshal(v any) ([]byte, error) Marshal(v any) ([]byte, error)
// Unmarshal decodes JSON data into a Go value.
Unmarshal(data []byte, v any) error Unmarshal(data []byte, v any) error
} }
// DefaultJSONBackend uses the standard library encoding/json // DefaultJSONBackend uses the standard library encoding/json.
type DefaultJSONBackend struct{} type DefaultJSONBackend struct{}
// Marshal encodes a Go value to JSON using encoding/json.
func (DefaultJSONBackend) Marshal(v any) ([]byte, error) { func (DefaultJSONBackend) Marshal(v any) ([]byte, error) {
return json.Marshal(v) return json.Marshal(v)
} }
// Unmarshal decodes JSON data into a Go value using encoding/json.
func (DefaultJSONBackend) Unmarshal(data []byte, v any) error { func (DefaultJSONBackend) Unmarshal(data []byte, v any) error {
return json.Unmarshal(data, v) return json.Unmarshal(data, v)
} }
// GetDefaultJSONBackend returns the default JSON backend // GetDefaultJSONBackend returns the default JSON backend (encoding/json).
func GetDefaultJSONBackend() JSONBackend { func GetDefaultJSONBackend() JSONBackend {
return DefaultJSONBackend{} return DefaultJSONBackend{}
} }
// RawMessage is a raw encoded JSON value // RawMessage is a raw encoded JSON value.
// It is an alias for encoding/json.RawMessage.
type RawMessage = json.RawMessage type RawMessage = json.RawMessage
// Marshal is a convenience function // Marshal is a convenience function that marshals a value using the default JSON backend.
func Marshal(v any) ([]byte, error) { func Marshal(v any) ([]byte, error) {
return GetDefaultJSONBackend().Marshal(v) return GetDefaultJSONBackend().Marshal(v)
} }
// Unmarshal is a convenience function // Unmarshal is a convenience function that unmarshals data using the default JSON backend.
func Unmarshal(data []byte, v any) error { func Unmarshal(data []byte, v any) error {
return GetDefaultJSONBackend().Unmarshal(data, v) return GetDefaultJSONBackend().Unmarshal(data, v)
} }
// ToSnakeCase converts camelCase to snake_case // ToSnakeCase converts camelCase to snake_case.
// For example, "camelCase" becomes "camel_case".
func ToSnakeCase(s string) string { func ToSnakeCase(s string) string {
if s == "" { if s == "" {
return s return s
@@ -60,7 +68,8 @@ func ToSnakeCase(s string) string {
return result.String() return result.String()
} }
// ToCamelCase converts snake_case to camelCase // ToCamelCase converts snake_case to camelCase.
// For example, "snake_case" becomes "snakeCase".
func ToCamelCase(s string) string { func ToCamelCase(s string) string {
if s == "" { if s == "" {
return s return s
@@ -79,7 +88,7 @@ func ToCamelCase(s string) string {
return result.String() return result.String()
} }
// MapKeysToSnakeCase converts all keys in a map to snake_case // MapKeysToSnakeCase converts all keys in a map to snake_case.
func MapKeysToSnakeCase(m map[string]any) map[string]any { func MapKeysToSnakeCase(m map[string]any) map[string]any {
result := make(map[string]any, len(m)) result := make(map[string]any, len(m))
for k, v := range m { for k, v := range m {
@@ -88,7 +97,7 @@ func MapKeysToSnakeCase(m map[string]any) map[string]any {
return result return result
} }
// MapKeysToCamelCase converts all keys in a map to camelCase // MapKeysToCamelCase converts all keys in a map to camelCase.
func MapKeysToCamelCase(m map[string]any) map[string]any { func MapKeysToCamelCase(m map[string]any) map[string]any {
result := make(map[string]any, len(m)) result := make(map[string]any, len(m))
for k, v := range m { for k, v := range m {
@@ -97,7 +106,8 @@ func MapKeysToCamelCase(m map[string]any) map[string]any {
return result return result
} }
// StructToMap converts a struct to a map using json tags // StructToMap converts a struct to a map using json tags.
// The forRequest parameter is currently unused but reserved for future use.
func StructToMap(v any, forRequest bool) (map[string]any, error) { func StructToMap(v any, forRequest bool) (map[string]any, error) {
data, err := Marshal(v) data, err := Marshal(v)
if err != nil { if err != nil {
@@ -110,7 +120,8 @@ func StructToMap(v any, forRequest bool) (map[string]any, error) {
return result, nil return result, nil
} }
// GetJSONTag returns the json tag for a struct field // GetJSONTag returns the json tag name for a struct field at the given index.
// It returns an empty string if the field has no json tag or the tag is "-".
func GetJSONTag(t reflect.Type, fieldIndex int) string { func GetJSONTag(t reflect.Type, fieldIndex int) string {
if fieldIndex >= t.NumField() { if fieldIndex >= t.NumField() {
return "" return ""
+26 -14
View File
@@ -1,3 +1,4 @@
// Package utils provides utility functions for the Yandex Music API client.
package utils package utils
import ( import (
@@ -5,19 +6,20 @@ import (
"errors" "errors"
) )
// Response represents an API response // Response represents an API response from the Yandex Music API.
type Response struct { type Response struct {
Result json.RawMessage `json:"result"` Result json.RawMessage `json:"result"`
Error string `json:"error,omitempty"` Error string `json:"error,omitempty"`
ErrorDescription string `json:"errorDescription,omitempty"` ErrorDescription string `json:"errorDescription,omitempty"`
} }
// GetResult returns the result as raw JSON // GetResult returns the result as raw JSON.
func (r *Response) GetResult() json.RawMessage { func (r *Response) GetResult() json.RawMessage {
return r.Result return r.Result
} }
// GetError returns the error message // GetError returns the error message from the response.
// It returns Error if set, otherwise ErrorDescription, or "Unknown error" if neither is set.
func (r *Response) GetError() string { func (r *Response) GetError() string {
if r.Error != "" { if r.Error != "" {
return r.Error return r.Error
@@ -28,12 +30,13 @@ func (r *Response) GetError() string {
return "Unknown error" return "Unknown error"
} }
// HasError returns true if the response contains an error // HasError returns true if the response contains an error.
func (r *Response) HasError() bool { func (r *Response) HasError() bool {
return r.Error != "" || r.ErrorDescription != "" return r.Error != "" || r.ErrorDescription != ""
} }
// UnmarshalJSON implements custom unmarshaling // UnmarshalJSON implements custom unmarshaling for Response.
// If result is missing, the whole response is wrapped in result.
func (r *Response) UnmarshalJSON(data []byte) error { func (r *Response) UnmarshalJSON(data []byte) error {
// First try to unmarshal as a standard response // First try to unmarshal as a standard response
type Alias Response type Alias Response
@@ -53,7 +56,7 @@ func (r *Response) UnmarshalJSON(data []byte) error {
return nil return nil
} }
// DeJSON creates a Response from JSON data // DeJSON creates a Response from JSON data.
func DeJSON(data []byte) (*Response, error) { func DeJSON(data []byte) (*Response, error) {
var resp Response var resp Response
if err := json.Unmarshal(data, &resp); err != nil { if err := json.Unmarshal(data, &resp); err != nil {
@@ -62,22 +65,30 @@ func DeJSON(data []byte) (*Response, error) {
return &resp, nil return &resp, nil
} }
// Error types // Error types for the Yandex Music API client.
var ( var (
// ErrUnauthorized is returned when authentication fails (HTTP 401/403).
ErrUnauthorized = errors.New("unauthorized") ErrUnauthorized = errors.New("unauthorized")
// ErrBadRequest is returned when the request is malformed (HTTP 400).
ErrBadRequest = errors.New("bad request") ErrBadRequest = errors.New("bad request")
// ErrNotFound is returned when the requested resource is not found (HTTP 404).
ErrNotFound = errors.New("not found") ErrNotFound = errors.New("not found")
// ErrNetwork is returned for network-related errors.
ErrNetwork = errors.New("network error") ErrNetwork = errors.New("network error")
// ErrTimeout is returned when a request times out.
ErrTimeout = errors.New("timeout") ErrTimeout = errors.New("timeout")
// ErrSchemaMismatch is returned when the API response schema doesn't match expectations.
ErrSchemaMismatch = errors.New("schema mismatch") ErrSchemaMismatch = errors.New("schema mismatch")
) )
// YandexMusicError is the base error type // YandexMusicError is the base error type for Yandex Music API errors.
// It wraps an underlying error with a descriptive message.
type YandexMusicError struct { type YandexMusicError struct {
Message string Message string
Err error Err error
} }
// Error returns the error message, including the wrapped error if present.
func (e *YandexMusicError) Error() string { func (e *YandexMusicError) Error() string {
if e.Err != nil { if e.Err != nil {
return e.Message + ": " + e.Err.Error() return e.Message + ": " + e.Err.Error()
@@ -85,16 +96,17 @@ func (e *YandexMusicError) Error() string {
return e.Message return e.Message
} }
// Unwrap returns the underlying error for compatibility with errors.Is and errors.As.
func (e *YandexMusicError) Unwrap() error { func (e *YandexMusicError) Unwrap() error {
return e.Err return e.Err
} }
// NewYandexMusicError creates a new YandexMusicError // NewYandexMusicError creates a new YandexMusicError with the given message and underlying error.
func NewYandexMusicError(message string, err error) *YandexMusicError { func NewYandexMusicError(message string, err error) *YandexMusicError {
return &YandexMusicError{Message: message, Err: err} return &YandexMusicError{Message: message, Err: err}
} }
// IsUnauthorizedError checks if error is unauthorized // IsUnauthorizedError checks if an error is an unauthorized error.
func IsUnauthorizedError(err error) bool { func IsUnauthorizedError(err error) bool {
var yme *YandexMusicError var yme *YandexMusicError
if errors.As(err, &yme) { if errors.As(err, &yme) {
@@ -103,7 +115,7 @@ func IsUnauthorizedError(err error) bool {
return errors.Is(err, ErrUnauthorized) return errors.Is(err, ErrUnauthorized)
} }
// IsBadRequestError checks if error is bad request // IsBadRequestError checks if an error is a bad request error.
func IsBadRequestError(err error) bool { func IsBadRequestError(err error) bool {
var yme *YandexMusicError var yme *YandexMusicError
if errors.As(err, &yme) { if errors.As(err, &yme) {
@@ -112,7 +124,7 @@ func IsBadRequestError(err error) bool {
return errors.Is(err, ErrBadRequest) return errors.Is(err, ErrBadRequest)
} }
// IsNotFoundError checks if error is not found // IsNotFoundError checks if an error is a not found error.
func IsNotFoundError(err error) bool { func IsNotFoundError(err error) bool {
var yme *YandexMusicError var yme *YandexMusicError
if errors.As(err, &yme) { if errors.As(err, &yme) {
@@ -121,7 +133,7 @@ func IsNotFoundError(err error) bool {
return errors.Is(err, ErrNotFound) return errors.Is(err, ErrNotFound)
} }
// IsNetworkError checks if error is network error // IsNetworkError checks if an error is a network error.
func IsNetworkError(err error) bool { func IsNetworkError(err error) bool {
var yme *YandexMusicError var yme *YandexMusicError
if errors.As(err, &yme) { if errors.As(err, &yme) {
@@ -130,7 +142,7 @@ func IsNetworkError(err error) bool {
return errors.Is(err, ErrNetwork) return errors.Is(err, ErrNetwork)
} }
// IsTimeoutError checks if error is timeout // IsTimeoutError checks if an error is a timeout error.
func IsTimeoutError(err error) bool { func IsTimeoutError(err error) bool {
var yme *YandexMusicError var yme *YandexMusicError
if errors.As(err, &yme) { if errors.As(err, &yme) {
+27 -6
View File
@@ -1,3 +1,5 @@
// Package utils provides utility functions for the Yandex Music API client,
// including JSON handling, request signing, schema mismatch tracking, and error types.
package utils package utils
import ( import (
@@ -7,16 +9,24 @@ import (
"sync" "sync"
) )
// SchemaMismatch represents a mismatch between expected and actual API response // SchemaMismatch represents a mismatch between expected and actual API response.
// It captures details about missing fields, unknown fields, and unknown values.
type SchemaMismatch struct { type SchemaMismatch struct {
// ModelName is the name of the model that had the mismatch.
ModelName string ModelName string
// MissingFields lists fields that were expected but not found in the response.
MissingFields []string MissingFields []string
// UnknownFields lists fields that were not expected but found in the response.
UnknownFields []string UnknownFields []string
// UnknownValues maps unknown field names to their values.
UnknownValues map[string]any UnknownValues map[string]any
// Endpoint is the API endpoint where the mismatch occurred.
Endpoint string Endpoint string
// LibraryVersion is the version of the library when the mismatch occurred.
LibraryVersion string LibraryVersion string
} }
// Describe returns a human-readable description of the schema mismatch.
func (sm *SchemaMismatch) Describe() string { func (sm *SchemaMismatch) Describe() string {
var parts []string var parts []string
parts = append(parts, fmt.Sprintf("Model: %s", sm.ModelName)) parts = append(parts, fmt.Sprintf("Model: %s", sm.ModelName))
@@ -34,17 +44,22 @@ func (sm *SchemaMismatch) Describe() string {
return strings.Join(parts, "\n") return strings.Join(parts, "\n")
} }
// SchemaMismatchHandler is a callback for handling schema mismatches // SchemaMismatchHandler is a callback for handling schema mismatches.
// It is invoked when a schema mismatch is detected, if configured.
type SchemaMismatchHandler func(*SchemaMismatch) type SchemaMismatchHandler func(*SchemaMismatch)
// SchemaMismatchConfig holds configuration for schema mismatch handling // SchemaMismatchConfig holds configuration for schema mismatch handling.
type SchemaMismatchConfig struct { type SchemaMismatchConfig struct {
// ReportUnknownFields indicates whether unknown fields should be reported.
ReportUnknownFields bool ReportUnknownFields bool
// Strict indicates whether schema mismatches should be treated as errors.
Strict bool Strict bool
// OnSchemaMismatch is an optional callback for custom mismatch handling.
OnSchemaMismatch SchemaMismatchHandler OnSchemaMismatch SchemaMismatchHandler
} }
// DefaultSchemaMismatchConfig returns the default configuration // DefaultSchemaMismatchConfig returns the default configuration for schema mismatch handling.
// The default config does not report unknown fields, is not strict, and has no callback.
func DefaultSchemaMismatchConfig() SchemaMismatchConfig { func DefaultSchemaMismatchConfig() SchemaMismatchConfig {
return SchemaMismatchConfig{ return SchemaMismatchConfig{
ReportUnknownFields: false, ReportUnknownFields: false,
@@ -53,7 +68,9 @@ func DefaultSchemaMismatchConfig() SchemaMismatchConfig {
} }
} }
// ReportSchemaMismatch reports a schema mismatch according to config // ReportSchemaMismatch reports a schema mismatch according to the provided config.
// If a callback is configured, it is invoked with the mismatch details.
// If strict mode is enabled, an error is returned. Otherwise, a warning is logged.
func ReportSchemaMismatch(config SchemaMismatchConfig, modelName, endpoint string, missingFields, unknownFields []string, unknownValues map[string]any) error { func ReportSchemaMismatch(config SchemaMismatchConfig, modelName, endpoint string, missingFields, unknownFields []string, unknownValues map[string]any) error {
mismatch := &SchemaMismatch{ mismatch := &SchemaMismatch{
ModelName: modelName, ModelName: modelName,
@@ -78,18 +95,22 @@ func ReportSchemaMismatch(config SchemaMismatchConfig, modelName, endpoint strin
return nil return nil
} }
// SchemaMismatchTracker tracks reported mismatches to avoid duplicates // SchemaMismatchTracker tracks reported mismatches to avoid duplicate reports.
// It is safe for concurrent use.
type SchemaMismatchTracker struct { type SchemaMismatchTracker struct {
mu sync.Mutex mu sync.Mutex
reported map[string]bool reported map[string]bool
} }
// NewSchemaMismatchTracker creates a new SchemaMismatchTracker.
func NewSchemaMismatchTracker() *SchemaMismatchTracker { func NewSchemaMismatchTracker() *SchemaMismatchTracker {
return &SchemaMismatchTracker{ return &SchemaMismatchTracker{
reported: make(map[string]bool), reported: make(map[string]bool),
} }
} }
// ShouldReport returns true if the given model and fields have not been reported before.
// It marks them as reported so subsequent calls return false for the same key.
func (t *SchemaMismatchTracker) ShouldReport(modelName string, fields []string) bool { func (t *SchemaMismatchTracker) ShouldReport(modelName string, fields []string) bool {
t.mu.Lock() t.mu.Lock()
defer t.mu.Unlock() defer t.mu.Unlock()
+12 -6
View File
@@ -1,3 +1,4 @@
// Package utils provides utility functions for the Yandex Music API client.
package utils package utils
import ( import (
@@ -10,21 +11,23 @@ import (
) )
const ( const (
// DefaultSignKey is the signing key from the Android app // DefaultSignKey is the signing key from the Android app.
DefaultSignKey = "p93jhgh689SBReK6ghtw62" DefaultSignKey = "p93jhgh689SBReK6ghtw62"
// DesktopSignKey is the signing key from the desktop app // DesktopSignKey is the signing key from the desktop app.
DesktopSignKey = "kzqU4XhfCaY6B6JTHODeq5" DesktopSignKey = "kzqU4XhfCaY6B6JTHODeq5"
// DesktopClient is the X-Yandex-Music-Client header value for desktop // DesktopClient is the X-Yandex-Music-Client header value for desktop.
DesktopClient = "YandexMusicDesktopAppWindows/5.95.0" DesktopClient = "YandexMusicDesktopAppWindows/5.95.0"
) )
// Sign represents a request signature // Sign represents a request signature for signed API requests.
type Sign struct { type Sign struct {
Timestamp int64 Timestamp int64
Value string Value string
} }
// GetSignRequest creates a signature for a track request // GetSignRequest creates a signature for a track request using HMAC-SHA256.
// It returns a Sign with the timestamp and base64-encoded signature value.
// If key is empty, DefaultSignKey is used.
func GetSignRequest(trackID string, key string) Sign { func GetSignRequest(trackID string, key string) Sign {
if key == "" { if key == "" {
key = DefaultSignKey key = DefaultSignKey
@@ -43,7 +46,9 @@ func GetSignRequest(trackID string, key string) Sign {
} }
} }
// GetFileInfoSign creates a signature for file info requests // GetFileInfoSign creates a signature for file info requests using HMAC-SHA256.
// It returns a Sign with the timestamp and base64-encoded signature value (last character removed as in Python).
// If key is empty, DesktopSignKey is used.
func GetFileInfoSign(trackID, quality string, codecs []string, transport, key string) Sign { func GetFileInfoSign(trackID, quality string, codecs []string, transport, key string) Sign {
if key == "" { if key == "" {
key = DesktopSignKey key = DesktopSignKey
@@ -66,6 +71,7 @@ func GetFileInfoSign(trackID, quality string, codecs []string, transport, key st
} }
} }
// joinCodecs joins a list of codec strings into a single string.
func joinCodecs(codecs []string) string { func joinCodecs(codecs []string) string {
result := "" result := ""
for _, c := range codecs { for _, c := range codecs {
+23 -19
View File
@@ -1,3 +1,4 @@
// Package ynison provides a Ynison WebSocket client for player synchronization across devices.
package ynison package ynison
import ( import (
@@ -12,7 +13,7 @@ import (
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
) )
// YnisonClient represents a Ynison WebSocket client // YnisonClient represents a Ynison WebSocket client for player synchronization across devices.
type YnisonClient struct { type YnisonClient struct {
client *client.Client client *client.Client
conn *websocket.Conn conn *websocket.Conn
@@ -28,7 +29,7 @@ type YnisonClient struct {
closeOnce sync.Once // Ensures doneCh is closed only once closeOnce sync.Once // Ensures doneCh is closed only once
} }
// DeviceConfig holds device configuration for Ynison // DeviceConfig holds device configuration for Ynison WebSocket connections.
type DeviceConfig struct { type DeviceConfig struct {
DeviceID string DeviceID string
Title string Title string
@@ -40,7 +41,8 @@ type DeviceConfig struct {
VolumeGran int VolumeGran int
} }
// NewYnisonClient creates a new Ynison client // NewYnisonClient creates a new Ynison client with the given client and device config.
// Default values are applied for empty fields.
func NewYnisonClient(c *client.Client, config DeviceConfig) *YnisonClient { func NewYnisonClient(c *client.Client, config DeviceConfig) *YnisonClient {
if config.DeviceID == "" { if config.DeviceID == "" {
config.DeviceID = fmt.Sprintf("go-ynison-%d", time.Now().UnixNano()) config.DeviceID = fmt.Sprintf("go-ynison-%d", time.Now().UnixNano())
@@ -67,7 +69,8 @@ func NewYnisonClient(c *client.Client, config DeviceConfig) *YnisonClient {
} }
} }
// Connect establishes a WebSocket connection to Ynison // Connect establishes a WebSocket connection to the Ynison service.
// It sends an initial full state update after connecting.
func (y *YnisonClient) Connect(ctx context.Context) error { func (y *YnisonClient) Connect(ctx context.Context) error {
token := y.client.GetToken() token := y.client.GetToken()
if token == "" { if token == "" {
@@ -95,12 +98,12 @@ func (y *YnisonClient) Connect(ctx context.Context) error {
return y.sendUpdateFullState(ctx) return y.sendUpdateFullState(ctx)
} }
// overrideWSURL sets a custom WebSocket URL for testing // overrideWSURL sets a custom WebSocket URL for testing purposes.
func (y *YnisonClient) overrideWSURL(url string) { func (y *YnisonClient) overrideWSURL(url string) {
y.wsURL = url y.wsURL = url
} }
// Disconnect closes the WebSocket connection // Disconnect closes the WebSocket connection and cleans up resources.
func (y *YnisonClient) Disconnect() error { func (y *YnisonClient) Disconnect() error {
y.mu.Lock() y.mu.Lock()
defer y.mu.Unlock() defer y.mu.Unlock()
@@ -117,7 +120,7 @@ func (y *YnisonClient) Disconnect() error {
return nil return nil
} }
// IsConnected returns true if the client is connected // IsConnected returns true if the client is currently connected to the Ynison service.
func (y *YnisonClient) IsConnected() bool { func (y *YnisonClient) IsConnected() bool {
y.mu.Lock() y.mu.Lock()
defer y.mu.Unlock() defer y.mu.Unlock()
@@ -276,21 +279,22 @@ func (y *YnisonClient) sendRequest(ctx context.Context, req *proto.PutYnisonStat
return y.conn.WriteMessage(websocket.TextMessage, data) return y.conn.WriteMessage(websocket.TextMessage, data)
} }
// RegisterHandler registers a response handler // RegisterHandler registers a response handler for the given name.
// Handlers are invoked when a PutYnisonStateResponse is received from the server.
func (y *YnisonClient) RegisterHandler(name string, handler func(*proto.PutYnisonStateResponse)) { func (y *YnisonClient) RegisterHandler(name string, handler func(*proto.PutYnisonStateResponse)) {
y.mu.Lock() y.mu.Lock()
defer y.mu.Unlock() defer y.mu.Unlock()
y.handlers[name] = handler y.handlers[name] = handler
} }
// UnregisterHandler removes a response handler // UnregisterHandler removes a previously registered response handler by name.
func (y *YnisonClient) UnregisterHandler(name string) { func (y *YnisonClient) UnregisterHandler(name string) {
y.mu.Lock() y.mu.Lock()
defer y.mu.Unlock() defer y.mu.Unlock()
delete(y.handlers, name) delete(y.handlers, name)
} }
// UpdatePlayingStatus updates the playing status // UpdatePlayingStatus updates the playing status on the Ynison server.
func (y *YnisonClient) UpdatePlayingStatus(ctx context.Context, status *proto.PlayingStatus) error { func (y *YnisonClient) UpdatePlayingStatus(ctx context.Context, status *proto.PlayingStatus) error {
req := &proto.PutYnisonStateRequest{ req := &proto.PutYnisonStateRequest{
UpdatePlayingStatus: &proto.UpdatePlayingStatus{ UpdatePlayingStatus: &proto.UpdatePlayingStatus{
@@ -302,7 +306,7 @@ func (y *YnisonClient) UpdatePlayingStatus(ctx context.Context, status *proto.Pl
return y.sendRequest(ctx, req) return y.sendRequest(ctx, req)
} }
// UpdateVolume updates the volume // UpdateVolume updates the volume for a device on the Ynison server.
func (y *YnisonClient) UpdateVolume(ctx context.Context, deviceID string, volume float64, version *proto.UpdateVersion) error { func (y *YnisonClient) UpdateVolume(ctx context.Context, deviceID string, volume float64, version *proto.UpdateVersion) error {
req := &proto.PutYnisonStateRequest{ req := &proto.PutYnisonStateRequest{
UpdateVolumeInfo: &proto.UpdateVolumeInfo{ UpdateVolumeInfo: &proto.UpdateVolumeInfo{
@@ -318,7 +322,7 @@ func (y *YnisonClient) UpdateVolume(ctx context.Context, deviceID string, volume
return y.sendRequest(ctx, req) return y.sendRequest(ctx, req)
} }
// UpdateActiveDevice updates the active device // UpdateActiveDevice updates the active device on the Ynison server.
func (y *YnisonClient) UpdateActiveDevice(ctx context.Context, deviceID string) error { func (y *YnisonClient) UpdateActiveDevice(ctx context.Context, deviceID string) error {
req := &proto.PutYnisonStateRequest{ req := &proto.PutYnisonStateRequest{
UpdateActiveDevice: &proto.UpdateActiveDevice{ UpdateActiveDevice: &proto.UpdateActiveDevice{
@@ -330,7 +334,7 @@ func (y *YnisonClient) UpdateActiveDevice(ctx context.Context, deviceID string)
return y.sendRequest(ctx, req) return y.sendRequest(ctx, req)
} }
// UpdatePlayerState updates the player state (queue, repeat, shuffle) // UpdatePlayerState updates the player state (queue, repeat, shuffle) on the Ynison server.
func (y *YnisonClient) UpdatePlayerState(ctx context.Context, state *proto.PlayerState) error { func (y *YnisonClient) UpdatePlayerState(ctx context.Context, state *proto.PlayerState) error {
req := &proto.PutYnisonStateRequest{ req := &proto.PutYnisonStateRequest{
UpdatePlayerState: &proto.UpdatePlayerState{ UpdatePlayerState: &proto.UpdatePlayerState{
@@ -342,7 +346,7 @@ func (y *YnisonClient) UpdatePlayerState(ctx context.Context, state *proto.Playe
return y.sendRequest(ctx, req) return y.sendRequest(ctx, req)
} }
// UpdatePlayerQueueInject updates the injected queue entity // UpdatePlayerQueueInject updates the injected queue entity on the Ynison server.
func (y *YnisonClient) UpdatePlayerQueueInject(ctx context.Context, inject *proto.PlayerQueueInject) error { func (y *YnisonClient) UpdatePlayerQueueInject(ctx context.Context, inject *proto.PlayerQueueInject) error {
req := &proto.PutYnisonStateRequest{ req := &proto.PutYnisonStateRequest{
UpdatePlayerQueueInject: &proto.UpdatePlayerQueueInject{ UpdatePlayerQueueInject: &proto.UpdatePlayerQueueInject{
@@ -354,7 +358,7 @@ func (y *YnisonClient) UpdatePlayerQueueInject(ctx context.Context, inject *prot
return y.sendRequest(ctx, req) return y.sendRequest(ctx, req)
} }
// SyncStateFromEOV requests synchronization from EOV // SyncStateFromEOV requests synchronization from EOV (End of Verification).
func (y *YnisonClient) SyncStateFromEOV(ctx context.Context, actualQueueID string) error { func (y *YnisonClient) SyncStateFromEOV(ctx context.Context, actualQueueID string) error {
req := &proto.PutYnisonStateRequest{ req := &proto.PutYnisonStateRequest{
SyncStateFromEov: &proto.SyncStateFromEOV{ SyncStateFromEov: &proto.SyncStateFromEOV{
@@ -366,25 +370,25 @@ func (y *YnisonClient) SyncStateFromEOV(ctx context.Context, actualQueueID strin
return y.sendRequest(ctx, req) return y.sendRequest(ctx, req)
} }
// GetDevices returns the list of connected devices from the last response // GetDevices returns the list of connected devices from the last response.
func (y *YnisonClient) GetDevices() []*proto.Device { func (y *YnisonClient) GetDevices() []*proto.Device {
// This would need to be implemented with proper state tracking // This would need to be implemented with proper state tracking
return nil return nil
} }
// GetActiveDeviceID returns the active device ID from the last response // GetActiveDeviceID returns the active device ID from the last response.
func (y *YnisonClient) GetActiveDeviceID() string { func (y *YnisonClient) GetActiveDeviceID() string {
// This would need to be implemented with proper state tracking // This would need to be implemented with proper state tracking
return "" return ""
} }
// GetPlayerState returns the current player state from the last response // GetPlayerState returns the current player state from the last response.
func (y *YnisonClient) GetPlayerState() *proto.PlayerState { func (y *YnisonClient) GetPlayerState() *proto.PlayerState {
// This would need to be implemented with proper state tracking // This would need to be implemented with proper state tracking
return nil return nil
} }
// WaitForConnection waits for the connection to be established or context to be done // WaitForConnection waits for the connection to be established or the context to be done.
func (y *YnisonClient) WaitForConnection(ctx context.Context) error { func (y *YnisonClient) WaitForConnection(ctx context.Context) error {
select { select {
case <-ctx.Done(): case <-ctx.Done():
+9 -7
View File
@@ -1,3 +1,4 @@
// Package ynison provides a Ynison WebSocket client for player synchronization.
package ynison package ynison
import ( import (
@@ -6,7 +7,8 @@ import (
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/ynison/proto" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/ynison/proto"
) )
// CreatePlayingStatus creates a playing status // CreatePlayingStatus creates a playing status with the given parameters.
// The version timestamp is set to the current time.
func CreatePlayingStatus(progressMs, durationMs int64, paused bool, playbackSpeed float64, deviceID string) *proto.PlayingStatus { func CreatePlayingStatus(progressMs, durationMs int64, paused bool, playbackSpeed float64, deviceID string) *proto.PlayingStatus {
return &proto.PlayingStatus{ return &proto.PlayingStatus{
ProgressMs: progressMs, ProgressMs: progressMs,
@@ -21,7 +23,7 @@ func CreatePlayingStatus(progressMs, durationMs int64, paused bool, playbackSpee
} }
} }
// CreatePlayable creates a playable entity // CreatePlayable creates a playable entity with the given parameters.
func CreatePlayable(playableID, albumID, from, title, coverURL string, playableType proto.PlayablePlayableType) *proto.Playable { func CreatePlayable(playableID, albumID, from, title, coverURL string, playableType proto.PlayablePlayableType) *proto.Playable {
return &proto.Playable{ return &proto.Playable{
PlayableId: playableID, PlayableId: playableID,
@@ -33,7 +35,7 @@ func CreatePlayable(playableID, albumID, from, title, coverURL string, playableT
} }
} }
// CreateTrackPlayable creates a track playable // CreateTrackPlayable creates a track playable with the given parameters.
func CreateTrackPlayable(playableID, albumID, from, title, coverURL string, trackSourceKey uint32, batchID string) *proto.Playable { func CreateTrackPlayable(playableID, albumID, from, title, coverURL string, trackSourceKey uint32, batchID string) *proto.Playable {
return &proto.Playable{ return &proto.Playable{
PlayableId: playableID, PlayableId: playableID,
@@ -49,7 +51,7 @@ func CreateTrackPlayable(playableID, albumID, from, title, coverURL string, trac
} }
} }
// CreateVideoClipPlayable creates a video clip playable // CreateVideoClipPlayable creates a video clip playable with the given parameters.
func CreateVideoClipPlayable(playableID, from, title, coverURL string, recommendationType proto.VideoClipInfoRecommendationType) *proto.Playable { func CreateVideoClipPlayable(playableID, from, title, coverURL string, recommendationType proto.VideoClipInfoRecommendationType) *proto.Playable {
return &proto.Playable{ return &proto.Playable{
PlayableId: playableID, PlayableId: playableID,
@@ -63,7 +65,7 @@ func CreateVideoClipPlayable(playableID, from, title, coverURL string, recommend
} }
} }
// CreatePlayerQueue creates a player queue // CreatePlayerQueue creates a player queue with the given parameters.
func CreatePlayerQueue(entityID string, entityType proto.PlayerQueueEntityType, playables []*proto.Playable, currentIndex int32, deviceID string) *proto.PlayerQueue { func CreatePlayerQueue(entityID string, entityType proto.PlayerQueueEntityType, playables []*proto.Playable, currentIndex int32, deviceID string) *proto.PlayerQueue {
return &proto.PlayerQueue{ return &proto.PlayerQueue{
EntityId: entityID, EntityId: entityID,
@@ -82,7 +84,7 @@ func CreatePlayerQueue(entityID string, entityType proto.PlayerQueueEntityType,
} }
} }
// CreateWaveQueue creates a wave queue // CreateWaveQueue creates a wave queue with the given parameters.
func CreateWaveQueue(recommended []*proto.Playable, liveIndex int32, sessionID string, deviceID string) *proto.PlayerQueueQueue { func CreateWaveQueue(recommended []*proto.Playable, liveIndex int32, sessionID string, deviceID string) *proto.PlayerQueueQueue {
// Build track sources from playables // Build track sources from playables
var trackSources []*proto.PlayerQueueQueueWaveQueueEntityOptionsTrackSourceWithKey var trackSources []*proto.PlayerQueueQueueWaveQueueEntityOptionsTrackSourceWithKey
@@ -111,7 +113,7 @@ func CreateWaveQueue(recommended []*proto.Playable, liveIndex int32, sessionID s
} }
} }
// CreateGenerativeQueue creates a generative queue // CreateGenerativeQueue creates a generative queue with the given ID.
func CreateGenerativeQueue(id string) *proto.PlayerQueueQueue { func CreateGenerativeQueue(id string) *proto.PlayerQueueQueue {
return &proto.PlayerQueueQueue{ return &proto.PlayerQueueQueue{
GenerativeQueue: &proto.PlayerQueueQueueGenerativeQueue{ GenerativeQueue: &proto.PlayerQueueQueueGenerativeQueue{
+107 -99
View File
@@ -1,3 +1,5 @@
// Package yandexmusic provides the public API for the Yandex Music Go client.
// It re-exports types and functions from the internal packages for convenient use.
package yandexmusic package yandexmusic
import ( import (
@@ -7,307 +9,313 @@ import (
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/ynison" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/ynison"
) )
// ClientConfig holds configuration for the Client // ClientConfig holds configuration for the Client.
type ClientConfig = client.ClientConfig type ClientConfig = client.ClientConfig
// Client is the main Yandex Music API client // Client is the main Yandex Music API client.
type Client = client.Client type Client = client.Client
// NewClient creates a new Yandex Music client // NewClient creates a new Yandex Music client with the given configuration.
func NewClient(config ClientConfig) *Client { func NewClient(config ClientConfig) *Client {
return client.NewClient(config) return client.NewClient(config)
} }
// Request is the HTTP request handler // Request is the HTTP request handler.
type Request = client.Request type Request = client.Request
// DeviceCode represents the device authorization response // DeviceCode represents the device authorization response.
type DeviceCode = client.DeviceCode type DeviceCode = client.DeviceCode
// OAuthToken represents an OAuth token response // OAuthToken represents an OAuth token response.
type OAuthToken = client.OAuthToken type OAuthToken = client.OAuthToken
// Status represents account status // Status represents account status.
type Status = client.Status type Status = client.Status
// Account represents a user account // Account represents a user account.
type Account = client.Account type Account = client.Account
// Track represents a music track // Track represents a music track.
type Track = models.Track type Track = models.Track
// Artist represents an artist // Artist represents an artist.
type Artist = models.Artist type Artist = models.Artist
// Album represents an album // Album represents an album.
type Album = models.Album type Album = models.Album
// Playlist represents a playlist // Playlist represents a playlist.
type Playlist = models.Playlist type Playlist = models.Playlist
// User represents a user // User represents a user.
type User = models.User type User = models.User
// DownloadInfo represents file download info // DownloadInfo represents file download info.
type DownloadInfo = models.DownloadInfo type DownloadInfo = models.DownloadInfo
// SupplementLyrics represents lyrics content from supplement // SupplementLyrics represents lyrics content from supplement.
type SupplementLyrics = models.SupplementLyrics type SupplementLyrics = models.SupplementLyrics
// Major represents major label info // Major represents major label info.
type Major = models.Major type Major = models.Major
// Label represents a label // Label represents a label.
type Label = models.Label type Label = models.Label
// Availability represents availability info // Availability represents availability info.
type Availability = models.Availability type Availability = models.Availability
// Upsell represents upsell info // Upsell represents upsell info.
type Upsell = models.Upsell type Upsell = models.Upsell
// AlbumMeta represents album metadata // AlbumMeta represents album metadata.
type AlbumMeta = models.AlbumMeta type AlbumMeta = models.AlbumMeta
// Best represents best tracks info // Best represents best tracks info.
type Best = models.Best type Best = models.Best
// Ratings represents artist ratings // Ratings represents artist ratings.
type Ratings = models.Ratings type Ratings = models.Ratings
// Counts represents artist counts // Counts represents artist counts.
type Counts = models.Counts type Counts = models.Counts
// TrackShort represents a lightweight track // TrackShort represents a lightweight track.
type TrackShort = models.TrackShort type TrackShort = models.TrackShort
// TracksList represents a list of tracks with metadata // TracksList represents a list of tracks with metadata.
type TracksList = models.TracksList type TracksList = models.TracksList
// Pager represents pagination info // Pager represents pagination info.
type Pager = models.Pager type Pager = models.Pager
// TrackPosition represents track position // TrackPosition represents track position.
type TrackPosition = models.TrackPosition type TrackPosition = models.TrackPosition
// TrailerInfo represents trailer info // TrailerInfo represents trailer info.
type TrailerInfo = models.TrailerInfo type TrailerInfo = models.TrailerInfo
// Cover represents cover image info // Cover represents cover image info.
type Cover = models.Cover type Cover = models.Cover
// ArtistBriefInfo represents brief artist info // ArtistBriefInfo represents brief artist info.
type ArtistBriefInfo = models.ArtistBriefInfo type ArtistBriefInfo = models.ArtistBriefInfo
// ArtistDonations represents artist donations info // ArtistDonations represents artist donations info.
type ArtistDonations = models.ArtistDonations type ArtistDonations = models.ArtistDonations
// DonationItem represents a donation item // DonationItem represents a donation item.
type DonationItem = models.DonationItem type DonationItem = models.DonationItem
// Concert represents a concert (minimal) // Concert represents a concert (minimal).
type Concert = models.Concert type Concert = models.Concert
// Clip represents a clip (short video) // Clip represents a clip (short video).
type Clip = models.Clip type Clip = models.Clip
// SearchResult represents search results // SearchResult represents search results.
type SearchResult = models.SearchResult type SearchResult = models.SearchResult
// SearchTracksResult represents track search results // SearchTracksResult represents track search results.
type SearchTracksResult = models.SearchTracksResult type SearchTracksResult = models.SearchTracksResult
// SearchAlbumsResult represents album search results // SearchAlbumsResult represents album search results.
type SearchAlbumsResult = models.SearchAlbumsResult type SearchAlbumsResult = models.SearchAlbumsResult
// SearchArtistsResult represents artist search results // SearchArtistsResult represents artist search results.
type SearchArtistsResult = models.SearchArtistsResult type SearchArtistsResult = models.SearchArtistsResult
// SearchPlaylistsResult represents playlist search results // SearchPlaylistsResult represents playlist search results.
type SearchPlaylistsResult = models.SearchPlaylistsResult type SearchPlaylistsResult = models.SearchPlaylistsResult
// SearchVideosResult represents video search results // SearchVideosResult represents video search results.
type SearchVideosResult = models.SearchVideosResult type SearchVideosResult = models.SearchVideosResult
// SearchUsersResult represents user search results // SearchUsersResult represents user search results.
type SearchUsersResult = models.SearchUsersResult type SearchUsersResult = models.SearchUsersResult
// SearchBanner represents a search banner // SearchBanner represents a search banner.
type SearchBanner = models.SearchBanner type SearchBanner = models.SearchBanner
// Tag represents a search tag // Tag represents a search tag.
type Tag = models.Tag type Tag = models.Tag
// Video represents a video // Video represents a video.
type Video = models.Video type Video = models.Video
// ChartInfoMenu represents a chart info menu // ChartInfoMenu represents a chart info menu.
type ChartInfoMenu = models.ChartInfoMenu type ChartInfoMenu = models.ChartInfoMenu
// ChartInfoMenuItem represents a chart info menu item // ChartInfoMenuItem represents a chart info menu item.
type ChartInfoMenuItem = models.ChartInfoMenuItem type ChartInfoMenuItem = models.ChartInfoMenuItem
// ChartItems represents chart items response // ChartItems represents chart items response.
type ChartItems = models.ChartItems type ChartItems = models.ChartItems
// AccountSettings represents account settings // AccountSettings represents account settings.
type AccountSettings = models.AccountSettings type AccountSettings = models.AccountSettings
// Subscription represents subscription info // Subscription represents subscription info.
type Subscription = models.Subscription type Subscription = models.Subscription
// PlusInfo represents Plus subscription info // PlusInfo represents Plus subscription info.
type PlusInfo = models.PlusInfo type PlusInfo = models.PlusInfo
// PromoCodeStatus represents promo code status // PromoCodeStatus represents promo code status.
type PromoCodeStatus = models.PromoCodeStatus type PromoCodeStatus = models.PromoCodeStatus
// Queue represents a playback queue // Queue represents a playback queue.
type Queue = models.Queue type Queue = models.Queue
// QueueItem represents a queue item // QueueItem represents a queue item.
type QueueItem = models.QueueItem type QueueItem = models.QueueItem
// PlayContext represents a play context // PlayContext represents a play context.
type PlayContext = models.PlayContext type PlayContext = models.PlayContext
// PlayContextsData represents play contexts data // PlayContextsData represents play contexts data.
type PlayContextsData = models.PlayContextsData type PlayContextsData = models.PlayContextsData
// Feed represents the main feed // Feed represents the main feed.
type Feed = models.Feed type Feed = models.Feed
// Day represents a day in the feed // Day represents a day in the feed.
type Day = models.Day type Day = models.Day
// DayBlock represents a block in a day // DayBlock represents a block in a day.
type DayBlock = models.DayBlock type DayBlock = models.DayBlock
// DayBlockData represents data in a day block // DayBlockData represents data in a day block.
type DayBlockData = models.DayBlockData type DayBlockData = models.DayBlockData
// GeneratedPlaylist represents a generated playlist // GeneratedPlaylist represents a generated playlist.
type GeneratedPlaylist = models.GeneratedPlaylist type GeneratedPlaylist = models.GeneratedPlaylist
// Event represents a feed event // Event represents a feed event.
type Event = models.Event type Event = models.Event
// AlbumEvent represents an album event // AlbumEvent represents an album event.
type AlbumEvent = models.AlbumEvent type AlbumEvent = models.AlbumEvent
// ArtistEvent represents an artist event // ArtistEvent represents an artist event.
type ArtistEvent = models.ArtistEvent type ArtistEvent = models.ArtistEvent
// TrackWithAds represents a track with ads // TrackWithAds represents a track with ads.
type TrackWithAds = models.TrackWithAds type TrackWithAds = models.TrackWithAds
// Ad represents an ad // Ad represents an ad.
type Ad = models.Ad type Ad = models.Ad
// AdParams represents ad parameters // AdParams represents ad parameters.
type AdParams = models.AdParams type AdParams = models.AdParams
// Landing represents a landing page // Landing represents a landing page.
type Landing = models.Landing type Landing = models.Landing
// LandingBlock represents a block in a landing // LandingBlock represents a block in a landing.
type LandingBlock = models.LandingBlock type LandingBlock = models.LandingBlock
// LandingBlockData represents data in a landing block // LandingBlockData represents data in a landing block.
type LandingBlockData = models.LandingBlockData type LandingBlockData = models.LandingBlockData
// LandingList represents a list of landings // LandingList represents a list of landings.
type LandingList = models.LandingList type LandingList = models.LandingList
// LyricsInfo represents lyrics information // LyricsInfo represents lyrics information.
type LyricsInfo = models.LyricsInfo type LyricsInfo = models.LyricsInfo
// VideoSupplement represents video supplement // VideoSupplement represents video supplement.
type VideoSupplement = models.VideoSupplement type VideoSupplement = models.VideoSupplement
// Supplement represents track supplement // Supplement represents track supplement.
type Supplement = models.Supplement type Supplement = models.Supplement
// LyricsMajor represents a lyrics provider (e.g., Genius, Musixmatch) // LyricsMajor represents a lyrics provider (e.g., Genius, Musixmatch).
type LyricsMajor = models.LyricsMajor type LyricsMajor = models.LyricsMajor
// TrackLyrics represents lyrics information from /tracks/{id}/lyrics endpoint // TrackLyrics represents lyrics information from /tracks/{id}/lyrics endpoint.
type TrackLyrics = models.TrackLyrics type TrackLyrics = models.TrackLyrics
// Credits represents track credits // Credits represents track credits.
type Credits = models.Credits type Credits = models.Credits
// Credit represents a credit item // Credit represents a credit item.
type Credit = models.Credit type Credit = models.Credit
// RadioStation represents a radio station // ArtistTracks represents a page of tracks by an artist.
type ArtistTracks = models.ArtistTracks
// ArtistAlbums represents a page of albums by an artist.
type ArtistAlbums = models.ArtistAlbums
// RadioStation represents a radio station.
type RadioStation = models.RadioStation type RadioStation = models.RadioStation
// StationTracksResult represents station tracks result // StationTracksResult represents station tracks result.
type StationTracksResult = models.StationTracksResult type StationTracksResult = models.StationTracksResult
// RotorStation represents a rotor (generative) station // RotorStation represents a rotor (generative) station.
type RotorStation = models.RotorStation type RotorStation = models.RotorStation
// RotorSettings represents rotor station settings // RotorSettings represents rotor station settings.
type RotorSettings = models.RotorSettings type RotorSettings = models.RotorSettings
// RotorStationTracksResult represents rotor station tracks result // RotorStationTracksResult represents rotor station tracks result.
type RotorStationTracksResult = models.RotorStationTracksResult type RotorStationTracksResult = models.RotorStationTracksResult
// RotorSession represents a rotor session // RotorSession represents a rotor session.
type RotorSession = models.RotorSession type RotorSession = models.RotorSession
// SessionFeedback represents session feedback // SessionFeedback represents session feedback.
type SessionFeedback = models.SessionFeedback type SessionFeedback = models.SessionFeedback
// SessionFeedbacks represents multiple session feedbacks // SessionFeedbacks represents multiple session feedbacks.
type SessionFeedbacks = models.SessionFeedbacks type SessionFeedbacks = models.SessionFeedbacks
// Experiments represents experiments // Experiments represents experiments.
type Experiments = models.Experiments type Experiments = models.Experiments
// ExperimentDetail represents experiment details // ExperimentDetail represents experiment details.
type ExperimentDetail = models.ExperimentDetail type ExperimentDetail = models.ExperimentDetail
// Permissions represents user permissions // Permissions represents user permissions.
type Permissions = models.Permissions type Permissions = models.Permissions
// PermissionAlerts represents permission alerts // PermissionAlerts represents permission alerts.
type PermissionAlerts = models.PermissionAlerts type PermissionAlerts = models.PermissionAlerts
// PermissionAlert represents a permission alert // PermissionAlert represents a permission alert.
type PermissionAlert = models.PermissionAlert type PermissionAlert = models.PermissionAlert
// ContentRestrictions represents content restrictions // ContentRestrictions represents content restrictions.
type ContentRestrictions = models.ContentRestrictions type ContentRestrictions = models.ContentRestrictions
// JSONBackend defines the interface for JSON encoding/decoding // JSONBackend defines the interface for JSON encoding/decoding.
type JSONBackend = utils.JSONBackend type JSONBackend = utils.JSONBackend
// GetDefaultJSONBackend returns the default JSON backend // GetDefaultJSONBackend returns the default JSON backend.
func GetDefaultJSONBackend() JSONBackend { func GetDefaultJSONBackend() JSONBackend {
return utils.GetDefaultJSONBackend() return utils.GetDefaultJSONBackend()
} }
// SchemaMismatchConfig holds configuration for schema mismatch handling // SchemaMismatchConfig holds configuration for schema mismatch handling.
type SchemaMismatchConfig = utils.SchemaMismatchConfig type SchemaMismatchConfig = utils.SchemaMismatchConfig
// DefaultSchemaMismatchConfig returns the default configuration // DefaultSchemaMismatchConfig returns the default configuration.
func DefaultSchemaMismatchConfig() SchemaMismatchConfig { func DefaultSchemaMismatchConfig() SchemaMismatchConfig {
return utils.DefaultSchemaMismatchConfig() return utils.DefaultSchemaMismatchConfig()
} }
// SchemaMismatchHandler is a callback for handling schema mismatches // SchemaMismatchHandler is a callback for handling schema mismatches.
type SchemaMismatchHandler = utils.SchemaMismatchHandler type SchemaMismatchHandler = utils.SchemaMismatchHandler
// SchemaMismatch represents a mismatch between expected and actual API response // SchemaMismatch represents a mismatch between expected and actual API response.
type SchemaMismatch = utils.SchemaMismatch type SchemaMismatch = utils.SchemaMismatch
// YnisonClient represents a Ynison WebSocket client // YnisonClient represents a Ynison WebSocket client.
type YnisonClient = ynison.YnisonClient type YnisonClient = ynison.YnisonClient
// DeviceConfig holds device configuration for Ynison // DeviceConfig holds device configuration for Ynison.
type DeviceConfig = ynison.DeviceConfig type DeviceConfig = ynison.DeviceConfig
// Example usage: // Example usage: