fix(ui): playlist search with artist+title, artist in missing list, no bounce on job cards
- Playlist track search: use 'Artist Title' query + songCount=10 (was title-only + 5) fixes songs like 'Dead' by Geoxor being missed due to common-word collision - Missing tracks now show 'Artist – Title' instead of just title - renderJobs: update existing cards instead of full innerHTML wipe every 1.5s eliminates slideUp animation re-triggering on every poll tick Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
4b6095f213
commit
cf8ba70af8
1 file changed
+22
-8
@@ -355,16 +355,19 @@ async function _createNdPlaylist(tracks, name) {
|
||||
const norm = s => s?.toLowerCase().trim() || '';
|
||||
for (const t of tracks) {
|
||||
if (!t.trackName) continue;
|
||||
const missingLabel = t.artistName ? `${t.artistName} – ${t.trackName}` : t.trackName;
|
||||
try {
|
||||
const r = await api('search3', `&query=${encodeURIComponent(t.trackName)}&songCount=5&albumCount=0&artistCount=0`);
|
||||
// Search with artist+title first for precision, fall back to title-only with more results
|
||||
const artistQuery = t.artistName ? `${t.artistName} ${t.trackName}` : t.trackName;
|
||||
const r = await api('search3', `&query=${encodeURIComponent(artistQuery)}&songCount=10&albumCount=0&artistCount=0`);
|
||||
const songs = r.searchResult3?.song || [];
|
||||
const match = songs.find(s =>
|
||||
norm(s.title) === norm(t.trackName) &&
|
||||
norm(s.artist).split(' ')[0] === norm(t.artistName).split(' ')[0]
|
||||
) || songs.find(s => norm(s.title) === norm(t.trackName));
|
||||
if (match) songIds.push(match.id);
|
||||
else missing.push(t.trackName);
|
||||
} catch { missing.push(t.trackName); }
|
||||
else missing.push(missingLabel);
|
||||
} catch { missing.push(missingLabel); }
|
||||
}
|
||||
if (!songIds.length) {
|
||||
toast('⚠ Keine Tracks in Navidrome gefunden — zuerst Library scannen, dann Playlist manuell anlegen');
|
||||
@@ -521,10 +524,22 @@ function updateJob(id, status, msg, progress) {
|
||||
}
|
||||
function renderJobs() {
|
||||
const panel = document.getElementById('jobs-panel');
|
||||
panel.innerHTML = '';
|
||||
Object.entries(S.jobs).forEach(([id,j]) => {
|
||||
const c = document.createElement('div'); c.className = 'job-card';
|
||||
const isDone=j.status==='done', isErr=j.status==='error';
|
||||
const activeIds = new Set(Object.keys(S.jobs));
|
||||
|
||||
// Remove cards whose jobs are gone
|
||||
panel.querySelectorAll('[data-job-id]').forEach(el => {
|
||||
if (!activeIds.has(el.dataset.jobId)) el.remove();
|
||||
});
|
||||
|
||||
Object.entries(S.jobs).forEach(([id, j]) => {
|
||||
let c = panel.querySelector(`[data-job-id="${id}"]`);
|
||||
if (!c) {
|
||||
c = document.createElement('div');
|
||||
c.className = 'job-card';
|
||||
c.dataset.jobId = id;
|
||||
panel.appendChild(c);
|
||||
}
|
||||
const isDone = j.status === 'done', isErr = j.status === 'error';
|
||||
const statusText = isErr ? 'Fehler' : isDone ? '✓ Fertig! Bibliothek wird gescannt…' : j.msg || 'Wird heruntergeladen…';
|
||||
c.innerHTML = `<div class="job-header">
|
||||
<div class="job-title">${esc(j.title)}</div>
|
||||
@@ -532,7 +547,6 @@ function renderJobs() {
|
||||
</div>
|
||||
<div class="job-bar"><div class="job-fill ${isDone?'done':isErr?'error':''}" style="width:${isDone?100:isErr?100:j.progress}%"></div></div>
|
||||
<div class="job-sub">${statusText}</div>`;
|
||||
panel.appendChild(c);
|
||||
});
|
||||
}
|
||||
async function pollJob(jobId) {
|
||||
|
||||
Reference in new issue
Block a user