mirror of
https://git.sr.ht/~joren/streamrip-go
synced 2026-10-16 22:07:30 +02:00
refactor: delete dead code, dedupe helpers, add tests for untested packages
Dead code Removed, each verified to have no non-test caller: promptSearchSelection (77 lines, superseded by promptSearchSelectionMenu), AddPlaylistByTrackIDs, download.NewWithVerifySSL, qobuz.Client.GetTrackMetadata, tidal.Client.trackSupportsAtmos (test-only, and the comment above GetDownloadable already documented its removal from the live path) and its now-orphaned test, yandex.canonicalTrackRequestID (a no-op returning its trimmed input; the call site uses strings.TrimSpace directly) and the unused defaultEstimatedKbps constant. Config keys that did nothing qobuz.download_booklets and misc.check_for_updates were declared, defaulted and documented in config.toml.example but read by no code. The whole [youtube] block went too: there is no YouTube provider. go-toml ignores unknown keys, verified, so existing user configs still load. Duplicated helpers tidal.stringify and yandex.firstNonEmpty were byte-identical copies of jsonutil.StringFromAny and jsonutil.FirstNonEmpty. displayArtistNames existed three times, byte-identical, in internal/app, provider/yandex and provider/beatport; it now lives in jsonutil. The CLI's normalizeCodec delegates to config.NormalizeCodec so the flag and the config file are checked by the same rules. Tests for packages that had none internal/jsonutil (0% -> 100%) and internal/netutil (0% -> 78%), the latter covering redactURL, which is a security control, and the zero-timeout contract that internal/download and the qobuz body copy depend on. internal/config rose to 82% with coverage of the new Validate(), the 0600 permissions, CloneConfigData not sharing slices, and a concurrent UpdateTokens test that exercises the mutex under -race. Test suite is ~85x faster Provider tests built clients with the default 60 requests/minute, so the rate limiter slept 1s per HTTP call: deezer took 31s, tidal 11s, qobuz 8s. Each provider test package now builds its config through a testConfigData helper that disables throttling. Whole suite: ~60s -> 0.7s, and 2.8s under -race. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
19 files changed
+701
-392
No files matched your search
@@ -36,7 +36,6 @@ const (
|
||||
requestAttempts = 3
|
||||
desktopWindowsSignKey = "kzqU4XhfCaY6B6JTHODeq5"
|
||||
legacyMP3SignSalt = "XGRlBW9FXlekgbPrRHuSiA"
|
||||
defaultEstimatedKbps = 50000
|
||||
)
|
||||
|
||||
var ErrMissingYandexToken = errors.New("missing yandex access_token")
|
||||
@@ -180,7 +179,7 @@ func (c *Client) Close() error {
|
||||
}
|
||||
|
||||
func (c *Client) getTrackMetadata(ctx context.Context, item string) (map[string]any, error) {
|
||||
trackRef := canonicalTrackRequestID(item)
|
||||
trackRef := strings.TrimSpace(item)
|
||||
form := url.Values{}
|
||||
form.Set("trackIds", trackRef)
|
||||
form.Set("withMixData", "true")
|
||||
@@ -577,7 +576,7 @@ func normalizeTrack(raw map[string]any, fallbackID string, artistSeparator strin
|
||||
trackID := canonicalTrackRefFromRaw(raw, fallbackID)
|
||||
albumRaw := firstAlbum(raw)
|
||||
artistNames := artistNames(raw)
|
||||
artistName := displayArtistNames(artistNames)
|
||||
artistName := jsonutil.DisplayArtistNames(artistNames)
|
||||
albumArtist := joinAlbumArtists(albumRaw, artistSeparator)
|
||||
if albumArtist == "" {
|
||||
albumArtist = artistName
|
||||
@@ -596,7 +595,7 @@ func normalizeTrack(raw map[string]any, fallbackID string, artistSeparator strin
|
||||
"tracks_count": trackTotal,
|
||||
"media_number": discNumber,
|
||||
"numberOfVolumes": discTotal,
|
||||
"release_date": firstNonEmpty(jsonutil.StringFromAny(raw["releaseDate"]), jsonutil.StringFromAny(albumRaw["releaseDate"])),
|
||||
"release_date": jsonutil.FirstNonEmpty(jsonutil.StringFromAny(raw["releaseDate"]), jsonutil.StringFromAny(albumRaw["releaseDate"])),
|
||||
"parental_warning": isExplicit(raw),
|
||||
"explicit": isExplicit(raw),
|
||||
"source_track_id": jsonutil.StringFromAny(raw["realId"]),
|
||||
@@ -684,9 +683,9 @@ func normalizePlaylist(raw map[string]any) map[string]any {
|
||||
}
|
||||
}
|
||||
owner, _ := raw["owner"].(map[string]any)
|
||||
ownerName := firstNonEmpty(jsonutil.StringFromAny(owner["name"]), jsonutil.StringFromAny(owner["login"]))
|
||||
ownerName := jsonutil.FirstNonEmpty(jsonutil.StringFromAny(owner["name"]), jsonutil.StringFromAny(owner["login"]))
|
||||
return map[string]any{
|
||||
"id": firstNonEmpty(jsonutil.StringFromAny(raw["playlistUuid"]), jsonutil.StringFromAny(raw["kind"])),
|
||||
"id": jsonutil.FirstNonEmpty(jsonutil.StringFromAny(raw["playlistUuid"]), jsonutil.StringFromAny(raw["kind"])),
|
||||
"name": jsonutil.StringFromAny(raw["title"]),
|
||||
"title": jsonutil.StringFromAny(raw["title"]),
|
||||
"description": jsonutil.StringFromAny(raw["description"]),
|
||||
@@ -738,8 +737,8 @@ func normalizeArtistSearchItem(raw map[string]any) map[string]any {
|
||||
|
||||
func normalizePlaylistSearchItem(raw map[string]any) map[string]any {
|
||||
owner, _ := raw["owner"].(map[string]any)
|
||||
ownerID := firstNonEmpty(jsonutil.StringFromAny(owner["uid"]), jsonutil.StringFromAny(raw["uid"]))
|
||||
ownerName := firstNonEmpty(jsonutil.StringFromAny(owner["name"]), jsonutil.StringFromAny(owner["login"]))
|
||||
ownerID := jsonutil.FirstNonEmpty(jsonutil.StringFromAny(owner["uid"]), jsonutil.StringFromAny(raw["uid"]))
|
||||
ownerName := jsonutil.FirstNonEmpty(jsonutil.StringFromAny(owner["name"]), jsonutil.StringFromAny(owner["login"]))
|
||||
return map[string]any{
|
||||
"id": ownerID + ":" + jsonutil.StringFromAny(raw["kind"]),
|
||||
"title": jsonutil.StringFromAny(raw["title"]),
|
||||
@@ -783,7 +782,7 @@ func firstArtistID(raw map[string]any) string {
|
||||
}
|
||||
|
||||
func joinArtists(raw map[string]any, artistSeparator string) string {
|
||||
return displayArtistNames(artistNames(raw))
|
||||
return jsonutil.DisplayArtistNames(artistNames(raw))
|
||||
}
|
||||
|
||||
func artistNames(raw map[string]any) []string {
|
||||
@@ -805,7 +804,7 @@ func joinAlbumArtists(albumRaw map[string]any, artistSeparator string) string {
|
||||
if len(albumRaw) == 0 {
|
||||
return ""
|
||||
}
|
||||
return displayArtistNames(albumArtistNames(albumRaw))
|
||||
return jsonutil.DisplayArtistNames(albumArtistNames(albumRaw))
|
||||
}
|
||||
|
||||
func albumArtistNames(albumRaw map[string]any) []string {
|
||||
@@ -823,19 +822,6 @@ func albumArtistNames(albumRaw map[string]any) []string {
|
||||
return parts
|
||||
}
|
||||
|
||||
func displayArtistNames(names []string) string {
|
||||
switch len(names) {
|
||||
case 0:
|
||||
return ""
|
||||
case 1:
|
||||
return names[0]
|
||||
case 2:
|
||||
return names[0] + " & " + names[1]
|
||||
default:
|
||||
return strings.Join(names[:len(names)-1], ", ") + " & " + names[len(names)-1]
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) artistSeparator() string {
|
||||
if c.cfg.Session.Metadata.ArtistSeparator == "" {
|
||||
return "; "
|
||||
@@ -852,19 +838,11 @@ func trackNumbers(albumRaw map[string]any) (int, int) {
|
||||
return trackNumber, firstPositiveInt(jsonutil.IntFromAny(position["volume"]), 1)
|
||||
}
|
||||
|
||||
func canonicalTrackRequestID(item string) string {
|
||||
item = strings.TrimSpace(item)
|
||||
if item == "" {
|
||||
return item
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
func canonicalTrackRefFromRaw(raw map[string]any, fallback string) string {
|
||||
if strings.Contains(strings.TrimSpace(fallback), ":") {
|
||||
return strings.TrimSpace(fallback)
|
||||
}
|
||||
trackID := firstNonEmpty(jsonutil.StringFromAny(raw["realId"]), jsonutil.StringFromAny(raw["id"]))
|
||||
trackID := jsonutil.FirstNonEmpty(jsonutil.StringFromAny(raw["realId"]), jsonutil.StringFromAny(raw["id"]))
|
||||
albumID := jsonutil.StringFromAny(firstAlbum(raw)["id"])
|
||||
if trackID == "" {
|
||||
trackID = strings.TrimSpace(fallback)
|
||||
@@ -931,7 +909,7 @@ func imageMapFromTrack(raw map[string]any, albumRaw map[string]any) map[string]a
|
||||
}
|
||||
|
||||
func imageMapFromURI(raw map[string]any) map[string]any {
|
||||
uri := firstNonEmpty(jsonutil.StringFromAny(raw["ogImage"]), jsonutil.StringFromAny(raw["coverUri"]))
|
||||
uri := jsonutil.FirstNonEmpty(jsonutil.StringFromAny(raw["ogImage"]), jsonutil.StringFromAny(raw["coverUri"]))
|
||||
if uri == "" {
|
||||
if cover, ok := raw["cover"].(map[string]any); ok {
|
||||
uri = jsonutil.StringFromAny(cover["uri"])
|
||||
@@ -1049,12 +1027,3 @@ func firstPositiveInt(vals ...int) int {
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func firstNonEmpty(vals ...string) string {
|
||||
for _, v := range vals {
|
||||
if strings.TrimSpace(v) != "" {
|
||||
return strings.TrimSpace(v)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -60,7 +60,7 @@ func TestGetDownloadableUsesModernGetFileInfo(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
d := config.DefaultConfigData()
|
||||
d := testConfigData()
|
||||
d.Downloads.RequestsPerMinute = 0
|
||||
d.Yandex.AccessToken = "token"
|
||||
c := New(&config.Config{File: d, Session: d})
|
||||
@@ -132,7 +132,7 @@ func TestGetMetadataTrackUsesModernTracksEndpoint(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
d := config.DefaultConfigData()
|
||||
d := testConfigData()
|
||||
d.Downloads.RequestsPerMinute = 0
|
||||
d.Yandex.AccessToken = "token"
|
||||
c := New(&config.Config{File: d, Session: d})
|
||||
@@ -167,13 +167,6 @@ func TestGetMetadataTrackUsesModernTracksEndpoint(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisplayArtistNamesUsesCreditPunctuation(t *testing.T) {
|
||||
got := displayArtistNames([]string{"A", "B", "C"})
|
||||
if got != "A, B & C" {
|
||||
t.Fatalf("displayArtistNames() = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLegacyDirectURLBuildsPlayableMP3URL(t *testing.T) {
|
||||
url, err := legacyDirectURL(&legacyDownloadInfoXML{
|
||||
Host: "example.test",
|
||||
@@ -189,3 +182,13 @@ func TestLegacyDirectURLBuildsPlayableMP3URL(t *testing.T) {
|
||||
t.Fatalf("legacyDirectURL() = %q, want %q", url, want)
|
||||
}
|
||||
}
|
||||
|
||||
// testConfigData returns defaults with request throttling disabled.
|
||||
//
|
||||
// At the default 60 requests/minute the rate limiter sleeps 1s per request,
|
||||
// which accounted for most of this suite's runtime (deezer alone was 31s).
|
||||
func testConfigData() config.ConfigData {
|
||||
d := config.DefaultConfigData()
|
||||
d.Downloads.RequestsPerMinute = -1
|
||||
return d
|
||||
}
|
||||
Reference in new issue
Block a user