feat(web): CoverArt + reusable AlbumModal
CoverArt loads album art from the Cover Art Archive CDN by release-group MBID (browser-side, cached, graceful placeholder fallback). AlbumModal shows cover + tracklist (fetched from MB) with slots for a status chip and context actions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
|
||||
/** Album art from the Cover Art Archive (keyed by release-group MBID), loaded straight from
|
||||
* their CDN by the browser — no backend, no MB API rate limit. Falls back to a placeholder
|
||||
* when there's no MBID or no 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={`https://coverartarchive.org/release-group/${rgMbid}/front-${size}`}
|
||||
alt={alt}
|
||||
loading="lazy"
|
||||
onError={() => setFailed(true)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user