Server · #10 trending MCP repo · ↗ +550 stars this week

Ffmpeg Skill

kajisho5/ffmpeg-skill

Local video editor for coding agents: 42 FFmpeg tools with a machine-readable contract and an MCP server, for Claude Code, Cursor and Codex. Cut, captions, reframe, loudness, platform exports. No cloud, no API keys.

Stars
4.7k
This week
+550
Forks
381
Open issues
13
Skills inside
14
Star trend · last 16 days+2,680 ★
Oct 22, 2026: 2,047 starsOct 23, 2026: 2,063 starsOct 24, 2026: 2,535 starsOct 25, 2026: 2,809 starsOct 26, 2026: 3,028 starsOct 27, 2026: 3,115 starsOct 28, 2026: 3,411 starsOct 29, 2026: 3,884 starsOct 30, 2026: 4,090 starsOct 31, 2026: 4,177 starsNov 1, 2026: 4,272 starsNov 2, 2026: 4,368 starsNov 3, 2026: 4,453 starsNov 4, 2026: 4,524 starsNov 5, 2026: 4,611 starsNov 6, 2026: 4,667 starsNov 7, 2026: 4,727 stars

Inside this repo

14 skills

shipping-build-artifactsMake the build step a real gate on what you actually distribute — build scripts that warn and exit 0 on a missing input, size checks with only an upper bound, hand-maintained file lists that drift from the entrypoints they must cover, committed bundles that go stale when only the source changes, GNU.claude/skills/build-artifactsci-pipeline-synthesizerGenerate GitHub Actions CI/CD pipeline configurations for automated building and testing of library and package projects. Use when creating or updating CI workflows for npm packages, Python packages, Go modules, Rust crates, or other library projects that need automated build and test pipelines. Inc.claude/skills/ci-pipeline-synthesizerreviewing-ffmpeg-skill-changesReview a change to the ffmpeg-skill repository for the failures its own contract makes possible — a claim in a result document that is true at one layer and false at the layer a caller reads, a new flag that reaches the code but not the contract/docs/demo surfaces, a "bug" that docs/design-decisions.claude/skills/code-reviewconcurrent-branchesResolve conflicts and merges when several branches are open against one repo at the same time — the hotspot files every change must touch (registry manifests, a single version field, shared tool config, one aggregated test module, tracked build output), union-vs-recompute-vs-rebuild as three differe.claude/skills/concurrent-branchesshipping-across-surfacesLand a change everywhere the same fact is stated — enumerating the full surface inventory (landing copy, docs, machine-readable summaries, changelog badges repeated across every page, sitemap, README, descriptions embedded in code, and untyped frontend consumers of typed responses), generating a sur.claude/skills/cross-surface-changeswriting-defect-reportsEstablish a finding before you publish it, and correct it after — headlines that overstate what actually reproduces at the layer a user sees, reporting code that no entry point can reach or that is already dead, filing a caveat the project's own records already answered, re-verifying your prior note.claude/skills/defect-reportsguarding-destructive-operationsAdd and review preconditions on operations that delete, overwrite, rewrite history, or resolve a caller-supplied name to a filesystem path — refusing instead of warning, placing the guard ahead of the first mutation, structural classification rather than string-prefix matching, name validation plus .claude/skills/destructive-operationskeeping-git-repos-cleanPrevents, detects, and remediates files that should never be committed — secrets (.env, API tokens, hardcoded credentials) and dev artifacts (build output, scratch databases, editor/OS files). Covers .gitignore (and why it does not untrack), git rm --cached, auditing tracked files, history scrubbing.claude/skills/git-hygienerunning-github-actions-efficientlyCut GitHub Actions minutes and wall-clock time without losing coverage — the OS billing multiplier (macOS 10x / Windows 2x / Linux 1x), trigger hygiene that stops push+pull_request double-firing, concurrency that cancels superseded runs, dependency caching keyed on lockfiles, matrix discipline, and .claude/skills/github-actionsbuilding-python-mcp-serversBuilds robust Python MCP (Model Context Protocol) servers with FastMCP — tool design, error contracts, event-loop-safe blocking work, subprocess/CLI wrapping, single-file vs packaged distribution, global-state-free testing, and prompt-injection awareness. Use when writing an MCP server, exposing a t.claude/skills/mcp-server-designmanaging-python-releasesManages Python library releases including semantic versioning, changelog maintenance (Keep a Changelog format), release automation with GitHub Actions, and deprecation workflows. Use when planning releases, writing changelogs, automating release pipelines, or communicating breaking changes..claude/skills/release-managementreproducing-ci-locallyRun the CI gate on your machine so it agrees with the runner — deriving the exact command, paths, markers, and env from the workflow file instead of the Makefile, unblocking gate steps that short-circuit and hide the next failure, pinning the linter version CI resolves, building the interpreter/tool.claude/skills/reproducing-ci-locallyverifying-external-behaviorConfirms what a third-party library, remote API, build backend, or scraped document actually does before writing code that depends on it — throwaway probes that run in seconds, permissive clients that forward wrong arguments instead of rejecting them, per-endpoint docs that don't generalize, respons.claude/skills/verifying-external-behaviorffmpeg-skillEdit video and audio with local FFmpeg: cut, join, reframe, speed, captions, overlays, silence removal, sync, loudness, HDR to SDR, platform exports and checks. Use whenever the user mentions a video or audio file (mp4, mov, wav, m4a), footage, captions, a reel or short, YouTube/TikTok delivery, LUF/

