Skip to content

feat(lyrics-plus): add performer tag for the Musixmatch provider#3689

Open
ianz56 wants to merge 6 commits intospicetify:mainfrom
ianz56:feat/performer-tag
Open

feat(lyrics-plus): add performer tag for the Musixmatch provider#3689
ianz56 wants to merge 6 commits intospicetify:mainfrom
ianz56:feat/performer-tag

Conversation

@ianz56
Copy link
Contributor

@ianz56 ianz56 commented Feb 3, 2026

Display performer tags for each line showing who the singer is. This can be enabled/disabled via the Option Menu.

output1.mp4

Summary by CodeRabbit

  • New Features

    • Added performer names display alongside lyrics when available from the music provider.
    • New "Show performers" toggle in the Adjustments menu to control visibility of performer labels.
  • Style

    • Enhanced visual styling for performer labels with subtle appearance.
    • Improved karaoke word hover feedback.

@coderabbitai
Copy link

coderabbitai bot commented Feb 3, 2026

📝 Walkthrough

Walkthrough

Introduces performer tagging functionality to lyrics display. Extracts performer data from Musixmatch API and integrates performer-aware rendering with conditional visibility controls. Adds configuration option to toggle performer label display and applies styling for performer elements.

Changes

Cohort / File(s) Summary
Data Provider
CustomApps/lyrics-plus/ProviderMusixmatch.js
Added performer data extraction via expanded Musixmatch API request parameters. Introduced parsePerformerData() to parse performer tagging metadata and matchSequential() to align lyric lines with performer snippets. Applied performer annotation across getKaraoke(), getSynced(), and getUnsynced() return objects with comma-separated performer names.
UI Configuration & Controls
CustomApps/lyrics-plus/OptionsMenu.js, CustomApps/lyrics-plus/index.js
Added show-performers configuration flag (default: true) and runtime hasPerformer detection. Enhanced AdjustmentsMenu to accept hasPerformer prop and conditionally render "Show performers" slider when performer data exists and mode is SYNCED, KARAOKE, or UNSYNCED.
Rendering Logic
CustomApps/lyrics-plus/Pages.js
Modified KaraokeLine signature to accept endTime parameter. Implemented conditional performer label rendering (class: lyrics-lyricsContainer-Performer) before lyric text across SyncedLyricsPage, SyncedExpandedLyricsPage, and UnsyncedLyricsPage. Added deduplication logic to suppress redundant performer labels for consecutive lines with matching performers in synced views.
Styling
CustomApps/lyrics-plus/style.css
Added .lyrics-lyricsContainer-Performer class for performer label styling (font-size: 0.6em, opacity: 0.7) and enhanced hover state for Karaoke-Word background-position.

Sequence Diagram(s)

sequenceDiagram
    participant API as Musixmatch API
    participant Provider as ProviderMusixmatch
    participant Container as LyricsContainer
    participant Menu as AdjustmentsMenu
    participant Page as LyricsPage

    API->>Provider: Return track_structure + performer_tagging
    Provider->>Provider: parsePerformerData()<br/>(extract snippets)
    Provider->>Provider: matchSequential()<br/>(align performers)
    Provider->>Container: Return lines with performer field
    Container->>Container: Detect hasPerformer<br/>from line data
    Container->>Menu: Pass hasPerformer prop
    Menu->>Menu: Conditionally show<br/>performers toggle
    Container->>Page: Pass show-performers<br/>config + lines
    Page->>Page: Render performer label<br/>if enabled + performer exists
    Page->>Page: Deduplicate consecutive<br/>performer labels
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • rxri

Poem

