Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FFmpeg Wrapper
A desktop GUI for common ffmpeg jobs: remuxing MKV files to Jellyfin-friendly MP4s, choosing which audio tracks to keep, stripping audio, re-encoding, and scaling. Works on single files or whole batches. It's a single executable for Windows and Linux, built with Python, Qt (PySide6) and PyInstaller.
Features
- Single file or batch. Use Add Files, Add Folder (recursive), or drag and drop. Files are probed in the background and shown with duration, video info, and audio and subtitle counts.
- Target container. MP4, MKV, MOV, M4V, WebM, AVI or MPEG-TS. The encoder lists only show codecs the chosen container can hold.
- Audio track picker. Every audio track in the selected file is listed with its language, codec, channels and title. Tick the tracks you want and drag them into order. The first ticked track becomes the default. Apply to All Files copies your choice to the rest of the queue, matching tracks by language, codec and layout, so a season of episodes takes one click.
- Strip audio. Set Audio mode to Strip audio.
- Keep streams unaltered. Copy mode remuxes video and/or audio without re-encoding. Auto copies whatever the container supports and re-encodes only what it can't hold (for example DTS/TrueHD to AAC for MP4).
- Up/downscale video. Pick 360p up to 8K, or a custom WxH, with aspect ratio kept and Lanczos scaling. Portrait and rotated phone videos are handled. You can also change the frame rate. Encoders: H.264, HEVC, AV1, VP9, MPEG-4, plus NVENC, QuickSync and AMF if your ffmpeg has them. There are CRF quality and speed preset controls.
- Up/downmix audio. Change channels (mono, stereo, 5.1, 7.1), sample rate, bitrate, and codec (AAC, AC-3, E-AC-3, Opus, MP3, Vorbis, FLAC, ALAC, PCM).
- Output folder. Save next to the source or in a chosen folder. You can add a filename suffix and choose what happens when the output file already exists (keep both, overwrite, or skip). The source file is never overwritten.
- Subtitles are kept where possible. MP4 and MOV turn text subs into
mov_text, and image subs (PGS) are dropped with a note in the log. MKV keeps everything, including font attachments. - MP4/MOV outputs get
+faststart, and HEVC is taggedhvc1for Apple and TV compatibility. - The window shows per-file and overall progress, has a Stop button, and keeps a full log. Show Command prints the exact ffmpeg command so you can copy it.
- ffmpeg writes to
name.part.extand the file is renamed only on success, so a failed or cancelled job never leaves a half-written file.
Getting ffmpeg
The app looks for ffmpeg and ffprobe in this order:
- The folder set in Settings > Set FFmpeg Location...
- Next to the executable (or in
ffmpeg/,ffmpeg/bin/orbin/beside it) - Your
PATH
The release archives (FFmpegWrapper-<version>-windows-x64.zip / -linux-x64.tar.gz) already
include ffmpeg and ffprobe next to the executable, with ffmpeg's license as ffmpeg-LICENSE.txt.
If you use the bare executable, install ffmpeg (sudo apt install ffmpeg, winget install ffmpeg,
...) or point the app at it.
Running from source
python -m venv .venv
.venv/bin/pip install -r requirements-dev.txt # Windows: .venv\Scripts\pip ...
python scripts/fetch_ffmpeg.py # optional: downloads ffmpeg into ./ffmpeg/bin
python run.py
Tests: python -m pytest. The integration tests run real conversions when ffmpeg is found, either
on PATH or through FFWRAP_FFMPEG_DIR=ffmpeg/bin.
Building the executables
PyInstaller does not cross-compile. Build the Windows .exe on Windows and the Linux binary on Linux.
pip install -r requirements-dev.txt
python build.py # -> dist/FFmpegWrapper or dist/FFmpegWrapper.exe
python build.py --ffmpeg-dir ffmpeg/bin # also bundles ffmpeg + ffprobe + their license into a .zip / .tar.gz
python scripts/smoke_test.py dist/FFmpegWrapper # optional: headless start-up check
--onedir builds a folder instead of a single file. It starts faster because nothing has to be
unpacked on launch.
CI: .github/workflows/build.yml builds, tests and smoke-tests both
platforms on every v* tag (or a manual run) and attaches the archives to the release. Gitea and
Forgejo Actions can run it too, but they need a Windows runner registered.
Notes
- Linux: the binary needs glibc 2.35 or newer (Ubuntu 22.04+, Debian 12+, Fedora 36+,
Mint 21+). It bundles the Qt XCB libraries, but uses the system's OpenGL/EGL driver libraries.
To get a menu entry, copy
assets/icon.pngand write a.desktopfile that points at the binary. - PySide6 is pinned below 6.12. PySide6 6.12.0 mis-counts references to Python's
None(inQComboBox.clear()/currentData()), which crashes the app on Python 3.11. Python 3.12+ is not affected, but keep the pin until a fixed PySide6 ships. - ffmpeg licensing: the bundled ffmpeg builds come from
BtbN/FFmpeg-Builds and are GPLv3, so their license has to
ship with them.
scripts/fetch_ffmpeg.pysaves it asffmpeg/LICENSE.txt, andbuild.py --ffmpeg-dircopies it into the archive asffmpeg-LICENSE.txt. It looks in the ffmpeg folder and the folder above it, and stops with an error if the license isn't in either. - Settings (last options, window size, ffmpeg location) are saved per user through Qt's QSettings:
the registry on Windows,
~/.config/ffwrap/FFmpegWrapper.confon Linux.
Project layout
ffwrap/
formats.py container/encoder tables and compatibility rules (no Qt)
probe.py ffprobe -> MediaInfo/Stream dataclasses
command.py Options -> ffmpeg arguments, output naming, audio-track matching (no Qt)
tools.py locating ffmpeg/ffprobe, encoder detection
gui/ main window, queue model, QProcess batch runner
build.py PyInstaller build + packaging
scripts/ fetch_ffmpeg.py, smoke_test.py, make_icon.py
tests/ unit tests + real-ffmpeg integration tests
License
FFmpeg Wrapper is public domain, released under the Unlicense. Kopimi: copy it, change it, share it, sell it. No permission needed.
That covers this project's own code. The ffmpeg and ffprobe binaries in the release archives are
GPLv3 (license in ffmpeg-LICENSE.txt, source at https://ffmpeg.org/download.html), and the Qt
libraries built into the executable are LGPLv3.
