Library now provides GetTrackDownloadURL (signed URL via XML download_info_url), so ym-dl delegates to it instead of reimplementing XML parsing and MD5 signing. Removed local DownloadTrackXML/DownloadInfoXML and unused crypto/md5, encoding/hex, encoding/xml, io imports from client.go. Track.Lyrics replaced by LyricsInfo (availability flags only, no text). Lyrics text requires separate GetTrackLyrics + GetTrackLyricsText calls — not implemented yet, lyrics field left empty.
159 lines
4.4 KiB
Go
159 lines
4.4 KiB
Go
package yandex
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"log/slog"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
|
|
"ym-dl/internal/config"
|
|
"gitea.mrixs.me/Mrixs/yandex-music-api-go/yandexmusic"
|
|
)
|
|
|
|
// TrackPosition represents track position in album
|
|
type TrackPosition struct {
|
|
Volume int `json:"volume"`
|
|
Index int `json:"index"`
|
|
}
|
|
|
|
// YandexClient wraps the external yandexmusic client
|
|
type YandexClient struct {
|
|
client *yandexmusic.Client
|
|
config *config.YandexConfig
|
|
httpClient *http.Client
|
|
debug bool
|
|
logger *slog.Logger
|
|
}
|
|
|
|
// NewYandexClient creates a new Yandex Music client wrapper
|
|
func NewYandexClient(cfg *config.YandexConfig, debug bool) (*YandexClient, error) {
|
|
if cfg.Token == "" {
|
|
return nil, errors.New("yandex.token is required")
|
|
}
|
|
|
|
httpClient := &http.Client{
|
|
Timeout: 30 * time.Second,
|
|
}
|
|
|
|
logLevel := slog.LevelInfo
|
|
if debug {
|
|
logLevel = slog.LevelDebug
|
|
}
|
|
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
|
|
Level: logLevel,
|
|
}))
|
|
|
|
client := yandexmusic.NewClient(yandexmusic.ClientConfig{
|
|
Token: cfg.Token,
|
|
})
|
|
|
|
if err := client.Init(context.Background()); err != nil {
|
|
return nil, fmt.Errorf("init client: %w", err)
|
|
}
|
|
|
|
return &YandexClient{
|
|
client: client,
|
|
config: cfg,
|
|
httpClient: httpClient,
|
|
debug: debug,
|
|
logger: logger,
|
|
}, nil
|
|
}
|
|
|
|
// GetArtistTracks fetches tracks for an artist
|
|
func (c *YandexClient) GetArtistTracks(ctx context.Context, artistID int, page, pageSize int) ([]*yandexmusic.Track, error) {
|
|
result, err := c.client.GetArtistTracks(ctx, fmt.Sprintf("%d", artistID), page, pageSize)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tracks := make([]*yandexmusic.Track, 0, len(result.Tracks))
|
|
for i := range result.Tracks {
|
|
tracks = append(tracks, &result.Tracks[i])
|
|
}
|
|
return tracks, nil
|
|
}
|
|
|
|
// GetArtistAlbums fetches albums for an artist
|
|
func (c *YandexClient) GetArtistAlbums(ctx context.Context, artistID int, page, pageSize int) ([]*yandexmusic.Album, error) {
|
|
result, err := c.client.GetArtistAlbums(ctx, fmt.Sprintf("%d", artistID), page, pageSize)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
albums := make([]*yandexmusic.Album, 0, len(result.Albums))
|
|
for i := range result.Albums {
|
|
albums = append(albums, &result.Albums[i])
|
|
}
|
|
return albums, nil
|
|
}
|
|
|
|
// GetAlbumWithTracks fetches an album with its tracks
|
|
func (c *YandexClient) GetAlbumWithTracks(ctx context.Context, albumID string) (*yandexmusic.Album, error) {
|
|
albums, err := c.client.GetAlbumsWithTracks(ctx, []string{albumID})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(albums) == 0 {
|
|
return nil, fmt.Errorf("album not found")
|
|
}
|
|
return albums[0], nil
|
|
}
|
|
|
|
// GetTrackInfo fetches a single track's info
|
|
func (c *YandexClient) GetTrackInfo(ctx context.Context, trackID string) (*yandexmusic.Track, error) {
|
|
return c.client.GetTrack(ctx, trackID)
|
|
}
|
|
|
|
// GetTrackInfoWithPosition fetches track info including track position in album
|
|
func (c *YandexClient) GetTrackInfoWithPosition(ctx context.Context, trackID string) (*yandexmusic.Track, *TrackPosition, error) {
|
|
track, err := c.client.GetTrack(ctx, trackID)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// Extract track position from first album
|
|
var position *TrackPosition
|
|
if len(track.Albums) > 0 {
|
|
albumID := string(track.Albums[0].ID)
|
|
albums, err := c.client.GetAlbumsWithTracks(ctx, []string{albumID})
|
|
if err == nil && len(albums) > 0 {
|
|
album := albums[0]
|
|
// Find track in album.Volumes and get position
|
|
for volIdx, volume := range album.Volumes {
|
|
for trackIdx, t := range volume {
|
|
if string(t.ID) == trackID {
|
|
position = &TrackPosition{
|
|
Volume: volIdx + 1,
|
|
Index: trackIdx + 1,
|
|
}
|
|
break
|
|
}
|
|
}
|
|
if position != nil {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return track, position, nil
|
|
}
|
|
|
|
// GetTrackDownloadInfo gets download info for a track
|
|
func (c *YandexClient) GetTrackDownloadInfo(ctx context.Context, trackID string) ([]yandexmusic.DownloadInfo, error) {
|
|
return c.client.GetTrackDownloadInfo(ctx, trackID, "")
|
|
}
|
|
|
|
// GetArtistInfo fetches artist info
|
|
func (c *YandexClient) GetArtistInfo(ctx context.Context, artistID int) (*yandexmusic.Artist, error) {
|
|
return c.client.GetArtist(ctx, fmt.Sprintf("%d", artistID))
|
|
}
|
|
|
|
// GetTrackDownloadURL gets the direct MP3 download URL for a track.
|
|
// Delegates to the library which handles XML download, parsing and URL signing.
|
|
func (c *YandexClient) GetTrackDownloadURL(ctx context.Context, trackID string) (string, error) {
|
|
return c.client.GetTrackDownloadURL(ctx, trackID, "mp3", 0)
|
|
}
|