diff --git a/web/src/lib/components/AlbumCard.test.ts b/web/src/lib/components/AlbumCard.test.ts index d85c8926..81ee840f 100644 --- a/web/src/lib/components/AlbumCard.test.ts +++ b/web/src/lib/components/AlbumCard.test.ts @@ -62,11 +62,13 @@ describe('AlbumCard', () => { expect(screen.queryByText(/1959/)).not.toBeInTheDocument(); }); - test('broken cover falls back to FALLBACK_COVER data URL', async () => { + test('broken cover falls back to FALLBACK_COVER asset', async () => { const { container } = render(AlbumCard, { props: { album } }); const img = container.querySelector('img') as HTMLImageElement; await fireEvent.error(img); - expect(img.src).toBe(FALLBACK_COVER); + // jsdom resolves relative URLs against the test origin; assert containment + // rather than strict equality to keep the test agnostic of the host. + expect(img.src).toContain(FALLBACK_COVER); }); test('+ queue button fetches album detail and calls enqueueTracks', async () => { diff --git a/web/src/lib/media/covers.ts b/web/src/lib/media/covers.ts index 7c53acab..5304a6fb 100644 --- a/web/src/lib/media/covers.ts +++ b/web/src/lib/media/covers.ts @@ -1,26 +1,10 @@ -// Fallback cover — an inline SVG data URL showing a beamed-eighth-notes -// glyph on a slate field. Used by AlbumCard / CompactTrackCard / PlayerBar's -// onerror handler when the real cover fails (e.g. scanner didn't pick up -// embedded art and there's no sidecar). Data URL avoids an extra HTTP -// round-trip; SVG keeps it crisp at any size we render. -// -// The glyph is two filled note heads connected by a slanted beam — bolder -// and more recognisable than a single-note silhouette, while staying muted -// enough to read as a placeholder rather than real content. -export const FALLBACK_COVER = - 'data:image/svg+xml;utf8,' + - encodeURIComponent( - ` - - - - - - - - - ` - ); +// Fallback cover used when the real cover fails to load (e.g. scanner +// didn't pick up embedded art and there's no sidecar). Served as a static +// asset from web/static/placeholders/ so the operator can swap the artwork +// without touching code. SvelteKit + adapter-static publishes everything +// under web/static/ at the URL root, and the Go binary embeds the build +// output, so this resolves identically in dev and production. +export const FALLBACK_COVER = '/placeholders/album-fallback.svg'; export function coverUrl(albumId: string): string { return `/api/albums/${albumId}/cover`; diff --git a/web/static/placeholders/album-fallback.svg b/web/static/placeholders/album-fallback.svg new file mode 100644 index 00000000..a864747f --- /dev/null +++ b/web/static/placeholders/album-fallback.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file