Documentation

README

View on GitHub ↗

ffmpeg-skill

Give your coding agent a video editor.
Claude Code, Cursor, Codex or any MCP client edits your footage with local FFmpeg, then checks its own output.
No cloud · No API keys · Python standard library

Website · Quick start · Demos · Tools · MCP · npm

tests npm FFmpeg 6.1, 7.1, 8 and 9 tested in CI MIT Sponsor

ffmpeg-skill v3: give your coding agent a video editor. An agent runs probe, cut, fit, export and check, and the final file is measured: 59.98 s, 1080x1920, 30 fps.

npx ffmpeg-skill
animated captions with a karaoke highlight
caption.py --animate pop --karaoke
16:9 reframed to 9:16
fit.py --aspect 9:16 --fit crop
silence removed, timeline shorter
silence.py --threshold -35
waveform before and after loudness normalisation
loudness.py -I -14 --tp -1

Left half is the input, right half is what the command produced. Every before/after demo, with the exact command under each one → — all of it generated from synthetic footage by python3 demos/build.py, so you can rebuild every frame of it yourself.

ffmpeg-skill is an Agent Skill for Claude Code, Cursor, Codex and any agent that reads SKILL.md. It teaches the agent a fixed workflow (probe → edit losslessly where possible → check → verify) and ships 42 tools that do the actual work with ffmpeg / ffprobe: cut, join, silence removal, fit to duration and aspect, captions and karaoke, overlays and motion graphics, HDR → SDR and LUTs, audio clean-up and typed dynamics, sync with drift correction, multicam, loudness, delivery checks, whole-edit project rendering, batch folders. Every tool is also callable as an MCP tool (tools/list advertises a core 12 by default to keep client context small, with FFMPEG_SKILL_MCP_FULL=1 listing all 42; every tool is reachable by name through tools/call either way), and the whole set is described by a machine-readable contract.

If ffmpeg and python3 are on your PATH, it works: offline, on footage you would rather not upload.

SPEC (Self-Producing Execution Contract): each tool's input_schema — the part of its contract and MCP tool definition that has to track the CLI flag-for-flag — is never hand-authored beside the code. It is derived, at run time, from the same argparse parser that already defines the CLI, and CI fails the build if any of it drifts. → full explanation


Standalone, and in an ecosystem

Standalone, this is a local FFmpeg engine: probe → edit → verify, npx ffmpeg-skill and nothing else. No API key, no account, no other repo required. Everything above and below this section describes that standalone tool, and none of it changes if you never read the rest of this one.

