Skip to content

Conversation

@ianz56
Copy link
Contributor

@ianz56 ianz56 commented Oct 21, 2025

Added dynamic language selection for Musixmatch translations. The dropdown now reflects available languages per track.

image image

Summary by CodeRabbit

  • New Features

    • Multi-language Musixmatch translations: dynamic language options in the translation menu, per-track language selection, and persistent translation-source selection.
    • Automatic fetching, caching and line-by-line mapping of Musixmatch translations with language-aware refresh when track or language changes.
  • Bug Fixes

    • UI and stored translation preference stay synchronized when changing Musixmatch language.
    • Translations reliably update or clear when a language or track becomes unavailable.

@coderabbitai
Copy link

coderabbitai bot commented Oct 21, 2025

Walkthrough

Adds multi-language Musixmatch translation support: extracts translation status from API responses, fetches translations by trackId, exposes available translations to the container, dynamically builds Musixmatch translation UI options, synchronizes selected language with CONFIG/localStorage, and adds container-level refresh/caching to apply translations per track and language.

Changes

Cohort / File(s) Summary
Provider & API changes
CustomApps/lyrics-plus/ProviderMusixmatch.js,
CustomApps/lyrics-plus/Providers.js
Recursively extract track_lyrics_translation_status; add query param; augment responses with __musixmatchTranslationStatus and __musixmatchTrackId; change getTranslation to accept trackId and return translations; expose musixmatchAvailableTranslations, musixmatchTrackId, and musixmatchTranslationLanguage; build/apply translation maps when translations exist.
UI: Options & Settings
CustomApps/lyrics-plus/OptionsMenu.js,
CustomApps/lyrics-plus/Settings.js
Add getMusixmatchTranslationPrefix() and expand TranslationMenu props (musixmatchLanguages, musixmatchSelectedLanguage); dynamically construct Musixmatch-prefixed translation options and labels; persist/sync selected Musixmatch language with CONFIG and localStorage; centralize change handling to invoke lyricContainerUpdate() and update Musixmatch selection when translation source changes.
Orchestration / Container
CustomApps/lyrics-plus/index.js
Add translation-prefix handling and persistence; introduce resolveTranslationSource(); add state fields and currentMusixmatchLanguage; implement refreshMusixmatchTranslation() to fetch/cache/apply translations per track and language; wire refresh/reset on track, language, and config changes; pass Musixmatch props to TranslationMenu and integrate translation overrides into caching/state restoration.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant OptionsMenu
    participant Settings
    participant LyricsContainer
    participant Providers
    participant ProviderMusixmatch
    participant MusixmatchAPI as API

    User->>OptionsMenu: select Musixmatch language
    OptionsMenu->>Settings: save translate:translated-lyrics-source + musixmatch-translation-language
    Settings->>LyricsContainer: lyricContainerUpdate()

    LyricsContainer->>LyricsContainer: resolveTranslationSource()
    LyricsContainer->>LyricsContainer: refreshMusixmatchTranslation()

    LyricsContainer->>Providers: request lyrics (include translation metadata)
    Providers->>ProviderMusixmatch: forward request (adds track_lyrics_translation_status)
    ProviderMusixmatch->>MusixmatchAPI: fetch lyrics + status
    MusixmatchAPI-->>ProviderMusixmatch: lyrics with status
    ProviderMusixmatch-->>Providers: return augmented body (__musixmatchTranslationStatus, __musixmatchTrackId)
    Providers-->>LyricsContainer: provider result (with available translations)

    alt selected language available
        LyricsContainer->>ProviderMusixmatch: getTranslation(trackId)
        ProviderMusixmatch->>MusixmatchAPI: fetch translation for trackId/language
        MusixmatchAPI-->>ProviderMusixmatch: translation payload
        ProviderMusixmatch-->>LyricsContainer: translation array
        LyricsContainer->>LyricsContainer: apply translation mapping to lyrics
    else unavailable
        LyricsContainer->>LyricsContainer: clear or skip translation
    end

    LyricsContainer->>User: render lyrics (with/without translation)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Focus review on refreshMusixmatchTranslation() for caching, concurrency, and edge-case clears.
  • Verify recursive extraction (findTranslationStatus) and correct augmentation of response bodies.
  • Check OptionsMenu memoization deps, option construction, and CONFIG/localStorage synchronization.
  • Confirm getTranslation(trackId) signature change is propagated and provider result fields are consumed correctly.

