diff --git a/database/music_database.py b/database/music_database.py index b8702752..f058a02c 100644 --- a/database/music_database.py +++ b/database/music_database.py @@ -10515,6 +10515,32 @@ class MusicDatabase: logger.error(f"Error checking similar artists freshness: {e}") return False # Default to re-fetching on error + def get_similar_artist_popularities(self, names, profile_id: int = 1): + """Map lowercased artist name -> max stored popularity (0-100) from ``similar_artists`` for + the given profile. Lets the Discover routes apply the adventurousness popularity-penalty at + request time (the stored listening-recs don't carry popularity inline). Fail-soft -> {}.""" + out = {} + clean = [str(n).strip().lower() for n in (names or []) if str(n or '').strip()] + if not clean: + return out + try: + with self._get_connection() as conn: + cursor = conn.cursor() + placeholders = ','.join('?' for _ in clean) + cursor.execute( + f"""SELECT LOWER(similar_artist_name) AS n, MAX(popularity) AS pop + FROM similar_artists + WHERE profile_id = ? AND LOWER(similar_artist_name) IN ({placeholders}) + GROUP BY LOWER(similar_artist_name)""", + [profile_id] + clean, + ) + for row in cursor.fetchall(): + if row['pop'] is not None: + out[row['n']] = row['pop'] + except Exception as e: + logger.debug(f"get_similar_artist_popularities failed: {e}") + return out + def get_top_similar_artists( self, limit: int = 50, diff --git a/web_server.py b/web_server.py index 96033fbf..f0b4bfdc 100644 --- a/web_server.py +++ b/web_server.py @@ -29604,6 +29604,24 @@ def get_discover_listening_recommendations(): except (ValueError, TypeError): stored = [] + # Adventurousness re-rank (aurral-style): enrich popularity at request time (the stored recs + # don't carry it inline, so this works on existing data), then push globally-popular picks + # down per the user's dial. level 0 -> unchanged. Fail-soft: any hiccup leaves the order. + try: + level = float(config_manager.get('discovery.adventurousness', 0.3) or 0) + except (TypeError, ValueError): + level = 0.0 + if level > 0 and stored: + try: + pops = database.get_similar_artist_popularities([a.get('name') for a in stored]) + for a in stored: + if a.get('popularity') is None: + a['popularity'] = pops.get((a.get('name') or '').strip().lower()) + from core.discovery.listening_recommendations import apply_adventurousness + stored = apply_adventurousness(stored, level) + except Exception as _adv_err: + logger.debug(f"adventurousness re-rank skipped: {_adv_err}") + result_artists = [] for a in stored: name = a.get('name')