Skip to content

fix(files): render audio and video stored as application/octet-stream - #6341

Merged
waleedlatif1 merged 4 commits into
stagingfrom
fix/file-viewer-octet-stream-render
Aug 6, 2026
Merged

fix(files): render audio and video stored as application/octet-stream#6341
waleedlatif1 merged 4 commits into
stagingfrom
fix/file-viewer-octet-stream-render

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • The file viewer built the blob backing <audio>/<video> from the record's stored content type with a truthiness fallback (file.type || fallback), so a stored application/octet-stream was passed straight through and the element could not determine the format. Downloading the same file worked because the download path derives its content type from the filename.
  • Added resolveEffectiveMimeType, which resolves a generic stored type against the filename, and routed the media blob, the Type column, and the type filter through it — an octet-stream video was also invisible to the Audio/Video/Image filters.
  • .webm now maps to video/webm rather than audio/webm: a <video> element plays an audio-only stream, an <audio> element drops the picture.
  • .bmp, .avif and .ico now preview — upload accepts them but the viewer sent them to the download-only path. They're also served with their real content type so nosniff doesn't block the <img>. .tiff and .heic stay unsupported since no browser renders them.
  • .jsonl opens in the text editor, and filled the extension-to-mime gaps for .mmd, .diff, .patch and .fish.

Type of Change

  • Bug fix

Testing

Unit tests added for resolveEffectiveMimeType and the new preview categories; verified they fail against the old behavior. bun run check:api-validation, typecheck, and lint pass.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

The file viewer built the blob backing <audio>/<video> from the record's stored
content type with a truthiness fallback, so a stored application/octet-stream
was passed straight through and the element could not determine the format.
Downloading the same file worked because the download path derives its content
type from the filename.

- Add resolveEffectiveMimeType, which resolves a generic stored type against the
  filename, and use it for the media blob, the type column, and the type filter
  (an octet-stream video was also invisible to the Audio/Video/Image filters)
- Map .webm to video/webm rather than audio/webm: a <video> element plays an
  audio-only stream, an <audio> element drops the picture
- Preview .bmp, .avif and .ico, which upload accepts but the viewer sent to the
  download-only path; serve them with their real content type so nosniff does
  not block them. .tiff and .heic stay unsupported - no browser renders them
- Open .jsonl in the text editor, and fill the extension-to-mime gaps for
  .mmd, .diff, .patch and .fish
@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Aug 6, 2026 9:47pm

Request Review

@cursor

cursor Bot commented Aug 6, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches file serving headers and persisted vs display MIME split (especially .webm); behavior is well-tested but affects upload pipelines and public share routes.

Overview
Fixes workspace file preview and listing when records store application/octet-stream (or empty/generic types) instead of a real MIME — common on browser uploads.

MIME resolution: Adds resolveEffectiveMimeType and resolveMediaMimeType so types are inferred from the filename when the stored type is generic. resolveFileType (persisted content_type) still uses the extension table only and keeps .webm as audio/webm so speech-to-text does not treat uploads as video/* and trigger unnecessary ffmpeg paths; user-facing presentation maps .webm to video/webm via a separate dual-container map.

Viewer & files UI: Audio/video blob URLs, the Type column, and audio/video/image filters use effective MIME instead of raw file.type.

Images: .bmp, .avif, and .ico are image-previewable; public shared file content uses filename-based getContentType (aligned with workspace serve) so nosniff does not block inline display.

Serve utils: Extends extension/MIME maps and safe inline types for more image, audio, and video formats.

Reviewed by Cursor Bugbot for commit 0a62147. Configure here.

@greptile-apps

greptile-apps Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR improves previews for files whose stored MIME type is generic by deriving an effective type from the filename.

  • Uses resolved MIME types for media blobs, file labels, and type filters.
  • Adds browser-previewable image formats and expands extension-to-MIME mappings.
  • Aligns public-share content responses with filename-derived serving behavior.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains within the eligible follow-up review scope.

No blocking failure remains.

Important Files Changed

Filename Overview
apps/sim/lib/uploads/utils/file-utils.ts Adds presentation-oriented MIME resolution, media-element retagging, and extension mappings while retaining legacy persisted WebM typing.
apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/file-viewer.tsx Uses the resolved media MIME type when constructing blob URLs for audio and video previews.
apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/file-category.ts Expands native image preview categories for AVIF, BMP, and ICO files.
apps/sim/app/workspace/[workspaceId]/files/files.tsx Applies effective MIME resolution consistently to displayed type labels and media/image filters.
apps/sim/app/api/files/public/[token]/content/route.ts Derives passthrough public-share response types from filenames to support inline rendering under nosniff.
apps/sim/app/api/files/utils.ts Adds MIME mappings and safe-inline entries for the newly previewable media and image formats.

Reviews (4): Last reviewed commit: "fix(files): keep the dual-container vide..." | Re-trigger Greptile

Follow-up to the review pass on this branch.

- Revert the global .webm -> video/webm remap. EXTENSION_TO_MIME is shared with
  non-viewer callers, and a .webm with an empty stored type would have started
  taking the STT route's video branch (stt/route.ts:211 -> extractAudioFromVideo),
  which 500s where no ffmpeg binary is on PATH. The ambiguity is now settled in
  resolveMediaMimeType, which knows which element the caller is rendering
- Resolve the public share route's Content-Type from the filename via
  getContentType, matching the workspace serve route, instead of echoing the
  client-declared stored type into a public unauthenticated response. Add the
  audio/video entries contentTypeMap was missing so a shared media file keeps a
  real Content-Type (disposition is unchanged - none are inline-safe)
- Make resolveEffectiveMimeType total (string, not string | null); the null
  contract only bought one label edge case and cost a ?? at every call site,
  one of which was dead
- Drop .jsonl from the text-editable set. The editor loads the whole file and
  only CSV has a byte cap, so a large .jsonl would trade a download-only
  fallback for a crashed tab. Needs the size guard generalized first
- Trim two comments that restated their code
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/lib/uploads/utils/file-utils.ts Outdated
…resents

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.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/lib/uploads/utils/file-utils.ts
…d 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.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 0a62147. Configure here.

@waleedlatif1
waleedlatif1 merged commit a4973ec into staging Aug 6, 2026
5 checks passed
@waleedlatif1
waleedlatif1 deleted the fix/file-viewer-octet-stream-render branch August 6, 2026 22:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant