Skip to content

Feature Request: Allow uploading non-video files (scripts, audio, documents) to projects | Agent Prompt provided #20

@prshv1

Description

@prshv1

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 below

For 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions