From 265fe5233e804ca3f6cac41c4a897a11562054f0 Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Sat, 16 May 2026 17:01:37 -0700 Subject: [PATCH] Fix Amazon artist detail: library upgrade lookup and artist images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- core/amazon_client.py | 19 +++++++++++++++++++ core/artist_source_lookup.py | 17 ++++++++--------- tests/metadata/test_artist_source_lookup.py | 10 +++++----- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/core/amazon_client.py b/core/amazon_client.py index 278493c4..3febed1e 100644 --- a/core/amazon_client.py +++ b/core/amazon_client.py @@ -601,6 +601,25 @@ class AmazonClient: """Not available from Amazon Music — returns None for compatibility.""" 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) ==================== get_album_metadata = get_album get_artist_info = get_artist diff --git a/core/artist_source_lookup.py b/core/artist_source_lookup.py index d7497e1c..72888af2 100644 --- a/core/artist_source_lookup.py +++ b/core/artist_source_lookup.py @@ -58,19 +58,18 @@ def find_library_artist_for_source( Returns ``None`` on miss or on any database error. """ column = SOURCE_ID_FIELD.get(source) - if not column: - return None try: with database._get_connection() as conn: cursor = conn.cursor() - cursor.execute( - f"SELECT id, name FROM artists WHERE {column} = ? LIMIT 1", - (str(source_artist_id),), - ) - row = cursor.fetchone() - if row: - return row[0] + if column: + cursor.execute( + f"SELECT id, name FROM artists WHERE {column} = ? LIMIT 1", + (str(source_artist_id),), + ) + row = cursor.fetchone() + if row: + return row[0] if artist_name and active_server: cursor.execute( diff --git a/tests/metadata/test_artist_source_lookup.py b/tests/metadata/test_artist_source_lookup.py index 75ca4582..7e803d70 100644 --- a/tests/metadata/test_artist_source_lookup.py +++ b/tests/metadata/test_artist_source_lookup.py @@ -68,11 +68,11 @@ class TestSourceIdFieldMapping: "(and the test body) to match." ) - def test_source_only_set_matches_mapping_keys(self): - """Sources eligible for the source-only fallback must all have a - column to look them up by — otherwise the upgrade path silently - returns None.""" - assert SOURCE_ONLY_ARTIST_SOURCES == frozenset(SOURCE_ID_FIELD.keys()) + def test_source_only_set_contains_all_mapped_sources(self): + """Every source with a SOURCE_ID_FIELD column must also be in + SOURCE_ONLY_ARTIST_SOURCES. The reverse is not required — sources + without a DB column (e.g. amazon) use name-based lookup only.""" + assert frozenset(SOURCE_ID_FIELD.keys()).issubset(SOURCE_ONLY_ARTIST_SOURCES) def test_every_mapped_column_exists_on_artists_table(self, db): """Regression for the 2026-04 ``deezer_artist_id`` typo: every column