fix: restore LRCLIB fallback for synced lyrics; fix np-drag-handle null crash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
654cde6796
commit
e4cc93d362
1 file changed
+46
-4
+46
-4
@@ -41,7 +41,7 @@ export function openNowPlaying() {
|
|||||||
}, {passive:true});
|
}, {passive:true});
|
||||||
overlay.addEventListener('touchmove', e => onMove(e.touches[0].clientY), {passive:true});
|
overlay.addEventListener('touchmove', e => onMove(e.touches[0].clientY), {passive:true});
|
||||||
overlay.addEventListener('touchend', e => onEnd(e.changedTouches[0].clientY), {passive:true});
|
overlay.addEventListener('touchend', e => onEnd(e.changedTouches[0].clientY), {passive:true});
|
||||||
handle.addEventListener('mousedown', e => { onStart(e.clientY); e.preventDefault(); });
|
handle?.addEventListener('mousedown', e => { onStart(e.clientY); e.preventDefault(); });
|
||||||
document.addEventListener('mousemove', e => onMove(e.clientY));
|
document.addEventListener('mousemove', e => onMove(e.clientY));
|
||||||
document.addEventListener('mouseup', e => onEnd(e.clientY));
|
document.addEventListener('mouseup', e => onEnd(e.clientY));
|
||||||
}
|
}
|
||||||
@@ -114,6 +114,19 @@ function _renderSyncedLines(content, lines) {
|
|||||||
updateLyricsHighlight(S.audio.currentTime * 1000);
|
updateLyricsHighlight(S.audio.currentTime * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _parseLrc(text) {
|
||||||
|
let offsetMs = 0;
|
||||||
|
const lines = [];
|
||||||
|
for(const raw of text.split('\n')) {
|
||||||
|
const offM = raw.match(/^\[offset:\s*([+-]?\d+)\s*\]/i);
|
||||||
|
if(offM) { offsetMs = parseInt(offM[1]); continue; }
|
||||||
|
const m = raw.match(/^\[(\d+):(\d+(?:\.\d+)?)\](.*)/);
|
||||||
|
if(m) lines.push({ start: Math.round((parseInt(m[1])*60 + parseFloat(m[2]))*1000), value: m[3].trim() });
|
||||||
|
}
|
||||||
|
if(offsetMs) lines.forEach(l => l.start = Math.max(0, l.start + offsetMs));
|
||||||
|
return lines.sort((a,b) => a.start - b.start).filter(l => l.value);
|
||||||
|
}
|
||||||
|
|
||||||
async function _loadNpLyrics(songId) {
|
async function _loadNpLyrics(songId) {
|
||||||
const content = document.getElementById('np-lyrics-content');
|
const content = document.getElementById('np-lyrics-content');
|
||||||
if(!content) return;
|
if(!content) return;
|
||||||
@@ -126,20 +139,49 @@ async function _loadNpLyrics(songId) {
|
|||||||
content.innerHTML = '<div style="text-align:center;padding:60px 0;color:rgba(255,255,255,.3);font-size:14px">Lade Lyrics…</div>';
|
content.innerHTML = '<div style="text-align:center;padding:60px 0;color:rgba(255,255,255,.3);font-size:14px">Lade Lyrics…</div>';
|
||||||
|
|
||||||
const token = ++_lyricsLoadToken;
|
const token = ++_lyricsLoadToken;
|
||||||
|
const s = S.queue[S.qIdx];
|
||||||
const { getLyricsBySongId } = await import('./api.js');
|
const { getLyricsBySongId } = await import('./api.js');
|
||||||
if(token !== _lyricsLoadToken) return;
|
if(token !== _lyricsLoadToken) return;
|
||||||
|
|
||||||
|
// 1. Navidrome (plugin handles fetching + caching server-side)
|
||||||
const lyrics = await getLyricsBySongId(songId);
|
const lyrics = await getLyricsBySongId(songId);
|
||||||
if(token !== _lyricsLoadToken) return;
|
if(token !== _lyricsLoadToken) return;
|
||||||
const track = lyrics[0];
|
const track = lyrics[0];
|
||||||
|
|
||||||
content.innerHTML = '';
|
|
||||||
if(track?.synced && track.line?.length) {
|
if(track?.synced && track.line?.length) {
|
||||||
|
content.innerHTML = '';
|
||||||
_renderSyncedLines(content, track.line.map(l => ({ start: l.start, value: l.value || '' })).filter(l => l.value));
|
_renderSyncedLines(content, track.line.map(l => ({ start: l.start, value: l.value || '' })).filter(l => l.value));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const plain = track?.line?.length ? track.line.map(l => l.value).join('\n') : null;
|
// 2. Fallback: LRCLIB directly (synced LRC format)
|
||||||
|
const firstArtist = (s?.artist||'').split(/[/,]/)[0].trim();
|
||||||
|
let lrcText = null;
|
||||||
|
let lrclibPlain = null;
|
||||||
|
try {
|
||||||
|
const attempts = [
|
||||||
|
new URLSearchParams({ artist_name: firstArtist, track_name: s?.title||'', ...(s?.album ? { album_name: s.album } : {}) }),
|
||||||
|
...(s?.album ? [new URLSearchParams({ artist_name: firstArtist, track_name: s?.title||'' })] : []),
|
||||||
|
];
|
||||||
|
for(const p of attempts) {
|
||||||
|
if(token !== _lyricsLoadToken) return;
|
||||||
|
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 {}
|
||||||
|
if(token !== _lyricsLoadToken) return;
|
||||||
|
|
||||||
|
content.innerHTML = '';
|
||||||
|
if(lrcText) {
|
||||||
|
const parsed = _parseLrc(lrcText);
|
||||||
|
if(parsed.length) { _renderSyncedLines(content, parsed); return; }
|
||||||
|
}
|
||||||
|
|
||||||
|
const plain = lrclibPlain || (track?.line?.length ? track.line.map(l => l.value).join('\n') : null);
|
||||||
if(plain) {
|
if(plain) {
|
||||||
const el = document.createElement('div');
|
const el = document.createElement('div');
|
||||||
el.className = 'lyrics-plain';
|
el.className = 'lyrics-plain';
|
||||||
|
|||||||
Reference in new issue
Block a user