From 9106617538e3d598290361ce9c64ec75f6a136e5 Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Wed, 22 Apr 2026 19:19:48 -0700 Subject: [PATCH] Show collection bars + Top Tracks for source artists, upgrade source clicks to library when possible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three issues from screenshots: 1. .collection-overview was hidden for source artists (CSS rule too aggressive). It actually renders fine — just shows 0/N "missing" for each release type, which is useful info. Removed from the hide rules. 2. #artist-hero-sidebar (Top Tracks "Popular on Last.fm") was also hidden. The renderer (_loadArtistTopTracks in library.js) already fetches by artist name via /api/artist/0/lastfm-top-tracks, so it works for source artists too. Removed from the hide rules. 3. Clicking a source-artist result for someone you ALREADY have in the library was loading the bare source view instead of the library view (bug). Backend now does a "library upgrade" lookup in get_artist_detail: when the direct ID lookup misses but a source param is provided, search the artists table by the source- specific ID column (deezer_id / spotify_artist_id / etc.). If a match exists, use that library PK and the rest of the library path runs normally — owned releases, enrichment, completion bars, all the goodies you'd see if you'd clicked from Library. Falls back to a name match within the active server, then to the source-only response if nothing matches. The remaining library-only items (artist-enrichment-coverage, Radio button, Enhance Quality button) stay hidden for source artists since they all require owned tracks. --- web_server.py | 66 ++++++++++++++++++++++++++++++++++++++++-- webui/static/style.css | 14 ++++----- 2 files changed, 70 insertions(+), 10 deletions(-) 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