From 1b7c05f66dd53aa6cdd8fd4d61cc2c9937f05972 Mon Sep 17 00:00:00 2001 From: Honey Date: Mon, 18 May 2026 22:16:15 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20notification=20'=C3=96ffnen'=20routes?= =?UTF-8?q?=20to=20album=20for=20track/album=20downloads?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Single track and album downloads now navigate to the album page - Artist downloads continue to navigate to the artist page - Button label changes to 'Zum Album →' or 'Zum Künstler →' accordingly - album name stored in job metadata so it survives until poll completes - Search uses exact album+artist match, falls back to album-name-only Co-Authored-By: Claude Sonnet 4.6 --- src/ui.js | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/src/ui.js b/src/ui.js index 4a9833c..3f433d6 100644 --- a/src/ui.js +++ b/src/ui.js @@ -298,7 +298,7 @@ export async function executeDownload() { discTotal: S.downloadTarget.discCount || undefined, durationMs: S.downloadTarget.trackTimeMillis || undefined, }; - addJob(jobId, payload.title, payload.artist, payload.type); + addJob(jobId, payload.title, payload.artist, payload.type, payload.album); try { const res = await fetch(`${S.cfg.backendUrl}/api/download`, { method: 'POST', headers: {'Content-Type':'application/json'}, @@ -489,9 +489,12 @@ function _renderNotifList() { const missingHtml = n.missing?.length ? `
${n.missing.length} Track${n.missing.length > 1 ? 's' : ''} nicht in Navidrome gefunden
    ${n.missing.map(f => `
  • ${esc(f)}
  • `).join('')}
` : ''; - const hasNav = n.status === 'done' && ((n.navType === 'playlist' && n.navId) || n.navArtist); + const hasNav = n.status === 'done' && ((n.navType === 'playlist' && n.navId) || n.navArtist || n.navAlbum); + const navLabel = n.navType === 'artist' ? 'Zum Künstler →' + : (n.navType === 'track' || n.navType === 'album') ? 'Zum Album →' + : 'Öffnen →'; const navBtn = hasNav - ? `` + ? `` : ''; return `
@@ -525,9 +528,31 @@ export async function _notifNavigate(notifId) { return; } + const norm = s => s?.toLowerCase().trim() || ''; + + // Track or album download → find album in Navidrome and navigate there. + if (n.navType === 'track' || n.navType === 'album') { + const albumQuery = n.navAlbum || n.navTitle; + if (albumQuery) { + try { + const r = await api('search3', `&query=${encodeURIComponent(albumQuery)}&artistCount=0&albumCount=10&songCount=0`); + const albums = r.searchResult3?.album || []; + // Prefer exact album + artist match, fall back to album-name-only match. + const match = albums.find(a => norm(a.name) === norm(albumQuery) && norm(a.artist) === norm(n.navArtist)) + || albums.find(a => norm(a.name) === norm(albumQuery)) + || albums[0]; + if (match) { + navigate('album-detail', { id: match.id, name: match.name, artist: match.artist || '', artistId: match.artistId || '', coverArt: match.coverArt || '' }); + return; + } + } catch {} + } + toast('Album nicht gefunden — Library-Scan nötig?'); + return; + } + if (n.navArtist) { try { - const norm = s => s?.toLowerCase().trim() || ''; const r = await api('search3', `&query=${encodeURIComponent(n.navArtist)}&artistCount=5&albumCount=0&songCount=0`); const artists = r.searchResult3?.artist || []; const match = artists.find(a => norm(a.name) === norm(n.navArtist)) || artists[0]; @@ -543,8 +568,8 @@ function _parseFailedTracks(log) { } // ─── JOBS ─── -function addJob(id, title, artist, type) { - S.jobs[id] = {title, artist, type: type || '', progress:0, status:'pending'}; +function addJob(id, title, artist, type, album) { + S.jobs[id] = {title, artist, type: type || '', album: album || '', progress:0, status:'pending'}; renderJobs(); } function updateJob(id, status, msg, progress) { @@ -603,6 +628,7 @@ async function pollJob(jobId) { read: false, navType: job?.type || '', navArtist: job?.artist || '', + navAlbum: job?.album || '', navTitle: title, }); if (d.status === 'done') {