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:
|
if is_rate_limited or cooldown_remaining > 0:
|
||||||
# During rate limit or post-ban cooldown, skip the auth probe entirely.
|
# During rate limit or post-ban cooldown, skip the auth probe entirely.
|
||||||
# Probing Spotify here would reset the rate limit timer.
|
# 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
|
spotify_response_time = 0
|
||||||
else:
|
else:
|
||||||
spotify_start = time.time()
|
spotify_start = time.time()
|
||||||
spotify_connected = spotify_client.is_spotify_authenticated() if spotify_client else False
|
spotify_connected = spotify_client.is_spotify_authenticated() if spotify_client else False
|
||||||
spotify_response_time = (time.time() - spotify_start) * 1000
|
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'] = {
|
_status_cache['spotify'] = {
|
||||||
'connected': True, # Always true — iTunes fallback is always available
|
'connected': True, # Always true — iTunes fallback is always available
|
||||||
|
|
@ -5981,7 +5987,7 @@ def test_connection_endpoint():
|
||||||
current_time = time.time()
|
current_time = time.time()
|
||||||
if service == 'spotify':
|
if service == 'spotify':
|
||||||
_status_cache['spotify']['connected'] = True
|
_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
|
_status_cache_timestamps['spotify'] = current_time
|
||||||
print("✅ Updated Spotify status cache after successful test")
|
print("✅ Updated Spotify status cache after successful test")
|
||||||
elif service in ['plex', 'jellyfin', 'navidrome']:
|
elif service in ['plex', 'jellyfin', 'navidrome']:
|
||||||
|
|
@ -6031,7 +6037,7 @@ def test_dashboard_connection_endpoint():
|
||||||
current_time = time.time()
|
current_time = time.time()
|
||||||
if service == 'spotify':
|
if service == 'spotify':
|
||||||
_status_cache['spotify']['connected'] = True
|
_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
|
_status_cache_timestamps['spotify'] = current_time
|
||||||
print("✅ Updated Spotify status cache after successful dashboard test")
|
print("✅ Updated Spotify status cache after successful dashboard test")
|
||||||
elif service in ['plex', 'jellyfin', 'navidrome']:
|
elif service in ['plex', 'jellyfin', 'navidrome']:
|
||||||
|
|
@ -7698,23 +7704,22 @@ def enhanced_search():
|
||||||
hydrabase_worker.enqueue(query, 'albums')
|
hydrabase_worker.enqueue(query, 'albums')
|
||||||
hydrabase_worker.enqueue(query, 'artists')
|
hydrabase_worker.enqueue(query, 'artists')
|
||||||
|
|
||||||
# Search primary source synchronously — use is_spotify_authenticated()
|
# Search using the user's configured primary metadata source
|
||||||
# (is_authenticated() always returns True because fallback is always available)
|
fb_source = _get_metadata_fallback_source()
|
||||||
if spotify_client and spotify_client.is_spotify_authenticated():
|
try:
|
||||||
try:
|
primary_results = _enhanced_search_source(query, _get_metadata_fallback_client())
|
||||||
primary_results = _enhanced_search_source(query, spotify_client)
|
primary_source = fb_source
|
||||||
primary_source = "spotify"
|
except Exception as e:
|
||||||
except Exception as e:
|
logger.debug(f"Primary source ({fb_source}) search failed: {e}")
|
||||||
logger.debug(f"Spotify search failed: {e}")
|
|
||||||
|
|
||||||
# If Spotify unavailable, use configured fallback as primary
|
# If primary source failed and it wasn't Spotify, try Spotify as fallback
|
||||||
if primary_results is empty_source:
|
if primary_results is empty_source and fb_source != 'spotify':
|
||||||
fb_source = _get_metadata_fallback_source()
|
if spotify_client and spotify_client.is_spotify_authenticated():
|
||||||
try:
|
try:
|
||||||
primary_results = _enhanced_search_source(query, _get_metadata_fallback_client())
|
primary_results = _enhanced_search_source(query, spotify_client)
|
||||||
primary_source = fb_source
|
primary_source = "spotify"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"Fallback ({fb_source}) search failed: {e}")
|
logger.debug(f"Spotify fallback search failed: {e}")
|
||||||
|
|
||||||
# Determine which alternate sources are available (for frontend to fetch async)
|
# Determine which alternate sources are available (for frontend to fetch async)
|
||||||
spotify_available = bool(spotify_client and spotify_client.is_spotify_authenticated())
|
spotify_available = bool(spotify_client and spotify_client.is_spotify_authenticated())
|
||||||
|
|
@ -32748,16 +32753,22 @@ def _get_deezer_client():
|
||||||
return _deezer_client_instance
|
return _deezer_client_instance
|
||||||
|
|
||||||
def _get_metadata_fallback_source():
|
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:
|
try:
|
||||||
return config_manager.get('metadata.fallback_source', 'deezer') or 'deezer'
|
return config_manager.get('metadata.fallback_source', 'deezer') or 'deezer'
|
||||||
except Exception:
|
except Exception:
|
||||||
return 'itunes'
|
return 'deezer'
|
||||||
|
|
||||||
def _get_metadata_fallback_client():
|
def _get_metadata_fallback_client():
|
||||||
"""Get the active metadata fallback client based on settings.
|
"""Get the active metadata client based on settings.
|
||||||
Returns an iTunesClient, DeezerClient, DiscogsClient, or HydrabaseClient instance with identical interfaces."""
|
Returns a SpotifyClient, iTunesClient, DeezerClient, DiscogsClient, or HydrabaseClient instance."""
|
||||||
source = _get_metadata_fallback_source()
|
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':
|
if source == 'deezer':
|
||||||
return _get_deezer_client()
|
return _get_deezer_client()
|
||||||
if source == 'discogs':
|
if source == 'discogs':
|
||||||
|
|
@ -32765,7 +32776,6 @@ def _get_metadata_fallback_client():
|
||||||
if token:
|
if token:
|
||||||
from core.discogs_client import DiscogsClient
|
from core.discogs_client import DiscogsClient
|
||||||
return DiscogsClient(token=token)
|
return DiscogsClient(token=token)
|
||||||
# No token — fall back to iTunes
|
|
||||||
from core.itunes_client import iTunesClient
|
from core.itunes_client import iTunesClient
|
||||||
return iTunesClient()
|
return iTunesClient()
|
||||||
if source == 'hydrabase':
|
if source == 'hydrabase':
|
||||||
|
|
@ -39499,11 +39509,13 @@ metadata_update_executor = ThreadPoolExecutor(max_workers=1, thread_name_prefix=
|
||||||
def _get_active_discovery_source():
|
def _get_active_discovery_source():
|
||||||
"""
|
"""
|
||||||
Determine which music source is active for discovery.
|
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():
|
source = _get_metadata_fallback_source()
|
||||||
return 'spotify'
|
if source == 'spotify' and not (spotify_client and spotify_client.is_spotify_authenticated()):
|
||||||
return _get_metadata_fallback_source()
|
return 'deezer'
|
||||||
|
return source
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/discover/hero', methods=['GET'])
|
@app.route('/api/discover/hero', methods=['GET'])
|
||||||
|
|
|
||||||
|
|
@ -4235,15 +4235,16 @@
|
||||||
<div class="api-service-frame">
|
<div class="api-service-frame">
|
||||||
<h4 class="service-title" style="color: #e8e8e8;">Metadata Source</h4>
|
<h4 class="service-title" style="color: #e8e8e8;">Metadata Source</h4>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Fallback Source:</label>
|
<label>Primary Source:</label>
|
||||||
<select id="metadata-fallback-source">
|
<select id="metadata-fallback-source">
|
||||||
|
<option value="spotify">Spotify</option>
|
||||||
<option value="itunes">iTunes / Apple Music</option>
|
<option value="itunes">iTunes / Apple Music</option>
|
||||||
<option value="deezer">Deezer</option>
|
<option value="deezer">Deezer</option>
|
||||||
<option value="discogs">Discogs</option>
|
<option value="discogs">Discogs</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="callback-info">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue