# typed-ffmpeg gives Python and TypeScript typed FFmpeg filter graphs

> typed-ffmpeg is an MIT licensed binding layer for FFmpeg with separate packages per FFmpeg major version, offering typed filter construction, JSON filter graph serialization, graph visualisation and validation, in Python and in an experimental TypeScript port. It suits teams whose media pipeline has outgrown shell commands, and it requires FFmpeg installed on the system.

**lucemia/typed-ffmpeg** — Type-safe FFmpeg bindings for Python & TypeScript — filters, typing, and docs

- Repository: https://github.com/lucemia/typed-ffmpeg
- Website: https://lucemia.github.io/typed-ffmpeg/
- Stars: 1,163 · Forks: 23
- Language: TypeScript
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/lucemia-typed-ffmpeg

## The problem is that an FFmpeg command line has no type system

typed-ffmpeg offers a modern, type-safe interface to FFmpeg for both Python and TypeScript, providing extensive support for complex filters with detailed typing and documentation. The project is inspired by ffmpeg-python, and the README says it enhances on it by addressing common limitations such as a lack of IDE integration and comprehensive typing, while introducing new features such as JSON serialization of filter graphs and automatic FFmpeg validation.

That framing is the key. An FFmpeg filter graph is a small language of its own, with filters, options, chains and links, and the only practical interfaces to it are a command line string or a Python wrapper that passes strings around. Neither gives you completion, and neither tells you at author time that a filter option does not exist.

The feature list shows the answer. There is comprehensive FFmpeg filter support out of the box with IDE auto-completion, and integrated documentation where in-line docstrings provide an immediate reference for filter usage, reducing the need to consult external documentation. Robust typing offers static and dynamic type checking. Graph visualization leverages graphviz for visual representation, aiding in understanding and debugging. And validation and auto-correction assists in identifying and fixing errors within filter graphs.

The zero dependencies claim is about the Python side and is worth stating precisely. It is built purely with the Python standard library, ensuring maximum compatibility and security. For a library that shells out to a binary, having no Python dependencies at all is a real property, since it means the package cannot break when a transitive dependency releases.

Other features cover the rest of the pipeline. Filter graph serialization enables saving and reloading of filter graphs in JSON format for ease of use and repeatability. Partial evaluation allows modular construction and reuse. Media file analysis provides built-in support for analyzing media files using FFmpeg's ffprobe utility, with detailed metadata extraction through both dictionary and dataclass interfaces. And input and output options are supported with a more comprehensive interface, including additional codecs and formats.

## One package per FFmpeg major version, with the same namespace

The v4 packaging is the most consequential change in the project, and it is what a new user has to understand first.

typed-ffmpeg v4 ships separate PyPI packages for each FFmpeg major version, and you install only the bindings you need. The table is explicit: typed-ffmpeg for the latest version, which is listed as v9 and recommended for most users, and then typed-ffmpeg-v5, typed-ffmpeg-v6, typed-ffmpeg-v7, typed-ffmpeg-v8 and typed-ffmpeg-v9 for the matching FFmpeg releases. There is also ffmpeg-core, described as a shared runtime that is auto-installed.

The point of the split is that FFmpeg's filter set changes between major versions, and a binding that offers every filter of every version would offer filters that do not exist on your machine. Pinning the bindings to the binary is the honest approach, and it also means the typing can be accurate for that version rather than approximately right.

The matching procedure is two commands:

```bash
ffmpeg -version | head -1        # e.g. ffmpeg version 6.1.1
pip install typed-ffmpeg-v6      # install the matching package
```

And the payoff is that all packages expose the same ffmpeg namespace, so your code works identically regardless of which package you install. That is a well-designed compromise, because it means the version pin is a deployment decision rather than a code change, and an application can move between FFmpeg majors without touching its source.

Two optional extras exist. The graph extra installs graphviz for graph visualisation, and the parse extra installs CLI parsing and compile_as_python() support. The parse extra needs version-specific cache data, ffmpeg-data-v5 through ffmpeg-data-v9, to reconstruct filter graphs from FFmpeg command lines, and the README says most users do not need it.

