fix: infer MIME type from file extension for Telegram downloads

Telegram's file API returns application/octet-stream instead of the
actual image type. Added inferMimeType() that detects the real type
from the URL extension (jpg→image/jpeg, png→image/png, etc.) when
the server response Content-Type is generic.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Martin Porwoll 2026-03-01 10:36:09 +00:00
parent 11e91a26c1
commit 86d3eeb51f

View file

@ -3,6 +3,29 @@ import { createLogger } from './logger.js';
const log = createLogger('Download');
const MAX_FILE_SIZE = 20 * 1024 * 1024; // 20 MB
// Telegram's file API often returns application/octet-stream — infer from extension
const EXT_MIME_MAP: Record<string, string> = {
jpg: 'image/jpeg',
jpeg: 'image/jpeg',
png: 'image/png',
gif: 'image/gif',
webp: 'image/webp',
avif: 'image/avif',
svg: 'image/svg+xml',
pdf: 'application/pdf',
mp4: 'video/mp4',
};
function inferMimeType(url: string, headerType: string): string {
// If server returned a specific image/video/pdf type, trust it
if (headerType && !headerType.startsWith('application/octet-stream') && headerType !== 'text/plain') {
return headerType.split(';')[0].trim();
}
// Infer from URL extension
const ext = url.split(/[?#]/)[0].split('.').pop()?.toLowerCase() ?? '';
return EXT_MIME_MAP[ext] ?? headerType ?? 'application/octet-stream';
}
export async function downloadFile(url: string): Promise<{ buffer: Buffer; mimeType: string }> {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 30_000);
@ -23,8 +46,9 @@ export async function downloadFile(url: string): Promise<{ buffer: Buffer; mimeT
throw new Error(`Datei zu groß (${(arrayBuffer.byteLength / 1024 / 1024).toFixed(1)} MB). Maximum: 20 MB`);
}
const mimeType = response.headers.get('content-type') || 'application/octet-stream';
log.debug(`Downloaded ${arrayBuffer.byteLength} bytes, type: ${mimeType}`);
const rawType = response.headers.get('content-type') || 'application/octet-stream';
const mimeType = inferMimeType(url, rawType);
log.info(`Downloaded ${arrayBuffer.byteLength} bytes, header: ${rawType}, resolved: ${mimeType}`);
return { buffer: Buffer.from(arrayBuffer), mimeType };
} catch (error) {