diff --git a/src/utils/download.ts b/src/utils/download.ts index 24239b0..0ec6351 100644 --- a/src/utils/download.ts +++ b/src/utils/download.ts @@ -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 = { + 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) {