Poem

🐰
I hopped through prefixes, tiny and spry,
Found track IDs and languages nearby,
I stitched translations, one nibble at a time,
Cached each chorus, made the UI chime,
Now lyrics hop in tongues sublime.

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main enhancement: adding dynamic language selection and translation status handling for Musixmatch integration, which is the primary focus of all file changes.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ad97b76 and 46b0893.

📒 Files selected for processing (1)
  • CustomApps/lyrics-plus/index.js (14 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • CustomApps/lyrics-plus/index.js

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
CustomApps/lyrics-plus/ProviderMusixmatch.js (1)

205-238: Handle network errors in getTranslation to avoid unhandled rejections

CosmosAsync.get can throw; without try/catch, refresh paths may break. Return null on errors.

-  async function getTranslation(trackId) {
+  async function getTranslation(trackId) {
     if (!trackId) return null;
@@
-    let result = await Spicetify.CosmosAsync.get(finalURL, null, headers);
+    let result;
+    try {
+      result = await Spicetify.CosmosAsync.get(finalURL, null, headers);
+    } catch (e) {
+      console.error("Musixmatch translation request failed", e);
+      return null;
+    }
🧹 Nitpick comments (3)
CustomApps/lyrics-plus/OptionsMenu.js (1)

121-135: Avoid duplicate Musixmatch language options

After pushing the active language, ensure uniqueness to prevent duplicate menu entries.

-    const availableMusixmatchLanguages = Array.isArray(musixmatchLanguages) ? [...new Set(musixmatchLanguages.filter(Boolean))] : [];
-    const activeMusixmatchLanguage =
+    let availableMusixmatchLanguages = Array.isArray(musixmatchLanguages) ? [...new Set(musixmatchLanguages.filter(Boolean))] : [];
+    const activeMusixmatchLanguage =
       musixmatchSelectedLanguage && musixmatchSelectedLanguage !== "none" ? musixmatchSelectedLanguage : null;
     if (hasTranslation.musixmatch && activeMusixmatchLanguage) {
-      availableMusixmatchLanguages.push(activeMusixmatchLanguage);
+      availableMusixmatchLanguages = [...new Set([...availableMusixmatchLanguages, activeMusixmatchLanguage])];
     }
CustomApps/lyrics-plus/index.js (1)

297-379: Harden refreshMusixmatchTranslation against fetch errors and unmounts

Great race checks and cache updates. Add a try/catch around the translation fetch; optionally guard setState after unmount.

-    const translation = await ProviderMusixmatch.getTranslation(trackId);
+    let translation = null;
+    try {
+      translation = await ProviderMusixmatch.getTranslation(trackId);
+    } catch (e) {
+      console.error("refreshMusixmatchTranslation failed", e);
+      translation = null;
+    }

Optional (nice-to-have): track mount status to avoid setState after unmount.

 class LyricsContainer extends react.Component {
   constructor() {
     super();
+    this._mounted = false;
@@
   componentDidMount() {
+    this._mounted = true;
@@
   componentWillUnmount() {
+    this._mounted = false;
@@
   async refreshMusixmatchTranslation() {
     // ...
-    this.setState({
+    if (!this._mounted) return;
+    this.setState({
       musixmatchTranslation: null,
       musixmatchTranslationLanguage: null,
     });
@@
-    this.setState({
+    if (!this._mounted) return;
+    this.setState({
       musixmatchTranslation: mappedTranslation,
       musixmatchTranslationLanguage: currentLanguage,
     });
CustomApps/lyrics-plus/Settings.js (1)

700-709: Remove duplicate lyricContainerUpdate call; init order is safe via optional chaining

The duplicate call at lines 706 and 708 should be consolidated. Move lyricContainerUpdate?.(); outside the if-else block:

// Reload Lyrics if translation language is changed
if (name === "musixmatch-translation-language") {
	if (value === "none") {
		CONFIG.visual["translate:translated-lyrics-source"] = "none";
		localStorage.setItem(`${APP_NAME}:visual:translate:translated-lyrics-source`, "none");
	}
}
lyricContainerUpdate?.();

Init order is already safe: lyricContainerUpdate is assigned in componentDidMount() before user interactions can occur, and optional chaining here guards against edge cases. The refactoring is sound.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 15d1365 and be409c3.

📒 Files selected for processing (5)
  • CustomApps/lyrics-plus/OptionsMenu.js (6 hunks)
  • CustomApps/lyrics-plus/ProviderMusixmatch.js (5 hunks)
  • CustomApps/lyrics-plus/Providers.js (2 hunks)
  • CustomApps/lyrics-plus/Settings.js (1 hunks)
  • CustomApps/lyrics-plus/index.js (14 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
CustomApps/lyrics-plus/Providers.js (2)
CustomApps/lyrics-plus/index.js (1)
  • CONFIG (40-106)
CustomApps/lyrics-plus/ProviderMusixmatch.js (1)
  • ProviderMusixmatch (1-241)
CustomApps/lyrics-plus/ProviderMusixmatch.js (1)
CustomApps/lyrics-plus/Providers.js (6)
  • body (14-14)
  • result (3-10)
  • result (47-59)
  • result (108-116)
  • result (151-158)
  • result (207-213)
CustomApps/lyrics-plus/OptionsMenu.js (1)
CustomApps/lyrics-plus/index.js (4)
  • react (6-6)
  • react (7-7)
  • CONFIG (40-106)
  • APP_NAME (23-23)
CustomApps/lyrics-plus/index.js (2)
CustomApps/lyrics-plus/ProviderMusixmatch.js (1)
  • ProviderMusixmatch (1-241)
CustomApps/lyrics-plus/Utils.js (1)
  • Utils (1-338)
CustomApps/lyrics-plus/Settings.js (1)
CustomApps/lyrics-plus/index.js (1)
  • lyricContainerUpdate (158-158)
🔇 Additional comments (12)
CustomApps/lyrics-plus/Providers.js (1)

54-57: New Musixmatch fields on result look good

Adding musixmatchAvailableTranslations, musixmatchTrackId, and musixmatchTranslationLanguage makes downstream state handling straightforward. LGTM.

CustomApps/lyrics-plus/ProviderMusixmatch.js (3)

7-35: Recursive translation-status extraction looks good

Traversal handles objects/arrays and returns early on first match. Works for current macro response shapes.


52-53: Including part=track_lyrics_translation_status is correct

Ensures the macro response contains translation status; necessary for dynamic language options.


78-91: Non-enumerable augmentation for status and trackId is neat

Using Object.defineProperties to stash __musixmatchTranslationStatus and __musixmatchTrackId avoids polluting consumers’ iterations. LGTM.

CustomApps/lyrics-plus/OptionsMenu.js (2)

89-96: Prefix helper aligns with global prefix use

Fetching the prefix from window with a sane default decouples UI from internal constants. Good.


266-278: Syncing musixmatch-translation-language with source selection is correct

This keeps config/UI consistent when a Musixmatch-prefixed source is chosen or cleared.

CustomApps/lyrics-plus/index.js (6)

23-33: Globalizable translation prefix is well factored

Constants + window propagation enable cross-file access without tight coupling. LGTM.


125-142: Backward-compat upgrade path is solid

Migrates legacy "musixmatchTranslation" to the new prefixed format and syncs musixmatch-translation-language. Nice touch.


146-156: Empty state extended with Musixmatch fields

Matches downstream usage and avoids stale data when switching tracks. Looks good.


166-177: resolveTranslationSource keeps state normalized

Cleanly maps prefixed sources to a key+language pair, reducing special-casing elsewhere.


483-505: Good UX: reset selection when chosen language isn’t available

Auto-clearing prefixed source and language avoids stuck states when a track lacks that translation.


593-606: Source-language sync on render path is correct

Keeps CONFIG.visual in sync with resolved source; reduces drift between menu and runtime state.

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: 0

🧹 Nitpick comments (1)
CustomApps/lyrics-plus/Providers.js (1)

94-121: Excellent O(n) optimization addresses past review concerns!

The translation mapping now uses a Map for O(1) lookups instead of nested find(), resolving the O(n²) performance issue flagged in the previous review. The fallback normalization function appropriately handles cases where Utils is unavailable due to module loading order.

However, consider adding defensive checks for the translation entries:

Apply this diff to guard against malformed API responses:

 const translationMap = new Map();
 for (const entry of translation) {
+   if (!entry || typeof entry.matchedLine !== "string" || typeof entry.translation !== "string") {
+     continue;
+   }
   const normalizedMatched = normalizeLyrics(entry.matchedLine);
-   if (!translationMap.has(normalizedMatched)) {
+   if (normalizedMatched && !translationMap.has(normalizedMatched)) {
     translationMap.set(normalizedMatched, entry.translation);
   }
 }

This ensures:

  • Entries with missing or non-string properties are skipped
  • Empty normalized keys (from empty matchedLine) don't create Map entries
  • The mapping remains robust against unexpected API responses
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between be409c3 and a208710.

📒 Files selected for processing (1)
  • CustomApps/lyrics-plus/Providers.js (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
CustomApps/lyrics-plus/Providers.js (3)
CustomApps/lyrics-plus/Utils.js (2)
  • result (22-36)
  • Utils (1-338)
CustomApps/lyrics-plus/index.js (2)
  • CONFIG (40-106)
  • value (19-19)
CustomApps/lyrics-plus/ProviderMusixmatch.js (1)
  • ProviderMusixmatch (1-241)
🔇 Additional comments (3)
CustomApps/lyrics-plus/Providers.js (3)

54-56: LGTM! Clean field initialization.

The new fields for translation support are properly initialized with appropriate default values (empty array for translations, null for trackId and language).


87-88: LGTM! Robust metadata extraction.

The defensive checks ensure safe handling of the properties attached by ProviderMusixmatch.findLyrics, with appropriate fallbacks for missing or malformed data.


90-92: LGTM! Effective translation request gating.

The logic correctly validates that a translation language is configured and available before making the API request, preventing unnecessary calls.

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

🧹 Nitpick comments (3)
CustomApps/lyrics-plus/OptionsMenu.js (1)

89-95: Consider caching the prefix retrieval.

The function reads from window on every call. Since the prefix is set once globally and doesn't change, consider retrieving it once at module initialization rather than recalculating it each render.

Apply this diff to optimize:

+const MUSIXMATCH_TRANSLATION_PREFIX = 
+	typeof window !== "undefined" && typeof window.__lyricsPlusMusixmatchTranslationPrefix === "string"
+		? window.__lyricsPlusMusixmatchTranslationPrefix
+		: "musixmatchTranslation:";
+
-function getMusixmatchTranslationPrefix() {
-	if (typeof window !== "undefined" && typeof window.__lyricsPlusMusixmatchTranslationPrefix === "string") {
-		return window.__lyricsPlusMusixmatchTranslationPrefix;
-	}
-
-	return "musixmatchTranslation:";
-}

Then replace getMusixmatchTranslationPrefix() calls with the constant.

CustomApps/lyrics-plus/index.js (2)

337-340: Clarify base lyrics selection.

Line 337 uses nullish coalescing (??) to prefer synced over unsynced. The early return at line 339 is correct, but the comment could clarify why the function returns early rather than clearing translation when base lyrics are unavailable.

Add a comment:

 const baseLyrics = this.state.synced ?? this.state.unsynced;
 if (!baseLyrics) {
+	// No base lyrics available to translate; preserve existing state
 	return;
 }

509-530: Complex conditional logic—verify correctness.

Lines 510-515 determine shouldRefreshMusixmatchTranslation based on trackId, selected language, available translations, and cached state. Lines 516-529 reset the translation source to "none" if the selected language is unavailable.

This logic appears correct but is dense. Consider extracting helper functions for readability.

Example refactor:

const isMusixmatchLanguageAvailable = (language, availableTranslations) => {
	return language !== "none" && 
	       Array.isArray(availableTranslations) && 
	       availableTranslations.includes(language);
};

Then use it to simplify the conditionals.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cf3bea0 and 3703cdf.

📒 Files selected for processing (2)
  • CustomApps/lyrics-plus/OptionsMenu.js (6 hunks)
  • CustomApps/lyrics-plus/index.js (14 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
CustomApps/lyrics-plus/OptionsMenu.js (1)
CustomApps/lyrics-plus/index.js (4)
  • react (6-6)
  • react (7-7)
  • CONFIG (42-108)
  • APP_NAME (23-23)
🔇 Additional comments (19)
CustomApps/lyrics-plus/OptionsMenu.js (5)

97-98: LGTM!

The props extension and prefix retrieval setup correctly support dynamic Musixmatch language options.


170-223: LGTM!

The refactor from a direct return to constructing configItems enables dynamic dependency tracking. The expanded memoization array correctly includes all derived values that affect the menu structure.


235-235: Correct dependency addition.

Including items in the useEffect dependency array ensures the custom event fires whenever the menu structure changes, which is necessary for the dynamic Musixmatch options.


262-278: Synchronization logic looks correct.

The code extracts the language suffix from prefixed values, updates both CONFIG and localStorage, and maintains consistency. The fallback to "none" is appropriate.


121-135: No upstream duplicates—the review comment's concern is not substantiated.

The data flow shows that __musixmatchTranslationStatus is created in ProviderMusixmatch.js (line 80) with explicit deduplication: [...new Set(translationStatus.map(...).filter(Boolean))]. This already-deduplicated array is passed unchanged through Providers.js to OptionsMenu, which then deduplicates it again (line 122, redundantly but safely) before the conditional push at lines 125–126.

The review comment asks to confirm no callers pass duplicate-laden arrays. The script results confirm they don't—the immediate upstream source deduplicates at the origin. The redundant dedup in OptionsMenu.js is defensive programming, and the potential duplicate from the unconditional push at line 126 is harmless since the reduce at line 129 overwrites duplicate keys.

CustomApps/lyrics-plus/index.js (14)

24-35: LGTM!

Constants are well-defined with clear naming. The prefix resolution checks window safely and falls back to the default. Persisting the prefix to the global scope enables cross-module coordination.


127-154: Migration logic handles legacy state correctly.

The code upgrades old "musixmatchTranslation" values to prefixed format, synchronizes musixmatch-translation-language bidirectionally with the translation source, and enforces mutual exclusivity between translate and translation providers.


164-167: State fields appropriately extended.

Adding musixmatchAvailableTranslations, musixmatchTrackId, and musixmatchTranslationLanguage to emptyState ensures proper reset when transitioning between tracks.


177-188: Helper function correctly resolves translation sources.

The logic distinguishes prefixed Musixmatch sources from other translation keys and extracts the language code. The fallback for non-prefixed sources is appropriate.


209-212: Initialization looks correct.

The constructor properly initializes Musixmatch state fields and currentMusixmatchLanguage from CONFIG.

Also applies to: 247-247


386-395: Translation mapping logic is sound.

The code maps base lyrics to translated text by matching processed original text. Preserving originalText enables future re-translation. The fallback to line.text when no match is found prevents data loss.


575-597: State update callback correctly triggers refresh.

The callback at line 591 ensures currentMusixmatchLanguage is updated before invoking refreshMusixmatchTranslation. This ordering is critical for the refresh logic to use the correct language.


601-606: Callback ordering matches initial load.

Consistent with lines 591-596, this ensures proper sequencing when updating state after cache retrieval.


621-626: Translation source resolution integrated correctly.

Using resolveTranslationSource to extract the language and synchronize CONFIG is appropriate. This keeps the Musixmatch language in sync with the UI selection.


894-897: Language change detection triggers refresh appropriately.

Comparing currentMusixmatchLanguage with CONFIG detects user-driven changes and invokes the refresh. This is the correct place to trigger translation updates in response to UI interactions.


900-900: Binding ensures correct context.

Binding refreshMusixmatchTranslation to the instance and assigning it to the module-level variable enables external invocation while preserving this context.


955-955: Cleanup prevents stale references.

Nulling the module-level refreshMusixmatchTranslation on unmount prevents dangling references and potential memory leaks.


1032-1033: Updated hasTranslation logic includes Musixmatch correctly.

hasMusixmatchLanguages checks for available translations, and hasTranslation now considers Musixmatch alongside Netease. This ensures the translation button appears when Musixmatch languages are available.


1126-1127: Props passed correctly to TranslationMenu.

musixmatchLanguages and musixmatchSelectedLanguage are sourced from state and CONFIG, providing the UI with the necessary data to render dynamic options.

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

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

127-153: Migration logic runs on every load.

The CONFIG upgrade logic correctly handles migrating from the old "musixmatchTranslation" format to the new prefixed format, and synchronizes the musixmatch-translation-language setting. However, this migration runs on every app initialization rather than once.

Consider adding a version flag to localStorage to run this migration only once:

+const MIGRATION_VERSION = "1";
+const currentMigrationVersion = localStorage.getItem(`${APP_NAME}:migration-version`) || "0";
+
+if (currentMigrationVersion < MIGRATION_VERSION) {
   if (CONFIG.visual["translate:translated-lyrics-source"] === "musixmatchTranslation") {
     // ... migration logic ...
   }
   // ... other migrations ...
+  localStorage.setItem(`${APP_NAME}:migration-version`, MIGRATION_VERSION);
+}

602-633: LGTM - Proper async refresh after state updates.

The callbacks (lines 618-623, 629-632) correctly trigger Musixmatch translation refresh after React has committed the state changes.

The callback logic is duplicated between lines 618-623 and 629-632. Consider extracting to a helper:

const afterStateUpdate = () => {
  this.currentMusixmatchLanguage = CONFIG.visual["musixmatch-translation-language"];
  if (shouldRefreshMusixmatchTranslation) {
    this.refreshMusixmatchTranslation();
  }
};

this.setState({ ...tempState, ...translationOverrides }, afterStateUpdate);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3703cdf and ad97b76.

📒 Files selected for processing (1)
  • CustomApps/lyrics-plus/index.js (14 hunks)
🔇 Additional comments (11)
CustomApps/lyrics-plus/index.js (11)

24-35: LGTM - Global prefix persistence pattern.

The use of a window global to persist the Musixmatch translation prefix across module reloads is appropriate, and the defensive typeof checks prevent errors in non-browser environments.


164-167: LGTM - emptyState extended for Musixmatch fields.

The new fields properly initialize Musixmatch-related state and will be used for state resets.


171-171: Module-level binding follows existing pattern.

The refreshMusixmatchTranslation module-level variable follows the same pattern as lyricContainerUpdate and reloadLyrics (lines 169-170), enabling external references to instance methods.


177-188: LGTM - Clean abstraction for translation source resolution.

The function correctly parses Musixmatch translation sources with language prefixes and handles edge cases (non-string sources, unprefixed sources).


209-212: LGTM - Proper state initialization.

The Musixmatch-related state fields are correctly initialized, with musixmatchAvailableTranslations as an empty array and the request ID tracker for preventing race conditions.

Also applies to: 247-248


309-432: Race condition protection implemented.

The Symbol-based request tracking (lines 314-316) and isLatestRequest() guards (lines 395, 418) properly address the concurrent fetch concern from the previous review.


536-558: Translation refresh logic is sound.

The code correctly determines when Musixmatch translation needs refreshing (lines 537-542) and resets unavailable language selections to "none" (lines 543-556). The translationOverrides pattern (line 557) appropriately clears stale translation data before the async refresh.


920-927: LGTM - Proper handling of external config changes.

The language change detection (lines 921-924) correctly triggers translation refresh when settings are updated externally, and the module-level binding (line 927) enables coordinated cleanup.


982-982: LGTM - Proper cleanup of module-level reference.

Clearing refreshMusixmatchTranslation on unmount prevents memory leaks and follows the existing cleanup pattern.


1059-1060: LGTM - Correct translation availability detection.

The logic properly determines translation availability by checking for existing translations or available language options, enabling the translation menu when appropriate.


1153-1154: LGTM - Appropriate fallback for selected language.

The props correctly pass available languages and the selected language (with appropriate fallback to config when translation hasn't been fetched yet).

@ianz56 ianz56 requested a review from rxri November 11, 2025 13:07
@rxri
Copy link
Member

rxri commented Nov 11, 2025

Address coderabbit's actionable review

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