🐰 A hop, skip, and jump through the performer's song,
Each name now tagged where it belongs,
With toggles to show and styles that gleam,
The lyrics app sings its brightest dream! 🎵✨

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(lyrics-plus): add performer tag for the Musixmatch provider' directly and accurately summarizes the main change: adding performer tags/data to the Musixmatch lyrics provider with full UI integration.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@CustomApps/lyrics-plus/ProviderMusixmatch.js`:
- Around line 94-157: parsePerformerData currently assumes
meta.track.performer_tagging exists and will throw if meta or meta.track or
performer_tagging is missing; add defensive checks at the top of
parsePerformerData to return an empty snippetQueue when meta, meta.track, or
meta.track.performer_tagging is falsy, ensure miscTags defaults to an empty
object and resourcesList is derived safely from tagging.resources?.artists
(falling back to []), and keep the rest of the logic (performerMap building,
normalizeForMatch, snippetQueue population) unchanged so the function never
operates on undefined fields.
🧹 Nitpick comments (1)
CustomApps/lyrics-plus/ProviderMusixmatch.js (1)

108-121: Consider safer parsing of fqid values.

The parseInt(fqid.split(":")[2]) pattern assumes a specific format. If the format is unexpected, this could return NaN or cause issues. The same pattern is repeated at line 119.

♻️ Suggested improvement
 const resolvedPerformers = c.performers.map((p) => {
 	let name = "Unknown";
 	if (p.type === "artist") {
 		const fqid = p.fqid;
-		const idFromFqid = fqid ? parseInt(fqid.split(":")[2]) : null;
+		const idFromFqid = fqid ? parseInt(fqid.split(":")[2], 10) : null;

 		const artist = resourcesList.find((r) => r.artist_id === idFromFqid);
 		if (artist) name = artist.artist_name;
 	} else if (miscTags[p.type]) {
 		name = miscTags[p.type];
 	}
 	return {
 		fqid: p.fqid,
-		artist_id: p.fqid ? parseInt(p.fqid.split(":")[2]) : null,
+		artist_id: p.fqid ? parseInt(p.fqid.split(":")[2], 10) : null,
 		name: name,
 	};
 });

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@CustomApps/lyrics-plus/ProviderMusixmatch.js`:
- Around line 110-135: resolvedPerformers currently includes entries with name
"Unknown" which end up in the UI; update the logic so only resolved performers
are kept by filtering out entries with name === "Unknown" (either filter the
resolvedPerformers array after the map or build it with a filter/flatMap) before
computing names and before returning the object so the returned performers array
and names.join(", ") contain only resolved performers; ensure the early return
(names.length === 0) stays intact and references
resolvedPerformers/names/snippet when constructing the returned object.

@rxri rxri changed the title feat(lyrics-plus): adding a performer tag for the Musixmatch provider feat(lyrics-plus): add performer tag for the Musixmatch provider Feb 12, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
CustomApps/lyrics-plus/Pages.js (2)

211-227: Performer rendering logic is correct; consider extracting the repeated IIFE.

The null-safety on previousLine is properly handled, and the synced-compact conditional deduplication is sound. However, this IIFE block is duplicated nearly identically across SyncedLyricsPage, SyncedExpandedLyricsPage, and UnsyncedLyricsPage. Extracting it into a shared helper (e.g., renderPerformerLabel(performer, previousPerformer, options)) would reduce duplication and simplify future changes.

♻️ Suggested helper extraction
+const renderPerformerLabel = (performer, previousPerformer, { deduplicate = true } = {}) => {
+	if (!CONFIG.visual["show-performers"] || !performer) return null;
+	if (deduplicate && previousPerformer === performer) return null;
+	return react.createElement(
+		"span",
+		{ className: "lyrics-lyricsContainer-Performer" },
+		performer
+	);
+};

Then in each component, replace the IIFE with a one-liner, e.g. for SyncedLyricsPage:

renderPerformerLabel(
    performer,
    lyricWithEmptyLines[lineNumber - 1]?.performer,
    { deduplicate: !CONFIG.visual["synced-compact"] }
),

506-522: Remove the redundant synced-compact check from SyncedExpandedLyricsPage.

The synced-compact configuration controls which component renders at the top level (lines 1075, 1084 in index.js): when true, SyncedLyricsPage renders; when false, SyncedExpandedLyricsPage renders. This means the check at line 509 (if (!CONFIG.visual["synced-compact"])) always evaluates to true, making it unnecessary. Since SyncedExpandedLyricsPage is only instantiated when synced-compact is false, deduplication should always happen here. Simplify by removing the condition and unconditionally performing deduplication in this view.

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.

2 participants