Skip to content

fix: No lyrics for this track appear even when LRCLIB has the specific song - #411

Merged
LargeModGames merged 3 commits into
LargeModGames:mainfrom
yhay81:agent/issue-410
Aug 3, 2026
Merged

fix: No lyrics for this track appear even when LRCLIB has the specific song#411
LargeModGames merged 3 commits into
LargeModGames:mainfrom
yhay81:agent/issue-410

Conversation

@yhay81

@yhay81 yhay81 commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Fixes #410.

Summary

No lyrics for this track appear even when LRCLIB has the specific song

Validation

  • Mechanical gate: +164/-70, tests passed
  • Adversarial review: approved

🤖 AI-authored PR, operated by @yhay81.

Summary by CodeRabbit

  • New Features

    • Improved lyrics retrieval with exact-match lookup and intelligent fallback search.
    • Prioritizes results with available, synchronized lyrics and matching duration.
  • Bug Fixes

    • Prevents empty lyric responses from being accepted.
    • Ensures lyric state updates consistently across lookup methods.
  • Tests

    • Added coverage for search-result selection and empty-result handling.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d58045b3-ff15-419c-8c74-c121598783b5

📥 Commits

Reviewing files that changed from the base of the PR and between 6c1efb7 and d9cc6cf.

📒 Files selected for processing (1)
  • src/infra/network/utils.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/infra/network/utils.rs

📝 Walkthrough

Walkthrough

Lyrics retrieval now uses a shared LRCLIB helper. It tries an exact lookup, then fuzzy search when needed. The code validates lyric content, selects results by lyric quality and duration, and updates lyrics state consistently.

Changes

Lyrics retrieval

Layer / File(s) Summary
LRCLIB lookup and result selection
src/infra/network/utils.rs
LrcResponse includes optional duration data and rejects empty lyrics. Retrieval tries an exact request, falls back to search, and selects synced lyrics or the closest valid plain-lyrics result. Tests cover result selection and empty candidates.
Lyrics state handling
src/infra/network/utils.rs
get_lyrics delegates retrieval to fetch_lrclib_lyrics, maps synced and plain lyrics, handles missing results, and bumps the lyrics generation.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant get_lyrics
  participant fetch_lrclib_lyrics
  participant LRCLIB
  participant LyricsState
  get_lyrics->>fetch_lrclib_lyrics: request lyrics
  fetch_lrclib_lyrics->>LRCLIB: try exact lookup with rounded duration
  LRCLIB-->>fetch_lrclib_lyrics: return exact response
  fetch_lrclib_lyrics->>LRCLIB: search when the exact response has no lyrics
  LRCLIB-->>fetch_lrclib_lyrics: return search results
  fetch_lrclib_lyrics-->>get_lyrics: return selected lyrics
  get_lyrics->>LyricsState: store lyrics and update generation
Loading

Possibly related PRs

  • LargeModGames/spotatui#378: Both changes modify src/infra/network/utils.rs and the get_lyrics flow, including synchronization-state handling.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title uses the required fix: prefix and clearly describes the lyrics retrieval bug addressed by the pull request.
Linked Issues check ✅ Passed The changes address issue #410 by retrieving LRCLIB lyrics through exact and fuzzy lookup paths and updating lyric state correctly.
Out of Scope Changes check ✅ Passed The changes remain focused on LRCLIB lyric retrieval, response validation, state updates, and related tests.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/infra/network/utils.rs (1)

536-561: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add cases for unknown duration and missing result duration.

The three tests cover the main ranking path. Two branches in pick_search_result stay untested: duration <= 0.0 at Line 390, and a result whose duration is None at Lines 391-394. Both branches decide which result a user sees when metadata is incomplete.

🧪 Proposed additional tests
   #[test]
   fn search_returns_none_when_no_result_has_lyrics() {
     let results = vec![search_result(None, Some("   "), Some(200.0))];
     assert!(pick_search_result(results, 200.0).is_none());
   }
+
+  #[test]
+  fn search_ignores_duration_when_track_duration_unknown() {
+    let results = vec![
+      search_result(Some("[00:01.00] first"), None, Some(900.0)),
+      search_result(Some("[00:01.00] second"), None, Some(10.0)),
+    ];
+    let picked = pick_search_result(results, 0.0).unwrap();
+    assert_eq!(picked.syncedLyrics.as_deref(), Some("[00:01.00] first"));
+  }
+
+  #[test]
+  fn search_deprioritizes_result_without_duration() {
+    let results = vec![
+      search_result(Some("[00:01.00] no duration"), None, None),
+      search_result(Some("[00:01.00] has duration"), None, Some(300.0)),
+    ];
+    let picked = pick_search_result(results, 200.0).unwrap();
+    assert_eq!(picked.syncedLyrics.as_deref(), Some("[00:01.00] has duration"));
+  }

