# vedit `vedit` is a compact postfix language that compiles linear video edits into one FFmpeg `-filter_complex` graph. ## Build and run ```sh make make test ./vedit -n example.edit output.mp4 # compile and print only ./vedit example.edit output.mp4 # print, then execute FFmpeg ``` On Windows, use an MSYS2 UCRT64 or MinGW64 shell with GCC and GNU Make installed: ```sh make make test ./vedit.exe -n example.edit output.mp4 ./vedit.exe example.edit output.mp4 ``` The Makefile detects Windows and produces `vedit.exe`. Native Windows process creation, temporary paths, cleanup, executable discovery, and `cmd.exe` quoting are selected automatically. Put both `ffmpeg.exe` and `ffprobe.exe` on `PATH`; Windows builds do not require WSL. Override the built-in `-c:v libx264 -c:a aac` output encoding arguments from the programme itself: ```text encoding "-c:v libx265 -crf 18 -preset slow -c:a libopus" ffmpeg-args "-movflags +faststart" ``` The most recent `encoding` instruction replaces the complete video/audio encoding vector; calling it again overwrites the earlier value. Every `ffmpeg-args` instruction appends more final output arguments. Argument strings are tokenized without shell evaluation and reproduced by `-n`; single-quoted groups preserve spaces inside one FFmpeg argument. Managed subtitle codec arguments are still generated separately unless appended arguments explicitly override them. Quoted strings may contain positional substitutions. Given `video "$1"` in a programme, run: ```sh ./vedit edit.txt output.mp4 "some input file.mp4" ``` Arguments after the output path are exposed as `$1`, `$2`, and so on. Substitutions may occur inside larger strings, such as `video "clips/$1.mp4"`. Values are never interpreted by a shell. `ffprobe` and `ffmpeg` must be available on `PATH` for normal CLI use. The command printed by `-n` is self-contained and is formatted for the current platform's shell (`cmd.exe` on Windows, a POSIX shell elsewhere), including codec-aware subtitle processing, cue clipping, concat-list creation, chapter metadata generation, and embedded-font extraction. Those steps appear as private helper invocations using the absolute path of the current `vedit` executable, so keep that executable available when running a saved command later. ## Language Sources and literals push values: ```text video "picture.mov" audio "dialogue.wav" combined "clip.mp4" subtitles "captions.srt" duration 250ms duration 01:23.500 scalar 0.7 index 0 burn-style "font=sans-serif; size=42; color=#FFFFFF" string "Japanese" attachment "font.ttf" ``` Paths are quoted and may contain spaces or `#`. Outside quotes, `#` begins a comment. There is one instruction per line. Stack operations are `dup`, `drop`, `swap`, `over`, and `rot`. Media operations are: ```text get-video get-audio get-subtitles split length slice take concat delay mux attach-subtitles video-to-combined burn-subtitles set-language set-title set-default set-forced set-tag chapter add-chapter drop-chapter move-chapter add-attachment drop-attachment move-attachment volume mix silence speed fade-in fade-out ``` The language also provides compile-time Boolean logic, typed scalar/duration arithmetic and comparisons, structured `if`/`else`/`end`, and bounded `begin`/`while`/`repeat` loops. Loops are fully unrolled while constructing the FFmpeg graph and are limited to 10,000 iterations. The separate typecheck pass records each instruction's consumed and produced types without modifying the parsed instruction array. `COMBINED` values retain ordered collections of every video, audio, and subtitle track in their source, as well as chapters, container tags, and attachments. Indexed operations select, take, drop, append, replace, and reorder tracks. Untouched compatible subtitle codecs are stream-copied; ASS/SSA styles, font attachments, metadata, dispositions, and chapters are preserved through Matroska workflows. Compilation requires exactly one final media value. Source durations come from ffprobe JSON. Stream-specific durations are preferred, with `format.duration` as fallback. Results are cached by path. Known durations propagate through the graph and are used by `length` and to calculate fade-out start times. See [LANGUAGE.md](LANGUAGE.md) for all value types, operations, signatures, and stack semantics.