fix(ai): name data-URL audio with its real extension

A data URL's filename came from its MIME subtype, so `audio/mpeg`
became `input.mpeg` and `audio/x-wav` became `input.x-wav`. OpenAI's
transcription API picks the decoder from the extension and rejected
both. Map the MIME types whose subtype isn't the extension.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Filip KujundžićandClaude Opus 5.5 committed 2026-10-02 19:28:13 +02:00
1 parent abd0d809d5
commit 22d52cf544
2 files changed
+33 -1

No files matched your search

@@ -237,6 +237,24 @@ describe('loadFileInput data URL', () => {
expect(result.mimeType).toBe('image/svg+xml');
});
// Audio upstreams pick the decoder from the extension; `input.mpeg` is
// rejected as corrupt.
it.each([
['audio/mpeg', 'input.mp3'],
['audio/x-wav', 'input.wav'],
['audio/wave', 'input.wav'],
['audio/x-m4a', 'input.m4a'],
['audio/ogg', 'input.ogg'],
])('names a %s data URL %s', async (mime, filename) => {
const { actor } = await makeUser();
const result = await callLoadFileInput(
actor,
`data:${mime};base64,QUJD`,
);
expect(result.filename).toBe(filename);
expect(result.mimeType).toBe(mime);
});
it('defaults MIME to application/octet-stream when omitted', async () => {
const { actor } = await makeUser();
const result = await callLoadFileInput(actor, 'data:;base64,QUJD');
+15 -1
View File
@@ -321,8 +321,22 @@ function assertMax(buffer: Buffer, maxBytes?: number): void {
}
}
/**
* MIME types whose subtype isn't the file extension. Upstreams like OpenAI's
* audio API pick the decoder from the extension and reject `input.mpeg`.
*/
const EXT_BY_MIME: Record<string, string> = {
'audio/mpeg': 'mp3',
'audio/x-wav': 'wav',
'audio/wave': 'wav',
'audio/x-m4a': 'm4a',
};
function filenameFromMime(mime: string): string {
const ext = mime.split('/')[1]?.split('+')[0] ?? 'bin';
const ext =
EXT_BY_MIME[mime.toLowerCase()] ??
mime.split('/')[1]?.split('+')[0] ??
'bin';
return `input.${ext}`;
}