Run these with cargo test --no-default-features --features telemetry.

As per coding guidelines: "Run tests with cargo test --no-default-features --features telemetry and add or adjust tests when behavior changes."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/infra/network/utils.rs` around lines 536 - 561, Add tests near the
existing pick_search_result tests covering an unknown target duration (duration
<= 0.0) and a candidate whose duration is None. Assert each case selects the
expected lyric result according to pick_search_result’s fallback behavior, using
the existing search_result helper and test command.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/infra/network/utils.rs`:
- Around line 536-561: Add tests near the existing pick_search_result tests
covering an unknown target duration (duration <= 0.0) and a candidate whose
duration is None. Assert each case selects the expected lyric result according
to pick_search_result’s fallback behavior, using the existing search_result
helper and test command.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 149a698b-0a45-4a5d-a484-db2f96454011

📥 Commits

Reviewing files that changed from the base of the PR and between dd291e2 and 6c1efb7.

📒 Files selected for processing (1)
  • src/infra/network/utils.rs

@LargeModGames
LargeModGames merged commit 5c3269a into LargeModGames:main Aug 3, 2026
12 checks passed
LargeModGames added a commit that referenced this pull request Aug 5, 2026
# Summary

Fixes #410. Lyrics never appeared for tracks credited to more than one
artist (e.g. "Take Me Back" by Kygo and Max McNown), even when LRCLIB
clearly had the track.

Root cause: LRCLIB indexes each track under a single artist string
(almost always the primary credit), but spotatui sent the full
**joined** credit (`"Kygo, Max McNown"`) to both the exact `/api/get`
and the fuzzy `/api/search` endpoints, so every collaboration missed.
The `PlaybackMetadata.artists` field was typed `Vec<String>` but in
practice always held one pre-joined display string, so there was no way
to recover the individual artists downstream.

The fix threads the real per-artist list through instead of a display
string:

- The playback snapshot now carries individual artist names as a
structured list (both the native-streaming and Spotify-context paths).
`NativeTrackInfo.artists_display: String` became `artists: Vec<String>`,
populated from the structured list already present at the player-event
boundary.
- The LRCLIB lookup (`network/utils.rs`) now tries a candidate ladder:
all `/api/get` candidates first, then all `/api/search`, where
candidates are `[full joined credit, primary artist alone]`. The primary
artist comes from the structured list (never a `", "` split), so an act
whose own name contains a comma (e.g. "Earth, Wind & Fire") plus a guest
is not corrupted.

Side effect: MPRIS `xesam:artist` is now a genuine array instead of a
single joined element. Display is unchanged (`primary_artist()` still
joins the list), and the friends-widget backend already joins the
artists array with `", "`, so there is no wire-format change.

# Testing

- `cargo fmt --all`
- `cargo clippy --no-default-features --features telemetry -- -D
warnings` (clean)
- `cargo clippy -- -D warnings` (default, clean)
- `cargo clippy --features all-sources -- -D warnings` (clean)
- `cargo test --no-default-features --features telemetry` (510 passed)
- `cargo test` (default, 771 passed)
- `cargo test --features all-sources` (843 passed)
- Added unit tests for the artist candidate ladder and a snapshot
regression test for a multi-artist native track.
- Live: played "Take Me Back" (Kygo, Max McNown) on the native device
and the synced lyrics now render.

# Additional notes

The earlier fix for this issue (#411) added the `/api/get` ->
`/api/search` fallback but still passed the joined multi-artist string
to both endpoints, so collaborations stayed broken; this addresses the
underlying data shape. Local-files (single lofty artist string) and
internet radio (no track duration, so no lyrics) are intentionally left
as-is.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
  - Lyrics now load correctly for collaboration tracks.
- Lookup tries the complete artist credits first, then falls back to the
primary artist.
  - Artist names containing commas are handled correctly.
  - Artist display formatting remains unchanged in the player interface.

- **Tests**
- Added coverage for solo artists, collaborations, fallback lookups, and
empty artist lists.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No lyrics for this track appear even when LRCLIB has the specific song

2 participants