fix: lyrics race condition, PC scroll, artwork matching, accidental artist/album taps
- nowplaying: add cancellation token to _loadNpLyrics to prevent stale async calls overwriting newer results (synced→plain race condition) - nowplaying: replace scrollIntoView with explicit container.scrollTo to prevent page-level scroll on desktop (topbar shifting / gap at edge) - backend: remove results[0] fallback in lookupItunesTrackMeta — only use exact title+artist match to avoid embedding wrong artwork - components/views: makeSongItem accepts noArtistLink/noAlbumLink opts; album-detail disables both, playlist-detail disables artist link Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
14e8226d1d
commit
9da48b88d8
4 files changed
+27
-9
No files matched your search
+14
-1
@@ -80,6 +80,7 @@ export function switchNpTab(tab) {
|
||||
// ─── LYRICS ───
|
||||
let _lyricsLines = [];
|
||||
let _lyricsSongId = null;
|
||||
let _lyricsLoadToken = 0;
|
||||
|
||||
const LYRICS_LOOKAHEAD_MS = 200;
|
||||
|
||||
@@ -125,11 +126,14 @@ async function _loadNpLyrics(songId) {
|
||||
_lyricsLines = [];
|
||||
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 s = S.queue[S.qIdx];
|
||||
const { getLyricsBySongId } = await import('./api.js');
|
||||
if(token !== _lyricsLoadToken) return;
|
||||
|
||||
// 1. Try Navidrome structured lyrics
|
||||
const lyrics = await getLyricsBySongId(songId);
|
||||
if(token !== _lyricsLoadToken) return;
|
||||
const track = lyrics[0];
|
||||
if(track?.synced && track.line?.length) {
|
||||
content.innerHTML = '';
|
||||
@@ -149,6 +153,7 @@ async function _loadNpLyrics(songId) {
|
||||
...(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();
|
||||
@@ -158,6 +163,7 @@ async function _loadNpLyrics(songId) {
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
if(token !== _lyricsLoadToken) return;
|
||||
|
||||
content.innerHTML = '';
|
||||
if(lrcText) {
|
||||
@@ -194,7 +200,14 @@ export function updateLyricsHighlight(currentMs) {
|
||||
line.el.classList.toggle('active', i === activeIdx);
|
||||
line.el.classList.toggle('past', i < activeIdx);
|
||||
});
|
||||
if(activeIdx >= 0) _lyricsLines[activeIdx].el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
if(activeIdx >= 0) {
|
||||
const activeEl = _lyricsLines[activeIdx].el;
|
||||
const container = document.getElementById('np-lyrics-view');
|
||||
if(container) {
|
||||
const target = activeEl.offsetTop - container.clientHeight / 2 + activeEl.clientHeight / 2;
|
||||
container.scrollTo({ top: Math.max(0, target), behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function onNpSongChange(songId) {
|
||||
|
||||
Reference in new issue
Block a user