Fix hero slider + recommended modal returning 0 artists

get_top_similar_artists now accepts require_source parameter to filter
by source ID in SQL. Previously fetched 200 artists then post-filtered,
but cycling logic (last_featured ASC) rotated artists without IDs to
the front, causing all 200 to be filtered out.

Both /api/discover/hero and /api/discover/similar-artists now pass
require_source=active_source so only artists with valid IDs are returned.
This commit is contained in:
Broque Thomas 2026-04-04 12:19:29 -07:00
parent 36e83e64ee
commit c336604b71
2 changed files with 20 additions and 22 deletions

View file

@ -7260,13 +7260,23 @@ class MusicDatabase:
logger.error(f"Error checking similar artists freshness: {e}")
return False # Default to re-fetching on error
def get_top_similar_artists(self, limit: int = 50, profile_id: int = 1) -> List[SimilarArtist]:
"""Get top similar artists excluding watchlist artists, with cycling support"""
def get_top_similar_artists(self, limit: int = 50, profile_id: int = 1, require_source: str = None) -> List[SimilarArtist]:
"""Get top similar artists excluding watchlist artists, with cycling support.
require_source: if set ('spotify','itunes','deezer'), only returns artists with that source ID."""
try:
with self._get_connection() as conn:
cursor = conn.cursor()
cursor.execute("""
# Build source filter
source_filter = ''
if require_source == 'spotify':
source_filter = "AND sa.similar_artist_spotify_id IS NOT NULL AND sa.similar_artist_spotify_id != ''"
elif require_source == 'itunes':
source_filter = "AND sa.similar_artist_itunes_id IS NOT NULL AND sa.similar_artist_itunes_id != ''"
elif require_source == 'deezer':
source_filter = "AND sa.similar_artist_deezer_id IS NOT NULL AND sa.similar_artist_deezer_id != ''"
cursor.execute(f"""
SELECT
MAX(sa.id) as id,
MAX(sa.source_artist_id) as source_artist_id,
@ -7287,7 +7297,7 @@ class MusicDatabase:
OR (sa.similar_artist_deezer_id IS NOT NULL AND sa.similar_artist_deezer_id = wa.deezer_artist_id)
OR LOWER(sa.similar_artist_name) = LOWER(wa.artist_name)
) AND wa.profile_id = ?
WHERE wa.id IS NULL AND sa.profile_id = ?
WHERE wa.id IS NULL AND sa.profile_id = ? {source_filter}
GROUP BY sa.similar_artist_name
ORDER BY
CASE WHEN MAX(sa.last_featured) IS NULL THEN 0 ELSE 1 END,

View file

@ -39021,7 +39021,8 @@ def get_discover_hero():
# Get top similar artists (excluding watchlist, cycled by last_featured)
# Fetch more than needed since strict source filtering may drop many
pid = get_current_profile_id()
similar_artists = database.get_top_similar_artists(limit=200, profile_id=pid)
print(f"[Discover Hero] Profile ID: {pid}, Active source: {active_source}")
similar_artists = database.get_top_similar_artists(limit=200, profile_id=pid, require_source=active_source)
# FALLBACK: If no similar artists exist, use watchlist artists for Hero section
if not similar_artists:
@ -39067,15 +39068,8 @@ def get_discover_hero():
print(f"[Discover Hero] Returning {len(hero_artists)} watchlist artists as fallback")
return jsonify({"success": True, "artists": hero_artists, "source": active_source, "fallback": "watchlist"})
# Filter to artists that have the appropriate ID for the active source
valid_artists = []
for artist in similar_artists:
if active_source == 'spotify' and artist.similar_artist_spotify_id:
valid_artists.append(artist)
elif active_source == 'deezer' and getattr(artist, 'similar_artist_deezer_id', None):
valid_artists.append(artist)
elif active_source == 'itunes' and artist.similar_artist_itunes_id:
valid_artists.append(artist)
# Artists are already filtered by source in SQL — no post-filter needed
valid_artists = list(similar_artists)
# FALLBACK: If no valid artists for fallback source, try to resolve IDs on-the-fly
if active_source in ('itunes', 'deezer') and not valid_artists:
@ -39198,20 +39192,14 @@ def get_discover_similar_artists():
database = get_database()
active_source = _get_active_discovery_source()
similar_artists = database.get_top_similar_artists(limit=200, profile_id=get_current_profile_id())
similar_artists = database.get_top_similar_artists(limit=200, profile_id=get_current_profile_id(), require_source=active_source)
if not similar_artists:
return jsonify({"success": True, "artists": [], "source": active_source, "count": 0})
# Filter to artists with valid ID for active source (strict — no cross-source)
# Artists already filtered by source in SQL
result_artists = []
for artist in similar_artists:
if active_source == 'spotify' and not artist.similar_artist_spotify_id:
continue
if active_source == 'deezer' and not getattr(artist, 'similar_artist_deezer_id', None) and not artist.similar_artist_itunes_id:
continue
if active_source == 'itunes' and not artist.similar_artist_itunes_id:
continue
if active_source == 'spotify':
artist_id = artist.similar_artist_spotify_id