Skip to main content

CLI toolkit to convert, generate, and verify videos and images to exact specs (fps, duration, resolution, format) — test fixtures, CI checks, and everyday conversions.

Project description

vidfix

PyPI CI Python License

Videos and images, exactly the way you need them. vidfix is a CLI-first media toolkit for anyone — developers, testers, creators, or someone who just wants a file converted: force videos to exact specs (fps, duration, resolution), generate test clips from nothing, convert any picture or video to any format, grab thumbnails and GIFs, and verify media specs in CI with proper exit codes.

FFmpeg is bundled (via imageio-ffmpeg), so pip install vidfix just works — no system FFmpeg required.

Install

pip install vidfix        # or: uv tool install vidfix

Quick start

No syntax needed — just run vidfix and answer the prompts:

$ vidfix
What do you want to do? (convert/generate/caption/format/verify/info/variants): convert
Which file do you want to convert?: raw.mov
Preset (none/broadcast-1080i/df30/df60/film24/.../web-720p): df60
Target duration (e.g. 30s, 1:30) (enter to skip): 10s
...
equivalent command: vidfix convert raw.mov --preset df60 --duration 10s --codec h264 -o raw_converted.mov

Every answer is validated on the spot, and the equivalent one-liner is printed so you can script it next time. Or go straight to the flags:

# A 60fps, 30s, 720p SMPTE-bars clip with a burned-in timecode
vidfix generate -o fixture.mp4 --fps 60 --duration 30s --res 720p --timecode

# Force real footage to exact specs
vidfix convert raw.mov --fps 29.97 --duration 10s --res 1280x720 -o fixture.mp4

# Assert specs in CI (exit 0 pass / 1 fail)
vidfix verify fixture.mp4 --fps 29.97 --duration 10s --res 1280x720

Commands

Everything vidfix can do, at a glance (details in the sections below):

Command What it does
vidfix (no args) Interactive wizard — answer prompts, see the equivalent one-liner, run it
vidfix generate Create a synthetic test video to exact specs — patterns, test tone, timecode burn-in, no source file needed
vidfix convert Force an existing video to exact fps / duration / resolution / codec (stream-copies when possible)
vidfix caption Burn a text caption into a video — position, size, color, optional start/end window
vidfix format Convert any picture or video to any other format by output extension (incl. palette-optimized GIF, thumbnails)
vidfix verify Assert a file matches specs; exit 0 pass / 1 fail — wire straight into CI
vidfix info Pretty-print media metadata: container, duration, fps, resolution, codecs (--json for scripts)
vidfix variants Generate the fps × resolution cartesian product of variants, in parallel
vidfix preset list / preset show Inspect built-in + user presets (broadcast rate family, see Presets)

info and variants were previously named probe and matrix; the old names still work as hidden aliases, so existing scripts keep running.

vidfix convert — exact-spec transforms

vidfix convert in.mp4 --fps 60 --res 1080p -o out.mp4
vidfix convert in.mp4 --fps 60 --smooth -o out.mp4          # motion-interpolated 30→60
vidfix convert in.mp4 --duration 10s -o out.mp4             # trim: instant stream copy
vidfix convert in.mp4 --duration 10s --precise -o out.mp4   # frame-accurate re-encode
vidfix convert in.mp4 --duration 60s --extend-mode loop -o out.mp4
vidfix convert in.mp4 --preset df60 -o out.mp4              # 59.94 = exact 60000/1001

Trims with an unchanged codec use FFmpeg stream copy (instant, no quality loss). Resolution changes pad with black bars to preserve aspect ratio (--stretch to disable). Drop-frame rates are passed to FFmpeg as exact rationals (30000/1001), never lossy floats.

Option Meaning Default
-o, --output Output file path required
--fps Target frame rate: 30, 59.94, 30000/1001 keep source
--duration Target duration: 30s, 1:30, 90 — trims or extends keep source
--res Target resolution: 1280x720, 720p, 4k keep source
--codec Video codec: h264, h265, prores, vp9 h264
--preset Preset name for defaults (vidfix preset list)
--smooth Motion-interpolate fps changes (minterpolate) off
--extend-mode When target duration > source: freeze last frame or loop freeze
--stretch Stretch to target resolution (no pad bars) off
--no-audio Drop the audio track off
--audio-tone Replace audio with a 440Hz test tone off
--precise Force re-encode for frame-accurate trims off

