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) }