From 6d246d6e5876f61e53af1ff1cd4626ad0455a43e Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 3 Feb 2026 11:05:37 -0800 Subject: [PATCH] fix(transcription): map unsupported audio formats for Whisper/GPT-4o (#96) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audio transcription may receive formats that aren't in Whisper's supported list. Add mappings in the transcription module: - aac → m4a (AAC is M4A compatible) - amr → mp3 (mobile voice format) - opus → ogg (Opus in OGG container) - caf/x-caf → m4a (Apple CAF) - 3gp/3gpp → mp4 (mobile video format) This works for both whisper-1 and gpt-4o-transcribe models. Fixes #92 Written by Cameron ◯ Letta Code "The devil is in the details." - Ludwig Mies van der Rohe --- src/transcription/openai.ts | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/transcription/openai.ts b/src/transcription/openai.ts index 378c2e5..201edc2 100644 --- a/src/transcription/openai.ts +++ b/src/transcription/openai.ts @@ -80,22 +80,42 @@ function getMimeType(filename: string): string { } /** - * Normalize filename for Whisper API + * Map unsupported extensions to Whisper-compatible equivalents + * These mappings work for whisper-1 and gpt-4o-transcribe models + */ +const FORMAT_MAP: Record = { + 'aac': 'm4a', // AAC codec - M4A is AAC in MP4 container + 'amr': 'mp3', // AMR (mobile voice) - try as mp3 + 'opus': 'ogg', // Opus codec typically in OGG container + 'x-caf': 'm4a', // Apple CAF format + 'caf': 'm4a', // Apple CAF format (alternate) + '3gp': 'mp4', // 3GP mobile format + '3gpp': 'mp4', // 3GPP mobile format +}; + +/** + * Normalize filename for Whisper/GPT-4o transcription API * Converts unsupported extensions to supported equivalents */ function normalizeFilename(filename: string): string { const ext = filename.split('.').pop()?.toLowerCase(); - // AAC is just the codec - Whisper accepts it as m4a - if (ext === 'aac') { - return filename.replace(/\.aac$/i, '.m4a'); + if (!ext) { + return filename + '.ogg'; } // Check if already supported - if (ext && SUPPORTED_FORMATS.includes(ext)) { + if (SUPPORTED_FORMATS.includes(ext)) { return filename; } + // Map to supported format if we have a mapping + const mapped = FORMAT_MAP[ext]; + if (mapped) { + console.log(`[Transcription] Mapping .${ext} → .${mapped}`); + return filename.replace(new RegExp(`\\.${ext}$`, 'i'), `.${mapped}`); + } + // Default fallback - try as ogg console.warn(`[Transcription] Unknown format .${ext}, trying as .ogg`); return filename.replace(/\.[^.]+$/, '.ogg');