Skip to content

Commit c43ca27

Browse files
committed
fix(files): resolve dual audio/video containers to the kind the app presents
The viewer routes .webm to the video player, but the Type column and the audio/video filters resolved it through EXTENSION_TO_MIME and read audio/webm, so one file showed as Audio and opened in a <video>. resolveEffectiveMimeType now consults a DUAL_CONTAINER_MIME map first. It stays out of EXTENSION_TO_MIME because the speech-to-text and ElevenLabs routes read that table directly, where a video/* label pushes a .webm into ffmpeg audio extraction it does not need.
1 parent 942cc08 commit c43ca27

2 files changed

Lines changed: 30 additions & 1 deletion

File tree

apps/sim/lib/uploads/utils/file-utils.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { createLogger } from '@sim/logger'
55
import { describe, expect, it } from 'vitest'
66
import {
77
extractStorageKey,
8+
getMimeTypeFromExtension,
89
inferContextFromKey,
910
isAbortError,
1011
isInternalFileUrl,
@@ -221,6 +222,19 @@ describe('resolveEffectiveMimeType', () => {
221222
expect(resolveEffectiveMimeType(undefined, 'clip.mp4')).toBe('video/mp4')
222223
})
223224

225+
it('resolves a dual audio/video container to video, matching how the app presents it', () => {
226+
expect(resolveEffectiveMimeType('application/octet-stream', 'clip.webm')).toBe('video/webm')
227+
expect(resolveEffectiveMimeType(null, 'clip.webm')).toBe('video/webm')
228+
})
229+
230+
it('still keeps an explicit audio/webm declared by the browser', () => {
231+
expect(resolveEffectiveMimeType('audio/webm', 'recording.webm')).toBe('audio/webm')
232+
})
233+
234+
it('leaves the upload-time extension table alone for dual containers', () => {
235+
expect(getMimeTypeFromExtension('webm')).toBe('audio/webm')
236+
})
237+
224238
it('stays generic when the extension identifies nothing either', () => {
225239
expect(resolveEffectiveMimeType('application/octet-stream', 'firmware.bin')).toBe(
226240
'application/octet-stream'

apps/sim/lib/uploads/utils/file-utils.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,19 @@ const EXTENSION_TO_MIME: Record<string, string> = {
382382

383383
const GENERIC_MIME_TYPE = 'application/octet-stream'
384384

385+
/**
386+
* Containers that hold either audio or video, mapped to the kind this app presents them as.
387+
* A filename cannot say which a `.webm` is, and the viewer already routes it to the video
388+
* player, so everything user-facing has to agree — otherwise one file reads "Audio" in the
389+
* Type column and opens in a `<video>`.
390+
*
391+
* Deliberately not folded into {@link EXTENSION_TO_MIME}: the speech-to-text and ElevenLabs
392+
* routes read that table directly to label an upload, and a `video/*` label there sends a
393+
* `.webm` down an ffmpeg extraction path it does not need. Callers that know which element
394+
* they are rendering retag from here — see {@link resolveMediaMimeType}.
395+
*/
396+
const DUAL_CONTAINER_MIME: Record<string, string> = { webm: 'video/webm' }
397+
385398
/** Every MIME type that identifies no format, including the legacy `binary/` spelling. */
386399
const GENERIC_MIME_TYPES = new Set([GENERIC_MIME_TYPE, 'binary/octet-stream'])
387400

@@ -409,7 +422,9 @@ export function resolveEffectiveMimeType(
409422
): string {
410423
const declared = declaredType?.trim()
411424
if (declared && !GENERIC_MIME_TYPES.has(declared)) return declared
412-
return getMimeTypeFromExtension(getFileExtension(filename))
425+
426+
const extension = getFileExtension(filename)
427+
return DUAL_CONTAINER_MIME[extension] ?? getMimeTypeFromExtension(extension)
413428
}
414429

415430
const MEDIA_FALLBACK_MIME = { audio: 'audio/mpeg', video: 'video/mp4' } as const

0 commit comments

Comments
 (0)