From 186671aa2e94496d79b0acb30723a3e8367c91c9 Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Sun, 15 Mar 2026 15:46:30 -0700 Subject: [PATCH] Add play button to repair findings & increase finding image sizes - Play button on acoustid_mismatch, acoustid_no_match, track_number_mismatch, fake_lossless, dead_file, orphan_file findings - Uses playLibraryTrack() for proper media player integration (track info, sidebar, album art) - Data attributes for safe escaping instead of inline onclick strings - Finding images increased from 56px to 150px with hover effects - Improved detail panel spacing and media card layout --- webui/static/script.js | 38 +++++++++++++++++++--- webui/static/style.css | 71 ++++++++++++++++++++++++++++++++---------- 2 files changed, 87 insertions(+), 22 deletions(-) diff --git a/webui/static/script.js b/webui/static/script.js index b0265281..d589a08d 100644 --- a/webui/static/script.js +++ b/webui/static/script.js @@ -50535,6 +50535,33 @@ function _formatFileSize(bytes) { return (bytes / 1048576).toFixed(1) + ' MB'; } +function _renderPlayButton(f) { + const d = f.details || {}; + const filePath = f.file_path || d.file_path || d.original_path; + if (!filePath) return ''; + const title = d.expected_title || d.title || d.file_title || d.matched_title || ''; + const artist = d.expected_artist || d.artist || d.artist_name || ''; + const album = d.album || d.album_title || ''; + const albumArt = d.album_thumb_url || ''; + return ``; +} + +function playFindingTrack(btn) { + const track = { + file_path: btn.dataset.path, + title: btn.dataset.title || 'Unknown Track', + id: btn.dataset.entityId || null + }; + const albumTitle = btn.dataset.album || ''; + const artistName = btn.dataset.artist || ''; + playLibraryTrack(track, albumTitle, artistName); +} + function _renderFindingMedia(d) { const albumUrl = d.album_thumb_url; const artistUrl = d.artist_thumb_url; @@ -50572,7 +50599,7 @@ function _renderFindingDetail(f) { if (d.title) rows.push(['Title', d.title]); if (d.track_id) rows.push(['Track ID', d.track_id]); if (d.original_path) rows.push(['Original Path', d.original_path, 'path']); - return media + _gridRows(rows); + return media + _gridRows(rows) + _renderPlayButton(f); case 'orphan_file': if (d.folder) rows.push(['Folder', d.folder, 'path']); @@ -50580,7 +50607,7 @@ function _renderFindingDetail(f) { if (d.file_size) rows.push(['File Size', _formatFileSize(d.file_size)]); if (d.modified) rows.push(['Last Modified', d.modified]); if (f.file_path) rows.push(['Full Path', f.file_path, 'path']); - return _gridRows(rows); + return _gridRows(rows) + _renderPlayButton(f); case 'acoustid_mismatch': { let html = media + '
'; @@ -50593,14 +50620,14 @@ function _renderFindingDetail(f) { rows.push(['AcoustID Title', d.acoustid_title || '-', 'highlight']); rows.push(['AcoustID Artist', d.acoustid_artist || '-', 'highlight']); if (f.file_path) rows.push(['File', f.file_path, 'path']); - return html + _gridRows(rows); + return html + _gridRows(rows) + _renderPlayButton(f); } case 'acoustid_no_match': if (d.expected_title) rows.push(['Expected Title', d.expected_title]); if (d.expected_artist) rows.push(['Expected Artist', d.expected_artist]); if (f.file_path) rows.push(['File', f.file_path, 'path']); - return media + _gridRows(rows); + return media + _gridRows(rows) + _renderPlayButton(f); case 'fake_lossless': { const cutoff = d.detected_cutoff_khz || 0; @@ -50628,7 +50655,7 @@ function _renderFindingDetail(f) { if (d.bitrate) rows.push(['Bitrate', `${d.bitrate} kbps`]); if (d.file_size) rows.push(['File Size', _formatFileSize(d.file_size)]); if (f.file_path) rows.push(['File', f.file_path, 'path']); - return flHtml + _gridRows(rows); + return flHtml + _gridRows(rows) + _renderPlayButton(f); } case 'duplicate_tracks': @@ -50736,6 +50763,7 @@ function _renderFindingDetail(f) { tnHtml += `
${d.changes.map(c => `
${_escFinding(c)}
`).join('')}
`; } + tnHtml += _renderPlayButton(f); return tnHtml; default: diff --git a/webui/static/style.css b/webui/static/style.css index a2fb0b55..e4d6ff8f 100644 --- a/webui/static/style.css +++ b/webui/static/style.css @@ -40547,10 +40547,10 @@ tr.tag-diff-same { border-top: 1px solid rgba(255, 255, 255, 0.04); } .repair-finding-detail-inner { - padding: 12px 14px 14px; + padding: 16px 18px 18px; display: flex; flex-direction: column; - gap: 10px; + gap: 12px; } /* Detail key-value rows */ @@ -40646,41 +40646,78 @@ tr.tag-diff-same { /* Finding media thumbnails (album art + artist image) */ .repair-finding-media { display: flex; - gap: 14px; - margin-bottom: 12px; - padding: 10px 12px; - background: rgba(255, 255, 255, 0.03); - border-radius: 10px; + gap: 18px; + margin-bottom: 14px; + padding: 14px 16px; + background: rgba(255, 255, 255, 0.025); + border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.06); + align-items: flex-start; } .repair-finding-media-card { display: flex; flex-direction: column; align-items: center; - gap: 6px; + gap: 8px; min-width: 0; } .repair-finding-media-img { - width: 56px; - height: 56px; - border-radius: 8px; + width: 150px; + height: 150px; + border-radius: 10px; object-fit: cover; - border: 1px solid rgba(255, 255, 255, 0.12); + border: 1px solid rgba(255, 255, 255, 0.1); flex-shrink: 0; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4); + transition: transform 0.2s ease, box-shadow 0.2s ease; +} +.repair-finding-media-img:hover { + transform: scale(1.03); + box-shadow: 0 6px 24px rgba(0, 0, 0, 0.5); } .repair-finding-media-img.artist { border-radius: 50%; } .repair-finding-media-label { - font-size: 10px; - color: rgba(255, 255, 255, 0.45); + font-size: 11px; + color: rgba(255, 255, 255, 0.5); text-align: center; - max-width: 72px; + max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - line-height: 1.2; + line-height: 1.3; + font-weight: 500; +} + +/* Play button for findings */ +.repair-finding-play-btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 14px; + background: rgba(var(--accent-rgb, 99, 102, 241), 0.12); + border: 1px solid rgba(var(--accent-rgb, 99, 102, 241), 0.25); + border-radius: 8px; + color: rgb(var(--accent-light-rgb, 129, 140, 248)); + font-size: 12px; + font-weight: 500; + cursor: pointer; + transition: all 0.2s ease; + margin-top: 4px; +} +.repair-finding-play-btn:hover { + background: rgba(var(--accent-rgb, 99, 102, 241), 0.2); + border-color: rgba(var(--accent-rgb, 99, 102, 241), 0.4); + transform: translateY(-1px); +} +.repair-finding-play-btn:active { + transform: translateY(0); +} +.repair-finding-play-btn svg { + width: 14px; + height: 14px; + fill: currentColor; } /* Artwork preview (for missing cover art) */