In kajisho5's wider video-production ecosystem, this repo is the hands: it cuts, measures and exports files, and reports back in structured JSON. It does not decide what to cut, whether a deliverable is approvable, what makes a highlight interesting, or what a caption should say (the user's cue text is burned as written, never rewritten to fit) — those are a brain's job, sitting in front of this engine, not inside it.

You want to... Use
Cut / join / measure / export a file right now this repo (ffmpeg-skill), standalone
Decide cut points, approve a deliverable, plan a whole edit video-production-agent / AI-video-production-OS
Build a typed editing graph across a workspace, without writing raw ffmpeg video-editing-skill / audio-production-skill
Put Remotion motion graphics (animated captions, silence jump cuts, audio-peak motion) on a real recording remotion-ffmpeg-skill: its compositions take their length, fps, frame, silences, scene cuts, audio peaks and beats from this repo's probe.py, silence.py and scenes.py. It verifies its render with check.py and look.py, composites a graphics layer with overlay.py (--place for its --sparse mode), and delivers through render.py --template

Other repos in the ecosystem — media-analysis-skill, transcription-skill, subtitle-skill, thumbnail-skill, color-grading-skill, motion-graphics-skill, qc-skill — read this repo's contract --json, its tools' --json output and doctor, the same way any agent framework would; this repo does not call into any of them. The dependency runs one way.


Contents Standalone, and in an ecosystem · Why · Quick start · How it works · Design principles · Tools · Audio · Built for agents · FAQ · FFmpeg compatibility · Tested on real footage · Install · Requirements · Development · Docs


Why

An agent that "knows FFmpeg" still guesses: it assumes a frame rate, picks a codec the container cannot hold, re-encodes a file that only needed a stream copy, and reports "done" without opening the result. This skill takes the guessing out:

  • Real files first. Every job starts with probe.py; the agent decides from the measured duration, fps, resolution, colour and audio layout, not from the file name.
  • Structured tools, not shell strings. Each operation is a script with typed arguments. Nothing runs through a shell; no filter graph is accepted from the caller.
  • A contract the agent can read. contract --json states, for every tool, what it takes, what it writes, which FFmpeg components it needs and how the result is verified. The MCP surface is derived from it.
  • Verification after execution. The result is probed, checked against the destination's spec and, when the picture changed, looked at as a contact sheet.
  • Local first. No cloud, no API keys, no Python dependencies. Optional local transcription is used when a speech engine (Parakeet or a whisper) is installed, never required — by caption.py --transcribe and, since 1.17, by silence.py --filler --transcribe; both take a transcript you already have instead, and both refuse with the install lines rather than guessing. Both take --engine and --model; FFMPEG_SKILL_ASR_ENGINE and FFMPEG_SKILL_ASR_MODEL set this machine's defaults (e.g. large-v3-turbo where that whisper.cpp model is installed; without either, auto and base). The engine runs on your machine and the audio is never uploaded; parakeet-mlx, faster-whisper and openai-whisper download their model weights the first time they run (parakeet-mlx's default from Hugging Face).

Paper

The design discipline behind this skill — per-operation verification for AI agents — is written up as a position paper. The paper scores ffmpeg-skill itself as only partially conformant with the discipline it defines.

Kajiyama, S. (2026). What Is Verification-Driven Coding? — A Discipline for Not Trusting Generation. Zenodo. https://doi.org/10.5281/zenodo.23720216

Quick start

# 0. FFmpeg itself, once (Ubuntu/Debian shown; macOS and Windows are in the Install table below)
sudo apt install ffmpeg

# 1. install the skill for Claude Code (Cursor: --cursor, Codex: --codex, all three: --all)
npx ffmpeg-skill

# 2. check the machine: ffmpeg, ffprobe and every FFmpeg component the tools need
npx ffmpeg-skill doctor

# 3. (for agent frameworks) read the machine-readable contract
npx ffmpeg-skill contract --json | head -40

Captions from speech (caption.py --transcribe) need one local speech engine, installed separately. pip install faster-whisper is the simplest; the first run downloads its model, and the audio never leaves the machine. Without an engine, everything else works, doctor lists the engine as missing optional, and --transcribe refuses with the install line for each engine it supports.

Already installed? re-run npx ffmpeg-skill to refresh ~/.claude/skills/ffmpeg-skill. Copies are not updated automatically.

doctor's overall ok and a single tool's usable are different signals: ok is false when anything some tool requires is missing, so one missing filter (a plain Homebrew ffmpeg without subtitles, or a build without libvidstab) turns it false even though most tools still run. Check doctor --json's tools field for the per-tool answer, not just ok.

Then talk to your agent:

"Take interview.mp4, keep 0:45–3:10 and 5:00–6:30, and make it exactly 60 seconds for Reels."

The agent runs probe.py, cut.py --segments 0:45-3:10,5:00-6:30, fit.py --duration 60 --aspect 9:16 --fit crop, export.py --preset reels, check.py --platform reels and look.py, then reports "final.mp4: 59.98 s, 1080×1920, 30 fps, AAC stereo" with the contact sheet it inspected.

Deliver to a platform

One command per destination, with the app's own UI taken into account:

python3 $S/render.py talk.mp4 --template tiktok --cues cues.txt

That fills the shipped templates/tiktok.json: 9:16 crop, captions popped word by word above TikTok's description bar and clear of its like column, −14 LUFS, the tiktok export preset, and a check.py --platform tiktok on the file it wrote. The caption size comes from the delivery table, so since 1.17.1 the filled project also states "fit_size": "on": a size nobody asked for shrinks to fit the cue instead of splitting the sentence across two cues. A template that states its own fit_size, and a --brand that states a caption size, still win, and a project with "fit_size": "off" renders the captions 1.17.0 rendered. Templates ship for tiktok, reels, shorts, youtube-shorts, youtube, x, linkedin, facebook, podcast and audiogram; --template all (or a comma-separated list) renders the seven video destinations (tiktok, reels, shorts, youtube, x, linkedin, facebook) from the same edit and writes a <name>_pack.md table of what each one produced. Files land next to the input unless -o says otherwise, and --dry-run shows every planned command rather than a result. render.py --list-templates prints them with their frames, limits and safe zones. Alias spellings work everywhere a platform is named (youtube-shorts = shorts, ig = reels, twitter = x, fb = facebook).

The tools also work on their own, from any shell:

S=~/.claude/skills/ffmpeg-skill/scripts
python3 $S/probe.py input.mp4 --compact
python3 $S/fit.py input.mp4 --duration 60 --aspect 9:16 --dry-run    # print the plan, run nothing
python3 $S/export.py input.mp4 --preset reels --json                 # structured result with a probe of the output

On Windows in Git Bash, python3 is only on PATH if Python was installed from the Microsoft Store; a python.org install exposes python (or the py launcher) instead — replace python3 with python above if you see a "command not found". bin/install.js and doctor/contract already handle this for you; only the raw script examples above need it spelled out manually.

More requests and the commands behind them: examples/README.md. To see everything run end-to-end on generated footage: npm run demo (the gallery in docs/demos.md).

How it works

flowchart TD
    U[User request] --> A[AI agent<br/>Claude Code · Cursor · Codex]
    A -->|reads| S[SKILL.md<br/>workflow, request → tool map, report format]
    A -->|runs| T[Structured tool<br/>scripts/&lt;name&gt;.py, typed argparse flags]
    T --> C[Contract<br/>input schema · role · capabilities · verification policy]
    C --> D[Capability detection<br/>doctor: available / missing / unknown]
    D --> F[FFmpeg execution<br/>no shell, stream copy when possible]
    F --> V[Verification<br/>probe · check · look.py contact sheet]
    V --> R[Structured result<br/>--json: status, output, commands, probe]
    R --> A

Over MCP the same tools are reached through a transport that holds no tool table of its own:

flowchart LR
    M[MCP client<br/>Claude Desktop · Cursor · any client] --> P[mcp/server.py<br/>stdio JSON-RPC]
    P -->|tools/list| C[Contract-derived ToolSpecs<br/>names · order · inputSchema]
    P -->|tools/call| T[scripts/&lt;name&gt;.py]
    C -.derived from.-> K[scripts/_contract.py]
    T -.described by.-> K

Names, order and inputSchema in tools/list are translated from each tool's argparse parser at start-up, so a new flag or a new script appears in MCP with no edit to mcp/. A test copies the skill, adds, removes and edits a script, and reads tools/list again to prove it.

Design principles

These are the rules the skill file gives the agent and the code enforces.

  1. Probe first. No tool decides from the file name. probe.py measures duration, fps (with variable-frame-rate detection), resolution, rotation, bit depth, HDR format including Dolby Vision, colour tags and every audio stream before anything is cut.
  2. Lossless when possible. cut.py and loudness.py stream-copy what they do not need to touch (join.py re-encodes so it can crossfade and conform mismatched clips; --copy --transition none joins clips that already match by stream copy). Re-encoding happens only when it must: frame-accurate cuts, filters, format changes, or a keyframe farther than the tolerance.
  3. Plan before render. Every tool takes --dry-run (print the ffmpeg command lines, write nothing), --plan FILE (the dry run saved as a plan with fingerprinted inputs that render.py FILE executes later, refusing if an input changed), --json (structured result with a probe of the output), --fast (preview quality), --progress (percent and ETA), --timeout (a hung ffmpeg is killed and reported, never waited on forever; Ctrl-C or SIGTERM likewise stops the running ffmpeg, removes its partial output and reports kind: interrupted) and --overwrite (explicit consent before an existing output is replaced). A test runs every tool under --dry-run behind a fake ffmpeg and asserts that no ffmpeg call happened and no file appeared.
  4. Machine-readable contract. contract --json describes all 42 tools: input schema generated from the parser, output schema, role, required and conditional FFmpeg capabilities, dry-run support, the verification tools to run afterwards, whether a visual check is required, mutates_input: false. provides lists all 42 by a cross-repository Capability id (ffmpeg-skill.cut, ffmpeg-skill.loudness, ...) for kajisho5/AI-video-production-OS's CapabilityContract.provides — see docs/contract.md.
  5. Contract-derived MCP. mcp/server.py builds its tools/list from the contract. Tool names, order and inputSchema cannot drift from the scripts; a test keeps the two byte-identical.
  6. Capability detection. doctor reads ffmpeg -encoders / -decoders / -filters / -bsfs and reports which of the components the tools need are present on this build (libx264, libass, zscale, loudnorm, xfade, …), before a job fails inside ffmpeg.
  7. Unknown is not missing. When a listing cannot be read (a layout the parser does not know, ffmpeg exiting non-zero) the affected capabilities are unknown: never missing, never silently available. An installed filter is not reported absent; a failed detection is not a pass.
  8. Verify the result. The output is probed, and when the picture changed (captions, overlays, crops, colour, transitions) the agent runs look.py and inspects the PNG. The report is not finished until its Look: line names that image; audio-only jobs say Look: not needed. "Inspects" means the calling agent's own vision, not a feature of this skill: look.py only renders a PNG; nothing in this repository detects faces, products, subjects, or "the interesting part" of a frame or a scene. When a crop or reframe needs to keep a specific part of the frame (fit.py --fit crop --crop-x/-y, see Tools), it is the multimodal agent looking at that PNG and choosing the anchor — a non-visual caller (a script, a CLI user without eyes on the sheet) has to supply that decision itself, and the default is a plain centre crop. Likewise scenes.py --highlights ranks candidate scenes by a measured proxy (--rank-by audio or --rank-by duration), never by content; it is the agent that turns a look at the sheet into a judgement.
  9. One label per report. A finished job is Done:, a failure or a refusal is Failed:, and a partial result is Done: with the shortfall named in Notes: — never a third label such as Done (partially):.
  10. Keep originals. No tool overwrites its input. Outputs are new files named <input>_<operation>.<ext> unless told otherwise, and a test hashes every input after the run.

