Files
Lyra/web/src/app/_ui/cover-art.tsx
T
Jonathan d52517ef39 feat(web): cache cover art + dedupe Recently Pressed
- Cover art now loads through GET /api/cover/[rgMbid], which fetches from the
  Cover Art Archive once, caches the image on disk (LYRA_COVER_CACHE, default
  /tmp/lyra-covers), and serves it with a 30-day Cache-Control — so the browser
  stops re-fetching it on every Library / Recently-Pressed render. Missing covers
  get a negative marker (re-checked daily). CoverArt points at the endpoint.
- Recently Pressed dedupes by artist+album (re-requesting/re-downloading an album
  left multiple completed Requests showing the same album repeatedly); newest of
  each is kept.

web 164 tests, tsc + build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 20:03:36 +02:00

39 lines
1010 B
TypeScript

"use client";
import { useState } from "react";
/** Album art keyed by release-group MBID, served through /api/cover (which caches it on disk +
* sends a long Cache-Control, so it isn't re-fetched from the Cover Art Archive on every
* Library / Recently-Pressed render). Falls back to a placeholder when there's no MBID or art. */
export function CoverArt({
rgMbid,
alt,
size = 250,
className,
}: {
rgMbid: string | null | undefined;
alt: string;
size?: 250 | 500;
className?: string;
}) {
const [failed, setFailed] = useState(false);
const cls = `cover ${className ?? ""}`.trim();
if (!rgMbid || failed) {
return (
<div className={`${cls} cover-fallback`} role="img" aria-label={alt}>
<span></span>
</div>
);
}
return (
// eslint-disable-next-line @next/next/no-img-element
<img
className={cls}
src={`/api/cover/${rgMbid}?size=${size}`}
alt={alt}
loading="lazy"
onError={() => setFailed(true)}
/>
);
}