vidfix generate — synthetic fixtures from nothing

vidfix generate -o bars.mp4 --fps 59.94 --duration 30s --res 1080p
vidfix generate -o count.mp4 --pattern testsrc --timecode    # running timecode overlay
vidfix generate -o df.mp4 --preset df30 --timecode           # drop-frame HH:MM:SS;FF burn-in
vidfix generate -o red.mp4 --pattern solid:red --audio none

Patterns: smpte, color-bars, testsrc, gradient, solid:COLOR. Audio: 440Hz tone (default), silence, none.

Option Meaning Default
-o, --output Output file path required
--fps Frame rate: 30, 59.94, 30000/1001 30
--duration Duration: 30s, 1:30, 90 5s
--res Resolution: 1280x720, 720p, 4k 1280x720
--pattern Test pattern: smpte, color-bars, testsrc, gradient, solid:COLOR smpte
--codec Video codec: h264, h265, prores, vp9 h264
--audio Audio track: tone (440Hz), silence, none tone
--timecode Burn in running timecode (HH:MM:SS:FF; drop-frame ;FF for NTSC) off
--text Burn a caption into the video
--preset Preset name for defaults (vidfix preset list)

vidfix verify — spec assertions for CI

vidfix verify out.mp4 --fps 60 --duration 30s --res 1280x720 --codec h264
vidfix verify out.mp4 --fps 29.97 --json | jq .passed

Prints a pass/fail table per property; exit code 0/1.

Option Meaning Default
--fps Expected frame rate not checked
--duration Expected duration not checked
--res Expected resolution not checked
--codec Expected video codec not checked
--fps-tolerance Allowed fps deviation 0.01
--duration-tolerance Allowed duration deviation in seconds 0.1
--json Machine-readable JSON instead of a table off

vidfix info — metadata at a glance

vidfix info clip.mp4             # rich table
vidfix info clip.mp4 --json      # jq-friendly
$ vidfix info clip.mp4
 container     mp4
 duration      0:00:30.030 (30.030s)
 resolution    1280x720
 fps           29.970 (df30, 30000/1001)
 video codec   h264
 pixel format  yuv420p
 bitrate       1200 kb/s
 audio         aac 44100 Hz 2ch

The container name is resolved from the file's major_brand tag (mp4/mov/m4a/3gp) or the extension — not ffprobe's raw demuxer list (mov,mp4,m4a,3gp,3g2,mj2); --json keeps the raw string as demuxer and the tag as major_brand alongside the friendly container. Broadcast rates get their preset-family label in the fps line (df30, df60, pal25, pal50, film24, film23976) so QA can read them at a glance. Uses system ffprobe when available, otherwise parses FFmpeg output directly.

vidfix caption — burn text into a video

vidfix caption in.mp4 --text "Take 42" -o out.mp4
vidfix caption in.mp4 --text "INTRO" --position top --color yellow -o out.mp4
vidfix caption in.mp4 --text "3..2..1" --start 0 --end 3 -o out.mp4
vidfix generate -o clip.mp4 --text "TEST CLIP"      # caption a generated video too

Positions: top, center, bottom (default). Audio is stream-copied untouched.

Option Meaning Default
-o, --output Output file path required
--text Caption text to burn in required
--position Placement: top, center, bottom bottom
--size Font size (pixels or expression like h/12) h/12
--color Font color: white, yellow, #ff0000 white
--start Show caption from this second onward whole video
--end Hide caption after this second whole video

vidfix format — any picture or video to any format

vidfix format clip.mov -o clip.mp4      # container conversion
vidfix format clip.mp4 -o clip.gif      # palette-optimized gif
vidfix format photo.png -o photo.webp   # image conversion
vidfix format clip.mp4 -o thumb.jpg     # first-frame thumbnail

The output extension picks the format — the only option is -o, --output (required).

vidfix variants — variant grids in parallel