Tools

42 public tools, all Python 3.10+ standard library, all with --help, --dry-run, --json, --plan FILE (a dry run written as a plan render.py executes later), non-zero exit and a reason on stderr on failure. --json-brief (1.11.0) prints the same result document trimmed to what a caller acts on — status, output, verified, a compact summary of the output probe, the tool's own keys and the command count instead of the command lines — for roughly a third of the bytes; --json itself is unchanged. Every re-encoding tool except export.py, render.py and batch.py (a preset or the project decides there) takes --codec h264|hevc|av1|prores and --quality N (1.8) and, with export.py, --hw/--no-hw (Apple VideoToolbox on Apple Silicon, opt-in; see doctor below), and every time flag takes seconds, mm:ss, hh:mm:ss.fff or SMPTE hh:mm:ss:ff with an optional @fps suffix (1.9).

Analysis and inspection

Tool What it does
probe.py Duration, fps (+ VFR detection), resolution, codecs, bit depth, HDR format incl. Dolby Vision (hdr for a real PQ/HLG/DV transfer only, bt2020_or_hdr also for BT.2020 SDR), colour space, rotation, every audio stream; --analyze flags Log footage
scenes.py Scene changes, audio peaks, highlight proposals (--rank-by audio loudest, or --rank-by duration longest — both proxies, not "best") and a per-scene sheet; cut list for cut.py --segments; --beats measures the music's beat grid (tempo, beat times, confidence); --shots classifies each shot static/pan/motion by measured optical flow; --audio-peaks lists per-second dBFS; --speech reports a speech-vs-music ratio, not a classification
look.py Contact sheet, single frames, side-by-side comparison as PNG so the agent can see what it made; --safe NAME shades the zones a platform's own UI covers; --guides title,action,4:3 outlines the broadcast safe areas (on the PNG only)

Editing

