vidfix 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vidfix-0.1.0/.github/workflows/ci.yml +34 -0
- vidfix-0.1.0/.github/workflows/publish.yml +22 -0
- vidfix-0.1.0/.gitignore +37 -0
- vidfix-0.1.0/.python-version +1 -0
- vidfix-0.1.0/CHANGELOG.md +38 -0
- vidfix-0.1.0/LICENSE +57 -0
- vidfix-0.1.0/PKG-INFO +331 -0
- vidfix-0.1.0/README.md +299 -0
- vidfix-0.1.0/pyproject.toml +102 -0
- vidfix-0.1.0/src/vidfix/__init__.py +38 -0
- vidfix-0.1.0/src/vidfix/cli.py +513 -0
- vidfix-0.1.0/src/vidfix/core/__init__.py +0 -0
- vidfix-0.1.0/src/vidfix/core/caption.py +96 -0
- vidfix-0.1.0/src/vidfix/core/convert.py +170 -0
- vidfix-0.1.0/src/vidfix/core/duration.py +190 -0
- vidfix-0.1.0/src/vidfix/core/ffmpeg.py +238 -0
- vidfix-0.1.0/src/vidfix/core/formats.py +71 -0
- vidfix-0.1.0/src/vidfix/core/generate.py +224 -0
- vidfix-0.1.0/src/vidfix/core/matrix.py +90 -0
- vidfix-0.1.0/src/vidfix/core/presets.py +100 -0
- vidfix-0.1.0/src/vidfix/core/probe.py +268 -0
- vidfix-0.1.0/src/vidfix/core/verify.py +110 -0
- vidfix-0.1.0/src/vidfix/data/presets.yaml +45 -0
- vidfix-0.1.0/src/vidfix/exceptions.py +44 -0
- vidfix-0.1.0/src/vidfix/interactive.py +169 -0
- vidfix-0.1.0/src/vidfix/py.typed +0 -0
- vidfix-0.1.0/tests/test_caption.py +58 -0
- vidfix-0.1.0/tests/test_cli.py +255 -0
- vidfix-0.1.0/tests/test_convert.py +111 -0
- vidfix-0.1.0/tests/test_duration.py +125 -0
- vidfix-0.1.0/tests/test_ffmpeg.py +111 -0
- vidfix-0.1.0/tests/test_formats.py +52 -0
- vidfix-0.1.0/tests/test_generate.py +115 -0
- vidfix-0.1.0/tests/test_integration.py +217 -0
- vidfix-0.1.0/tests/test_interactive.py +109 -0
- vidfix-0.1.0/tests/test_matrix.py +40 -0
- vidfix-0.1.0/tests/test_presets.py +133 -0
- vidfix-0.1.0/tests/test_probe.py +192 -0
- vidfix-0.1.0/tests/test_verify.py +69 -0
- vidfix-0.1.0/uv.lock +838 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
lint:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: astral-sh/setup-uv@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.12"
|
|
16
|
+
- run: uv sync --locked
|
|
17
|
+
- run: uv run ruff check .
|
|
18
|
+
- run: uv run ruff format --check .
|
|
19
|
+
- run: uv run mypy
|
|
20
|
+
|
|
21
|
+
test:
|
|
22
|
+
strategy:
|
|
23
|
+
fail-fast: false
|
|
24
|
+
matrix:
|
|
25
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
26
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
27
|
+
runs-on: ${{ matrix.os }}
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v4
|
|
30
|
+
- uses: astral-sh/setup-uv@v5
|
|
31
|
+
with:
|
|
32
|
+
python-version: ${{ matrix.python-version }}
|
|
33
|
+
- run: uv sync --locked
|
|
34
|
+
- run: uv run pytest --cov --cov-report=term
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# PyPI release via trusted publishing. Activates when the repo goes public at
|
|
2
|
+
# v0.1.0 — configure the "vidfix" project on PyPI with this repo + workflow as
|
|
3
|
+
# a trusted publisher first.
|
|
4
|
+
name: Publish
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
tags: ["v*"]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
publish:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
environment: pypi
|
|
14
|
+
permissions:
|
|
15
|
+
id-token: write # required for PyPI trusted publishing
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: astral-sh/setup-uv@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
- run: uv build
|
|
22
|
+
- run: uv publish
|
vidfix-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
|
|
7
|
+
# Virtual environments
|
|
8
|
+
.venv/
|
|
9
|
+
venv/
|
|
10
|
+
|
|
11
|
+
# Build artifacts
|
|
12
|
+
dist/
|
|
13
|
+
build/
|
|
14
|
+
|
|
15
|
+
# Tooling caches
|
|
16
|
+
.pytest_cache/
|
|
17
|
+
.mypy_cache/
|
|
18
|
+
.ruff_cache/
|
|
19
|
+
.coverage
|
|
20
|
+
coverage.xml
|
|
21
|
+
htmlcov/
|
|
22
|
+
|
|
23
|
+
# Local-only session notes (never publish)
|
|
24
|
+
CLAUDE.md
|
|
25
|
+
plan.md
|
|
26
|
+
|
|
27
|
+
# Editors / OS
|
|
28
|
+
.idea/
|
|
29
|
+
.vscode/
|
|
30
|
+
.DS_Store
|
|
31
|
+
|
|
32
|
+
# Test media output
|
|
33
|
+
*.mp4
|
|
34
|
+
*.mov
|
|
35
|
+
*.webm
|
|
36
|
+
*.mkv
|
|
37
|
+
!tests/fixtures/*.mp4
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-07-18
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `vidfix convert` — transform videos to exact fps/duration/resolution/codec with
|
|
13
|
+
stream-copy fast path for trims, `--smooth` motion interpolation, `--extend-mode
|
|
14
|
+
freeze|loop`, aspect-preserving pad (or `--stretch`), `--no-audio`, `--audio-tone`,
|
|
15
|
+
`--precise`.
|
|
16
|
+
- `vidfix generate` — synthetic test videos from FFmpeg lavfi sources: smpte,
|
|
17
|
+
color-bars, testsrc, gradient, solid:COLOR patterns; tone/silence/no audio;
|
|
18
|
+
`--timecode` real SMPTE timecode burn-in (drop-frame `HH:MM:SS;FF` with
|
|
19
|
+
semicolon, non-drop with colon); `--text` caption burn-in.
|
|
20
|
+
- `vidfix caption` — burn text onto existing videos: positions top/center/bottom,
|
|
21
|
+
`--start`/`--end` time window.
|
|
22
|
+
- `vidfix verify` — assert fps/duration/resolution/codec with tolerances; rich
|
|
23
|
+
pass/fail table or `--json`; exit 0/1 for CI.
|
|
24
|
+
- `vidfix info` — metadata via system ffprobe (JSON) or `ffmpeg -i` banner
|
|
25
|
+
fallback; friendly container names ("mp4 (major_brand: isom)").
|
|
26
|
+
- `vidfix format` — convert any picture or video to any format (image↔image,
|
|
27
|
+
video→any container, palette-optimized GIFs, first-frame thumbnails).
|
|
28
|
+
- `vidfix variants` — parallel cartesian product of fps x resolution variants.
|
|
29
|
+
- Interactive wizard — bare `vidfix` walks through action/file/preset/specs
|
|
30
|
+
step-by-step, prints the equivalent command, then runs it.
|
|
31
|
+
- Presets: built-in broadcast rate family `df30`, `df60`, `ndf30`, `ndf60`,
|
|
32
|
+
`pal25` (alias `ndf25`), `pal50`, `film24`, `film23976`, plus
|
|
33
|
+
`broadcast-1080i`, `web-720p` (drop-frame rates as exact rationals); user
|
|
34
|
+
presets in `~/.config/vidfix/presets.yaml`; `vidfix preset list|show`.
|
|
35
|
+
- Typed Python API: `convert`, `generate`, `verify`, `probe`, `to_format`.
|
|
36
|
+
- Bundled FFmpeg via imageio-ffmpeg — no system install needed.
|
|
37
|
+
|
|
38
|
+
[0.1.0]: https://github.com/gouravrana7/vidfix/releases/tag/v0.1.0
|
vidfix-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
Required Notice: Copyright (c) 2026 Gourav Rana (https://github.com/gouravrana7/vidfix)
|
|
2
|
+
|
|
3
|
+
# PolyForm Internal Use License 1.0.0
|
|
4
|
+
|
|
5
|
+
<https://polyformproject.org/licenses/internal-use/1.0.0>
|
|
6
|
+
|
|
7
|
+
## Acceptance
|
|
8
|
+
|
|
9
|
+
In order to get any license under these terms, you must agree to them as both strict obligations and conditions to all your licenses.
|
|
10
|
+
|
|
11
|
+
## Copyright License
|
|
12
|
+
|
|
13
|
+
The licensor grants you a copyright license for the software to do everything you might do with the software that would otherwise infringe the licensor's copyright in it for any permitted purpose. However, you may only make changes or new works based on the software according to [Changes and New Works License](#changes-and-new-works-license), and you may not distribute the software.
|
|
14
|
+
|
|
15
|
+
## Changes and New Works License
|
|
16
|
+
|
|
17
|
+
The licensor grants you an additional copyright license to make changes and new works based on the software for any permitted purpose.
|
|
18
|
+
|
|
19
|
+
## Patent License
|
|
20
|
+
|
|
21
|
+
The licensor grants you a patent license for the software that covers patent claims the licensor can license, or becomes able to license, that you would infringe by using the software.
|
|
22
|
+
|
|
23
|
+
## Fair Use
|
|
24
|
+
|
|
25
|
+
You may have "fair use" rights for the software under the law. These terms do not limit them.
|
|
26
|
+
|
|
27
|
+
## Internal Business Use
|
|
28
|
+
|
|
29
|
+
Use of the software for the internal business operations of you and your company is use for a permitted purpose.
|
|
30
|
+
|
|
31
|
+
## No Other Rights
|
|
32
|
+
|
|
33
|
+
These terms do not allow you to sublicense or transfer any of your licenses to anyone else, or prevent the licensor from granting licenses to anyone else. These terms do not imply any other licenses.
|
|
34
|
+
|
|
35
|
+
## Patent Defense
|
|
36
|
+
|
|
37
|
+
If you make any written claim that the software infringes or contributes to infringement of any patent, your patent license for the software granted under these terms ends immediately. If your company makes such a claim, your patent license ends immediately for work on behalf of your company.
|
|
38
|
+
|
|
39
|
+
## Violations
|
|
40
|
+
|
|
41
|
+
The first time you are notified in writing that you have violated any of these terms, or done anything with the software not covered by your licenses, your licenses can nonetheless continue if you come into full compliance with these terms, and take practical steps to correct past violations, within 32 days of receiving notice. Otherwise, all your licenses end immediately.
|
|
42
|
+
|
|
43
|
+
## No Liability
|
|
44
|
+
|
|
45
|
+
***As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim.***
|
|
46
|
+
|
|
47
|
+
## Definitions
|
|
48
|
+
|
|
49
|
+
The **licensor** is the individual or entity offering these terms, and the **software** is the software the licensor makes available under these terms.
|
|
50
|
+
|
|
51
|
+
**You** refers to the individual or entity agreeing to these terms.
|
|
52
|
+
|
|
53
|
+
**Your company** is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all organizations that have control over, are under the control of, or are under common control with that organization. **Control** means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise. Control can be direct or indirect.
|
|
54
|
+
|
|
55
|
+
**Your licenses** are all the licenses granted to you for the software under these terms.
|
|
56
|
+
|
|
57
|
+
**Use** means anything you do with the software requiring one of your licenses.
|
vidfix-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vidfix
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CLI toolkit to convert, generate, and verify videos and images to exact specs (fps, duration, resolution, format) — test fixtures, CI checks, and everyday conversions.
|
|
5
|
+
Project-URL: Homepage, https://github.com/gouravrana7/vidfix
|
|
6
|
+
Project-URL: Issues, https://github.com/gouravrana7/vidfix/issues
|
|
7
|
+
Project-URL: Changelog, https://github.com/gouravrana7/vidfix/blob/main/CHANGELOG.md
|
|
8
|
+
Author-email: Gourav Rana <gourav.rana70000@gmail.com>
|
|
9
|
+
License: PolyForm-Internal-Use-1.0.0
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ci,ffmpeg,qa,test-fixtures,testing,video
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: Other/Proprietary License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: Multimedia :: Video :: Conversion
|
|
23
|
+
Classifier: Topic :: Software Development :: Testing
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Requires-Dist: imageio-ffmpeg>=0.6.0
|
|
27
|
+
Requires-Dist: pydantic>=2
|
|
28
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
29
|
+
Requires-Dist: rich>=13.0.0
|
|
30
|
+
Requires-Dist: typer>=0.12.0
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# vidfix
|
|
34
|
+
|
|
35
|
+
<!-- badges: PyPI badges activate once published -->
|
|
36
|
+
[](https://pypi.org/project/vidfix/)
|
|
37
|
+
[](https://github.com/gouravrana7/vidfix/actions/workflows/ci.yml)
|
|
38
|
+
[](https://pypi.org/project/vidfix/)
|
|
39
|
+
[](LICENSE)
|
|
40
|
+
|
|
41
|
+
**Videos and images, exactly the way you need them.** vidfix is a CLI-first
|
|
42
|
+
media toolkit for anyone — developers, QA engineers, creators, or someone who
|
|
43
|
+
just wants a file converted: force videos to exact specs (fps, duration,
|
|
44
|
+
resolution), generate test clips from nothing, convert any picture or video to
|
|
45
|
+
any format, grab thumbnails and GIFs, and verify media specs in CI with proper
|
|
46
|
+
exit codes.
|
|
47
|
+
|
|
48
|
+
FFmpeg is bundled (via imageio-ffmpeg), so `pip install vidfix` just works — no
|
|
49
|
+
system FFmpeg required.
|
|
50
|
+
|
|
51
|
+
## Install
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install vidfix # or: uv tool install vidfix
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Quick start
|
|
58
|
+
|
|
59
|
+
**No syntax needed** — just run `vidfix` and answer the prompts:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
$ vidfix
|
|
63
|
+
What do you want to do? (convert/generate/caption/format/verify/info/variants): convert
|
|
64
|
+
Which file do you want to convert?: raw.mov
|
|
65
|
+
Preset (none/broadcast-1080i/df30/df60/film24/.../web-720p): df60
|
|
66
|
+
Target duration (e.g. 30s, 1:30) (enter to skip): 10s
|
|
67
|
+
...
|
|
68
|
+
equivalent command: vidfix convert raw.mov --preset df60 --duration 10s --codec h264 -o raw_converted.mov
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Every answer is validated on the spot, and the equivalent one-liner is printed
|
|
72
|
+
so you can script it next time. Or go straight to the flags:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# A 60fps, 30s, 720p SMPTE-bars clip with a burned-in timecode
|
|
76
|
+
vidfix generate -o fixture.mp4 --fps 60 --duration 30s --res 720p --timecode
|
|
77
|
+
|
|
78
|
+
# Force real footage to exact specs
|
|
79
|
+
vidfix convert raw.mov --fps 29.97 --duration 10s --res 1280x720 -o fixture.mp4
|
|
80
|
+
|
|
81
|
+
# Assert specs in CI (exit 0 pass / 1 fail)
|
|
82
|
+
vidfix verify fixture.mp4 --fps 29.97 --duration 10s --res 1280x720
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Commands
|
|
86
|
+
|
|
87
|
+
Everything vidfix can do, at a glance (details in the sections below):
|
|
88
|
+
|
|
89
|
+
| Command | What it does |
|
|
90
|
+
|---|---|
|
|
91
|
+
| `vidfix` (no args) | Interactive wizard — answer prompts, see the equivalent one-liner, run it |
|
|
92
|
+
| `vidfix generate` | Create a synthetic test video to exact specs — patterns, test tone, timecode burn-in, no source file needed |
|
|
93
|
+
| `vidfix convert` | Force an existing video to exact fps / duration / resolution / codec (stream-copies when possible) |
|
|
94
|
+
| `vidfix caption` | Burn a text caption into a video — position, size, color, optional start/end window |
|
|
95
|
+
| `vidfix format` | Convert any picture or video to any other format by output extension (incl. palette-optimized GIF, thumbnails) |
|
|
96
|
+
| `vidfix verify` | Assert a file matches specs; exit 0 pass / 1 fail — wire straight into CI |
|
|
97
|
+
| `vidfix info` | Pretty-print media metadata: container, duration, fps, resolution, codecs (`--json` for scripts) |
|
|
98
|
+
| `vidfix variants` | Generate the fps × resolution cartesian product of variants, in parallel |
|
|
99
|
+
| `vidfix preset list` / `preset show` | Inspect built-in + user presets (broadcast rate family, see [Presets](#presets)) |
|
|
100
|
+
|
|
101
|
+
`info` and `variants` were previously named `probe` and `matrix`; the old names
|
|
102
|
+
still work as hidden aliases, so existing scripts keep running.
|
|
103
|
+
|
|
104
|
+
### `vidfix convert` — exact-spec transforms
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
vidfix convert in.mp4 --fps 60 --res 1080p -o out.mp4
|
|
108
|
+
vidfix convert in.mp4 --fps 60 --smooth -o out.mp4 # motion-interpolated 30→60
|
|
109
|
+
vidfix convert in.mp4 --duration 10s -o out.mp4 # trim: instant stream copy
|
|
110
|
+
vidfix convert in.mp4 --duration 10s --precise -o out.mp4 # frame-accurate re-encode
|
|
111
|
+
vidfix convert in.mp4 --duration 60s --extend-mode loop -o out.mp4
|
|
112
|
+
vidfix convert in.mp4 --preset df60 -o out.mp4 # 59.94 = exact 60000/1001
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Trims with an unchanged codec use FFmpeg stream copy (instant, no quality loss).
|
|
116
|
+
Resolution changes pad with black bars to preserve aspect ratio (`--stretch` to
|
|
117
|
+
disable). Drop-frame rates are passed to FFmpeg as exact rationals
|
|
118
|
+
(`30000/1001`), never lossy floats.
|
|
119
|
+
|
|
120
|
+
| Option | Meaning | Default |
|
|
121
|
+
|---|---|---|
|
|
122
|
+
| `-o, --output` | Output file path | required |
|
|
123
|
+
| `--fps` | Target frame rate: `30`, `59.94`, `30000/1001` | keep source |
|
|
124
|
+
| `--duration` | Target duration: `30s`, `1:30`, `90` — trims or extends | keep source |
|
|
125
|
+
| `--res` | Target resolution: `1280x720`, `720p`, `4k` | keep source |
|
|
126
|
+
| `--codec` | Video codec: `h264`, `h265`, `prores`, `vp9` | `h264` |
|
|
127
|
+
| `--preset` | Preset name for defaults (`vidfix preset list`) | — |
|
|
128
|
+
| `--smooth` | Motion-interpolate fps changes (minterpolate) | off |
|
|
129
|
+
| `--extend-mode` | When target duration > source: `freeze` last frame or `loop` | `freeze` |
|
|
130
|
+
| `--stretch` | Stretch to target resolution (no pad bars) | off |
|
|
131
|
+
| `--no-audio` | Drop the audio track | off |
|
|
132
|
+
| `--audio-tone` | Replace audio with a 440Hz test tone | off |
|
|
133
|
+
| `--precise` | Force re-encode for frame-accurate trims | off |
|
|
134
|
+
|
|
135
|
+
### `vidfix generate` — synthetic fixtures from nothing
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
vidfix generate -o bars.mp4 --fps 59.94 --duration 30s --res 1080p
|
|
139
|
+
vidfix generate -o count.mp4 --pattern testsrc --timecode # running timecode overlay
|
|
140
|
+
vidfix generate -o df.mp4 --preset df30 --timecode # drop-frame HH:MM:SS;FF burn-in
|
|
141
|
+
vidfix generate -o red.mp4 --pattern solid:red --audio none
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Patterns: `smpte`, `color-bars`, `testsrc`, `gradient`, `solid:COLOR`.
|
|
145
|
+
Audio: 440Hz `tone` (default), `silence`, `none`.
|
|
146
|
+
|
|
147
|
+
| Option | Meaning | Default |
|
|
148
|
+
|---|---|---|
|
|
149
|
+
| `-o, --output` | Output file path | required |
|
|
150
|
+
| `--fps` | Frame rate: `30`, `59.94`, `30000/1001` | `30` |
|
|
151
|
+
| `--duration` | Duration: `30s`, `1:30`, `90` | `5s` |
|
|
152
|
+
| `--res` | Resolution: `1280x720`, `720p`, `4k` | `1280x720` |
|
|
153
|
+
| `--pattern` | Test pattern: `smpte`, `color-bars`, `testsrc`, `gradient`, `solid:COLOR` | `smpte` |
|
|
154
|
+
| `--codec` | Video codec: `h264`, `h265`, `prores`, `vp9` | `h264` |
|
|
155
|
+
| `--audio` | Audio track: `tone` (440Hz), `silence`, `none` | `tone` |
|
|
156
|
+
| `--timecode` | Burn in running timecode (`HH:MM:SS:FF`; drop-frame `;FF` for NTSC) | off |
|
|
157
|
+
| `--text` | Burn a caption into the video | — |
|
|
158
|
+
| `--preset` | Preset name for defaults (`vidfix preset list`) | — |
|
|
159
|
+
|
|
160
|
+
### `vidfix verify` — spec assertions for CI
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
vidfix verify out.mp4 --fps 60 --duration 30s --res 1280x720 --codec h264
|
|
164
|
+
vidfix verify out.mp4 --fps 29.97 --json | jq .passed
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Prints a pass/fail table per property; exit code 0/1.
|
|
168
|
+
|
|
169
|
+
| Option | Meaning | Default |
|
|
170
|
+
|---|---|---|
|
|
171
|
+
| `--fps` | Expected frame rate | not checked |
|
|
172
|
+
| `--duration` | Expected duration | not checked |
|
|
173
|
+
| `--res` | Expected resolution | not checked |
|
|
174
|
+
| `--codec` | Expected video codec | not checked |
|
|
175
|
+
| `--fps-tolerance` | Allowed fps deviation | `0.01` |
|
|
176
|
+
| `--duration-tolerance` | Allowed duration deviation in seconds | `0.1` |
|
|
177
|
+
| `--json` | Machine-readable JSON instead of a table | off |
|
|
178
|
+
|
|
179
|
+
### `vidfix info` — metadata at a glance
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
vidfix info clip.mp4 # rich table
|
|
183
|
+
vidfix info clip.mp4 --json # jq-friendly
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
$ vidfix info clip.mp4
|
|
188
|
+
container mp4
|
|
189
|
+
duration 0:00:30.030 (30.030s)
|
|
190
|
+
resolution 1280x720
|
|
191
|
+
fps 29.970 (df30, 30000/1001)
|
|
192
|
+
video codec h264
|
|
193
|
+
pixel format yuv420p
|
|
194
|
+
bitrate 1200 kb/s
|
|
195
|
+
audio aac 44100 Hz 2ch
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
The container name is resolved from the file's `major_brand` tag (mp4/mov/m4a/3gp)
|
|
199
|
+
or the extension — not ffprobe's raw demuxer list (`mov,mp4,m4a,3gp,3g2,mj2`);
|
|
200
|
+
`--json` keeps the raw string as `demuxer` and the tag as `major_brand` alongside
|
|
201
|
+
the friendly `container`. Broadcast rates get their preset-family label in the fps
|
|
202
|
+
line (`df30`, `df60`, `pal25`, `pal50`, `film24`, `film23976`) so QA can read them
|
|
203
|
+
at a glance. Uses system `ffprobe` when available, otherwise parses FFmpeg output
|
|
204
|
+
directly.
|
|
205
|
+
|
|
206
|
+
### `vidfix caption` — burn text into a video
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
vidfix caption in.mp4 --text "Take 42" -o out.mp4
|
|
210
|
+
vidfix caption in.mp4 --text "INTRO" --position top --color yellow -o out.mp4
|
|
211
|
+
vidfix caption in.mp4 --text "3..2..1" --start 0 --end 3 -o out.mp4
|
|
212
|
+
vidfix generate -o clip.mp4 --text "TEST CLIP" # caption a generated video too
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Positions: `top`, `center`, `bottom` (default). Audio is stream-copied untouched.
|
|
216
|
+
|
|
217
|
+
| Option | Meaning | Default |
|
|
218
|
+
|---|---|---|
|
|
219
|
+
| `-o, --output` | Output file path | required |
|
|
220
|
+
| `--text` | Caption text to burn in | required |
|
|
221
|
+
| `--position` | Placement: `top`, `center`, `bottom` | `bottom` |
|
|
222
|
+
| `--size` | Font size (pixels or expression like `h/12`) | `h/12` |
|
|
223
|
+
| `--color` | Font color: `white`, `yellow`, `#ff0000` | `white` |
|
|
224
|
+
| `--start` | Show caption from this second onward | whole video |
|
|
225
|
+
| `--end` | Hide caption after this second | whole video |
|
|
226
|
+
|
|
227
|
+
### `vidfix format` — any picture or video to any format
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
vidfix format clip.mov -o clip.mp4 # container conversion
|
|
231
|
+
vidfix format clip.mp4 -o clip.gif # palette-optimized gif
|
|
232
|
+
vidfix format photo.png -o photo.webp # image conversion
|
|
233
|
+
vidfix format clip.mp4 -o thumb.jpg # first-frame thumbnail
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
The output extension picks the format — the only option is `-o, --output` (required).
|
|
237
|
+
|
|
238
|
+
### `vidfix variants` — variant grids in parallel
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
vidfix variants in.mp4 --fps 30,60 --res 720p,1080p -o out/
|
|
242
|
+
# → variants/in_30fps_720p.mp4, in_30fps_1080p.mp4, in_60fps_720p.mp4, ...
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
| Option | Meaning | Default |
|
|
246
|
+
|---|---|---|
|
|
247
|
+
| `-o, --output` | Output directory | required |
|
|
248
|
+
| `--fps` | Comma-separated frame rates, e.g. `30,60` | required |
|
|
249
|
+
| `--res` | Comma-separated resolutions, e.g. `720p,1080p` | required |
|
|
250
|
+
| `--codec` | Video codec for all variants | `h264` |
|
|
251
|
+
| `--jobs` | Parallel FFmpeg processes | `min(4, cpus)` |
|
|
252
|
+
|
|
253
|
+
Exit code is 1 if any variant fails. `vidfix info` takes just the file and
|
|
254
|
+
`--json`; `vidfix preset list` / `preset show NAME` take no options.
|
|
255
|
+
|
|
256
|
+
### Presets
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
vidfix preset list
|
|
260
|
+
vidfix preset show df30
|
|
261
|
+
vidfix generate -o pal.mp4 --preset pal25 --timecode
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Built-in broadcast rate family:
|
|
265
|
+
|
|
266
|
+
| Preset | fps | Timecode counting |
|
|
267
|
+
|---|---|---|
|
|
268
|
+
| `df30` | 29.97 (30000/1001) | drop-frame (`HH:MM:SS;FF`) |
|
|
269
|
+
| `df60` | 59.94 (60000/1001) | drop-frame (`HH:MM:SS;FF`) |
|
|
270
|
+
| `ndf30` | 29.97 (30000/1001) | non-drop (`HH:MM:SS:FF`) |
|
|
271
|
+
| `ndf60` | 59.94 (60000/1001) | non-drop (`HH:MM:SS:FF`) |
|
|
272
|
+
| `pal25` (alias `ndf25`) | exact 25 | non-drop (`HH:MM:SS:FF`) |
|
|
273
|
+
| `pal50` | exact 50 | non-drop (`HH:MM:SS:FF`) |
|
|
274
|
+
| `film24` | exact 24 | non-drop (`HH:MM:SS:FF`) |
|
|
275
|
+
| `film23976` | 23.976 (24000/1001) | non-drop (`HH:MM:SS:FF`) |
|
|
276
|
+
|
|
277
|
+
Plus `broadcast-1080i` (25 fps, 1920x1080) and `web-720p` (30 fps, 720p, h264).
|
|
278
|
+
With `--timecode`, DF presets burn semicolon drop-frame notation and NDF/PAL/film
|
|
279
|
+
presets colon notation, matching broadcast convention. Aliases (`alias: pal25`)
|
|
280
|
+
work in user presets too. Add your own in `~/.config/vidfix/presets.yaml`;
|
|
281
|
+
explicit flags always override.
|
|
282
|
+
|
|
283
|
+
## Python API
|
|
284
|
+
|
|
285
|
+
```python
|
|
286
|
+
from vidfix import convert, generate, verify, probe
|
|
287
|
+
|
|
288
|
+
generate("fixture.mp4", fps="59.94", duration="30s", res="720p")
|
|
289
|
+
info = probe("fixture.mp4") # MediaInfo (pydantic)
|
|
290
|
+
result = verify("fixture.mp4", duration=30.0)
|
|
291
|
+
assert result.passed
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## vidfix vs moviepy vs raw FFmpeg
|
|
295
|
+
|
|
296
|
+
| | vidfix | moviepy | raw ffmpeg |
|
|
297
|
+
|---|---|---|---|
|
|
298
|
+
| Exact-spec test fixtures | ✅ one command | ⚠️ manual | ⚠️ long filter incantations |
|
|
299
|
+
| Spec verification + exit codes | ✅ built in | ❌ | ⚠️ ffprobe + shell glue |
|
|
300
|
+
| Install without system FFmpeg | ✅ bundled | ✅ bundled | ❌ |
|
|
301
|
+
| Editing/compositing/effects | ❌ not the goal | ✅ | ✅ |
|
|
302
|
+
| Programmatic frame access | ❌ | ✅ numpy frames | ⚠️ |
|
|
303
|
+
| Speed | ✅ direct filters, stream copy | ⚠️ python frame loop | ✅ |
|
|
304
|
+
|
|
305
|
+
Use **moviepy** to *edit* videos, **raw ffmpeg** for full control, **vidfix**
|
|
306
|
+
for exact-spec media, quick conversions, and CI checks with zero setup.
|
|
307
|
+
|
|
308
|
+
## CI usage
|
|
309
|
+
|
|
310
|
+
```yaml
|
|
311
|
+
- name: Verify rendered output specs
|
|
312
|
+
run: |
|
|
313
|
+
pip install vidfix
|
|
314
|
+
vidfix verify build/output.mp4 --fps 60 --duration 30s --res 1280x720
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## Development
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
uv sync # install with dev deps
|
|
321
|
+
uv run pytest # unit + integration tests
|
|
322
|
+
uv run pytest -m "not integration" # fast tests only (no FFmpeg)
|
|
323
|
+
uv run ruff check . && uv run mypy # lint + strict types
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## License
|
|
327
|
+
|
|
328
|
+
[PolyForm Internal Use 1.0.0](LICENSE) — you may use vidfix freely for personal
|
|
329
|
+
and internal business purposes (commercial included). Copying, redistributing,
|
|
330
|
+
or building products from this source code is not permitted. Contributions are
|
|
331
|
+
welcome via pull request.
|