diff --git a/web_server.py b/web_server.py index 6fc0780e..8e4d3eea 100644 --- a/web_server.py +++ b/web_server.py @@ -25645,7 +25645,13 @@ def get_active_sources(): 'success': True, 'editable': get_current_profile_id() == 1, # admin writes the global default 'metadata': { + # `active` = the configured choice (what the user picked / edits). + # `effective` = what's actually used after auth/availability + # fallback (e.g. configured 'spotify' but not authenticated → + # the app falls back). Surfacing both stops the modal disagreeing + # with the sidebar/Settings status. 'active': config_manager.get('metadata.fallback_source', 'deezer') or 'deezer', + 'effective': _get_metadata_fallback_source(), 'options': [{'id': s, 'available': _qs_metadata_available(s)} for s in _QS_METADATA_SOURCES], }, 'server': { diff --git a/webui/static/service-switch.js b/webui/static/service-switch.js index 9a33bfb0..f046f4a2 100644 --- a/webui/static/service-switch.js +++ b/webui/static/service-switch.js @@ -27,6 +27,15 @@ const _SS_SERVER_INFO = { const _SS_META_FALLBACK = { spotify_free: { text: 'Spotify (no auth)', icon: '🆓', logo: 'https://storage.googleapis.com/pr-newsroom-wp/1/2023/05/Spotify_Primary_Logo_RGB_Green.png' }, }; +// Brand colors drive each card's logo ring + active glow (the Manage-Workers feel). +const _SS_BRAND = { + spotify: '#1db954', spotify_free: '#1db954', itunes: '#fc5c7d', deezer: '#a238ff', + discogs: '#ff5500', musicbrainz: '#ba478f', amazon: '#ff9900', + plex: '#e5a00d', jellyfin: '#aa5cc3', navidrome: '#3b6cf6', soulsync: '#7c5cff', + soulseek: '#22a7f0', youtube: '#ff0000', tidal: '#00cfe8', qobuz: '#0a6e9e', + hifi: '#16c79a', torrent: '#8a2be2', usenet: '#e67e22', +}; +function _ssBrand(id) { return _SS_BRAND[id] || 'var(--accent-light-rgb-hex, #7c5cff)'; } let _ssState = { tab: 'metadata', data: null }; @@ -99,17 +108,48 @@ async function _ssLoad() { } catch (e) { _ssState.data = null; } + _ssRenderRail(); // re-render now that we know each tab's active choice _ssRenderPanel(); } +function _ssRailCurrent(tabId) { + // The active choice for a tab → {logo/emoji, label, brand} for the rail chip. + const d = _ssState.data; + if (!d || !d.success) return null; + if (tabId === 'metadata') { + const id = d.metadata.active; const info = _ssMetaInfo(id); + return { logo: info.logo, emoji: info.icon, label: info.text || id, brand: _ssBrand(id) }; + } + if (tabId === 'server') { + const id = d.server.active; const info = _SS_SERVER_INFO[id] || { name: id }; + return { logo: info.logo, emoji: '🖥️', label: info.name, brand: _ssBrand(id) }; + } + const id = d.download.mode; + if (id === 'hybrid') return { emoji: '🔀', label: 'Hybrid', brand: 'var(--accent-light-rgb-hex,#7c5cff)' }; + const info = _ssDownloadInfo(id); + return { logo: info.logo, emoji: info.emoji, label: info.name, brand: _ssBrand(id) }; +} + function _ssRenderRail() { const rail = document.getElementById('ss-rail'); if (!rail) return; - rail.innerHTML = _SS_TABS.map(t => ` - `).join(''); + rail.innerHTML = _SS_TABS.map(t => { + const cur = _ssRailCurrent(t.id); + const media = cur + ? (cur.logo + ? `` + : `${cur.emoji}`) + : `${t.emoji}`; + return ` + `; + }).join(''); } function switchServiceSwitchTab(tab) { @@ -118,15 +158,15 @@ function switchServiceSwitchTab(tab) { _ssRenderPanel(); } -function _ssCard({ logo, emoji, label, active, available, onclick, badge }) { +function _ssCard({ logo, emoji, label, active, available, onclick, badge, brand }) { const dim = available === false ? ' ss-card--locked' : ''; const act = active ? ' active' : ''; const media = logo ? `` : `${emoji || '🎵'}`; return ` -