Fix Amazon artist detail: library upgrade lookup and artist images
Library upgrade: find_library_artist_for_source returned None immediately for Amazon because SOURCE_ID_FIELD has no 'amazon' entry (no DB column for Amazon artist IDs). The name-based fallback was unreachable. Fix: only skip the column query when column is None, not the whole function — name lookup now runs for any source when artist_name + active_server are provided. Artist images: add AmazonClient._get_artist_image_from_albums so the standard _get_artist_image_from_source path in metadata/artist_image.py can call it as a fallback (same hook iTunes/Deezer/Discogs expose). Searches by unslugified artist name, matches primary artist, fetches album cover from album_metadata. Test: updated test_source_only_set_matches_mapping_keys → _contains_all_mapped_ sources to assert subset (not equality) — SOURCE_ONLY_ARTIST_SOURCES intentionally includes sources without a DB column that rely on name-only lookup.
This commit is contained in:
parent
d944884ab4
commit
265fe5233e
3 changed files with 32 additions and 14 deletions
|
|
@ -601,6 +601,25 @@ class AmazonClient:
|
||||||
"""Not available from Amazon Music — returns None for compatibility."""
|
"""Not available from Amazon Music — returns None for compatibility."""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _get_artist_image_from_albums(self, artist_id: str) -> Optional[str]:
|
||||||
|
"""Return an album cover as artist image stand-in (T2Tunes has no artist images)."""
|
||||||
|
search_name = _unslugify(artist_id)
|
||||||
|
try:
|
||||||
|
items = self.search_raw(search_name, types="track,album")
|
||||||
|
except AmazonClientError:
|
||||||
|
return None
|
||||||
|
name_lower = search_name.lower()
|
||||||
|
for item in items:
|
||||||
|
if _primary_artist(item.artist_name).lower() != name_lower:
|
||||||
|
continue
|
||||||
|
asin = item.album_asin or item.asin
|
||||||
|
if not asin:
|
||||||
|
continue
|
||||||
|
metas = self._fetch_album_metas([asin])
|
||||||
|
if asin in metas and metas[asin].get("image"):
|
||||||
|
return metas[asin]["image"]
|
||||||
|
return None
|
||||||
|
|
||||||
# ==================== Interface Aliases (match DeezerClient method names) ====================
|
# ==================== Interface Aliases (match DeezerClient method names) ====================
|
||||||
get_album_metadata = get_album
|
get_album_metadata = get_album
|
||||||
get_artist_info = get_artist
|
get_artist_info = get_artist
|
||||||
|
|
|
||||||
|
|
@ -58,19 +58,18 @@ def find_library_artist_for_source(
|
||||||
Returns ``None`` on miss or on any database error.
|
Returns ``None`` on miss or on any database error.
|
||||||
"""
|
"""
|
||||||
column = SOURCE_ID_FIELD.get(source)
|
column = SOURCE_ID_FIELD.get(source)
|
||||||
if not column:
|
|
||||||
return None
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with database._get_connection() as conn:
|
with database._get_connection() as conn:
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute(
|
if column:
|
||||||
f"SELECT id, name FROM artists WHERE {column} = ? LIMIT 1",
|
cursor.execute(
|
||||||
(str(source_artist_id),),
|
f"SELECT id, name FROM artists WHERE {column} = ? LIMIT 1",
|
||||||
)
|
(str(source_artist_id),),
|
||||||
row = cursor.fetchone()
|
)
|
||||||
if row:
|
row = cursor.fetchone()
|
||||||
return row[0]
|
if row:
|
||||||
|
return row[0]
|
||||||
|
|
||||||
if artist_name and active_server:
|
if artist_name and active_server:
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
|
|
|
||||||
|
|
@ -68,11 +68,11 @@ class TestSourceIdFieldMapping:
|
||||||
"(and the test body) to match."
|
"(and the test body) to match."
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_source_only_set_matches_mapping_keys(self):
|
def test_source_only_set_contains_all_mapped_sources(self):
|
||||||
"""Sources eligible for the source-only fallback must all have a
|
"""Every source with a SOURCE_ID_FIELD column must also be in
|
||||||
column to look them up by — otherwise the upgrade path silently
|
SOURCE_ONLY_ARTIST_SOURCES. The reverse is not required — sources
|
||||||
returns None."""
|
without a DB column (e.g. amazon) use name-based lookup only."""
|
||||||
assert SOURCE_ONLY_ARTIST_SOURCES == frozenset(SOURCE_ID_FIELD.keys())
|
assert frozenset(SOURCE_ID_FIELD.keys()).issubset(SOURCE_ONLY_ARTIST_SOURCES)
|
||||||
|
|
||||||
def test_every_mapped_column_exists_on_artists_table(self, db):
|
def test_every_mapped_column_exists_on_artists_table(self, db):
|
||||||
"""Regression for the 2026-04 ``deezer_artist_id`` typo: every column
|
"""Regression for the 2026-04 ``deezer_artist_id`` typo: every column
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue