diff --git a/web_server.py b/web_server.py index 16600209..edcee566 100644 --- a/web_server.py +++ b/web_server.py @@ -11331,6 +11331,51 @@ _SOURCE_ID_FIELD = { } +def _find_library_artist_for_source(database, source, source_artist_id, artist_name): + """Try to upgrade a source-artist click to a library lookup. + + Returns the library PK of an artist that matches either the source-specific + ID column (e.g. WHERE deezer_id = source_artist_id) or, as a fallback, the + artist name in the active server. Returns None if no match. + """ + column = _SOURCE_ID_FIELD.get(source) + if not column: + return None + try: + with database._get_connection() as conn: + cursor = conn.cursor() + # Match by source-specific ID column. Server-source-agnostic: any + # library record (Plex/Jellyfin/Navidrome/SoulSync) that has the + # right external ID is a hit. + cursor.execute( + f"SELECT id, name FROM artists WHERE {column} = ? LIMIT 1", + (str(source_artist_id),) + ) + row = cursor.fetchone() + if row: + return row[0] + + # Fallback: case-insensitive name match within the active server only, + # to avoid jumping the user across server contexts unintentionally. + if artist_name: + try: + active_server = config_manager.get_active_media_server() + except Exception: + active_server = None + if active_server: + cursor.execute( + "SELECT id FROM artists " + "WHERE LOWER(name) = LOWER(?) AND server_source = ? LIMIT 1", + (artist_name, active_server) + ) + row = cursor.fetchone() + if row: + return row[0] + except Exception as e: + logger.debug(f"Library upgrade lookup failed for {source}:{source_artist_id}: {e}") + return None + + def _build_source_only_artist_detail(artist_id, artist_name, source): """Synthesize an artist-detail response from a single metadata source for an artist that isn't in the local library. Used when `/api/artist-detail/` @@ -11514,9 +11559,26 @@ def get_artist_detail(artist_id): # Get artist discography from database db_result = database.get_artist_discography(artist_id) + # Library upgrade: if direct ID lookup missed AND we have a source hint, + # check whether the user already owns this artist in the library under + # a different ID (e.g. clicking a Deezer search result for an artist + # they have indexed in Plex). Prefer the library record so they get + # all their owned releases + enrichment instead of a bare source view. + if not db_result.get('success') and source_param in _SOURCE_ONLY_ARTIST_SOURCES: + library_pk = _find_library_artist_for_source( + database, source_param, artist_id, artist_name_arg + ) + if library_pk: + logger.info( + f"Source-id {source_param}:{artist_id} matched library artist " + f"PK={library_pk} — upgrading to library response" + ) + db_result = database.get_artist_discography(library_pk) + if not db_result.get('success'): - # Library lookup failed. If a metadata source was specified, fall back - # to a source-only response so the page can render a non-library artist. + # Library lookup still failed. If a metadata source was specified, + # fall back to a source-only response so the page can render a + # non-library artist. if source_param in _SOURCE_ONLY_ARTIST_SOURCES: return _build_source_only_artist_detail( artist_id, artist_name_arg, source_param diff --git a/webui/static/style.css b/webui/static/style.css index 9720cd29..99f28c7d 100644 --- a/webui/static/style.css +++ b/webui/static/style.css @@ -59400,20 +59400,18 @@ body[data-artist-source="source"] #artist-detail-page #enhanced-view-container { display: none !important; } -/* Library-only: completion bars, enrichment coverage, Top Tracks sidebar, Radio / Enhance Quality buttons */ -body[data-artist-source="source"] #artist-detail-page .collection-overview, +/* Library-only buttons: Radio + Enhance Quality both require owned tracks. + Enrichment coverage is a per-track DB enrichment percentage that doesn't + apply to artists you don't own. + .collection-overview and #artist-hero-sidebar both render usefully for + source artists (collection shows 0/N missing, Top Tracks pulls from + Last.fm by name) so they stay visible. */ body[data-artist-source="source"] #artist-detail-page .artist-enrichment-coverage, -body[data-artist-source="source"] #artist-detail-page #artist-hero-sidebar, body[data-artist-source="source"] #artist-detail-page #library-artist-radio-btn, body[data-artist-source="source"] #artist-detail-page #library-artist-enhance-btn { display: none !important; } -/* Library-only: section-stats "0 owned / 0 missing" counts don't apply */ -body[data-artist-source="source"] #artist-detail-page .section-stats { - display: none !important; -} - /* ========================================================================= Standalone /artist-detail page hero — blurred-background treatment