-
Notifications
You must be signed in to change notification settings - Fork 130
Description
Why I need this
I use lawn as a video review platform for my editing team. When I create a new project, I don't just share the video — I also need to share supporting files with my editors:
.docx— script files, shot lists, revision notes.wav— raw audio exports, music stems, voiceover takes.aup3— Audacity project files so editors can reference or remix audio
Right now, the dropzone only accepts .webm, .mp4, and .mov. Everything else gets rejected. This means I'm context-switching to Google Drive or WeTransfer just to share non-video assets that belong in the same project. That defeats the purpose of having a centralized review tool.
No video preview needed for these files. I just want them to appear in the project as downloadable links. Dead simple.
What needs to change
This is a contained change across ~4 files. The core idea: non-video files should skip Mux entirely, get stored directly in Convex storage, and render as download links in the project view instead of triggering the video player.
Agent Prompt
Copy and paste this directly into your agent of choice.
Feature: Support non-video file uploads (.docx, .wav, .aup3) in lawn projects
In this Convex + React/Vite codebase, add the ability to upload non-video files alongside videos when creating or viewing a project. The three file types to support are .docx (Word documents), .wav (WAV audio), and .aup3 (Audacity project files). No preview UI is needed — just store them and render a download link.
Here is exactly what to change:
1. Upload dropzone component
Find the component where file upload accepts are defined. Search for video/webm or video/mp4 in the src/ directory — that's where the accept prop lives on the dropzone.
Add the following MIME types to the accepted list:
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": [".docx"],
"audio/wav": [".wav"],
"application/octet-stream": [".aup3"],Also update any UI label that says something like "Drop videos here" to say "Drop videos, audio, or documents".
2. Convex schema (convex/schema.ts)
Find the table that stores uploaded files or videos. If the type field uses a v.union(v.literal(...)) pattern, add:
v.literal("document"),
v.literal("audio"),If the type field is a plain v.string(), no schema change is needed. Make sure the table allows rows without a muxAssetId and muxPlaybackId (either mark them v.optional(...) or confirm they already are).
3. Upload mutation/action — gate Mux behind a video type check
Find where Mux is invoked after a file is stored in Convex storage. Search for mux inside the convex/ directory. There will be a call like mux.video.assets.create(...) or similar that sends the file to Mux for processing.
Wrap this in a check:
const isVideo = file.type.startsWith("video/");
if (isVideo) {
// existing Mux processing path — leave this completely untouched
} else {
// Non-video: skip Mux, insert directly into the DB
await ctx.db.insert("files", { // use whatever the actual table name is
storageId,
projectId,
name: fileName, // pass filename through from the client
mimeType: file.type, // store the MIME type
// do NOT include muxAssetId or muxPlaybackId
});
}Make sure the mutation/action also accepts fileName and mimeType as arguments from the client if it doesn't already.
4. Project view / file list component
Find where uploaded files are rendered in the project view. There will be a video player or a list of videos. Add a conditional branch: if a file record has no muxPlaybackId, render a download link instead of the player.
// Pseudocode — adapt to the actual component structure
if (!file.muxPlaybackId) {
const fileUrl = await getFileUrl(file.storageId); // use whatever the existing Convex storage URL helper is
return (
<a href={fileUrl} download={file.name} className="...">
{getFileIcon(file.mimeType)} {file.name}
</a>
);
}
// existing video player rendering belowFor getFileIcon, a simple mapping is fine:
audio/wav→ 🎵application/vnd.openxmlformats-officedocument.wordprocessingml.document→ 📄application/octet-stream(.aup3) → 🎛️
Edge case: .aup3 MIME type
.aup3 is an Audacity SQLite-based project format with no officially registered MIME type. Browsers will send it as application/octet-stream. On the frontend, the dropzone's accept filter should work fine with application/octet-stream: [".aup3"]. On the backend, do an extension-based check as a secondary guard:
const isAup3 = fileName.endsWith(".aup3");Do not send .aup3 files to Mux regardless of MIME type.
Summary of files to touch
| File | What to change |
|---|---|
Upload dropzone component (src/) |
Add 3 MIME types to accept, update label copy |
convex/schema.ts |
Ensure muxAssetId and muxPlaybackId are v.optional, add type literals if needed |
Upload mutation/action (convex/) |
Gate Mux call behind isVideo check, insert non-video files directly |
Project view component (src/) |
Render download link for files without muxPlaybackId |
No new dependencies needed. No preview implementation needed. Keep all existing video upload behavior completely unchanged.