An ffmpeg wrapper that helps you navigate the flags to save some space by compressing video.
- Python 3.8+ (uses only the standard library)
- ffmpeg and ffprobe installed and available on
PATH
On macOS, hardware acceleration uses VideoToolbox (videotoolbox) which requires an ffmpeg build with VideoToolbox support (brew-installed ffmpeg typically includes this). I also noticed that ffmpeg has started requiring ffmpeg-full formula to be installed to get h265 support.
- Clone the repo:
git clone https://github.com/Jeshii/compress-buddy.git
cd compress-buddy- Verify
ffmpegandffprobeare available and list encoders you may need:
command -v ffmpeg && command -v ffprobe
ffmpeg -encoders | grep -E "libx264|libx265|hevc_videotoolbox|h264_videotoolbox|videotoolbox"Basic:
python3 compress_buddy.py video.movPlace converted files into an output directory:
python3 compress_buddy.py -o /tmp/outdir video.movDry run (no ffmpeg executed, just prints decisions):
python3 compress_buddy.py --dry-run -o /tmp/outdir video.movCRF mode with 4 workers (when you hate your computer):
python3 compress_buddy.py --mode crf --quality 28 --workers 4 *.mp4Chunk output into 15-minute parts:
python3 compress_buddy.py --chunk-minutes 15 -o /tmp/outdir long_video.movJoin multiple inputs:
python3 compress_buddy.py --join -o /tmp/outdir part1.mov part2.movGeneral
-h,--help— show this help message and exit.--dry-run— show decisions and planned actions, do not run ffmpeg.--log-level <DEBUG|INFO|WARNING|ERROR>— set logging verbosity (default: INFO).--no-bell— suppress terminal bell on completion.
Encoding
--mode {hardware,crf,software}— encode mode.hardwareuses platform HW accel,crfuses constant-rate-factor software encoding.--quality <0-100>— user-facing quality (0 worst, 100 best). In CRF mode we map this to ffmpeg's CRF scale.--codec {h264,h265,avc,hevc,x264,x265,size,compatibility}— high-level codec choice (we normalize toh264/h265).--force-encoder <TOKEN>— force an exact ffmpeg encoder token (e.g.hevc_videotoolbox,h264_nvenc). We validate the token againstffmpeg -encoders.--suffix {mp4,mov,mkv,avi}— output file suffix (default:mov).
Quality
--min-kbps <N>— minimum target bitrate in kbps (optional). If ffprobe cannot determine source bitrate, we will ask for bounds or abort.--max-kbps <N>— maximum target bitrate in kbps (optional).--target-factor <float>— fraction of source bitrate to target (0.0 < factor <= 1.0). Default:0.7.\--motion-multiplier <float>— Specify motion multiplier. Less than 1.0 for low motion videos, more than 1.0 for high motion videos.
Audio
--copy-audio— copy AAC audio streams instead of re-encoding.- If not copying and input audio is not AAC, we encode audio to AAC at 128k.
Scaling
--max-width <px>— maximum output width in pixels (preserves aspect ratio).--max-height <px>— maximum output height in pixels (preserves aspect ratio).- If only one of
--max-width/--max-heightis provided, we infer the other from the input aspect ratio.
Output
--output, -o <DIR>— output directory (place converted files here).--chunk-minutes <N>— split output into N-minute segments. Segments are written to a temporary directory next to the output and then moved into place after encoding.--overwrite— overwrite existing outputs.--delete-original— delete original file after successful compression.--join- attempt to join videos together quickly with ffmpeg copy and then pass that on to processing.
Performance / Resource Control
--workers <N>— number of parallel workers (we force1for macOS hardware mode for stability).--threads <N>— pass-threads Nto ffmpeg to bound encoder threads.
Motion
Notes
- We clamp computed target bitrates to
--min-kbps/--max-kbpsif provided, and never intentionally target a bitrate higher than the source (as reported by ffprobe). - If ffprobe cannot determine a source bitrate and you do not provide bounds, the run will abort rather than guessing from file size.
- Use
--dry-runto validate decisions without running ffmpeg. - For Windows specifics (path handling, AV interference, long paths), see the Windows Notes section below.
- ffprobe is used to check for bitrates on the source. By default, we calculate a recommended bitrate based on the ini baseline, resolution, and codec. We prefer per-stream
bit_ratewhen available and fall back to the container-levelformat.bit_ratefor formats like WebM/Matroska. You can also use the--target-factorflag to calculate a target bitrate. The target bitrate is computed assource_kbps * --target-factor(default 0.7).--min-kbpsand--max-kbpsare optional and, if provided, clamp the computed target. If ffprobe cannot determine a bitrate at all the tool will ask you to provide--min-kbpsor--max-kbpsexplicitly rather than guessing from file size. - Quality mapping:
--qualityis a user-facing 0–100 scale, with 0 being worst and 100 being best. This is inverted in CRF mode automatically to keep things simple. - macOS + VideoToolbox: hardware mode uses VideoToolbox and we force
--workers 1on Darwin for stability. - Audio handling: if the input audio is AAC or you pass
--copy-audio, audio will be copied. Otherwise it will be re-encoded to AAC at 128k. - Byte display: sizes shown by the tool use IEC binary units (
KiB,MiB,GiB) which are based on 1024 bytes. This makes it explicit that values like2.59 GiBare using 1024-based math and explains apparent differences with Finder which often reports decimal GB. - Bitrate cap: the computed target bitrate is capped to the original source bitrate (as reported by ffprobe) so we will never intentionally target a bitrate higher than the source. If ffprobe cannot determine the source bitrate, the tool will continue to ask you to provide explicit
--min-kbps/--max-kbpsor accept defaults. - Chunking: segments are written to a temporary directory next to the output and moved into place after encoding. If the process fails mid-run, partial files may need manual cleanup.
- Destructive flags:
--overwriteand--delete-originalwill replace or remove files — use with caution.
- The flag
--codecaccepts codec names and common synonyms. Use--codec h264or--codec hevc, the aliasesavc,x264,x265, or even simplysize(h265) orcompatibility(h264). - If you need to force a specific ffmpeg encoder token (for example,
hevc_videotoolbox,h264_nvenc, orhevc_qsv), use--force-encoder "<token>". We will validate the token exists in yourffmpeg -encodersoutput before using it.
Examples:
# use the codec name
python3 compress_buddy.py --codec hevc myvideo.mov
# force a specific ffmpeg encoder token
python3 compress_buddy.py --mode hardware --force-encoder hevc_videotoolbox myvideo.mov- Verify ffmpeg/ffprobe availability:
where.exe ffmpeg
where.exe ffprobe- Check available hardware acceleration methods and encoders:
ffmpeg -hwaccels
ffmpeg -encoders-
Are these some Windows hardware encoders names? Not sure!
- NVIDIA:
h264_nvenc,hevc_nvenc - Intel QSV:
h264_qsv,hevc_qsv - Direct3D/DXVA:
h264_d3d11va,h264_dxva2
- NVIDIA:
-
Path and file handling:
- Windows prevents deleting files that are still open
- Stuff may fail across different drives
- Very long paths (>260 chars) can be problematic
-
Quick test (PowerShell):
# dry-run to confirm behavior without running ffmpeg
python compress_buddy.py --dry-run -o C:\tmp test.mp4
# real run (ensure ffmpeg.exe is on PATH)
python compress_buddy.py -o C:\tmp test.mp4-
1) Install Python 3.8+
- Download the latest Windows installer from https://www.python.org/downloads/windows and run it.
- On the first installer screen, check "Add Python 3.x to PATH", then choose "Install Now". This puts
pythonandpipon your PATH so PowerShell can run them directly.
-
2) Verify Python is on PATH
-
Open PowerShell (press Windows, type PowerShell, press Enter) and run:
python --version pip --version
-
You should see version strings (for example
Python 3.11.4). If not, re-run the installer and ensure the "Add to PATH" option is checked.
-
-
3) Install ffmpeg and ffprobe
- Download a static build ("essentials" or "full") from https://www.gyan.dev/ffmpeg/builds/.
- Extract the zip to a folder such as
C:\ffmpegso the binaries live atC:\ffmpeg\bin\ffmpeg.exeandC:\ffmpeg\bin\ffprobe.exe.
-
4) Add ffmpeg to PATH
-
Run this PowerShell snippet to add
C:\ffmpeg\binto your user PATH (change path if you extracted elsewhere):$userPath = [Environment]::GetEnvironmentVariable('PATH', 'User') if ($userPath -notlike '*C:\ffmpeg\bin*') { [Environment]::SetEnvironmentVariable('PATH', "$userPath;C:\ffmpeg\bin", 'User') }
-
Close and re-open PowerShell to pick up the updated PATH.
-
-
5) Verify ffmpeg and ffprobe
-
Run:
where.exe ffmpeg where.exe ffprobe ffmpeg -version ffmepg -h ffmpeg -encoders"
-
Confirm the commands print paths and a version. If encoders are missing, download the "full" ffmpeg build instead of "essentials".
-
-
6) Clone the repo and run compress-buddy
-
From PowerShell:
git clone https://github.com/Jeshii/compress-buddy.git cd compress-buddy # dry-run to confirm decisions (no ffmpeg executed) python compress_buddy.py --dry-run -o C:\tmp example.mp4 # real run python compress_buddy.py -o C:\tmp example.mp4
-
-
7) If
pythonis not recognized after install- The installer may have skipped adding Python to PATH. Find your Python install folder (for example
C:\Users\<you>\AppData\Local\Programs\Python\Python311) and add both the base andScriptsdirectories to the user PATH using the snippet in step 4.
- The installer may have skipped adding Python to PATH. Find your Python install folder (for example
-
8) Long path and permission notes
- Avoid deeply nested directories to prevent Windows path-length issues (>260 characters). Use shorter paths like
C:\tmpwhen troubleshooting. - If you get permission errors, try running PowerShell as Administrator or choose an output folder under your user profile.
- Avoid deeply nested directories to prevent Windows path-length issues (>260 characters). Use shorter paths like
-
9) Example PowerShell session
python --version where.exe ffmpeg python compress_buddy.py --dry-run -o C:\Users\Public\Videos test.mp4 python compress_buddy.py -o C:\Users\Public\Videos test.mp4
- If you see errors about unknown encoders, try installing
ffmpeg-full, install an ffmpeg build with the required encoders (e.g.,libx264,libx265) - To get more visibility into ffmpeg progress and script decisions, run with
--log-level DEBUG(it is very VERBOSE tho)
- Locations searched (first found wins):
./compress_buddy.ini(script directory)$XDG_CONFIG_HOME/compress_buddy/config.ini~/.config/compress_buddy/config.ini~/.compress_buddy.ini
- How it works: the tool reads a
[defaults]section and applies values when CLI flags are not provided. This is useful to set your preferred encoder, output suffix, and other defaults so you don't need to pass them on every run. - Common keys you can set:
preferred_mode—hardwareorcrf(software). If omitted the script defaults tohardware.preferred_codec—h264orh265(controls codec preference when not explicitly provided on the command-line).default_bit_depth—8or10.suffix— default output container (mp4,mov, etc.).target_factor— fraction of source bitrate used to compute target bitrate (default0.7).
Edit compress_buddy.ini or one of the other config paths above to make these defaults persistent. Example compress_buddy.ini snippet:
[defaults]
preferred_mode = hardware
preferred_codec = h265
default_bit_depth = 10
suffix = mp4
target_factor = 0.7If an encode uses too much CPU you can limit ffmpeg:
--threads Npasses-threads Nto ffmpeg directly- If you run with
--workers > 1and do not specify--threads, we will auto-calculate athreadsvalue per worker by dividing the logical CPU count by the number of workers (minimum 1)
Examples:
# limit encoder threads to 4
python3 compress_buddy.py --threads 4 myvideo.mov
If you run into a bug, please open an issue with the command you ran and the ffmpeg version (output from ffmpeg -version). Or feel free to open a PR!