mirror of
https://git.sr.ht/~joren/streamrip-go
synced 2026-06-17 15:05:39 +02:00
Auto-upgrade outdated configs on startup, add actionable SSL verification hints in rip error paths, and harden SoundCloud search/metadata with canonical URL handling and richer source IDs.
180 lines
5.9 KiB
Go
180 lines
5.9 KiB
Go
package soundcloud
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"streamrip-go/internal/config"
|
|
)
|
|
|
|
func TestGetTrackMetadataAndDownloadable(t *testing.T) {
|
|
cfgData := config.DefaultConfigData()
|
|
c := New(&config.Config{File: cfgData, Session: cfgData})
|
|
c.loggedIn = true
|
|
c.run = func(_ context.Context, _ string, args ...string) ([]byte, error) {
|
|
joined := strings.Join(args, " ")
|
|
if strings.Contains(joined, "--no-playlist") {
|
|
return []byte(`{"title":"Lean On","uploader":"Major Lazer","url":"https://cdn.example/audio.m4a","ext":"m4a","thumbnail":"https://img.example/cover.jpg"}`), nil
|
|
}
|
|
return nil, fmt.Errorf("unexpected args: %v", args)
|
|
}
|
|
|
|
meta, err := c.GetMetadata(context.Background(), "https://soundcloud.com/a/b", "track")
|
|
if err != nil {
|
|
t.Fatalf("GetMetadata() error = %v", err)
|
|
}
|
|
if stringFromAny(meta["title"]) != "Lean On" {
|
|
t.Fatalf("title = %q, want Lean On", stringFromAny(meta["title"]))
|
|
}
|
|
if stringFromAny(meta["id"]) != "https://soundcloud.com/a/b" {
|
|
t.Fatalf("id = %q, want canonical soundcloud url", stringFromAny(meta["id"]))
|
|
}
|
|
|
|
d, err := c.GetDownloadable(context.Background(), "https://soundcloud.com/a/b", 0)
|
|
if err != nil {
|
|
t.Fatalf("GetDownloadable() error = %v", err)
|
|
}
|
|
if d.URL != "https://cdn.example/audio.m4a" || d.Extension != "m4a" {
|
|
t.Fatalf("unexpected downloadable: %+v", d)
|
|
}
|
|
}
|
|
|
|
func TestGetPlaylistMetadata(t *testing.T) {
|
|
cfgData := config.DefaultConfigData()
|
|
c := New(&config.Config{File: cfgData, Session: cfgData})
|
|
c.loggedIn = true
|
|
c.run = func(_ context.Context, _ string, args ...string) ([]byte, error) {
|
|
joined := strings.Join(args, " ")
|
|
if strings.Contains(joined, "--skip-download") && !strings.Contains(joined, "--no-playlist") {
|
|
return []byte(`{"title":"Road Trip","entries":[{"webpage_url":"https://soundcloud.com/a/t1"},{"url":"https://soundcloud.com/a/t2"}]}`), nil
|
|
}
|
|
return nil, fmt.Errorf("unexpected args: %v", args)
|
|
}
|
|
|
|
meta, err := c.GetMetadata(context.Background(), "https://soundcloud.com/a/sets/road-trip", "playlist")
|
|
if err != nil {
|
|
t.Fatalf("GetMetadata() error = %v", err)
|
|
}
|
|
if stringFromAny(meta["name"]) != "Road Trip" {
|
|
t.Fatalf("name = %q, want Road Trip", stringFromAny(meta["name"]))
|
|
}
|
|
tracksMap, ok := meta["tracks"].(map[string]any)
|
|
if !ok {
|
|
t.Fatalf("tracks missing")
|
|
}
|
|
items := asAnySlice(tracksMap["items"])
|
|
if len(items) != 2 {
|
|
t.Fatalf("playlist items len = %d, want 2", len(items))
|
|
}
|
|
}
|
|
|
|
func TestSearchTrack(t *testing.T) {
|
|
cfgData := config.DefaultConfigData()
|
|
c := New(&config.Config{File: cfgData, Session: cfgData})
|
|
c.loggedIn = true
|
|
c.run = func(_ context.Context, _ string, args ...string) ([]byte, error) {
|
|
joined := strings.Join(args, " ")
|
|
if strings.Contains(joined, "scsearch2:lean on") {
|
|
return []byte(`{"entries":[{"title":"Lean On","uploader":"Major Lazer","webpage_url":"https://soundcloud.com/a/b"}]}`), nil
|
|
}
|
|
return nil, fmt.Errorf("unexpected args: %v", args)
|
|
}
|
|
|
|
pages, err := c.Search(context.Background(), "track", "lean on", 2)
|
|
if err != nil {
|
|
t.Fatalf("Search() error = %v", err)
|
|
}
|
|
if len(pages) != 1 {
|
|
t.Fatalf("pages len = %d, want 1", len(pages))
|
|
}
|
|
items := asAnySlice(pages[0]["items"])
|
|
if len(items) != 1 {
|
|
t.Fatalf("items len = %d, want 1", len(items))
|
|
}
|
|
}
|
|
|
|
func TestSearchPlaylist(t *testing.T) {
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path == "/search/sets" {
|
|
_, _ = w.Write([]byte(`<html><body><a href="/a/sets/road-trip">x</a></body></html>`))
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusNotFound)
|
|
}))
|
|
defer ts.Close()
|
|
|
|
cfgData := config.DefaultConfigData()
|
|
c := New(&config.Config{File: cfgData, Session: cfgData})
|
|
c.loggedIn = true
|
|
c.http = ts.Client()
|
|
origBase := soundcloudSearchBaseURL
|
|
soundcloudSearchBaseURL = ts.URL
|
|
defer func() { soundcloudSearchBaseURL = origBase }()
|
|
c.run = func(_ context.Context, _ string, args ...string) ([]byte, error) {
|
|
joined := strings.Join(args, " ")
|
|
if strings.Contains(joined, "https://soundcloud.com/a/sets/road-trip") {
|
|
return []byte(`{"title":"Road Trip","uploader":"User","entries":[{"webpage_url":"https://soundcloud.com/a/t1"}]}`), nil
|
|
}
|
|
return nil, fmt.Errorf("unexpected args: %v", args)
|
|
}
|
|
|
|
pages, err := c.Search(context.Background(), "playlist", "road trip", 5)
|
|
if err != nil {
|
|
t.Fatalf("Search() error = %v", err)
|
|
}
|
|
if len(pages) != 1 {
|
|
t.Fatalf("pages len = %d, want 1", len(pages))
|
|
}
|
|
items := asAnySlice(pages[0]["items"])
|
|
if len(items) != 1 {
|
|
t.Fatalf("items len = %d, want 1", len(items))
|
|
}
|
|
}
|
|
|
|
func TestLoginShowsYtDlpHint(t *testing.T) {
|
|
cfgData := config.DefaultConfigData()
|
|
c := New(&config.Config{File: cfgData, Session: cfgData})
|
|
c.bin = "definitely-not-a-real-yt-dlp-bin"
|
|
err := c.Login(context.Background())
|
|
if err == nil {
|
|
t.Fatalf("expected login error")
|
|
}
|
|
if !strings.Contains(strings.ToLower(err.Error()), "yt-dlp is required") {
|
|
t.Fatalf("expected yt-dlp hint in error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestTrackMetadataIncludesExplicitAndISRC(t *testing.T) {
|
|
meta := trackMetadataFromInfo("https://soundcloud.com/a/b", map[string]any{
|
|
"title": "T",
|
|
"uploader": "U",
|
|
"isrc": "US123",
|
|
"id": "9876",
|
|
"webpage_url": "https://soundcloud.com/a/b?si=abc",
|
|
"age_limit": float64(18),
|
|
"thumbnail": "https://img",
|
|
"upload_date": "20240101",
|
|
})
|
|
if stringFromAny(meta["isrc"]) != "US123" {
|
|
t.Fatalf("isrc = %q, want US123", stringFromAny(meta["isrc"]))
|
|
}
|
|
explicit, _ := meta["explicit"].(bool)
|
|
if !explicit {
|
|
t.Fatalf("expected explicit=true")
|
|
}
|
|
if stringFromAny(meta["source_track_id"]) != "9876" {
|
|
t.Fatalf("source_track_id = %q, want 9876", stringFromAny(meta["source_track_id"]))
|
|
}
|
|
}
|
|
|
|
func TestCanonicalSoundcloudURL(t *testing.T) {
|
|
got := canonicalSoundcloudURL(map[string]any{"webpage_url": "https://soundcloud.com/a/b/?si=x#frag"})
|
|
if got != "https://soundcloud.com/a/b" {
|
|
t.Fatalf("canonical url = %q, want %q", got, "https://soundcloud.com/a/b")
|
|
}
|
|
}
|