From 22947794e06d6e1eca9509072287b7a30a870ec3 Mon Sep 17 00:00:00 2001 From: BoulderBadgeDad Date: Wed, 10 Jun 2026 10:41:09 -0700 Subject: [PATCH] Profiles: label the no-auth Spotify composite everywhere it's shown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to the modal fix: the sidebar Service Status + dashboard service card also mislabeled "Spotify (no auth)" as plain "Spotify". They read the status `source`, which came straight from metadata.fallback_source ('spotify') with no awareness of the metadata.spotify_free flag. - get_primary_source_status now reports a DISPLAY source of 'spotify_free' when fallback_source='spotify' + metadata.spotify_free is set (the raw 'spotify' is still used for the auth/connected checks), and treats the free path's availability as "connected" so the dot isn't falsely red on a no-auth setup. - getMetadataSourceLabel maps 'spotify_free' → "Spotify (no auth)"; the status presentation treats spotify_free as part of the Spotify family (session / rate-limit / cooldown display still work). Added a SOURCE_LABELS entry. - testDashboardConnection normalizes spotify_free → spotify (the only logic consumer of the source value — the dashboard Test button). Routing is unchanged (the real source stays 'spotify' + free flag); this is purely the display layer. Settings was always correct. 64 integrity tests pass; the 2 failing soundcloud tests are pre-existing (confirmed identical on a clean tree). --- core/metadata/registry.py | 19 ++++++++++++++++++- webui/static/settings.js | 3 +++ webui/static/shared-helpers.js | 17 +++++++++++++---- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/core/metadata/registry.py b/core/metadata/registry.py index 3299d55a..3aab4468 100644 --- a/core/metadata/registry.py +++ b/core/metadata/registry.py @@ -415,6 +415,14 @@ def get_primary_source_status( ) if source == "spotify": connected = bool(client and client.is_spotify_authenticated()) + # No-auth composite (fallback_source='spotify' + metadata.spotify_free): + # works without authentication, so treat the free path's availability + # as "connected" too. + if not connected and _get_config_value("metadata.spotify_free", False): + try: + connected = bool(client and client.is_spotify_metadata_available()) + except Exception: + connected = False elif source == "hydrabase": connected = bool(client and (client.is_connected() if hasattr(client, "is_connected") else client.is_authenticated())) elif client is not None and hasattr(client, "is_authenticated"): @@ -424,8 +432,17 @@ def get_primary_source_status( except Exception: connected = False + # Report the composite-aware source for DISPLAY: "Spotify (no auth)" is + # stored as fallback_source='spotify' + metadata.spotify_free=true. The raw + # 'spotify' is kept above for the connected/auth checks; consumers that + # label the source (sidebar, dashboard card) get 'spotify_free' so they + # stop mislabeling no-auth as plain Spotify. + display_source = source + if source == "spotify" and _get_config_value("metadata.spotify_free", False): + display_source = "spotify_free" + return { - "source": source, + "source": display_source, "connected": connected, "response_time": round((time.time() - started) * 1000, 1), } diff --git a/webui/static/settings.js b/webui/static/settings.js index 9cc9e341..99140053 100644 --- a/webui/static/settings.js +++ b/webui/static/settings.js @@ -3438,6 +3438,9 @@ async function revokeApiKey(keyId, label) { // Dashboard-specific test functions that create activity items async function testDashboardConnection(service) { + // 'spotify_free' is a display-only label for the no-auth composite; the real + // service to test is 'spotify'. + if (service === 'spotify_free') service = 'spotify'; try { showLoadingOverlay(`Testing ${service} service...`); diff --git a/webui/static/shared-helpers.js b/webui/static/shared-helpers.js index 5e1c1e62..f5a0b958 100644 --- a/webui/static/shared-helpers.js +++ b/webui/static/shared-helpers.js @@ -42,6 +42,11 @@ const SOURCE_LABELS = { logo: 'https://storage.googleapis.com/pr-newsroom-wp/1/2023/05/Spotify_Primary_Logo_RGB_Green.png', tabClass: 'enh-tab-spotify', badgeClass: 'enh-badge-spotify', }, + spotify_free: { + text: 'Spotify (no auth)', icon: '🎵', + logo: 'https://storage.googleapis.com/pr-newsroom-wp/1/2023/05/Spotify_Primary_Logo_RGB_Green.png', + tabClass: 'enh-tab-spotify', badgeClass: 'enh-badge-spotify', + }, itunes: { text: 'Apple Music', icon: '🍎', logo: 'https://upload.wikimedia.org/wikipedia/commons/thumb/d/df/ITunes_logo.svg/960px-ITunes_logo.svg.png', @@ -3633,6 +3638,7 @@ function getMetadataSourceLabel(source) { if (source === 'hydrabase') return 'Hydrabase'; if (source === 'itunes') return 'iTunes'; if (source === 'musicbrainz') return 'MusicBrainz'; + if (source === 'spotify_free') return 'Spotify (no auth)'; if (source === 'spotify') return 'Spotify'; return 'Unmapped'; } @@ -3641,9 +3647,12 @@ function getMetadataSourcePresentation(metadataStatus, spotifyStatus) { const source = metadataStatus?.source; const sourceLabel = getMetadataSourceLabel(source); const connected = metadataStatus?.connected === true; - const sessionActive = spotifyStatus?.authenticated === true || (source === 'spotify' && connected); - const rateLimited = !!(source === 'spotify' && spotifyStatus?.rate_limited && spotifyStatus?.rate_limit); - const cooldown = !!(source === 'spotify' && spotifyStatus?.post_ban_cooldown > 0); + // 'spotify_free' (the no-auth composite) is part of the Spotify family for + // session/rate-limit/cooldown display. + const spotifyFamily = (source === 'spotify' || source === 'spotify_free'); + const sessionActive = spotifyStatus?.authenticated === true || (spotifyFamily && connected); + const rateLimited = !!(spotifyFamily && spotifyStatus?.rate_limited && spotifyStatus?.rate_limit); + const cooldown = !!(spotifyFamily && spotifyStatus?.post_ban_cooldown > 0); if (rateLimited) { const remaining = spotifyStatus.rate_limit?.remaining_seconds || 0; @@ -3670,7 +3679,7 @@ function getMetadataSourcePresentation(metadataStatus, spotifyStatus) { if (source) { return { statusClass: connected ? 'connected' : 'disconnected', - statusText: connected ? (source === 'spotify' ? `Connected (${metadataStatus?.response_time}ms)` : sourceLabel) : 'Disconnected', + statusText: connected ? (spotifyFamily ? `Connected (${metadataStatus?.response_time}ms)` : sourceLabel) : 'Disconnected', dotClass: connected ? 'connected' : 'disconnected', dotTitle: connected ? sourceLabel : 'Disconnected', sessionActive