From 891b18540b5ad512a38dbafb84bb40fb9204216e Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Tue, 21 Apr 2026 15:38:50 -0700 Subject: [PATCH] Fix Spotify Playlist Discovery: Fix button, header readability, [object Object] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three separate issues reported on the Spotify Playlist Discovery modal: 1. Fix button fails with "Track data not found" openDiscoveryFixModal() branched on platform name to locate the discovery state but had no case for 'spotify_public'. Row rendering passes that platform value when the source is a Spotify public playlist, so state lookup failed and the toast fired. Added the spotify_public branch — state lives in youtubePlaylistStates alongside the other reused platforms. 2. Table header too transparent to read .youtube-discovery-modal .discovery-table th used rgba(255,255,255,0.1) as background (10% white) with white text, which lost contrast when the orange progress bar or varied row content scrolled underneath. Switched to near-solid dark rgba(17,17,20,0.96) with a brighter border-bottom and z-index:5 so the sticky header stacks cleanly above table content. 3. "[object Object]" in the matched-artist column for Wing-It tracks The Spotify Public Playlist row-transform joined result.spotify_data.artists directly with .join(', '). Wing-It stub metadata (built server-side by _build_discovery_wing_it_stub) returns artists as [{name: "..."}] — array of objects. .join() stringified each object to "[object Object]". Same pattern existed in three places in script.js; all now map objects to .name and filter empties before joining. Graceful fallback to "-" if the result is empty. No existing tests touched. Full suite stays at 263 passed. Ruff clean. --- webui/static/script.js | 26 +++++++++++++++++++++++--- webui/static/style.css | 7 ++++--- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/webui/static/script.js b/webui/static/script.js index e360ad0c..6bca2e39 100644 --- a/webui/static/script.js +++ b/webui/static/script.js @@ -19429,6 +19429,8 @@ function openDiscoveryFixModal(platform, identifier, trackIndex) { state = youtubePlaylistStates[identifier]; // Deezer uses YouTube state infrastructure } else if (platform === 'mirrored') { state = youtubePlaylistStates[identifier]; // Mirrored playlists use YouTube state infrastructure + } else if (platform === 'spotify_public') { + state = youtubePlaylistStates[identifier]; // Spotify public playlists use YouTube state infrastructure } // Support both camelCase and snake_case for discovery results @@ -27121,7 +27123,13 @@ async function openTidalDiscoveryModal(playlistId, playlistData) { status_class: isFound ? 'found' : 'not-found', spotify_track: result.spotify_data ? result.spotify_data.name : (result.spotify_track || '-'), spotify_artist: result.spotify_data && result.spotify_data.artists ? - (Array.isArray(result.spotify_data.artists) ? result.spotify_data.artists.join(', ') : result.spotify_data.artists) : (result.spotify_artist || '-'), + (Array.isArray(result.spotify_data.artists) + ? result.spotify_data.artists + .map(a => (typeof a === 'object' && a !== null) ? (a.name || '') : a) + .filter(Boolean) + .join(', ') || '-' + : result.spotify_data.artists) + : (result.spotify_artist || '-'), spotify_album: result.spotify_data ? (typeof result.spotify_data.album === 'object' ? result.spotify_data.album.name : result.spotify_data.album) : (result.spotify_album || '-'), spotify_data: result.spotify_data, // Pass through spotify_data spotify_id: result.spotify_id, // Pass through spotify_id @@ -28582,7 +28590,13 @@ async function openDeezerDiscoveryModal(playlistId, playlistData) { status_class: isFound ? 'found' : 'not-found', spotify_track: result.spotify_data ? result.spotify_data.name : (result.spotify_track || '-'), spotify_artist: result.spotify_data && result.spotify_data.artists ? - (Array.isArray(result.spotify_data.artists) ? result.spotify_data.artists.join(', ') : result.spotify_data.artists) : (result.spotify_artist || '-'), + (Array.isArray(result.spotify_data.artists) + ? result.spotify_data.artists + .map(a => (typeof a === 'object' && a !== null) ? (a.name || '') : a) + .filter(Boolean) + .join(', ') || '-' + : result.spotify_data.artists) + : (result.spotify_artist || '-'), spotify_album: result.spotify_data ? (typeof result.spotify_data.album === 'object' ? result.spotify_data.album.name : result.spotify_data.album) : (result.spotify_album || '-'), spotify_data: result.spotify_data, spotify_id: result.spotify_id, @@ -32461,7 +32475,13 @@ async function openSpotifyPublicDiscoveryModal(urlHash, playlistData) { status_class: isFound ? 'found' : 'not-found', spotify_track: result.spotify_data ? result.spotify_data.name : (result.spotify_track || '-'), spotify_artist: result.spotify_data && result.spotify_data.artists ? - (Array.isArray(result.spotify_data.artists) ? result.spotify_data.artists.join(', ') : result.spotify_data.artists) : (result.spotify_artist || '-'), + (Array.isArray(result.spotify_data.artists) + ? result.spotify_data.artists + .map(a => (typeof a === 'object' && a !== null) ? (a.name || '') : a) + .filter(Boolean) + .join(', ') || '-' + : result.spotify_data.artists) + : (result.spotify_artist || '-'), spotify_album: result.spotify_data ? (typeof result.spotify_data.album === 'object' ? result.spotify_data.album.name : result.spotify_data.album) : (result.spotify_album || '-'), spotify_data: result.spotify_data, spotify_id: result.spotify_id, diff --git a/webui/static/style.css b/webui/static/style.css index 4c2df1fa..07789621 100644 --- a/webui/static/style.css +++ b/webui/static/style.css @@ -14808,16 +14808,17 @@ body.helper-mode-active #dashboard-activity-feed:hover { } .youtube-discovery-modal .discovery-table th { - background: rgba(255, 255, 255, 0.1); + background: rgba(17, 17, 20, 0.96); color: #ffffff; font-weight: 600; padding: 12px 8px; text-align: left; - border-bottom: 1px solid rgba(255, 255, 255, 0.1); + border-top: 1px solid rgba(255, 255, 255, 0.08); + border-bottom: 1px solid rgba(255, 255, 255, 0.18); font-size: 14px; position: sticky; top: 0; - z-index: 1; + z-index: 5; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;