Initial commit: ym-dl with yandex-music-api-go library migration

Migrate ym-dl from custom oapi-codegen Yandex Music API client to external
gitea.mrixs.me/Mrixs/yandex-music-api-go library with token-only authentication.

Key changes:
- Replace custom Yandex API client with yandexmusic library wrapper
- Token-based OAuth authentication only (no username/password fallback)
- SQLite storage index with JSON index migration path
- Extended ID3 tag support: genre, comment, lyrics, publisher, ISRC, copyright
- High-resolution album covers (1000x1000)
- Fixed album download check (requires all tracks, not just one)
This commit is contained in:
Mrixs committed 2026-10-17 17:09:42 +03:00
commit fe62910311
16 files changed
+6688

No files matched your search

+199
View File
@@ -0,0 +1,199 @@
package config
import (
"os"
"testing"
"time"
)
func TestLoadValidConfig(t *testing.T) {
content := `
yandex:
token: "test-token"
artists:
- id: 12345
storage:
path: "/data/music"
organize: true
monitor:
interval: "24h"
`
tmpfile, err := os.CreateTemp("", "config-*.yaml")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmpfile.Name())
if _, err := tmpfile.WriteString(content); err != nil {
t.Fatal(err)
}
tmpfile.Close()
cfg, err := Load(tmpfile.Name())
if err != nil {
t.Fatalf("Load failed: %v", err)
}
if cfg.Yandex.Token != "test-token" {
t.Errorf("expected token 'test-token', got '%s'", cfg.Yandex.Token)
}
if len(cfg.Artists) != 1 || cfg.Artists[0].ID != 12345 {
t.Errorf("expected artist ID 12345, got %v", cfg.Artists)
}
if cfg.Storage.Path != "/data/music" {
t.Errorf("expected path '/data/music', got '%s'", cfg.Storage.Path)
}
if !cfg.Storage.Organize {
t.Error("expected organize=true")
}
interval, err := cfg.GetMonitorInterval()
if err != nil {
t.Fatalf("GetMonitorInterval failed: %v", err)
}
if interval != 24*time.Hour {
t.Errorf("expected 24h, got %v", interval)
}
}
func TestLoadConfigWithUsernamePassword(t *testing.T) {
content := `
yandex:
username: "user"
password: "pass"
artists:
- id: 1
storage:
path: "/data"
monitor:
interval: "1h"
`
tmpfile, err := os.CreateTemp("", "config-*.yaml")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmpfile.Name())
if _, err := tmpfile.WriteString(content); err != nil {
t.Fatal(err)
}
tmpfile.Close()
cfg, err := Load(tmpfile.Name())
if err != nil {
t.Fatalf("Load failed: %v", err)
}
if cfg.Yandex.Username != "user" || cfg.Yandex.Password != "pass" {
t.Errorf("expected username/password, got %v", cfg.Yandex)
}
}
func TestLoadConfigMissingAuth(t *testing.T) {
content := `
yandex: {}
artists:
- id: 1
storage:
path: "/data"
monitor:
interval: "1h"
`
tmpfile, err := os.CreateTemp("", "config-*.yaml")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmpfile.Name())
if _, err := tmpfile.WriteString(content); err != nil {
t.Fatal(err)
}
tmpfile.Close()
_, err = Load(tmpfile.Name())
if err == nil {
t.Error("expected error for missing auth")
}
}
func TestLoadConfigNoArtists(t *testing.T) {
content := `
yandex:
token: "test"
artists: []
storage:
path: "/data"
monitor:
interval: "1h"
`
tmpfile, err := os.CreateTemp("", "config-*.yaml")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmpfile.Name())
if _, err := tmpfile.WriteString(content); err != nil {
t.Fatal(err)
}
tmpfile.Close()
_, err = Load(tmpfile.Name())
if err == nil {
t.Error("expected error for no artists")
}
}
func TestLoadConfigInvalidInterval(t *testing.T) {
content := `
yandex:
token: "test"
artists:
- id: 1
storage:
path: "/data"
monitor:
interval: "invalid"
`
tmpfile, err := os.CreateTemp("", "config-*.yaml")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmpfile.Name())
if _, err := tmpfile.WriteString(content); err != nil {
t.Fatal(err)
}
tmpfile.Close()
_, err = Load(tmpfile.Name())
if err == nil {
t.Error("expected error for invalid interval")
}
}
func TestLoadConfigNegativeArtistID(t *testing.T) {
content := `
yandex:
token: "test"
artists:
- id: -1
storage:
path: "/data"
monitor:
interval: "1h"
`
tmpfile, err := os.CreateTemp("", "config-*.yaml")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmpfile.Name())
if _, err := tmpfile.WriteString(content); err != nil {
t.Fatal(err)
}
tmpfile.Close()
_, err = Load(tmpfile.Name())
if err == nil {
t.Error("expected error for negative artist ID")
}
}