package yandex import ( "context" "encoding/json" "io" "net/http" "net/http/httptest" "net/url" "strings" "testing" "streamrip-go/internal/config" "streamrip-go/internal/jsonutil" ) func TestYandexDownloadSignMatchesCapturedFormat(t *testing.T) { sign, ts := yandexDownloadSign("32038184", "lossless", []string{"flac", "aac", "he-aac", "mp3", "flac-mp4", "aac-mp4", "he-aac-mp4"}, "raw") if ts <= 0 { t.Fatalf("timestamp = %d", ts) } if strings.TrimSpace(sign) == "" { t.Fatalf("decoded sign is empty") } if strings.Contains(sign, "=") { t.Fatalf("sign unexpectedly contains base64 padding: %q", sign) } if strings.Contains(sign, " ") { t.Fatalf("sign unexpectedly contains space: %q", sign) } } func TestGetDownloadableUsesModernGetFileInfo(t *testing.T) { var gotPath string var gotQuery url.Values ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { gotPath = r.URL.Path gotQuery = r.URL.Query() if r.URL.Path == "/account/about" { _ = json.NewEncoder(w).Encode(map[string]any{"result": map[string]any{"uid": "123"}}) return } if r.URL.Path != "/get-file-info" { w.WriteHeader(http.StatusNotFound) return } _ = json.NewEncoder(w).Encode(map[string]any{ "result": map[string]any{ "downloadInfo": map[string]any{ "trackId": "32038184", "quality": "lossless", "codec": "flac-mp4", "transport": "encraw", "key": "00112233445566778899aabbccddeeff", "bitrate": 0, "url": "https://strm.example/music-v2/crypt/x/flac-mp4", }, }, }) })) defer ts.Close() d := config.DefaultConfigData() d.Downloads.RequestsPerMinute = 0 d.Yandex.AccessToken = "token" c := New(&config.Config{File: d, Session: d}) c.baseURL = ts.URL c.loggedIn = true dl, err := c.GetDownloadable(context.Background(), "32038184:1683700", 2) if err != nil { t.Fatalf("GetDownloadable() error = %v", err) } if gotPath != "/get-file-info" { t.Fatalf("path = %q, want /get-file-info", gotPath) } if gotQuery.Get("trackId") != "32038184" { t.Fatalf("trackId = %q, want 32038184", gotQuery.Get("trackId")) } if gotQuery.Get("quality") != "lossless" { t.Fatalf("quality = %q, want lossless", gotQuery.Get("quality")) } if gotQuery.Get("transports") != "encraw" { t.Fatalf("transports = %q, want encraw", gotQuery.Get("transports")) } if dl.Extension != "m4a" { t.Fatalf("extension = %q, want m4a", dl.Extension) } if dl.Audio.Codec != "FLAC" || dl.Audio.Quality != "LOSSLESS" { t.Fatalf("unexpected audio profile: %+v", dl.Audio) } if dl.TrackID != "32038184" { t.Fatalf("track id = %q, want 32038184", dl.TrackID) } if dl.Cipher != "AES_CTR" || dl.Key == "" { t.Fatalf("expected yandex cipher metadata, got cipher=%q key=%q", dl.Cipher, dl.Key) } } func TestGetMetadataTrackUsesModernTracksEndpoint(t *testing.T) { var gotMethod string var gotPath string var gotBody string ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { gotMethod = r.Method gotPath = r.URL.Path if r.URL.Path != "/tracks" { w.WriteHeader(http.StatusNotFound) return } body, _ := io.ReadAll(r.Body) gotBody = string(body) _ = json.NewEncoder(w).Encode(map[string]any{ "result": []map[string]any{{ "id": "9442712", "realId": "9442712", "title": "Nightcall", "artists": []map[string]any{{"id": "1433871", "name": "Kavinsky"}}, "albums": []map[string]any{{ "id": "1000856", "title": "OutRun", "releaseDate": "2013-02-25T00:00:00+04:00", "trackCount": 13, "artists": []map[string]any{{"id": "1433871", "name": "Kavinsky"}}, "trackPosition": map[string]any{ "index": 0, "volume": 1, }, }}, }}, }) })) defer ts.Close() d := config.DefaultConfigData() d.Downloads.RequestsPerMinute = 0 d.Yandex.AccessToken = "token" c := New(&config.Config{File: d, Session: d}) c.baseURL = ts.URL c.loggedIn = true meta, err := c.GetMetadata(context.Background(), "9442712:1000856", "track") if err != nil { t.Fatalf("GetMetadata() error = %v", err) } if gotMethod != http.MethodPost || gotPath != "/tracks" { t.Fatalf("unexpected request: %s %s", gotMethod, gotPath) } if !strings.Contains(gotBody, "trackIds=9442712%3A1000856") { t.Fatalf("body = %q", gotBody) } if meta["id"] != "9442712:1000856" { t.Fatalf("id = %v", meta["id"]) } if album, _ := meta["album"].(map[string]any); jsonutil.StringFromAny(album["title"]) != "OutRun" { t.Fatalf("unexpected album: %+v", album) } } func TestLegacyDirectURLBuildsPlayableMP3URL(t *testing.T) { url, err := legacyDirectURL(&legacyDownloadInfoXML{ Host: "example.test", Path: "/abc123", TS: "1234567890", S: "tailxyz", }) if err != nil { t.Fatalf("legacyDirectURL() error = %v", err) } want := "https://example.test/get-mp3/248c1c6ff5daf481560d3bd9f24e8058/1234567890/abc123" if url != want { t.Fatalf("legacyDirectURL() = %q, want %q", url, want) } }