vidfix variants in.mp4 --fps 30,60 --res 720p,1080p -o out/
# → variants/in_30fps_720p.mp4, in_30fps_1080p.mp4, in_60fps_720p.mp4, ...
Option Meaning Default
-o, --output Output directory required
--fps Comma-separated frame rates, e.g. 30,60 required
--res Comma-separated resolutions, e.g. 720p,1080p required
--codec Video codec for all variants h264
--jobs Parallel FFmpeg processes min(4, cpus)

Exit code is 1 if any variant fails. vidfix info takes just the file and --json; vidfix preset list / preset show NAME take no options.

Presets

vidfix preset list
vidfix preset show df30
vidfix generate -o pal.mp4 --preset pal25 --timecode

Built-in broadcast rate family:

Preset fps Timecode counting
df30 29.97 (30000/1001) drop-frame (HH:MM:SS;FF)
df60 59.94 (60000/1001) drop-frame (HH:MM:SS;FF)
ndf30 29.97 (30000/1001) non-drop (HH:MM:SS:FF)
ndf60 59.94 (60000/1001) non-drop (HH:MM:SS:FF)
pal25 (alias ndf25) exact 25 non-drop (HH:MM:SS:FF)
pal50 exact 50 non-drop (HH:MM:SS:FF)
film24 exact 24 non-drop (HH:MM:SS:FF)
film23976 23.976 (24000/1001) non-drop (HH:MM:SS:FF)

Plus broadcast-1080i (25 fps, 1920x1080) and web-720p (30 fps, 720p, h264). With --timecode, DF presets burn semicolon drop-frame notation and NDF/PAL/film presets colon notation, matching broadcast convention. Aliases (alias: pal25) work in user presets too. Add your own in ~/.config/vidfix/presets.yaml; explicit flags always override.

Python API

from vidfix import convert, generate, verify, probe

generate("fixture.mp4", fps="59.94", duration="30s", res="720p")
info = probe("fixture.mp4")           # MediaInfo (pydantic)
result = verify("fixture.mp4", duration=30.0)
assert result.passed

vidfix vs moviepy vs raw FFmpeg

vidfix moviepy raw ffmpeg
Exact-spec test fixtures ✅ one command ⚠️ manual ⚠️ long filter incantations
Spec verification + exit codes ✅ built in ⚠️ ffprobe + shell glue
Install without system FFmpeg ✅ bundled ✅ bundled
Editing/compositing/effects ❌ not the goal
Programmatic frame access ✅ numpy frames ⚠️
Speed ✅ direct filters, stream copy ⚠️ python frame loop

Use moviepy to edit videos, raw ffmpeg for full control, vidfix for exact-spec media, quick conversions, and CI checks with zero setup.

CI usage

- name: Verify rendered output specs
  run: |
    pip install vidfix
    vidfix verify build/output.mp4 --fps 60 --duration 30s --res 1280x720

Development

uv sync                                   # install with dev deps
uv run pytest                             # unit + integration tests
uv run pytest -m "not integration"        # fast tests only (no FFmpeg)
uv run ruff check . && uv run mypy        # lint + strict types

License

PolyForm Internal Use 1.0.0 — you may use vidfix freely for personal and internal business purposes (commercial included). Copying, redistributing, or building products from this source code is not permitted. Contributions are welcome via pull request.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vidfix-0.1.1.tar.gz (99.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vidfix-0.1.1-py3-none-any.whl (37.6 kB view details)

Uploaded Python 3

File details

Details for the file vidfix-0.1.1.tar.gz.

File metadata

  • Download URL: vidfix-0.1.1.tar.gz
  • Upload date:
  • Size: 99.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for vidfix-0.1.1.tar.gz
Algorithm Hash digest
SHA256 0c74add036ada7c3491349298ca74d64be2ae118a1d8e73edcdc3443b2f6f42e
MD5 7191897479caf39667e005b1deb610d4
BLAKE2b-256 988f31fdc1e6ea2e6b657de93497979771550930da1209fdd4c7e7f16296b95c

See more details on using hashes here.

File details

Details for the file vidfix-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: vidfix-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 37.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for vidfix-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 89e5d46ef025bea103b959ddd4a8a3dd7fa1faf845570f6351991524eb70bffb
MD5 ea766f7009bb68d60c175b8a848636cd
BLAKE2b-256 0848cf9fe697ea0c79d580f125bb6097fab1d116e90bbece80db785d4c12986f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page