Make Spotify a selectable metadata source instead of auto-override
Spotify was automatically the primary metadata source whenever authenticated, ignoring the user's fallback dropdown selection. Now: - Dropdown renamed "Fallback Source" → "Primary Source" with Spotify added as an option alongside iTunes, Deezer, Discogs - User's selection drives search results, discovery page, status display - Enhanced search uses selected primary source first, others as tabs - Discovery page filters by selected source's artist IDs - Spotify auth still works for playlists, followed artists, enrichment - Default is 'deezer' — users who want Spotify select it explicitly
This commit is contained in:
parent
db5bdb9c59
commit
3c211eaac8
2 changed files with 44 additions and 31 deletions
|
|
@ -4424,13 +4424,19 @@ def get_status():
|
|||
if is_rate_limited or cooldown_remaining > 0:
|
||||
# During rate limit or post-ban cooldown, skip the auth probe entirely.
|
||||
# Probing Spotify here would reset the rate limit timer.
|
||||
music_source = _get_metadata_fallback_source() # App uses fallback during ban
|
||||
music_source = _get_metadata_fallback_source()
|
||||
if music_source == 'spotify':
|
||||
music_source = 'deezer' # Spotify rate limited — can't use it
|
||||
spotify_response_time = 0
|
||||
else:
|
||||
spotify_start = time.time()
|
||||
spotify_connected = spotify_client.is_spotify_authenticated() if spotify_client else False
|
||||
spotify_response_time = (time.time() - spotify_start) * 1000
|
||||
music_source = 'spotify' if spotify_connected else _get_metadata_fallback_source()
|
||||
# Use whatever the user configured as primary metadata source
|
||||
music_source = _get_metadata_fallback_source()
|
||||
# If user selected spotify but it's not authed, fall back
|
||||
if music_source == 'spotify' and not spotify_connected:
|
||||
music_source = 'deezer'
|
||||
|
||||
_status_cache['spotify'] = {
|
||||
'connected': True, # Always true — iTunes fallback is always available
|
||||
|
|
@ -5981,7 +5987,7 @@ def test_connection_endpoint():
|
|||
current_time = time.time()
|
||||
if service == 'spotify':
|
||||
_status_cache['spotify']['connected'] = True
|
||||
_status_cache['spotify']['source'] = 'spotify' if (spotify_client and spotify_client.is_spotify_authenticated()) else _get_metadata_fallback_source()
|
||||
_status_cache['spotify']['source'] = _get_metadata_fallback_source()
|
||||
_status_cache_timestamps['spotify'] = current_time
|
||||
print("✅ Updated Spotify status cache after successful test")
|
||||
elif service in ['plex', 'jellyfin', 'navidrome']:
|
||||
|
|
@ -6031,7 +6037,7 @@ def test_dashboard_connection_endpoint():
|
|||
current_time = time.time()
|
||||
if service == 'spotify':
|
||||
_status_cache['spotify']['connected'] = True
|
||||
_status_cache['spotify']['source'] = 'spotify' if (spotify_client and spotify_client.is_spotify_authenticated()) else _get_metadata_fallback_source()
|
||||
_status_cache['spotify']['source'] = _get_metadata_fallback_source()
|
||||
_status_cache_timestamps['spotify'] = current_time
|
||||
print("✅ Updated Spotify status cache after successful dashboard test")
|
||||
elif service in ['plex', 'jellyfin', 'navidrome']:
|
||||
|
|
@ -7698,23 +7704,22 @@ def enhanced_search():
|
|||
hydrabase_worker.enqueue(query, 'albums')
|
||||
hydrabase_worker.enqueue(query, 'artists')
|
||||
|
||||
# Search primary source synchronously — use is_spotify_authenticated()
|
||||
# (is_authenticated() always returns True because fallback is always available)
|
||||
if spotify_client and spotify_client.is_spotify_authenticated():
|
||||
try:
|
||||
primary_results = _enhanced_search_source(query, spotify_client)
|
||||
primary_source = "spotify"
|
||||
except Exception as e:
|
||||
logger.debug(f"Spotify search failed: {e}")
|
||||
# Search using the user's configured primary metadata source
|
||||
fb_source = _get_metadata_fallback_source()
|
||||
try:
|
||||
primary_results = _enhanced_search_source(query, _get_metadata_fallback_client())
|
||||
primary_source = fb_source
|
||||
except Exception as e:
|
||||
logger.debug(f"Primary source ({fb_source}) search failed: {e}")
|
||||
|
||||
# If Spotify unavailable, use configured fallback as primary
|
||||
if primary_results is empty_source:
|
||||
fb_source = _get_metadata_fallback_source()
|
||||
try:
|
||||
primary_results = _enhanced_search_source(query, _get_metadata_fallback_client())
|
||||
primary_source = fb_source
|
||||
except Exception as e:
|
||||
logger.debug(f"Fallback ({fb_source}) search failed: {e}")
|
||||
# If primary source failed and it wasn't Spotify, try Spotify as fallback
|
||||
if primary_results is empty_source and fb_source != 'spotify':
|
||||
if spotify_client and spotify_client.is_spotify_authenticated():
|
||||
try:
|
||||
primary_results = _enhanced_search_source(query, spotify_client)
|
||||
primary_source = "spotify"
|
||||
except Exception as e:
|
||||
logger.debug(f"Spotify fallback search failed: {e}")
|
||||
|
||||
# Determine which alternate sources are available (for frontend to fetch async)
|
||||
spotify_available = bool(spotify_client and spotify_client.is_spotify_authenticated())
|
||||
|
|
@ -32748,16 +32753,22 @@ def _get_deezer_client():
|
|||
return _deezer_client_instance
|
||||
|
||||
def _get_metadata_fallback_source():
|
||||
"""Get the configured metadata fallback source ('itunes', 'deezer', 'discogs', or 'hydrabase')."""
|
||||
"""Get the configured primary metadata source.
|
||||
Returns 'spotify', 'itunes', 'deezer', 'discogs', or 'hydrabase'."""
|
||||
try:
|
||||
return config_manager.get('metadata.fallback_source', 'deezer') or 'deezer'
|
||||
except Exception:
|
||||
return 'itunes'
|
||||
return 'deezer'
|
||||
|
||||
def _get_metadata_fallback_client():
|
||||
"""Get the active metadata fallback client based on settings.
|
||||
Returns an iTunesClient, DeezerClient, DiscogsClient, or HydrabaseClient instance with identical interfaces."""
|
||||
"""Get the active metadata client based on settings.
|
||||
Returns a SpotifyClient, iTunesClient, DeezerClient, DiscogsClient, or HydrabaseClient instance."""
|
||||
source = _get_metadata_fallback_source()
|
||||
if source == 'spotify':
|
||||
if spotify_client and spotify_client.is_spotify_authenticated():
|
||||
return spotify_client
|
||||
# Spotify selected but not authed — fall back to deezer
|
||||
return _get_deezer_client()
|
||||
if source == 'deezer':
|
||||
return _get_deezer_client()
|
||||
if source == 'discogs':
|
||||
|
|
@ -32765,7 +32776,6 @@ def _get_metadata_fallback_client():
|
|||
if token:
|
||||
from core.discogs_client import DiscogsClient
|
||||
return DiscogsClient(token=token)
|
||||
# No token — fall back to iTunes
|
||||
from core.itunes_client import iTunesClient
|
||||
return iTunesClient()
|
||||
if source == 'hydrabase':
|
||||
|
|
@ -39499,11 +39509,13 @@ metadata_update_executor = ThreadPoolExecutor(max_workers=1, thread_name_prefix=
|
|||
def _get_active_discovery_source():
|
||||
"""
|
||||
Determine which music source is active for discovery.
|
||||
Returns 'spotify' if Spotify is authenticated, otherwise the configured fallback.
|
||||
Returns the user's configured primary metadata source.
|
||||
If the selected source requires auth and isn't available, falls back.
|
||||
"""
|
||||
if spotify_client and spotify_client.is_spotify_authenticated():
|
||||
return 'spotify'
|
||||
return _get_metadata_fallback_source()
|
||||
source = _get_metadata_fallback_source()
|
||||
if source == 'spotify' and not (spotify_client and spotify_client.is_spotify_authenticated()):
|
||||
return 'deezer'
|
||||
return source
|
||||
|
||||
|
||||
@app.route('/api/discover/hero', methods=['GET'])
|
||||
|
|
|
|||
|
|
@ -4235,15 +4235,16 @@
|
|||
<div class="api-service-frame">
|
||||
<h4 class="service-title" style="color: #e8e8e8;">Metadata Source</h4>
|
||||
<div class="form-group">
|
||||
<label>Fallback Source:</label>
|
||||
<label>Primary Source:</label>
|
||||
<select id="metadata-fallback-source">
|
||||
<option value="spotify">Spotify</option>
|
||||
<option value="itunes">iTunes / Apple Music</option>
|
||||
<option value="deezer">Deezer</option>
|
||||
<option value="discogs">Discogs</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="callback-info">
|
||||
<div class="callback-help">When Spotify is not connected, this source provides artist, album, and track metadata. Discogs requires a personal token configured above.</div>
|
||||
<div class="callback-help">The primary source for artist, album, and track metadata. Spotify requires authentication above. Discogs requires a personal token.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue