A CLI that converts videos. Wrapped in more BBS ANSI chrome than any video converter has any business having.
Built for people who find ffmpeg's man page relaxing and consider a file picker GUI a personal insult.
minimuvi converts video files using ffmpeg presets. No GUI. No wrapper libraries lying to you about what ffmpeg is actually doing. No wizard asking you to think about bitrate. Arrows. Space. Enter. Done.
The name is Italian for "my movies", stripped of vowels and self-importance.
folder navigator + file picker · conversion plan, live progress, results table
Every video conversion workflow starts the same way.
You drag a file into some app. The app wants fifteen decisions about codecs you don't have opinions about. You install ffmpeg because at least the man page is honest. You write an alias. The alias grows into a shell function. The shell function grows into a script with presets hardcoded in comments next to a TODO: make this interactive from fourteen months ago.
So you make it interactive. You add a file picker. Then a preset selector. Then a spinner because staring at a blank terminal while ffmpeg runs is not a life. Then a before/after size table because the results deserve a frame. Then a figlet banner and BBS color gradients because at this point you are committed and there is no going back. Then a savings summary because the numbers alone aren't enough — you need to know that you freed up roughly the Lord of the Rings extended edition, one of the three, not the good one.
Is it overkill for a video converter? Yes. Is slowmovie (a real preset for e-ink displays, 1fps, no audio) also in here? Also yes. Both things are simultaneously true and we are at peace with it.
Five presets, all defined as dataclasses in presets.py. Add your own in thirty seconds without touching anything else.
| Preset | Resolution | FPS | CRF | Audio | For |
|---|---|---|---|---|---|
slowmovie |
640×400 | 1 | 35 | silent | e-ink displays (see TaleVision) |
web |
1920×1080 | 24 | 23 | AAC 192k | standard web delivery |
archive |
original | src | 18 | AAC 320k | vault it and never think about it again |
instagram |
1080×1080 crop | 30 | 26 | AAC 192k | square, center-cropped, no apologies |
gif |
480px wide | 10 | n/a | silent | palette-optimized, not the garbage kind |
The slowmovie preset exists because TaleVision exists.
640×400. 1fps. CRF 35. No audio. The output is a video that an e-ink display can play one frame at a time, looking like a moving painting, drawing almost no power, making everyone in the room quietly confused about what they're looking at.
It is a legitimate use case. We are not embarrassed by it.
- Python 3.10+
- ffmpeg in
$PATH
That is the real dependency. Everything else is Python packages.
git clone https://github.com/enuzzo/minimuvi
cd minimuvi
python -m venv venv && source venv/bin/activate
pip install -r requirements.txtConfirm ffmpeg exists:
ffmpeg -versionIf it doesn't: brew install ffmpeg or your OS equivalent. minimuvi expects ffmpeg in $PATH like an adult and will not go looking for it.
Run it from anywhere:
python minimuvi.py # start in current directory
python minimuvi.py /path/to/dir # jump straight to a specific folderThe rest is arrows and the space bar.
- Folder navigator: if the current directory has no videos, an interactive browser opens. Subdirectories show their video count inline. Navigate up and down, enter to descend, select "use this folder" when you're there.
- Select videos: checkbox multi-select. Pick individually, or hit
[a] select allat the top to grab everything in one move. - Choose a preset: arrow keys, specs and description shown inline.
- Output directory: defaults to
./outputinside the video folder, editable. - Review the plan: double-border summary table before anything runs.
- Watch it go: live spinner with real elapsed time per file.
- Results: before/after size, compression ratio, time, all in a table.
- Space Reclaimed: total saved across all files, with a contextual note about what that many bytes actually means. It will be sardonic. You will appreciate it.
Output files are named original_name__preset.ext. Double underscore. The source is never touched.
Open presets.py. Append a Preset(...) to PRESETS. Nothing else changes. The UI picks it up automatically.
Preset(
name="vertical",
description="1080×1920, 30fps, CRF 24, for phones held wrong",
video_codec="libx264",
crf=24,
fps=30,
width=1080,
height=1920,
audio_codec="aac",
audio_bitrate="192k",
extra_flags=["-pix_fmt", "yuv420p"],
crop_center=True,
),Presets are dataclasses. They don't know about the UI. The UI doesn't know about ffmpeg. converter.py knows about ffmpeg and nothing else. This separation exists so that adding a preset is thirty seconds of work, not an archaeology dig.
After every batch, minimuvi totals up what you saved and puts it in context.
Not just "you saved 1.84 GB". That number means nothing. Instead: "About the Lord of the Rings extended edition. One of the three. Not the good one."
There are 106 of these. They cover everything from a favicon's worth of savings (not nothing, but close) to numbers that make you consider whether your encoding pipeline deserves a name. The phrase is picked at random from the bucket that matches your actual savings. It will be different every time. It will always be accurate.
The gif preset runs two passes:
- Pass 1:
palettegenextracts an optimized 256-color palette from the source frames - Pass 2:
paletteuseencodes the GIF using that palette
The difference between this and a one-liner ffmpeg -i input.mp4 output.gif is immediately visible. The temp palette file is deleted after conversion. Both passes show a separate spinner line so you know what's actually happening.
MIT. Convert whatever you own. Fork whatever you want. Run it wherever.
No warranty. No liability. No hard feelings.
Built with ffmpeg, rich, questionary, and the firm conviction that a video converter in 2025 deserves a terminal UI you actually want to look at.

