fix: rate-limit MB url-rels fetches, serialize frontend image loads

Artist image resolution was hitting musicbrainz.org with 10
concurrent unthrottled requests per search — enough to trigger
MB's rate limit rejection. Two fixes:

Backend: add dedicated 1 req/s RateLimiter for MB url-rels fetches
in ArtistImageProvider. Each fetch waits on the limiter before
the HTTP call. Results are cached 30 days so repeat lookups are
instant.

Frontend: switch loadArtistImages from concurrent fire-all to
sequential await loop. Each artist image loads one at a time,
images appear progressively as they resolve instead of all
failing from rate limit rejection.
This commit is contained in:
2026-03-25 22:19:18 -04:00
parent 104e469774
commit 9b88b88523
3 changed files with 27 additions and 19 deletions
@@ -666,25 +666,25 @@ export class ExploreView extends LitElement {
* Load artist images for all visible artist cards. Each call
* is async and updates the cache + re-renders on success.
*/
private loadArtistImages() {
private async loadArtistImages() {
if (!this.results?.artists?.length) return;
// Load sequentially to avoid hammering the MB rate limiter.
for (const a of this.results.artists) {
if (this.artistImageCache.has(a.mbid)) continue;
// Mark as loading.
this.artistImageCache.set(a.mbid, '');
GetArtistImageURL(a.mbid)
.then((url) => {
if (url) {
this.artistImageCache.set(a.mbid, url);
this.requestUpdate();
}
})
.catch(() => {
// No image — leave empty string.
});
try {
const url = await GetArtistImageURL(a.mbid);
if (url) {
this.artistImageCache.set(a.mbid, url);
this.requestUpdate();
}
} catch {
// No image — leave empty string.
}
}
}