ffmpeg-zeo
Typed FFmpeg filter graphs for Python applications, command-line automation, and coding agents.
ffmpeg-zeo gives the same media job one portable representation: build a
graph with a fluent Python API or a Pydantic model, inspect the generated
command, serialize it as JSON, and run it through Python, the CLI, or MCP. It
is inspired by
ffmpeg-python's graph compiler
and pyffmpeg's approachable
setup, but is a new library rather than a drop-in fork.
Why ffmpeg-zeo?
- Typed, validated graph IR built on Pydantic
- Fluent graphs with stream selectors, fan-out, multi-input filters, and
automatic
split/asplitinsertion - Synchronous and asynchronous execution
- Typed
ffproberesults with useful media properties - Reusable recipes for common conversions
- JSON-first CLI designed for scripts and agents
- Optional stdio MCP server for Cursor and Claude Code
- Live filter and codec discovery from the FFmpeg installed on your machine
- No FFmpeg binary bundled in the Python wheel
Requirements
- Python 3.12 or newer
ffmpegandffprobe
Binary discovery checks FFMPEG_BINARY / FFPROBE_BINARY, then PATH, then
the ffmpeg-zeo cache. Linux x86_64/aarch64 and Windows x86_64 users can
explicitly download BtbN LGPL essentials builds:
ffmpeg-zeo doctor --download
macOS users should install FFmpeg through a system package manager, for
example brew install ffmpeg; automatic download is not available on macOS.
Run ffmpeg-zeo doctor to verify the active binaries.
Installation
uv add ffmpeg-zeo
# or
python -m pip install ffmpeg-zeo
Install the optional MCP server with:
uv add "ffmpeg-zeo[mcp]"
# or
python -m pip install "ffmpeg-zeo[mcp]"
The Python import is always:
import ffmpeg_zeo
Quick start
Probe first, use a recipe when one fits, and compile custom graphs before running them:
import ffmpeg_zeo
info = ffmpeg_zeo.probe("input.mp4")
print(info.duration_seconds, info.width, info.height, info.video_codec)
graph = ffmpeg_zeo.convert("input.mp4", "output.mp3")
print(ffmpeg_zeo.compile_graph(graph))
result = ffmpeg_zeo.run(graph)
print(result.returncode)
Fluent Python graphs
Build a custom graph and inspect the exact argv before execution:
from ffmpeg_zeo import input
job = (
input("input.mp4")
.video
.filter("scale", 1280, -2)
.output("output.mp4", vcodec="libx264", crf=23, an=None)
.overwrite("always")
)
print(job.compile())
result = job.run(capture_stderr=True)
Stream selectors use .video, .audio, ["v"], or ["a"]. Multi-input
filters merge their input graphs:
from ffmpeg_zeo import input
background = input("background.mp4").video
logo = input("logo.png").video.filter("scale", 160, -1)
(
background
.overlay(logo, x="W-w-24", y="H-h-24")
.output("branded.mp4", vcodec="libx264")
.overwrite("always")
.run()
)
When one stream feeds multiple downstream nodes, the compiler inserts the
required split or asplit filter automatically.
Async execution and progress
import asyncio
from ffmpeg_zeo import input
async def main() -> None:
job = (
input("input.mp4")
.filter("scale", 1280, -2)
.output("output.mp4")
.overwrite("always")
)
result = await job.run_async(capture_stderr=True)
print(result.returncode)
asyncio.run(main())
For progress callbacks, pass on_progress= to ffmpeg_zeo.run() or
ffmpeg_zeo.run_async(). A Progress event exposes frame, fps,
out_time_seconds, total_size, and speed.
Failures raise FFmpegError, which includes the argv, return code, stdout,
and stderr. Missing executables raise BinaryNotFoundError.
Recipes
Recipes return a Graph; they do not hide compilation or execution.
convert(src, dst, overwrite="always")thumbnail(src, dst, time=1.0)transcode_h264(src, dst, crf=23, preset="medium")extract_audio(src, dst, codec="copy")extract_cover(src, dst)scale(src, dst, width=1280, height=-2)clip(src, dst, start, end=None)burn_subtitles(src, dst, subtitles)concat_demuxer(paths, dst)
from ffmpeg_zeo import run
from ffmpeg_zeo.recipes import thumbnail, transcode_h264
run(thumbnail("input.mp4", "cover.jpg", time=5.0))
run(transcode_h264("input.mov", "output.mp4", crf=20, preset="slow"))
Discover recipe names with ffmpeg-zeo catalog recipes.
Graph JSON
Every graph is a Pydantic model and can round-trip through JSON:
from ffmpeg_zeo import Graph, input
graph = (
input("input.mp4")
.filter("scale", 1280, -2)
.output("output.mp4", vcodec="libx264")
.overwrite("always")
.build()
)
payload = graph.model_dump_json(indent=2)
restored = Graph.model_validate_json(payload)
The same document can be compiled without executing it:
ffmpeg-zeo compile graph.json
ffmpeg-zeo run graph.json --json
cat graph.json | ffmpeg-zeo compile -
A graph contains inputs, filters, outputs, global_args, and an
optional overwrite policy (always or never). This stable JSON boundary is
useful for reviewing agent-generated jobs before allowing execution.
CLI
The CLI emits structured JSON for automation:
ffmpeg-zeo doctor
ffmpeg-zeo probe input.mp4 --json
ffmpeg-zeo convert input.mp4 output.mp3 --overwrite always
ffmpeg-zeo compile graph.json
ffmpeg-zeo run graph.json --json
ffmpeg-zeo recipe thumbnail --params src=input.mp4 --params dst=cover.jpg
ffmpeg-zeo catalog filters
ffmpeg-zeo catalog codecs
ffmpeg-zeo catalog recipes
ffmpeg-zeo filter-help scale
ffmpeg-zeo version
Use ffmpeg-zeo COMMAND --help for complete command options. compile and
run accept - to read a graph from stdin.
MCP and coding agents
After installing the mcp extra, start the stdio server with:
ffmpeg-zeo-mcp
Example MCP configuration:
{
"mcpServers": {
"ffmpeg-zeo": {
"command": "ffmpeg-zeo-mcp"
}
}
}
The server exposes tools to check binaries, probe files, compile and run Graph JSON, convert files, discover filters and codecs, inspect filter help, and list or run named recipes. It logs only to stderr so stdout remains a valid stdio protocol stream.
Repository integrations are available in:
- Cursor skill:
.cursor/skills/ffmpeg-zeo/ - Cursor MCP example:
.cursor/mcp.json.example - Claude Code plugin:
plugin/ffmpeg-zeo/
Best practices
- Run
ffmpeg-zeo doctorbefore processing media in a new environment. - Probe input files; do not assume duration, dimensions, codecs, or streams.
- Prefer a named recipe for common jobs.
- For a custom graph, inspect
compile()or use the CLIcompilecommand before execution. - Discover available filters with
catalog filtersand inspect parameters withfilter-help; FFmpeg builds differ. - Set overwrite behavior explicitly to
alwaysornever. ffmpeg-zeo never opens an interactive overwrite prompt. - Treat paths and filter expressions from untrusted users as untrusted input.
Architecture
flowchart LR
Python[PythonAPI] --> Graph[TypedGraphIR]
CLI[JSONCLI] --> Graph
MCP[MCPServer] --> Graph
Recipes[Recipes] --> Graph
Graph --> Compiler[Compiler]
Compiler --> Runner[SyncAsyncRunner]
Runner --> FFmpeg[FFmpeg]
Probe[TypedProbe] --> FFprobe[FFprobe]
Catalog[LiveCatalog] --> FFmpeg
The graph IR is the shared boundary. The compiler performs graph ordering, labeling, stream selection, escaping, and fan-out insertion; the runner owns process execution and typed failures.
Development
git clone https://github.com/zeroemployeeorg/ffmpeg-zeo.git
cd ffmpeg-zeo
uv sync --extra dev --extra mcp
make check
uv build
See CONTRIBUTING.md for the complete contributor workflow and CHANGELOG.md for release history.
Support and security
Use GitHub Issues for
reproducible bugs and feature requests. Include the output of
ffmpeg-zeo version, the compiled argv, operating system, and FFmpeg build
when reporting media-specific failures. Do not include private media or
credentials.
License
ffmpeg-zeo is licensed under Apache-2.0. FFmpeg and ffprobe are separate programs with their own licenses; downloaded BtbN LGPL builds are not part of the Python distribution. Review LICENSE and NOTICE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ffmpeg_zeo-0.1.0.tar.gz.
File metadata
- Download URL: ffmpeg_zeo-0.1.0.tar.gz
- Upload date:
- Size: 1.3 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c2acfdd4b61abd4f53d77a6836798e0365c2664666bb8d05fb48c25baeedc2ba
|
|
| MD5 |
e1039827c620548d0c4bf06d7f92dddc
|
|
| BLAKE2b-256 |
72367ab2ded689109086b2bc29c4f6623447a2b20ea8afbef09c6419162ac833
|
Provenance
The following attestation bundles were made for ffmpeg_zeo-0.1.0.tar.gz:
Publisher:
publish.yml on zeroemployeeorg/ffmpeg-zeo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ffmpeg_zeo-0.1.0.tar.gz -
Subject digest:
c2acfdd4b61abd4f53d77a6836798e0365c2664666bb8d05fb48c25baeedc2ba - Sigstore transparency entry: 2521521170
- Sigstore integration time:
-
Permalink:
zeroemployeeorg/ffmpeg-zeo@8019cfe19d035050e3c5540899bed6d6b19ab021 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/zeroemployeeorg
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8019cfe19d035050e3c5540899bed6d6b19ab021 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ffmpeg_zeo-0.1.0-py3-none-any.whl.
File metadata
- Download URL: ffmpeg_zeo-0.1.0-py3-none-any.whl
- Upload date:
- Size: 30.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
03c83c0153d5e84f4e8885d59b44c64b70b223d9f59b7f1236a6f9acd4588bcb
|
|
| MD5 |
1d57afb22878d9f8ebb95a7c386ffb75
|
|
| BLAKE2b-256 |
1f617689cbc13e9aa56cf34dfdb287e46359c1207e9c8273fbfdfb594ccdac31
|
Provenance
The following attestation bundles were made for ffmpeg_zeo-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on zeroemployeeorg/ffmpeg-zeo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ffmpeg_zeo-0.1.0-py3-none-any.whl -
Subject digest:
03c83c0153d5e84f4e8885d59b44c64b70b223d9f59b7f1236a6f9acd4588bcb - Sigstore transparency entry: 2521521739
- Sigstore integration time:
-
Permalink:
zeroemployeeorg/ffmpeg-zeo@8019cfe19d035050e3c5540899bed6d6b19ab021 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/zeroemployeeorg
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8019cfe19d035050e3c5540899bed6d6b19ab021 -
Trigger Event:
push
-
Statement type: