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:
commit
fe62910311
16 files changed
+6688
No files matched your search
@@ -0,0 +1,291 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitea.mrixs.me/Mrixs/yandex-music-api-go/yandexmusic"
|
||||
)
|
||||
|
||||
func TestStorageNew(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
s, err := NewStorage(tmpDir, true)
|
||||
if err != nil {
|
||||
t.Fatalf("NewStorage failed: %v", err)
|
||||
}
|
||||
if s == nil {
|
||||
t.Fatal("expected non-nil storage")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageSaveAndLoadTrack(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
s, err := NewStorage(tmpDir, true)
|
||||
if err != nil {
|
||||
t.Fatalf("NewStorage failed: %v", err)
|
||||
}
|
||||
|
||||
meta := TrackMeta{
|
||||
TrackID: "123",
|
||||
AlbumID: "456",
|
||||
ArtistID: "789",
|
||||
Title: "Test Track",
|
||||
ArtistName: "Test Artist",
|
||||
AlbumTitle: "Test Album",
|
||||
TrackNumber: 1,
|
||||
DiscNumber: 1,
|
||||
DurationMs: 180000,
|
||||
Bitrate: 320,
|
||||
Codec: "mp3",
|
||||
DownloadedAt: time.Now().Format(time.RFC3339),
|
||||
}
|
||||
|
||||
data := []byte("fake audio data")
|
||||
|
||||
if err := s.SaveTrack(meta, data, nil); err != nil {
|
||||
t.Fatalf("SaveTrack failed: %v", err)
|
||||
}
|
||||
|
||||
// Check index
|
||||
if !s.IsTrackDownloaded("123") {
|
||||
t.Error("track not marked as downloaded")
|
||||
}
|
||||
|
||||
loadedMeta, ok := s.GetTrackMeta("123")
|
||||
if !ok {
|
||||
t.Fatal("track not found in index")
|
||||
}
|
||||
if loadedMeta.Title != "Test Track" {
|
||||
t.Errorf("expected title 'Test Track', got '%s'", loadedMeta.Title)
|
||||
}
|
||||
if loadedMeta.FileSize != int64(len(data)) {
|
||||
t.Errorf("expected file size %d, got %d", len(data), loadedMeta.FileSize)
|
||||
}
|
||||
// Check file exists using the stored path
|
||||
if _, err := os.Stat(loadedMeta.FilePath); os.IsNotExist(err) {
|
||||
t.Error("track file not created")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageIdempotent(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
s, err := NewStorage(tmpDir, true)
|
||||
if err != nil {
|
||||
t.Fatalf("NewStorage failed: %v", err)
|
||||
}
|
||||
|
||||
meta := TrackMeta{
|
||||
TrackID: "123",
|
||||
Title: "Test Track",
|
||||
ArtistName: "Test Artist",
|
||||
AlbumTitle: "Test Album",
|
||||
TrackNumber: 1,
|
||||
Codec: "mp3",
|
||||
}
|
||||
|
||||
data := []byte("fake audio data")
|
||||
|
||||
// Save first time
|
||||
if err := s.SaveTrack(meta, data, nil); err != nil {
|
||||
t.Fatalf("first SaveTrack failed: %v", err)
|
||||
}
|
||||
|
||||
firstPath := meta.FilePath
|
||||
|
||||
// Save second time (should not create duplicate)
|
||||
if err := s.SaveTrack(meta, data, nil); err != nil {
|
||||
t.Fatalf("second SaveTrack failed: %v", err)
|
||||
}
|
||||
|
||||
if meta.FilePath != firstPath {
|
||||
t.Errorf("file path changed on second save: %s vs %s", meta.FilePath, firstPath)
|
||||
}
|
||||
|
||||
// Verify only one file exists
|
||||
files, _ := os.ReadDir(filepath.Join(tmpDir, "Test Artist", "Test Album"))
|
||||
count := 0
|
||||
for _, f := range files {
|
||||
if !f.IsDir() {
|
||||
count++
|
||||
}
|
||||
}
|
||||
if count != 1 {
|
||||
t.Errorf("expected 1 file, found %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageOrganizeFalse(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
s, err := NewStorage(tmpDir, false)
|
||||
if err != nil {
|
||||
t.Fatalf("NewStorage failed: %v", err)
|
||||
}
|
||||
|
||||
meta := TrackMeta{
|
||||
TrackID: "123",
|
||||
Title: "Test Track",
|
||||
ArtistName: "Test Artist",
|
||||
AlbumTitle: "Test Album",
|
||||
TrackNumber: 1,
|
||||
Codec: "mp3",
|
||||
DownloadedAt: time.Now().Format(time.RFC3339),
|
||||
}
|
||||
|
||||
data := []byte("fake audio data")
|
||||
|
||||
if err := s.SaveTrack(meta, data, nil); err != nil {
|
||||
t.Fatalf("SaveTrack failed: %v", err)
|
||||
}
|
||||
|
||||
// Should be flat structure - get the saved meta from index
|
||||
loadedMeta, ok := s.GetTrackMeta("123")
|
||||
if !ok {
|
||||
t.Fatal("track not found in index")
|
||||
}
|
||||
expectedDir := tmpDir
|
||||
if filepath.Dir(loadedMeta.FilePath) != expectedDir {
|
||||
t.Errorf("expected flat structure in %s, got %s", expectedDir, filepath.Dir(loadedMeta.FilePath))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageSanitizeFilename(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"normal", "normal"},
|
||||
{"with/slash", "with_slash"},
|
||||
{"with\\backslash", "with_backslash"},
|
||||
{"with:colon", "with_colon"},
|
||||
{"with*star", "with_star"},
|
||||
{"with?question", "with_question"},
|
||||
{"with\"quote", "with_quote"},
|
||||
{"with<lt", "with_lt"},
|
||||
{"with>gt", "with_gt"},
|
||||
{"with|pipe", "with_pipe"},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
result := sanitizeFilename(tc.input)
|
||||
if result != tc.expected {
|
||||
t.Errorf("sanitizeFilename(%q) = %q, want %q", tc.input, result, tc.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageBuildTrackMetaFromYandex(t *testing.T) {
|
||||
track := &yandexmusic.Track{
|
||||
ID: "123",
|
||||
Title: "Test Track",
|
||||
Genre: "Pop",
|
||||
DurationMs: 180000,
|
||||
Artists: []yandexmusic.Artist{
|
||||
{ID: "789", Name: "Test Artist"},
|
||||
},
|
||||
Albums: []yandexmusic.Album{
|
||||
{ID: "456", Title: "Test Album"},
|
||||
},
|
||||
}
|
||||
|
||||
album := &yandexmusic.Album{
|
||||
ID: "456",
|
||||
Title: "Test Album",
|
||||
Genre: "Album Pop",
|
||||
Year: 2023,
|
||||
ReleaseDate: "2023-05-15",
|
||||
Label: "Test Label",
|
||||
Description: "Test album description",
|
||||
CatalogNumber: "TC-001",
|
||||
Artists: []yandexmusic.Artist{
|
||||
{ID: "789", Name: "Test Artist"},
|
||||
},
|
||||
}
|
||||
|
||||
downloadInfo := yandexmusic.DownloadInfo{
|
||||
Bitrate: 320,
|
||||
Codec: "mp3",
|
||||
}
|
||||
|
||||
meta := BuildTrackMetaFromYandex(track, album, 1, 1, downloadInfo)
|
||||
|
||||
if meta.TrackID != "123" {
|
||||
t.Errorf("expected track ID 123, got %s", meta.TrackID)
|
||||
}
|
||||
if meta.Title != "Test Track" {
|
||||
t.Errorf("expected title 'Test Track', got '%s'", meta.Title)
|
||||
}
|
||||
if meta.ArtistName != "Test Artist" {
|
||||
t.Errorf("expected artist 'Test Artist', got '%s'", meta.ArtistName)
|
||||
}
|
||||
if meta.AlbumTitle != "Test Album" {
|
||||
t.Errorf("expected album 'Test Album', got '%s'", meta.AlbumTitle)
|
||||
}
|
||||
if meta.Bitrate != 320 {
|
||||
t.Errorf("expected bitrate 320, got %d", meta.Bitrate)
|
||||
}
|
||||
if meta.Codec != "mp3" {
|
||||
t.Errorf("expected codec mp3, got %s", meta.Codec)
|
||||
}
|
||||
// Extended metadata checks
|
||||
if meta.Genre != "Pop" {
|
||||
t.Errorf("expected genre 'Pop', got '%s'", meta.Genre)
|
||||
}
|
||||
if meta.Comment != "Test album description" {
|
||||
t.Errorf("expected comment 'Test album description', got '%s'", meta.Comment)
|
||||
}
|
||||
if meta.Lyrics != "" {
|
||||
t.Errorf("expected empty lyrics (no lyrics in track), got '%s'", meta.Lyrics)
|
||||
}
|
||||
if meta.Publisher != "Test Label" {
|
||||
t.Errorf("expected publisher 'Test Label', got '%s'", meta.Publisher)
|
||||
}
|
||||
if meta.EncodedBy != "ym-dl" {
|
||||
t.Errorf("expected encoded_by 'ym-dl', got '%s'", meta.EncodedBy)
|
||||
}
|
||||
if meta.OriginalYear != "2023" {
|
||||
t.Errorf("expected original_year '2023', got '%s'", meta.OriginalYear)
|
||||
}
|
||||
if meta.Copyright != "TC-001" {
|
||||
t.Errorf("expected copyright 'TC-001', got '%s'", meta.Copyright)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageBuildAlbumMetaFromYandex(t *testing.T) {
|
||||
album := &yandexmusic.Album{
|
||||
ID: "456",
|
||||
Title: "Test Album",
|
||||
Artists: []yandexmusic.Artist{
|
||||
{ID: "789", Name: "Test Artist"},
|
||||
},
|
||||
Year: 2023,
|
||||
}
|
||||
|
||||
tracks := []TrackMeta{
|
||||
{TrackID: "1", Title: "Track 1"},
|
||||
{TrackID: "2", Title: "Track 2"},
|
||||
}
|
||||
|
||||
meta := BuildAlbumMetaFromYandex(album, tracks)
|
||||
|
||||
if meta.AlbumID != "456" {
|
||||
t.Errorf("expected album ID 456, got %s", meta.AlbumID)
|
||||
}
|
||||
if meta.Title != "Test Album" {
|
||||
t.Errorf("expected title 'Test Album', got '%s'", meta.Title)
|
||||
}
|
||||
if meta.ArtistName != "Test Artist" {
|
||||
t.Errorf("expected artist 'Test Artist', got '%s'", meta.ArtistName)
|
||||
}
|
||||
if meta.Year != 2023 {
|
||||
t.Errorf("expected year 2023, got %d", meta.Year)
|
||||
}
|
||||
if meta.TrackCount != 2 {
|
||||
t.Errorf("expected track count 2, got %d", meta.TrackCount)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user