There is one naming trap worth knowing about. If you need to install ffmpeg-python at the same time, the README says to use pip install typed-ffmpeg-compatible to prevent conflicts with the module name, and then to use import typed_ffmpeg as ffmpeg instead of import ffmpeg. Both libraries want the ffmpeg module name, so the compatible package renames itself.

## The TypeScript port mirrors the Python API with options objects

The TypeScript side exists, and the README labels it experimental.

The npm packages follow the same per-version scheme: @typed-ffmpeg/core is the core runtime for Node.js and browser ESM, and @typed-ffmpeg/v5 through @typed-ffmpeg/v9 are the bindings for each FFmpeg major version. Installing a pair is the normal case:

```bash
npm install @typed-ffmpeg/core @typed-ffmpeg/v9   # latest FFmpeg bindings
```

And the API is a fluent chain that compiles to the argument array:

```typescript
import { input } from "@typed-ffmpeg/v8";

const cmd = input("input.mp4")
  .video
  .scale({ w: 1280, h: 720 })
  .output("output.mp4")
  .overwriteOutput()
  .compile();
// => ["-i", "input.mp4", "-filter_complex", "...", "output.mp4"]
```

The API mirrors the Python API with idiomatic TypeScript patterns, options objects instead of keyword arguments, so scale takes an object rather than a keyword list. The compile() call returns the argument array rather than a string, which is the right shape for a JavaScript tool that spawns a process, since it avoids the quoting problems that come with building a command line as one string.

The core package ships three builds, CJS as the Node.js default, ESM, and a browser-safe ESM bundle, selected automatically through the exports field. Each version package includes JSDoc annotations indicating filter availability across FFmpeg versions, which is how a TypeScript user discovers that a filter exists in FFmpeg 8 but not in FFmpeg 6.

The repository is a monorepo with the TypeScript side in packages/ts-core/ and packages/ts-v5/ through packages/ts-v9/, and a packages/playground/ workspace for the interactive playground that the README's table of contents links to. The root package.json is private and declares the workspaces as packages/ts-* and packages/playground, with Vite as the only dev dependency at the root, which is the playground's bundler.

## A uv workspace of core, data and version packages

The build system is the modern Python one, and the pyproject tells you the structure of the project.

The root pyproject declares a uv workspace with twelve members: packages/core, packages/data-v5 through packages/data-v9, packages/v5 through packages/v9, and packages/latest. The naming shows the architecture. There is one core package, one data package per FFmpeg version holding the version-specific cache data the parse extra needs, one bindings package per FFmpeg version, and a latest package that is presumably the one published as the plain typed-ffmpeg on PyPI.

The root project is named typed-ffmpeg-workspace with the description Monorepo root for typed-ffmpeg, type-safe FFmpeg bindings for Python & TypeScript, and it depends on ffmpeg-core and all five ffmpeg-data packages. The requires-python is 3.10 or newer, and the classifiers list Python 3.10 through 3.13.

The runtime dependencies of the published library are minimal by design, since the feature list claims zero dependencies built purely with the Python standard library. The one optional dependency declared at the root is graphviz for the graph extra.

The repository layout matches. There is src/ for the Python source, packages/ for the workspace members, examples/ with a typescript-demo directory, docs/ with an mdbook or mkdocs site and a media directory, scripts/ and docker/ directories, a.devcontainer/ for the development environment, a.uv/ directory, uv.lock, and pyproject-workspace.toml alongside the main pyproject.toml. The docs are built with mkdocs material and published to lucemia.github.io/typed-ffmpeg.

The project also carries a CLAUDE.md and a.claude/ directory, a CONTRIBUTING.md, a CHANGELOG.md, a.pre-commit-config.yaml and a.husky/ directory for git hooks, plus a codecov.yml. The CI badge points at a monorepo test workflow, which is what you would expect for a workspace where one change has to be validated across twelve packages.

