Harden cover art fetching
This commit is contained in:
@@ -494,12 +494,47 @@ export class YoutubeService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await fetch(coverUrl);
|
let parsedCoverUrl: URL;
|
||||||
|
try {
|
||||||
|
parsedCoverUrl = new URL(coverUrl);
|
||||||
|
} catch {
|
||||||
|
throw new Error('Invalid cover art URL');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!['http:', 'https:'].includes(parsedCoverUrl.protocol)) {
|
||||||
|
throw new Error('Rejected non-HTTP cover art URL');
|
||||||
|
}
|
||||||
|
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeout = setTimeout(() => controller.abort(), 15_000);
|
||||||
|
|
||||||
|
let res: Response;
|
||||||
|
try {
|
||||||
|
res = await fetch(parsedCoverUrl.toString(), { signal: controller.signal });
|
||||||
|
} finally {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
throw new Error(`Failed to fetch cover art: ${res.status}`);
|
throw new Error(`Failed to fetch cover art: ${res.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const contentType = res.headers.get('content-type')?.split(';')[0].toLowerCase() || '';
|
||||||
|
if (!['image/jpeg', 'image/png', 'image/webp'].includes(contentType)) {
|
||||||
|
throw new Error(`Rejected unsupported cover art type: ${contentType || 'unknown'}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxCoverBytes = 5 * 1024 * 1024;
|
||||||
|
const contentLength = Number(res.headers.get('content-length') || 0);
|
||||||
|
if (contentLength > maxCoverBytes) {
|
||||||
|
throw new Error(`Rejected oversized cover art: ${contentLength} bytes`);
|
||||||
|
}
|
||||||
|
|
||||||
const arrayBuf = await res.arrayBuffer();
|
const arrayBuf = await res.arrayBuffer();
|
||||||
|
if (arrayBuf.byteLength > maxCoverBytes) {
|
||||||
|
throw new Error(`Rejected oversized cover art: ${arrayBuf.byteLength} bytes`);
|
||||||
|
}
|
||||||
|
|
||||||
const imageBuffer = Buffer.from(arrayBuf);
|
const imageBuffer = Buffer.from(arrayBuf);
|
||||||
|
|
||||||
NodeID3.write(
|
NodeID3.write(
|
||||||
@@ -507,7 +542,7 @@ export class YoutubeService {
|
|||||||
title,
|
title,
|
||||||
artist,
|
artist,
|
||||||
APIC: {
|
APIC: {
|
||||||
mime: 'image/jpeg',
|
mime: contentType,
|
||||||
type: { id: 3, name: 'front cover' },
|
type: { id: 3, name: 'front cover' },
|
||||||
description: 'cover',
|
description: 'cover',
|
||||||
imageBuffer,
|
imageBuffer,
|
||||||
|
|||||||
Reference in New Issue
Block a user