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) */