diff --git a/backend/novasonic-server.js b/backend/novasonic-server.js
index 7aa104e..b068b51 100644
--- a/backend/novasonic-server.js
+++ b/backend/novasonic-server.js
@@ -145,7 +145,7 @@ async function lookupItunesTrackMeta(artist, title) {
const match = results.find(t =>
norm(t.trackName) === norm(title) &&
norm(t.artistName).split(' ')[0] === norm(artist).split(' ')[0]
- ) || results[0];
+ );
if (!match) return {};
return {
album: match.collectionName || '',
diff --git a/src/components.js b/src/components.js
index abf0e5f..7396a1c 100644
--- a/src/components.js
+++ b/src/components.js
@@ -27,19 +27,24 @@ export function makeAlbumCard(a) {
}
// ─── SONG LIST ITEM (library) ───
-export function makeSongItem(s, idx, list) {
+export function makeSongItem(s, idx, list, { noArtistLink = false, noAlbumLink = false } = {}) {
const el = document.createElement('div');
if(s.starred && !s._starred) s._starred = true;
el.className = 'song-item' + (S.queue[S.qIdx]?.id === s.id ? ' playing' : '');
const cv = coverUrl(s.coverArt, 60);
+ const artistHtml = s.artistId||s.artist
+ ? (noArtistLink
+ ? `${esc(s.artist||'')}`
+ : `${esc(s.artist||'')}`)
+ : '';
+ const albumHtml = !noAlbumLink && (s.albumId||s.album)
+ ? ` · ${esc(s.album||'')}`
+ : '';
el.innerHTML = `
${idx+1}
${cv ? `

` : '
♪
'}
${esc(s.title)}
-
- ${s.artistId||s.artist ? `${esc(s.artist||'')}` : ''}
- ${s.albumId||s.album ? ` · ${esc(s.album||'')}` : ''}
-
+
${artistHtml}${albumHtml}
${formatDur(s.duration)}
diff --git a/src/nowplaying.js b/src/nowplaying.js
index fc37bdc..300b59a 100644
--- a/src/nowplaying.js
+++ b/src/nowplaying.js
@@ -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 = '
Lade Lyrics…
';
+ 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) {
diff --git a/src/views.js b/src/views.js
index a4bf0df..e19ea53 100644
--- a/src/views.js
+++ b/src/views.js
@@ -440,7 +440,7 @@ export async function loadAlbumDetail(album) {
toast(`⬇ ${songs.length} Titel Download gestartet`);
};
sl.innerHTML = '';
- songs.forEach((s,i) => sl.appendChild(makeSongItem(s, i, songs)));
+ songs.forEach((s,i) => sl.appendChild(makeSongItem(s, i, songs, { noArtistLink: true, noAlbumLink: true })));
} catch(e) { sl.innerHTML = `
${e.message}
`; }
}
@@ -492,7 +492,7 @@ export async function loadPlaylistDetail(pl) {
playQueue(shuffled, 0);
};
sl.innerHTML = '';
- songs.forEach((s,i) => sl.appendChild(makeSongItem(s, i, songs)));
+ songs.forEach((s,i) => sl.appendChild(makeSongItem(s, i, songs, { noArtistLink: true })));
} catch(e) { sl.innerHTML = `
${e.message}
`; }
}