Skip to content

Commit 0a62147

Browse files
committed
fix(files): keep the dual-container video default out of the persisted type
resolveFileType writes user_file.content_type, and it delegated to resolveEffectiveMimeType, so DUAL_CONTAINER_MIME could persist video/webm. The speech-to-text route reads that back as file.type, which sends the upload into the ffmpeg extraction path the previous commit set out to avoid. resolveFileType now resolves through EXTENSION_TO_MIME alone; the video default stays on the presentation path. Both share an identifiesFormat predicate.
1 parent c43ca27 commit 0a62147

2 files changed

Lines changed: 27 additions & 2 deletions

File tree

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
isNetworkError,
1414
processSingleFileToUserFile,
1515
resolveEffectiveMimeType,
16+
resolveFileType,
1617
resolveMediaMimeType,
1718
resolveTrustedFileContext,
1819
} from '@/lib/uploads/utils/file-utils'
@@ -235,6 +236,16 @@ describe('resolveEffectiveMimeType', () => {
235236
expect(getMimeTypeFromExtension('webm')).toBe('audio/webm')
236237
})
237238

239+
it('never lets the video default reach the type that gets persisted', () => {
240+
// resolveFileType writes user_file.content_type, which the speech-to-text route reads
241+
// back as file.type — a video/* value there sends the upload into ffmpeg extraction.
242+
expect(resolveFileType({ type: '', name: 'clip.webm' })).toBe('audio/webm')
243+
expect(resolveFileType({ type: 'application/octet-stream', name: 'clip.webm' })).toBe(
244+
'audio/webm'
245+
)
246+
expect(resolveFileType({ type: 'audio/webm', name: 'clip.webm' })).toBe('audio/webm')
247+
})
248+
238249
it('stays generic when the extension identifies nothing either', () => {
239250
expect(resolveEffectiveMimeType('application/octet-stream', 'firmware.bin')).toBe(
240251
'application/octet-stream'

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,11 @@ const DUAL_CONTAINER_MIME: Record<string, string> = { webm: 'video/webm' }
398398
/** Every MIME type that identifies no format, including the legacy `binary/` spelling. */
399399
const GENERIC_MIME_TYPES = new Set([GENERIC_MIME_TYPE, 'binary/octet-stream'])
400400

401+
/** Whether a declared MIME type names an actual format, rather than "some bytes". */
402+
function identifiesFormat(declared: string | undefined): declared is string {
403+
return declared !== undefined && declared !== '' && !GENERIC_MIME_TYPES.has(declared)
404+
}
405+
401406
/**
402407
* Get MIME type from file extension (fallback if not provided)
403408
*/
@@ -421,7 +426,7 @@ export function resolveEffectiveMimeType(
421426
filename: string
422427
): string {
423428
const declared = declaredType?.trim()
424-
if (declared && !GENERIC_MIME_TYPES.has(declared)) return declared
429+
if (identifiesFormat(declared)) return declared
425430

426431
const extension = getFileExtension(filename)
427432
return DUAL_CONTAINER_MIME[extension] ?? getMimeTypeFromExtension(extension)
@@ -459,14 +464,23 @@ export function resolveMediaMimeType(
459464
* when the browser reports an empty or generic type. Pass
460465
* `{ preserveOctetStream: true }` for direct PUT uploads where the
461466
* browser-supplied content-type must match the presigned handshake exactly.
467+
*
468+
* This is the type that gets *persisted*, so it resolves through
469+
* {@link EXTENSION_TO_MIME} alone and deliberately skips {@link DUAL_CONTAINER_MIME}.
470+
* Storing `video/webm` here would put a `video/*` type on the record that the
471+
* speech-to-text route reads as `file.type`, sending the upload down the ffmpeg
472+
* extraction path — the same failure keeping the dual-container default out of the
473+
* extension table avoids. Presentation resolves separately, in
474+
* {@link resolveEffectiveMimeType}.
462475
*/
463476
export function resolveFileType(
464477
file: { type: string; name: string },
465478
options?: { preserveOctetStream?: boolean }
466479
): string {
467480
const browserType = file.type?.trim()
468481
if (browserType && options?.preserveOctetStream) return browserType
469-
return resolveEffectiveMimeType(browserType, file.name)
482+
if (identifiesFormat(browserType)) return browserType
483+
return getMimeTypeFromExtension(getFileExtension(file.name))
470484
}
471485

472486
/**

0 commit comments

Comments
 (0)