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:
JorenandClaude Opus 5 committed 2026-10-03 17:05:55 +02:00
1 parent 201aa5daa7
commit b3b6a2eb1d
19 files changed
+701 -392

No files matched your search

+26 -65
View File
@@ -120,10 +120,10 @@ func (c *Client) Login(ctx context.Context) error {
return fmt.Errorf("tidal login failed: status=%d body=%v", status, resp)
}
if v := stringify(resp["countryCode"]); v != "" {
if v := jsonutil.StringFromAny(resp["countryCode"]); v != "" {
c.cfg.Session.Tidal.CountryCode = v
}
if v := stringify(resp["userId"]); v != "" {
if v := jsonutil.StringFromAny(resp["userId"]); v != "" {
c.cfg.Session.Tidal.UserID = v
}
@@ -159,12 +159,12 @@ func (c *Client) refreshAccessToken(ctx context.Context) error {
return fmt.Errorf("tidal token refresh failed: status=%d body=%v", status, resp)
}
newToken := stringify(resp["access_token"])
newToken := jsonutil.StringFromAny(resp["access_token"])
if newToken == "" {
return errors.New("tidal token refresh missing access_token")
}
newRefresh := stringify(resp["refresh_token"])
newRefresh := jsonutil.StringFromAny(resp["refresh_token"])
expiresIn := int64(jsonutil.IntFromAny(resp["expires_in"]))
if expiresIn <= 0 {
expiresIn = 7 * 24 * 3600
@@ -263,8 +263,8 @@ func (c *Client) fetchTrackLyrics(ctx context.Context, trackID string) (string,
return "", ""
}
lyrics := strings.TrimSpace(stringify(resp["lyrics"]))
lrc := strings.TrimSpace(stringify(resp["subtitles"]))
lyrics := strings.TrimSpace(jsonutil.StringFromAny(resp["lyrics"]))
lrc := strings.TrimSpace(jsonutil.StringFromAny(resp["subtitles"]))
return lyrics, lrc
}
@@ -339,30 +339,6 @@ func (c *Client) GetDownloadable(ctx context.Context, trackID string, quality in
return c.getDownloadableFromTrackManifest(ctx, trackID, quality)
}
func (c *Client) trackSupportsAtmos(ctx context.Context, trackID string) bool {
resp, status, err := c.apiRequest(ctx, "tracks/"+trackID, url.Values{}, c.baseURL)
if err != nil || status != http.StatusOK {
return false
}
if modes, ok := resp["audioModes"].([]any); ok {
for _, mode := range modes {
if strings.Contains(strings.ToUpper(stringify(mode)), "ATMOS") {
return true
}
}
}
if mm, ok := resp["mediaMetadata"].(map[string]any); ok {
if tags, ok := mm["tags"].([]any); ok {
for _, tag := range tags {
if strings.Contains(strings.ToUpper(stringify(tag)), "ATMOS") {
return true
}
}
}
}
return false
}
func (c *Client) getAtmosDownloadable(ctx context.Context, trackID string) (*provider.Downloadable, error) {
var lastErr error
for _, aq := range atmosAudioQualities {
@@ -407,18 +383,18 @@ func (c *Client) getAtmosDownloadable(ctx context.Context, trackID string) (*pro
}
func playbackLooksAtmos(resp map[string]any) bool {
if strings.Contains(strings.ToUpper(stringify(resp["audioMode"])), "ATMOS") {
if strings.Contains(strings.ToUpper(jsonutil.StringFromAny(resp["audioMode"])), "ATMOS") {
return true
}
if modes, ok := resp["audioModes"].([]any); ok {
for _, raw := range modes {
if strings.Contains(strings.ToUpper(stringify(raw)), "ATMOS") {
if strings.Contains(strings.ToUpper(jsonutil.StringFromAny(raw)), "ATMOS") {
return true
}
}
}
manifestB64 := stringify(resp["manifest"])
manifestB64 := jsonutil.StringFromAny(resp["manifest"])
if manifestB64 == "" {
return false
}
@@ -430,17 +406,17 @@ func playbackLooksAtmos(resp map[string]any) bool {
if err = json.Unmarshal(b, &manifest); err != nil {
return false
}
if strings.Contains(strings.ToUpper(stringify(manifest["audioMode"])), "ATMOS") {
if strings.Contains(strings.ToUpper(jsonutil.StringFromAny(manifest["audioMode"])), "ATMOS") {
return true
}
if modes, ok := manifest["audioModes"].([]any); ok {
for _, raw := range modes {
if strings.Contains(strings.ToUpper(stringify(raw)), "ATMOS") {
if strings.Contains(strings.ToUpper(jsonutil.StringFromAny(raw)), "ATMOS") {
return true
}
}
}
codec := strings.ToLower(stringify(manifest["codecs"]))
codec := strings.ToLower(jsonutil.StringFromAny(manifest["codecs"]))
return strings.Contains(codec, "ec-3") || strings.Contains(codec, "eac3") || strings.Contains(codec, "joc") || strings.Contains(codec, "atmos")
}
@@ -524,7 +500,7 @@ func (c *Client) fetchArtistAlbums(ctx context.Context, artistID string) ([]map[
if wrapped, ok := itm["item"].(map[string]any); ok {
itm = wrapped
}
id := stringify(itm["id"])
id := jsonutil.StringFromAny(itm["id"])
if id == "" {
continue
}
@@ -576,14 +552,14 @@ func (c *Client) getDownloadableFromTrackManifestForFormats(ctx context.Context,
if !ok {
return nil, errors.New("tidal trackManifests missing attributes")
}
uri := stringify(attrs["uri"])
uri := jsonutil.StringFromAny(attrs["uri"])
if uri == "" {
return nil, errors.New("tidal trackManifests missing uri")
}
attrFormats, _ := attrs["formats"].([]any)
ext := "m4a"
for _, f := range attrFormats {
fv := strings.ToUpper(stringify(f))
fv := strings.ToUpper(jsonutil.StringFromAny(f))
if strings.Contains(fv, "FLAC") {
ext = "flac"
break
@@ -630,7 +606,7 @@ func (c *Client) GetVideoDownloadable(ctx context.Context, videoID string) (*pro
return nil, fmt.Errorf("tidal video playbackinfo failed: status=%d", status)
}
manifestB64 := stringify(resp["manifest"])
manifestB64 := jsonutil.StringFromAny(resp["manifest"])
if manifestB64 == "" {
return nil, errors.New("tidal video manifest missing")
}
@@ -646,7 +622,7 @@ func (c *Client) GetVideoDownloadable(ctx context.Context, videoID string) (*pro
if !ok || len(urls) == 0 {
return nil, errors.New("tidal video manifest urls missing")
}
masterURL := stringify(urls[0])
masterURL := jsonutil.StringFromAny(urls[0])
if masterURL == "" {
return nil, errors.New("tidal video master url missing")
}
@@ -677,7 +653,7 @@ func (c *Client) GetVideoDownloadable(ctx context.Context, videoID string) (*pro
}
func downloadableFromPlaybackManifest(resp map[string]any) *provider.Downloadable {
manifestB64 := stringify(resp["manifest"])
manifestB64 := jsonutil.StringFromAny(resp["manifest"])
if manifestB64 == "" {
return nil
}
@@ -693,11 +669,11 @@ func downloadableFromPlaybackManifest(resp map[string]any) *provider.Downloadabl
if !ok || len(urls) == 0 {
return nil
}
streamURL := stringify(urls[0])
streamURL := jsonutil.StringFromAny(urls[0])
if streamURL == "" {
return nil
}
codec := strings.ToLower(stringify(manifest["codecs"]))
codec := strings.ToLower(jsonutil.StringFromAny(manifest["codecs"]))
ext := "m4a"
if strings.Contains(codec, "flac") {
ext = "flac"
@@ -708,14 +684,14 @@ func downloadableFromPlaybackManifest(resp map[string]any) *provider.Downloadabl
if profile.Container == "" {
profile = tidalAudioProfileFromExtension(ext)
}
audioQuality := strings.ToUpper(strings.TrimSpace(stringify(resp["audioQuality"])))
audioQuality := strings.ToUpper(strings.TrimSpace(jsonutil.StringFromAny(resp["audioQuality"])))
if audioQuality == "" {
audioQuality = strings.ToUpper(strings.TrimSpace(stringify(manifest["audioQuality"])))
audioQuality = strings.ToUpper(strings.TrimSpace(jsonutil.StringFromAny(manifest["audioQuality"])))
}
if audioQuality != "" {
profile = applyTidalAudioQuality(profile, audioQuality)
}
if strings.Contains(strings.ToUpper(stringify(resp["audioMode"])), "ATMOS") {
if strings.Contains(strings.ToUpper(jsonutil.StringFromAny(resp["audioMode"])), "ATMOS") {
profile = tidalAtmosAudioProfile()
}
return &provider.Downloadable{URL: streamURL, Extension: ext, Source: "tidal", Audio: profile}
@@ -724,7 +700,7 @@ func downloadableFromPlaybackManifest(resp map[string]any) *provider.Downloadabl
func tidalAudioProfileFromFormats(formats []any) provider.AudioProfile {
best := provider.AudioProfile{}
for _, raw := range formats {
f := strings.ToUpper(strings.TrimSpace(stringify(raw)))
f := strings.ToUpper(strings.TrimSpace(jsonutil.StringFromAny(raw)))
switch {
case strings.Contains(f, "EAC3") || strings.Contains(f, "JOC") || strings.Contains(f, "ATMOS"):
return tidalAtmosAudioProfile()
@@ -1003,28 +979,13 @@ func (c *Client) apiPost(ctx context.Context, endpoint string, form url.Values,
})
}
func stringify(v any) string {
switch t := v.(type) {
case string:
return t
case int:
return strconv.Itoa(t)
case int64:
return strconv.FormatInt(t, 10)
case float64:
return strconv.FormatFloat(t, 'f', -1, 64)
default:
return ""
}
}
func enrichTidalImage(meta map[string]any) {
if _, ok := meta["image"].(map[string]any); ok {
return
}
cover := stringify(meta["cover"])
cover := jsonutil.StringFromAny(meta["cover"])
if cover == "" {
cover = stringify(meta["squareImage"])
cover = jsonutil.StringFromAny(meta["squareImage"])
}
if cover == "" {
return