fix(lyrics): improve LRCLIB lookup reliability

- Retry LRCLIB without album_name when exact match fails (404) —
  album name mismatch was the most common cause of falling back to plain
- Capture plainLyrics from LRCLIB and prefer it over Navidrome plain
  (LRCLIB plain text is usually higher quality)
- Use only first artist for LRCLIB lookup (multi-artist strings like
  'Artist A / Artist B' cause 404)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Sonnet 4.6 committed 2026-04-27 19:27:19 +02:00
1 parent 16b7faee2b
commit ec681b9057
1 file changed
+21 -9
+21 -9
View File
@@ -137,13 +137,26 @@ async function _loadNpLyrics(songId) {
return; return;
} }
// 2. Fallback: fetch synced lyrics from LRCLIB // 2. Fallback: fetch lyrics from LRCLIB
// Use only first artist (multi-artist strings like "A / B" break lookup)
const firstArtist = (s?.artist||'').split(/[/,]/)[0].trim();
let lrcText = null; let lrcText = null;
let lrclibPlain = null;
try { try {
const p = new URLSearchParams({ artist_name: s?.artist||'', track_name: s?.title||'' }); // Try with album first, then without (album mismatch causes 404)
if(s?.album) p.set('album_name', s.album); const attempts = [
const r = await fetch(`https://lrclib.net/api/get?${p}`, { signal: AbortSignal.timeout(8000) }); new URLSearchParams({ artist_name: firstArtist, track_name: s?.title||'', ...(s?.album ? { album_name: s.album } : {}) }),
if(r.ok) { const d = await r.json(); lrcText = d.syncedLyrics || null; } ...(s?.album ? [new URLSearchParams({ artist_name: firstArtist, track_name: s?.title||'' })] : []),
];
for(const p of attempts) {
const r = await fetch(`https://lrclib.net/api/get?${p}`, { signal: AbortSignal.timeout(6000) });
if(r.ok) {
const d = await r.json();
lrcText = d.syncedLyrics || null;
lrclibPlain = d.plainLyrics || null;
break;
}
}
} catch {} } catch {}
content.innerHTML = ''; content.innerHTML = '';
@@ -152,10 +165,9 @@ async function _loadNpLyrics(songId) {
if(parsed.length) { _renderSyncedLines(content, parsed); return; } if(parsed.length) { _renderSyncedLines(content, parsed); return; }
} }
// 3. Plain text fallback // 3. Plain text — prefer LRCLIB plain over Navidrome plain (usually better quality)
const plain = track?.line?.length const plain = lrclibPlain
? track.line.map(l => l.value).join('\n') || (track?.line?.length ? track.line.map(l => l.value).join('\n') : null);
: null;
if(plain) { if(plain) {
const el = document.createElement('div'); const el = document.createElement('div');
el.className = 'lyrics-plain'; el.className = 'lyrics-plain';