Improve Genius artist search for manual matching (#233)

The manual match modal for Genius only returned 0 or 1 artist result
because search_artist() searched songs (per_page=5), extracted the
primary artist, and returned the first match or None.

Added search_artists() that returns multiple unique artists extracted
from song results with broader search (per_page=20). The manual match
endpoint now shows up to 8 artist candidates and multiple track results
instead of one-or-nothing. Also shows the Genius URL as extra info.
This commit is contained in:
Broque Thomas 2026-04-01 08:31:17 -07:00
parent 982ca77501
commit 778e68a844
2 changed files with 47 additions and 17 deletions

View file

@ -212,24 +212,47 @@ class GeniusClient:
Returns:
Artist dict with: id, name, url, image_url
"""
hits = self.search(artist_name, per_page=5)
if not hits:
artists = self.search_artists(artist_name, limit=10)
if not artists:
return None
artist_lower = artist_name.lower().strip()
for hit in hits:
result = hit.get('result', {})
primary = result.get('primary_artist', {})
primary_name = (primary.get('name') or '').lower()
if primary and (artist_lower in primary_name or primary_name in artist_lower):
logger.debug(f"Found artist: {primary.get('name')}")
return primary
for a in artists:
a_name = (a.get('name') or '').lower()
if artist_lower in a_name or a_name in artist_lower:
logger.debug(f"Found artist: {a.get('name')}")
return a
# No confident match — let the worker mark as not_found and retry later
logger.debug(f"No artist match found in search results for: {artist_name}")
return None
@rate_limited
def search_artists(self, query: str, limit: int = 10) -> List[Dict[str, Any]]:
"""
Search for artists by name. Extracts unique artists from song results.
Returns:
List of artist dicts with: id, name, url, image_url
"""
hits = self.search(query, per_page=min(limit * 2, 20))
if not hits:
return []
seen_ids = set()
artists = []
for hit in hits:
result = hit.get('result', {})
primary = result.get('primary_artist', {})
if primary and primary.get('id') and primary['id'] not in seen_ids:
seen_ids.add(primary['id'])
artists.append(primary)
if len(artists) >= limit:
break
return artists
@rate_limited
def get_artist(self, artist_id: int) -> Optional[Dict[str, Any]]:
"""

View file

@ -12614,15 +12614,22 @@ def _search_service(service, entity_type, query):
raise ValueError("Genius worker not initialized")
client = genius_worker.client
if entity_type == 'artist':
result = client.search_artist(query)
if result:
return [{'id': str(result.get('id', '')), 'name': result.get('name', ''),
'image': result.get('image_url'), 'extra': ''}]
artists = client.search_artists(query, limit=8)
return [{'id': str(a.get('id', '')), 'name': a.get('name', ''),
'image': a.get('image_url'), 'extra': a.get('url', '')} for a in artists]
elif entity_type == 'track':
result = client.search_song(query, '')
if result:
return [{'id': str(result.get('id', '')), 'name': result.get('title', ''),
'image': result.get('song_art_image_url'), 'extra': result.get('artist_names', '')}]
# Search with broader results for manual matching
hits = client.search(f"{query}", per_page=10)
results = []
seen_ids = set()
for hit in hits:
r = hit.get('result', {})
rid = r.get('id')
if rid and rid not in seen_ids:
seen_ids.add(rid)
results.append({'id': str(rid), 'name': r.get('title', ''),
'image': r.get('song_art_image_url'), 'extra': r.get('artist_names', '')})
return results
return []
elif service == 'tidal':