Tool What it does
cut.py In/out or multi-segment cuts, lossless -c copy first, re-encode fallback, --accurate for frame-exact video and sample-exact audio, --smart for a frame-exact H.264 cut that re-encodes only the partial GOPs at the cut points (matched to the source, verified against its packets and frames) and copies the rest, an SDR HEVC source re-encodes as HEVC (--no-keep-hevc for x264), a single .mp4/.mov copy keeps its edit list, so the picture starts at --start unless a keyframe lands later (start_snapped then says so; --no-edit-list for 2.x's shifted start), --vfr-guard to choose the VFR check (sampled by default); reports precision; --snap beats moves the in/out points onto a measured beat, or refuses when there is no measurable pulse ; --remove A-B,... drops ranges and keeps the rest
join.py Concatenate clips with xfade transitions, normalising size, fps, sample rate and channel layout (the widest clip's, or --channels); audio-only inputs are joined as audio; --list parts.txt reads generated segments (TTS lines) and checks every one first — missing, empty or unreadable parts are all named in one refusal, or skipped and reported with --on-missing skip ; --copy --transition none joins clips with matching streams by stream copy, measured afterwards, and refuses a mismatch by name
silence.py Detect and remove dead air (jump cuts) with a margin around speech (--margin, 0.2 s each side); list or export the cut list, or a source-to-output time map (--timemap, JSON or CSV); reports the level at every cut and warns where one may land in a breath or a word's tail; --filler also removes filler words, but only where a speech engine timed them; --speech-aware keeps a breath inside a sentence and cuts only at sentence-boundary pauses, composing with --filler through the same keep_ranges()
fit.py Fit to a duration (pitch-preserving speed change or trim, smooth slow-mo) and/or aspect ratio (pad, crop or --fit blur's blurred, darkened fill, with --crop-x/--crop-y to keep an off-centre subject) and/or exact --width/--height; rotate 90/180/270, flip h/v; force constant fps
crop.py Crop to an exact pixel rectangle (--x --y --width --height) — distinct from fit.py --fit crop, which crops to an aspect ratio it computes itself
cropdetect.py Measure existing black letterbox/pillarbox bars and report the crop.py-ready rectangle that removes them — analysis only, writes no file; --motion-centre reports the per-second motion centroid instead, for a 9:16 reframe — report only, the calling agent picks the crop, no auto-reframing
deinterlace.py Deinterlace interlaced source footage (yadif), --mode frame/field, --parity
denoise.py Reduce video noise/grain (hqdn3d), --strength low/medium/high or individual spatial/temporal overrides
redact.py Blur, pixelate, or (--mode delogo) interpolate away an exact pixel rectangle for the whole clip. The box is given: nothing here finds a face, a plate or a logo, and delogo is a border blend, not inpainting
sphere.py Extract a flat rectilinear viewport from a 360/spherical video (--yaw --pitch --roll --h-fov --v-fov); no subject tracking, only the aim you give it
straighten.py Rotate by an arbitrary angle for horizon correction (--degrees, --fit crop/pad) — distinct from fit.py --rotate's exact 90-degree turns
insert.py Turn a still image into a silent, fixed-duration video clip (title card, end slate) at an exact frame size / fps, with an optional Ken Burns zoom/pan
background.py Generate a solid-colour or two-colour gradient clip at an exact size/duration — no input file; --transparent writes a fully transparent canvas (ProRes 4444, QuickTime Animation, PNG or VP9 with alpha) to draw graphics on
reverse.py Reverse playback (video and, unless --no-audio, audio)
stabilize.py Two-pass motion stabilisation (vidstabdetect/vidstabtransform)
sequence.py Numbered (frame_%04d.png) or glob-matched still images into a video
waveform.py Render an audio track as a waveform or spectrum visualization video (showwaves/showspectrum) — for audio-only inputs with no picture worth showing; --image puts the visualisation over a still plate (an audiogram), with --platform, --title and burnt-in captions
freeze.py Hold a frame for N seconds (--at, --hold, --mode insert/extend) — an end-card hold or a comedic beat
pad.py Add black/silent padding at the start and/or end of the timeline (--start, --end) — distinct from fit.py --fit pad's per-frame letterbox bars
speedramp.py Step through different constant speeds across a clip via --segment START-END:FACTOR (repeatable) — distinct from fit.py's single whole-clip speed factor
loop.py Repeat a clip --times N or to a target --duration — for background loops and filling a fixed slot length; --crossfade smooths the seam, --boomerang plays it forward then backward
broll.py Cut away to a B-roll clip over the A-roll for a window (--insert B --at T --duration D, repeatable) and come back; A's length and audio untouched by default
metadata.py Write container chapter markers from a TIME TITLE text file and title/artist/comment tags, every stream copied bit for bit; --auto-chapters proposes the markers from measured pauses and scene cuts and names them Chapter N for you to rename
grid.py Composite --colsx--rows clips into one grid, each cell letterboxed and labelled with its filename by default (--label none to skip)

Audio

Tool What it does
audio.py Voice clean-up chain at three strengths (--voice light|medium|strong), FFT denoise, typed compressor / limiter / gate, music bed with sidechain ducking (--duck-amount/-threshold/-attack/-release), a never-ducked effects bed (--effects), --stereo-widen, fades, 5.1 → stereo, track replacement, extraction (-o out.wav), --audio-stream N
sync.py Offset between two recordings by audio cross-correlation (1 ms, pure Python), clock-drift correction; aligned video or audio out (audio-to-audio only — no lip-sync/face detection); a third or later camera is an additional more_sources positional (sync.py REF A B ...), the second positional itself keeps its name
loudness.py Two-pass EBU R128 loudnorm to −14 LUFS / −1 dBTP or any target (--lra for the range), video stream-copied; the written file is measured again and re-encoded until it meets --tp (lossy encoders overshoot); --measure-only; --versions streaming,ebu-r128,broadcast-jp (or LUFS/TP pairs) writes one file per target from one master, each measured after and listed in versions (output is null), verified only when every version met its own target

Picture

Tool What it does
caption.py Burn SRT/ASS with font, size, colour, outline, position; build SRT from timed plain text; wraps to the safe area — with --platform, the destination's own left/right safe zone, which since 1.17.2 is also what the burnt ASS states as its side margins — with a phrase-aware breaker (--wrap phrase|measured) and --max-lines/--min-duration/--offset; --mode mux takes a repeated --srt file:lang for several language-tagged, toggleable tracks in one file; picks a font by script for non-Latin text (--lang); animated and word-by-word karaoke timed to the speech energy or real word timings; --fit-size shrinks the size until a cue fits --max-lines instead of splitting the sentence (on by default on the delivery-template path since 1.17.1, where the size comes from the platform table); says caption text unchanged when it burned the cues exactly as given; optional local transcription
overlay.py Logos, watermarks and titles with position, time range, opacity, fades; --platform NAME keeps them clear of that destination's UI; --video for picture-in-picture, --chromakey for green-screen compositing, --place to lay ranges of a layer at chosen times; --keep-alpha composites onto a transparent canvas or alpha clip and writes the result with its alpha; a VP9 layer with alpha keeps its transparency
graphics.py Lower-thirds, title cards, chapter chips, progress bars, countdowns, corner bugs, social stickers, opening hook cards and meme captions drawn by FFmpeg from a brand kit; --platform NAME keeps them inside that destination's safe zone; --text-render routes shaping scripts through libass and --emoji-assets composites colour emoji; --keep-alpha renders a template on a transparent canvas as a per-element alpha layer
color.py HDR10 / HLG / Dolby Vision → SDR BT.709 tone mapping, DV layer stripping, 3D LUT (.cube), colour-tag rewriting, typed primary correction (exposure/contrast/saturation/gamma/white balance/lift-gain/levels/curves); --keep-alpha grades an alpha clip and keeps its alpha, checked frame by frame; --legalize bt709|ebu-r103: a legal-range clamp after the 4:2:0 conversion, measured on the written file

Delivery

Tool What it does
export.py Presets youtube, youtube4k, reels, tiktok, shorts, linkedin, facebook, x, youtube-hdr (HEVC Main10, source HDR tags kept), youtube-av1, prores, dnxhr-lb/sq/hq/hqx/444, the alpha masters prores4444/prores4444xq/qtrle/vp9-alpha/hap-alpha (alpha read back), key-fill (a switcher's fill + key pair), h265, gif, copy, all tagged BT.709 unless they carry HDR or are an editing master (prores, dnxhr-*: the source's tags kept, codec, profile and pixel format read back) (a BT.601-matrix or RGB source is converted to BT.709, not relabelled); --normalize meets the platform's loudness in the same call (render.py turns it on by default for platform presets) ; --max-mb MB fits the file under a size limit by a two-pass encode and measures it ; --preset copy --repair remuxes a damaged file (timestamps regenerated, corrupt packets dropped) and counts the errors before and after
proxy.py Small, low-bitrate proxy for downstream AI analysis/preview/editing decisions — resize by --width/--scale, proxy-grade --quality (default 30), --fps, --no-audio; not a delivery preset
check.py PASS / WARN / FAIL against YouTube, Shorts, Reels, TikTok, X, LinkedIn, Facebook, broadcast and podcast specs, from the same delivery table the export presets and templates read (podcast also reports chapter markers and channel count), with the fix for each failure and a format / judgement kind per row; --content adds black-frame, frozen-frame, silence, short audio-dropout, single-frame-flash and flat-first-frame rows ; --compare REF measures SSIM/PSNR against a reference; --levels bt709|ebu-r103 counts the luma and chroma samples outside the broadcast legal range, per clip and per window, with min/max Y/U/V; --expect-identity checks that the file is the one a run reported writing (output_identity)
report.py

…

Releases

Recent versions

All releases ↗
  1. v3.2.3
  2. v3.2.2
  3. v3.2.1
  4. v3.2.0
  5. v3.1.8
  6. v3.1.7