diff --git a/server/src/artwork.ts b/server/src/artwork.ts index e0cd156..3af1218 100644 --- a/server/src/artwork.ts +++ b/server/src/artwork.ts @@ -22,20 +22,24 @@ export async function cacheArtwork( ): 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) + 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 } 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 } diff --git a/server/test/artwork.test.ts b/server/test/artwork.test.ts index ada14f5..225cb86 100644 --- a/server/test/artwork.test.ts +++ b/server/test/artwork.test.ts @@ -75,4 +75,20 @@ describe('cacheArtwork', () => { rmSync(dir, { recursive: true, force: true }) } }) + + it('returns null when the response body fails mid-download', async () => { + const dir = tempDir() + try { + const fetcher = (async () => + new Response(new ReadableStream({ + start(controller) { + controller.enqueue(new Uint8Array([0xff])) + controller.error(new TypeError('network error mid-body')) + }, + }), { status: 200, headers: { 'content-type': 'image/jpeg' } })) as unknown as typeof fetch + expect(await cacheArtwork(dir, 'https://x/abort.jpg', fetcher)).toBeNull() + } finally { + rmSync(dir, { recursive: true, force: true }) + } + }) })