import { createHash } from 'node:crypto' import { existsSync, mkdirSync } from 'node:fs' import { writeFile } from 'node:fs/promises' import path from 'node:path' const EXT_BY_TYPE: Record = { 'image/jpeg': '.jpg', 'image/png': '.png', 'image/webp': '.webp', 'image/gif': '.gif', } /** * Downloads `url` into `artworkDir` keyed by its sha256 hash. Returns the * stored file name (e.g. `abc123….jpg`) or null when the url is empty or the * download fails — artwork caching is best-effort and must never break adds. */ export async function cacheArtwork( artworkDir: string, url: string, fetchImpl: typeof fetch = fetch ): Promise { if (!url) return null const key = createHash('sha256').update(url).digest('hex') mkdirSync(artworkDir, { recursive: true }) for (const ext of Object.values(EXT_BY_TYPE)) { if (existsSync(path.join(artworkDir, key + ext))) return key + ext } let res: Response try { res = await fetchImpl(url) } catch { return null } if (!res.ok) return null const type = (res.headers.get('content-type') ?? '').split(';')[0]?.trim() ?? '' const ext = EXT_BY_TYPE[type] ?? '.jpg' const buf = Buffer.from(await res.arrayBuffer()) await writeFile(path.join(artworkDir, key + ext), buf) return key + ext }