## Recent releases, a permissive licence, and an FFmpeg you must provide

The licence is MIT, with the LICENSE file in the repository as the authoritative text, and this is a licensing statement rather than legal advice.

The release record shows regular work. The recent versions are v4.5 on 2026-08-22, v4.4 on 2026-08-16 and v4.3 on 2026-07-06, and the last push was on 2026-10-19, with the repository not archived.

The v4 line is a redesign rather than an increment, and the README points to two documents for anyone coming from the previous major version. There is a v4 Package Architecture document for the structure of the split packages, and a Migration Guide at docs/migration/v3-to-v4.md for upgrading from typed-ffmpeg 3.x. That guide existing at all is a good sign, because a change in how a library is installed and imported is exactly the kind of change that breaks a project silently if it is not documented.

The one hard external requirement is stated plainly: FFmpeg must be installed on your system. This is a binding layer, not a distribution. That is a reasonable design, because shipping an FFmpeg binary inside a Python wheel would multiply the size and raise the licensing and platform questions, and because a user who is processing media usually already has the FFmpeg they need. The consequence is that the version pairing is the user's job, which is why the README documents the ffmpeg -version check as the first step.

The documentation site also hosts an interactive playground, and the media analysis support through ffprobe with both dictionary and dataclass interfaces is the part of the feature set most likely to be used on its own, since metadata extraction is a common first step in a media pipeline and the ffprobe output is deeply nested JSON that is unpleasant to handle by hand.

## Conclusion

Adopt typed-ffmpeg if you build media pipelines in Python and want filter graphs that are typed, introspectable and validated rather than assembled as strings. Do not adopt it if you want a self-contained binary, because FFmpeg must be installed on the system, and do not treat the TypeScript bindings as production-ready, since the README labels them experimental. Before you install, check your FFmpeg major version and pick the matching package, because the v4 line ships one PyPI package per FFmpeg version and the namespace is the same across them.

## FAQ

### How do I install typed-ffmpeg for my FFmpeg version?

Check your version with ffmpeg -version | head -1 and install the matching package, for example pip install typed-ffmpeg-v6 for FFmpeg 6.x, or pip install typed-ffmpeg for the latest. All packages expose the same ffmpeg namespace, so your code works the same whichever you install.

### What does typed-ffmpeg add over ffmpeg-python?

The README says it is inspired by ffmpeg-python and enhances on it by addressing common limitations such as lack of IDE integration and comprehensive typing, and adds features such as JSON serialization of filter graphs and automatic FFmpeg validation. It also offers graph visualisation through graphviz, partial evaluation, and media file analysis via ffprobe.

### Is the TypeScript version of typed-ffmpeg ready to use?

The README labels it experimental. The npm packages are @typed-ffmpeg/core plus one per FFmpeg major version from v5 to v9, the API mirrors Python with options objects instead of keyword arguments, and each version package includes JSDoc annotations indicating filter availability across FFmpeg versions.

### Can I use typed-ffmpeg alongside ffmpeg-python?

Yes, but the README says to install typed-ffmpeg-compatible rather than typed-ffmpeg to prevent a conflict with the module name, and then to import it as import typed_ffmpeg as ffmpeg instead of import ffmpeg, since both libraries want the ffmpeg module name.

### Does typed-ffmpeg include FFmpeg itself?

No. The README states that FFmpeg must be installed on your system. The library is a binding layer over it, which is why the per-version packages exist, so that the bindings match the filter set of the binary you have.

## Sources

- [License: MIT](https://github.com/lucemia/typed-ffmpeg/blob/main/LICENSE)
- [lucemia/typed-ffmpeg on GitHub](https://github.com/lucemia/typed-ffmpeg)
- [Project website](https://lucemia.github.io/typed-ffmpeg/)
- [README](https://github.com/lucemia/typed-ffmpeg/blob/main/README.md)
- [Releases](https://github.com/lucemia/typed-ffmpeg/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/lucemia-typed-ffmpeg
