From 4e9df89f93477e6378999ebb1161f7f2ec2898fe Mon Sep 17 00:00:00 2001 From: Caleb Allen Date: Sun, 29 Mar 2026 15:53:45 -0400 Subject: [PATCH] feat: add artist images to similar artists section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After similar artists load, fetch images for each via GetArtistImageURL in parallel. Images pop in as they resolve — letter avatars remain as fallback for artists without images. Each image is cached on disk after first resolution, so subsequent views are instant. --- .../explore-artist-details.ts | 48 ++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/explore-artist-details/explore-artist-details.ts b/frontend/src/components/explore-artist-details/explore-artist-details.ts index 1dc618a..e36a18d 100644 --- a/frontend/src/components/explore-artist-details/explore-artist-details.ts +++ b/frontend/src/components/explore-artist-details/explore-artist-details.ts @@ -91,6 +91,7 @@ export class ExploreArtistDetails extends LitElement { @state() private similarArtists: LBSimilarArtist[] = []; @state() private loadingSimilar = true; @state() private artistImageURL = ''; + @state() private similarImageURLs = new Map(); private libraryMBIDs = new Set(); /* ── Styles ── */ @@ -474,6 +475,13 @@ export class ExploreArtistDetails extends LitElement { text-transform: uppercase; user-select: none; flex-shrink: 0; + overflow: hidden; + } + + .similar-avatar img { + width: 100%; + height: 100%; + object-fit: cover; } .similar-name { @@ -587,6 +595,31 @@ export class ExploreArtistDetails extends LitElement { } finally { this.loadingSimilar = false; } + + // Fire-and-forget: resolve images for similar artists in parallel. + if (this.similarArtists.length > 0) { + void this.fetchSimilarArtistImages(); + } + } + + private async fetchSimilarArtistImages() { + const artists = this.similarArtists; + // Fetch in parallel — each call is cached after first resolution. + await Promise.allSettled( + artists.map(async (a) => { + try { + const url = await GetArtistImageURL(a.artistMbid); + if (url) { + this.similarImageURLs = new Map(this.similarImageURLs).set( + a.artistMbid, + url, + ); + } + } catch { + // No image — letter avatar stays. + } + }), + ); } private async fetchArtistImage(mbid: string) { @@ -675,6 +708,12 @@ export class ExploreArtistDetails extends LitElement { } } + /** On similar-artist image error, remove the img so the letter initial shows. */ + private handleSimilarImageError(e: Event) { + const img = e.target as HTMLImageElement; + img.remove(); + } + /* ── Helpers ── */ private getInitial(name: string): string { @@ -977,6 +1016,7 @@ export class ExploreArtistDetails extends LitElement {
${this.similarArtists.map((a) => { const hue = nameToHue(a.name); + const imgURL = this.similarImageURLs.get(a.artistMbid); return html`
- ${a.name.charAt(0).toUpperCase()} + ${imgURL + ? html`${a.name}` + : a.name.charAt(0).toUpperCase()}