From df31d42b942517f67cafb2d335fe2375a048f23d Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Tue, 26 May 2026 14:25:01 -0700 Subject: [PATCH] Fix LB Sync tab card data shape + tone down styling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two bugs from the initial LB tab commit (a7053a60): 1. **All cards showed identical "ListenBrainz Playlist / 0 tracks" defaults.** The /api/discover/listenbrainz/* endpoints wrap each entry in JSPF shape — ``{playlist: {identifier, title, creator, annotation, track}}`` — but renderListenBrainzSyncPlaylists was reading ``p.title`` / ``p.creator`` / ``p.track_count`` directly, so every field hit its fallback. Now unwraps the inner playlist object, extracts the MBID from the identifier URL via ``.split('/').pop()`` (matches buildListenBrainzPlaylistsHtml on the Discover page), and reads track_count from ``annotation.track_count`` with a fallback to ``track.length``. 2. **The tab looked too orange.** The initial commit gave the sub-tabs a saturated orange surface that clashed with the rest of the app, and the new ``.listenbrainz-playlist-card`` class wasn't in the unified ``.youtube-playlist-card, .tidal-playlist-card, ...`` selector group — so the card lost its dark glass base and inherited only my override CSS. Two fixes: - Added ``.listenbrainz-playlist-card`` to the unified card selector group (base + ::before + hover + hover::before + icon) so it picks up the dark glass background. The brand accent stripe + hover glow use ``rgba(235, 116, 59, ...)`` matching the other source cards' subtle accent pattern. - Sub-tabs reverted to a neutral dark surface (``rgba(255, 255, 255, 0.04)``) with the orange used only as a thin accent on the active state's border + inset shadow. - Dropped the ``.refresh-button.listenbrainz`` override so the refresh button falls back to the user's chosen accent like the Spotify / Qobuz refresh buttons do. --- webui/static/style.css | 39 +++++++++++++++---------------- webui/static/sync-listenbrainz.js | 18 ++++++++++---- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/webui/static/style.css b/webui/static/style.css index 8c253041..b252989c 100644 --- a/webui/static/style.css +++ b/webui/static/style.css @@ -13272,7 +13272,9 @@ body.helper-mode-active #dashboard-activity-feed:hover { background-image: url('data:image/svg+xml;charset=utf-8,'); } -/* ListenBrainz Sync tab sub-tabs (For You / My Playlists / Collaborative) */ +/* ListenBrainz Sync tab sub-tabs (For You / My Playlists / Collaborative). + * Neutral dark surface; orange used only as a subtle accent on the + * active state — matches the rest of the app's tone. */ .listenbrainz-sub-tabs { display: inline-flex; gap: 6px; @@ -13280,9 +13282,9 @@ body.helper-mode-active #dashboard-activity-feed:hover { } .listenbrainz-sub-tab-btn { - background: rgba(255, 255, 255, 0.05); - color: rgba(255, 255, 255, 0.7); - border: 1px solid rgba(255, 255, 255, 0.1); + background: rgba(255, 255, 255, 0.04); + color: rgba(255, 255, 255, 0.65); + border: 1px solid rgba(255, 255, 255, 0.08); padding: 6px 12px; border-radius: 6px; font-size: 12px; @@ -13291,24 +13293,15 @@ body.helper-mode-active #dashboard-activity-feed:hover { } .listenbrainz-sub-tab-btn:hover { - background: rgba(235, 116, 59, 0.15); + background: rgba(255, 255, 255, 0.07); color: #fff; - border-color: rgba(235, 116, 59, 0.4); } .listenbrainz-sub-tab-btn.active { - background: rgba(235, 116, 59, 0.25); + background: rgba(255, 255, 255, 0.08); color: #fff; - border-color: rgba(235, 116, 59, 0.6); -} - -.refresh-button.listenbrainz { - background: rgba(235, 116, 59, 0.15); - border: 1px solid rgba(235, 116, 59, 0.4); - color: #fff; -} -.refresh-button.listenbrainz:hover { - background: rgba(235, 116, 59, 0.3); + border-color: rgba(235, 116, 59, 0.45); + box-shadow: 0 0 0 1px rgba(235, 116, 59, 0.15) inset; } .itunes-icon { @@ -17542,7 +17535,8 @@ body.helper-mode-active #dashboard-activity-feed:hover { .youtube-playlist-card, .tidal-playlist-card, .deezer-playlist-card, -.spotify-public-card { +.spotify-public-card, +.listenbrainz-playlist-card { background: rgba(18, 18, 22, 0.9); backdrop-filter: blur(12px); border-radius: 16px; @@ -17563,7 +17557,8 @@ body.helper-mode-active #dashboard-activity-feed:hover { .youtube-playlist-card::before, .tidal-playlist-card::before, .deezer-playlist-card::before, -.spotify-public-card::before { +.spotify-public-card::before, +.listenbrainz-playlist-card::before { content: ''; position: absolute; top: 0; @@ -17578,23 +17573,27 @@ body.helper-mode-active #dashboard-activity-feed:hover { .tidal-playlist-card::before { background: linear-gradient(90deg, transparent, rgba(255, 102, 0, 0.4), transparent); } .deezer-playlist-card::before { background: linear-gradient(90deg, transparent, rgba(162, 56, 255, 0.4), transparent); } .spotify-public-card::before { background: linear-gradient(90deg, transparent, rgba(29, 185, 84, 0.4), transparent); } +.listenbrainz-playlist-card::before { background: linear-gradient(90deg, transparent, rgba(235, 116, 59, 0.4), transparent); } /* Hover — brand glow */ .youtube-playlist-card:hover { border-color: rgba(255, 0, 0, 0.15); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 16px rgba(255, 0, 0, 0.06); transform: translateY(-2px); } .tidal-playlist-card:hover { border-color: rgba(255, 102, 0, 0.15); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 16px rgba(255, 102, 0, 0.06); transform: translateY(-2px); } .deezer-playlist-card:hover { border-color: rgba(162, 56, 255, 0.15); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 16px rgba(162, 56, 255, 0.06); transform: translateY(-2px); } .spotify-public-card:hover { border-color: rgba(29, 185, 84, 0.15); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 16px rgba(29, 185, 84, 0.06); transform: translateY(-2px); } +.listenbrainz-playlist-card:hover { border-color: rgba(235, 116, 59, 0.15); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 16px rgba(235, 116, 59, 0.06); transform: translateY(-2px); } .youtube-playlist-card:hover::before { left: 10%; right: 10%; background: linear-gradient(90deg, transparent, rgba(255, 0, 0, 0.7), transparent); } .tidal-playlist-card:hover::before { left: 10%; right: 10%; background: linear-gradient(90deg, transparent, rgba(255, 102, 0, 0.7), transparent); } .deezer-playlist-card:hover::before { left: 10%; right: 10%; background: linear-gradient(90deg, transparent, rgba(162, 56, 255, 0.7), transparent); } .spotify-public-card:hover::before { left: 10%; right: 10%; background: linear-gradient(90deg, transparent, rgba(29, 185, 84, 0.7), transparent); } +.listenbrainz-playlist-card:hover::before { left: 10%; right: 10%; background: linear-gradient(90deg, transparent, rgba(235, 116, 59, 0.7), transparent); } /* Source icons */ .youtube-playlist-card .playlist-card-icon, .tidal-playlist-card .playlist-card-icon, .deezer-playlist-card .playlist-card-icon, -.spotify-public-card .playlist-card-icon { +.spotify-public-card .playlist-card-icon, +.listenbrainz-playlist-card .playlist-card-icon { width: 40px; height: 40px; border-radius: 10px; diff --git a/webui/static/sync-listenbrainz.js b/webui/static/sync-listenbrainz.js index d43ad46d..bf440422 100644 --- a/webui/static/sync-listenbrainz.js +++ b/webui/static/sync-listenbrainz.js @@ -87,10 +87,20 @@ function renderListenBrainzSyncPlaylists() { } container.innerHTML = playlists.map(p => { - const mbid = p.playlist_mbid || p.id; - const title = p.title || p.name || 'ListenBrainz Playlist'; - const creator = p.creator || 'ListenBrainz'; - const count = p.track_count || 0; + // The Discover-page endpoints wrap each entry in JSPF shape: + // { playlist: { identifier: 'https://.../', title, creator, + // annotation: {track_count}, track: [...] } } + // Pull out the inner playlist object + extract the mbid from the URL. + const inner = p.playlist || p; + const mbid = (inner.identifier || '').split('/').pop() || inner.id || ''; + const title = inner.title || inner.name || 'ListenBrainz Playlist'; + const creator = inner.creator || 'ListenBrainz'; + let count = 0; + if (inner.annotation && inner.annotation.track_count) { + count = inner.annotation.track_count; + } else if (Array.isArray(inner.track) && inner.track.length > 0) { + count = inner.track.length; + } // Reuse listenbrainzPlaylistStates so the modal state survives // tab switches (matches Discover-page behavior). const state = (typeof listenbrainzPlaylistStates !== 'undefined'