// Package client provides the core Yandex Music API client and related types. package client import ( "context" "fmt" "time" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/models" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils" ) // ClientConfig holds configuration for the Client. type ClientConfig struct { Token string BaseURL string ProxyURL string Timeout int Language string ReportUnknownFields bool Strict bool OnSchemaMismatch utils.SchemaMismatchHandler JSONBackend utils.JSONBackend } // 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 { config ClientConfig request *Request device string me *Status accountUID *int64 } // 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 { if config.BaseURL == "" { config.BaseURL = DefaultBaseURL } if config.Language == "" { config.Language = "ru" } if config.Timeout == 0 { config.Timeout = int(DefaultTimeout.Seconds()) } device := fmt.Sprintf("os=Go; os_version=; manufacturer=Mrixs; model=Yandex Music API Go; clid=; device_id=go-client; uuid=go-client") reqConfig := RequestConfig{ BaseURL: config.BaseURL, Token: config.Token, ProxyURL: config.ProxyURL, Timeout: time.Duration(config.Timeout) * time.Second, Language: config.Language, JSONBackend: config.JSONBackend, ReportUnknownFields: config.ReportUnknownFields, Strict: config.Strict, OnSchemaMismatch: config.OnSchemaMismatch, } request := NewRequest(reqConfig) return &Client{ config: config, request: request, device: device, } } // 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 { status, err := c.GetAccountStatus(ctx) if err != nil { return err } c.me = status if status.Account != nil { c.accountUID = &status.Account.UID } return nil } // 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 { return c.request } // GetToken returns the current OAuth token used for authentication. func (c *Client) GetToken() string { return c.config.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) { c.config.Token = token c.request.SetToken(token) } // GetDevice returns the device identifier string used in API requests. func (c *Client) GetDevice() string { return c.device } // GetMe returns the account status fetched during Init, or nil if Init has not been called. func (c *Client) GetMe() *Status { return c.me } // GetAccountUID returns the authenticated user's account UID, or nil if not available. func (c *Client) GetAccountUID() *int64 { return c.accountUID } // Context returns a context for requests (can be overridden for timeouts). // By default it returns context.Background(). func (c *Client) Context() context.Context { return context.Background() } // 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 { // In Go, we typically pass context to each method rather than storing it return c } // 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) { return c.request.DeviceAuth(ctx, onCode) } // 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) { return c.request.RefreshToken(ctx, refreshToken) } // Status represents account status returned by the /account/status endpoint. type Status struct { Account *Account `json:"account"` } // Account represents a user account. type Account struct { UID int64 `json:"uid"` Login string `json:"login"` DisplayName string `json:"display_name"` } // Track represents a music track (minimal subset of fields). type Track struct { ID string `json:"id"` Title string `json:"title"` Duration int `json:"duration_ms"` }