Respect configured primary source in seasonal, playlists, and explorer

Seasonal discovery, personalized playlists, and playlist explorer all
defaulted to Spotify when authenticated, ignoring the user's configured
primary source. Now they read from config first.

Spotify's related_artists API (no Deezer/iTunes equivalent) is preserved
as a fallback for all users in personalized playlists. Artist discography
endpoint intentionally unchanged — ID-based lookups need the source that
owns the ID.
This commit is contained in:
Broque Thomas 2026-04-10 11:43:06 -07:00
parent 7d21385ce9
commit 57fc18f994
3 changed files with 21 additions and 21 deletions

View file

@ -102,17 +102,16 @@ class PersonalizedPlaylistsService:
def _get_active_source(self) -> str: def _get_active_source(self) -> str:
""" """
Determine which music source is active for discovery. Determine which music source is active respects user's configured primary source.
Returns 'spotify' if Spotify is authenticated, otherwise the configured fallback ('itunes' or 'deezer').
""" """
if self.spotify_client and hasattr(self.spotify_client, 'is_spotify_authenticated'):
if self.spotify_client.is_spotify_authenticated():
return 'spotify'
try: try:
from config.settings import config_manager from config.settings import config_manager
return config_manager.get('metadata.fallback_source', 'itunes') or 'itunes' source = config_manager.get('metadata.fallback_source', 'deezer') or 'deezer'
if source == 'spotify' and not (self.spotify_client and hasattr(self.spotify_client, 'is_spotify_authenticated') and self.spotify_client.is_spotify_authenticated()):
return 'deezer'
return source
except Exception: except Exception:
return 'itunes' return 'deezer'
def _build_track_dict(self, row, source: str) -> Dict: def _build_track_dict(self, row, source: str) -> Dict:
"""Build a standardized track dictionary from a database row.""" """Build a standardized track dictionary from a database row."""
@ -882,8 +881,8 @@ class PersonalizedPlaylistsService:
logger.error(f"Invalid seed artists count: {len(seed_artist_ids)}") logger.error(f"Invalid seed artists count: {len(seed_artist_ids)}")
return {'tracks': [], 'error': 'Must provide 1-5 seed artists'} return {'tracks': [], 'error': 'Must provide 1-5 seed artists'}
use_spotify = self.spotify_client and self.spotify_client.sp active_source = self._get_active_source()
active_source = 'spotify' if use_spotify else self._get_active_source() use_spotify = (active_source == 'spotify') and self.spotify_client and self.spotify_client.sp
logger.info(f"Building custom playlist from {len(seed_artist_ids)} seed artists (source: {active_source})") logger.info(f"Building custom playlist from {len(seed_artist_ids)} seed artists (source: {active_source})")
# Step 1: Get similar artists for each seed # Step 1: Get similar artists for each seed
@ -914,8 +913,8 @@ class PersonalizedPlaylistsService:
seen_artist_ids.add(artist_id) seen_artist_ids.add(artist_id)
if len(all_similar_artists) >= 25: if len(all_similar_artists) >= 25:
break break
elif use_spotify: elif self.spotify_client and self.spotify_client.sp:
# Fallback: fetch related artists from Spotify API # Fallback: fetch related artists from Spotify API (no Deezer/iTunes equivalent)
logger.info(f"No cached similar artists for {seed_artist_id}, trying Spotify related artists API") logger.info(f"No cached similar artists for {seed_artist_id}, trying Spotify related artists API")
try: try:
related = self.spotify_client.sp.artist_related_artists(seed_artist_id) related = self.spotify_client.sp.artist_related_artists(seed_artist_id)

View file

@ -96,14 +96,16 @@ class SeasonalDiscoveryService:
self._ensure_database_schema() self._ensure_database_schema()
def _get_source(self): def _get_source(self):
"""Determine active music source (matches _get_active_discovery_source in web_server)""" """Determine active music source — respects user's configured primary source"""
if self.spotify_client and self.spotify_client.is_spotify_authenticated():
return 'spotify'
try: try:
from core.metadata_service import _get_configured_fallback_source from config.settings import config_manager
return _get_configured_fallback_source() source = config_manager.get('metadata.fallback_source', 'deezer') or 'deezer'
# If user selected spotify, verify it's actually authenticated
if source == 'spotify' and not (self.spotify_client and self.spotify_client.is_spotify_authenticated()):
return 'deezer'
return source
except Exception: except Exception:
return 'itunes' return 'deezer'
def _ensure_database_schema(self): def _ensure_database_schema(self):
"""Create seasonal content tables if they don't exist""" """Create seasonal content tables if they don't exist"""

View file

@ -47377,11 +47377,10 @@ def playlist_explorer_build_tree():
if not tracks: if not tracks:
return jsonify({"success": False, "error": "Playlist has no tracks"}), 400 return jsonify({"success": False, "error": "Playlist has no tracks"}), 400
# Determine active metadata source # Determine active metadata source — respect user's configured primary
spotify_available = spotify_client and spotify_client.is_spotify_authenticated() source_name = _get_active_discovery_source()
if spotify_available: if source_name == 'spotify' and spotify_client and spotify_client.is_spotify_authenticated():
active_client = spotify_client active_client = spotify_client
source_name = 'spotify'
else: else:
active_client = _get_metadata_fallback_client() active_client = _get_metadata_fallback_client()
source_name = _get_metadata_fallback_source() source_name = _get_metadata_fallback_source()