What does wecut do?
wecut renders a JSON project into an MP4 (or GIF) with the FFmpeg installed on this machine. You describe the edit; WeCut does the video work. It never calls a network service.
If wecut is not on PATH, use npx -y wecut (Node 16+) in its place everywhere below. If WeCut is connected as an MCP server, the same steps are the tools wecut_doctor, wecut_probe, wecut_validate, wecut_preview (returns the frames as images), wecut_render, wecut_list, wecut_schema, wecut_example, wecut_analyze (returns the frame sheet as an image), wecut_recipe_list, wecut_recipe_show, wecut_recipe_apply and wecut_recipe_from_project; pass a project as project (object) or projectFile (path).
Workflow
- Check the machine once:
wecut doctor. If it reports FFmpeg missing, follow itshint(for examplesudo apt-get install -y ffmpeg). - Put media in the workspace. Every path in a project is relative to the folder you run
wecutfrom (or--root DIR). URLs are refused: download files first. - Look at the media:
wecut probe media/talk.mp4 media/music.mp3gives duration, size, fps and whether there is audio. Use these numbers forfrom/to. - Write
project.json(format below).wecut example podcast-clipprints a full example;wecut schemaprints the JSON Schema;wecut listshows every style, font, transition and filter name. - Validate:
wecut validate project.json. Errors come back with a JSON pointer (/clips/0/to) and ahint. Fix and repeat. - Look before rendering:
wecut preview project.json --at 0.5,3,10writes small JPG frames toout/preview/. Open them and check framing, text size and caption position. - Render:
wecut render project.json. Use--preset draftfor a fast 540p check, the defaultfinalfor delivery.
Every command prints one JSON object. Exit codes: 0 ok, 2 invalid input, 3 missing dependency, 4 render failed, 5 over a limit. You can pipe a project in with wecut render -.
Project format
{
"wecut": "1",
"output": { "file": "out/clip-01.mp4", "ratio": "9:16", "res": 1080, "fps": 30 },
"clips": [
{ "src": "media/talk.mp4", "from": 754.2, "to": 781.0, "focus": { "x": 0.42, "y": 0.5 } },
{ "src": "media/talk.mp4", "from": 795.5, "to": 812.0, "zoom": 1.15 }
],
"texts": [
{ "text": "How I made my first $10k", "start": 0, "end": 3, "style": "hook", "y": 0.2, "in": { "type": "pop" } }
],
"captions": { "words": "media/talk.words.json", "src": "media/talk.mp4", "style": "karaoke" },
"audio": [ { "src": "media/music.mp3", "volume": 25, "loop": true, "duck": true, "fadeOut": 1.5 } ]
}
Rules that matter:
- Two clocks. Clip
from/toare seconds of the source file. Everything else (texts,overlays,audio.at,filters,effects) is seconds of the finished video.wecut validateprints the finishedduration. - Transitions overlap. A
transitionon clip N blends into clip N+1 and makes the video shorter by itsduration. - Framing. Default
fitiscover(fill and crop). Steer the crop withfocus(0..1; x 0.3 keeps the left side) andzoom. Use"fit": "contain"with"background": "blur"to show the whole frame. - Positions are fractions of the canvas, 0.5 is the centre. Font
sizeis pixels at 1080p. - Images use
duration(default 3) and can havemotion: in, out, left, right, up, down. - Emoji are not drawn in text. Put them in a PNG and use
overlays.
Captions
Give WeCut a transcript with word timings and it builds subtitles:
words: a.json,.srtor.vttpath, or an inline array[{"word": "hi", "start": 0.1, "end": 0.4}]. OpenAI/Groqverbose_jsonand whisper.cpp JSON are read as they are.src: set this to the video the transcript belongs to. Then the transcript keeps the source file's own times, and WeCut moves every word onto the output timeline through each clip cut from that file (speed included). Withoutsrc, times must already be output times.style:classic,yellow,box-black,box-white,bold,comic,shadow(a phrase at a time),karaoke,karaoke-green(spoken word lights up),pop(one big word at a time).
If you need a transcript: wecut doctor reports local_transcription: true when this FFmpeg has the Whisper filter. Otherwise use whatever speech-to-text you already have and save its word timings as JSON.
Templates: copy what works
A recipe is the shape of an edit without its content: sections (for example a 5 s hook with a headline card, then the talk), text slots, caption and music style, effects. Applying it to new clips, words and music gives a project.
- Analyse a video that worked:
wecut analyze media/viral.mp4returns shots and pace, cuts in the first 3 s, flashes, tempo, pauses, a silent platform outro (leave it out), andsheet: one frame per shot in a single image. Look at the sheet for the headline card, text style, framing and caption style. - Pick or write a recipe:
wecut recipe list(built-in: news-headline, news-headline-blur, quote-hook, vlog-phrases, podcast-punch, photo-slideshow, plus the workspace'srecipes/*.json).wecut recipe show <name>lists its roles and text slots. To write one, followwecut recipe schema. - Apply it:
{
"recipe": "news-headline",
"clips": [
{ "src": "media/broll.mp4", "role": "hook" },
{ "src": "media/talk.mp4", "from": 120, "to": 165 }
],
"texts": { "label": { "text": "Page name" }, "headline": { "text": "The claim in one line", "emphasis": ["claim"] } },
"transcript": "media/talk.words.json",
"write": "projects/clip-01.json"
}
wecut recipe apply --input request.json (or --input - with the JSON on stdin) checks everything and saves the project; then preview and render it. Clips without a role feed body; a section without clips borrows the start of the next one's footage.
- Save your own:
wecut recipe from-project projects/clip-01.json --name my-style --write recipes/my-style.jsonkeeps sections, transitions, text slots, caption style, effects and the music as the default track.
Music: a recipe may carry its own track (music.src), used by default like the song in a CapCut template. Pass "music": "media/song.mp3" (or a video, whose sound is used) to replace it, or "none". A song taken from someone else's video may be muted by the platform after upload; adding a trending sound in the app when posting avoids that.
Copy the structure, not the content: footage, exact wording and music of other creators stay theirs.
Making several clips from one long video
wecut probethe source and get a transcript with word timings.- Read the transcript and pick the moments (you decide; WeCut does not judge content).
- Write one project per clip, each with
captions.srcpointing at the source so one transcript serves every clip. wecut previewone of them, adjustfocus, thenwecut rendereach.
Troubleshooting
E_RANGE:tois past the end of the file. Usewecut probefor the real duration.E_PATH: the path is outside the workspace or is a URL.E_MEDIA: the file is not a readable video, image or audio file, or the wrong kind for that field (music inclips, video inoverlays).E_DEPENDENCY: FFmpeg missing, older than 6, or built without libass. Runwecut doctor.- Render too slow:
--preset draft, a loweroutput.res, or fewer clips per project.render_secondsandrealtime_factorin the result tell you the speed on this machine. - To see exactly what FFmpeg will run:
wecut render project.json --dry-run.