|
|
||
|---|---|---|
| logs | ||
| opentranscode | ||
| tests | ||
| CHANGELOG.md | ||
| LICENSE | ||
| README.md | ||
| open-transcode.py | ||
| pyproject.toml | ||
| pytest.ini | ||
README.md
OpenTranscode
Open-source batch video transcoder for Linux. Encodes folders of video files to AV1 / VP9 / HEVC with configurable audio codecs, resolution scaling, and source-file management. Built on ffmpeg (default) with an optional av1an chunk-parallel path for users with a working VapourSynth setup.
- Default encoder: ffmpeg + libsvtav1 (reliable across distros)
- Optional encoder: av1an chunk-parallel (opt-in via UI toggle)
- Codecs: AV1 (SVT-AV1), VP9, x265 (HEVC) video; Opus, Vorbis, FLAC, IAMF audio
- Containers: MKV, WebM, MP4
- Resolution: Original or scaled (16:9, 21:9, 32:9 presets from 480p to 4K)
- Skip-existing: Probes output with ffprobe; skips files whose codec matches
- Audio normalization: Per-file loudness analysis with volume gain
- Subtitle mux: Optional English subtitle passthrough
- Source management: Optional verified-source deletion after encode
Requirements
- Linux (POSIX)
- Python ≥ 3.12
- ffmpeg (with libsvtav1, libvpx, libx265, libopus, libvorbis, flac)
- ffprobe
- PySide6 (for the GUI)
- Optional: av1an + VapourSynth (only if using the av1an toggle)
Quick start
Install as a package (recommended)
cd /path/to/opentranscode
pip install -e .
opentranscode # launch the GUI
python -m opentranscode --version # → opentranscode 4.5.0
python -m opentranscode --help
Run the launcher script (backwards compat)
python open-transcode.py # launches the GUI
Verify environment without encoding
opentranscode --dry-run # probe + smoke test, no encode
opentranscode --verify-only FILE.mkv # re-verify an existing output
Usage
Default (ffmpeg-only, recommended)
opentranscode
- Encodes with
ffmpeg -c:v libsvtav1(or libvpx-vp9 / libx265 based on selection) - Single-pass per file
- Reliable across distros; no VapourSynth dependency
Skip-existing (default ON)
Files whose output already exists with a matching video+audio codec are
skipped. Detection uses ffprobe — verifies codec_name for both video
and audio streams, plus resolution when scaling is requested.
opentranscode # skip-existing ON (default)
opentranscode --force-reencode # re-encode everything
Verbose logging
Default log output is minimal — two lines per file (start + finish):
Found 180 file(s) to process.
[1/180] filename.mkv
[1/180] filename.mkv — OK: 1.6MB -> 1.3MB (81%)
[2/180] already_done.mkv — SKIP (already av1/opus)
[3/180] next.mkv
[3/180] next.mkv — OK: 2.4MB -> 1.8MB (75%)
QUEUE COMPLETE. Success: 178, Failed: 0, Skipped: 2.
For diagnostics (CMD lines, live tail of ffmpeg/av1an stderr, disk-space warnings, heartbeats):
opentranscode --verbose
Massive-file support
For 30GB+ BluRay rips:
- 24-hour per-file timeout (configurable via
--timeout SECONDS) - 1KB absolute integrity minimum (no false "output too small" failures on high-bitrate sources — duration check is the real gate)
- Disk-space warnings (verbose only) for output and temp partitions
opentranscode --timeout 36000 # 10h per-file timeout
av1an chunk-parallel (opt-in)
For users with a working VapourSynth + source plugin (lsmash, ffms2, bestsource) setup who want scene-detection-based chunk-parallel encoding:
- UI: Check the "av1an (chunk-parallel)" checkbox
- CLI:
opentranscode --use-av1an
When av1an fails per-file (concat failures, scene-detection panics), the code automatically falls back to ffmpeg for that file. When av1an fails systematically (VSScript API mismatch, missing encoder), the queue aborts with an actionable diagnostic.
CLI reference
opentranscode [--version] [--dry-run] [--verify-only PATH] [--force]
[--chunk-method METHOD] [--max-workers N] [--threads-per-worker N]
[--use-av1an] [--verbose] [--skip-existing | --force-reencode]
[--timeout SECONDS]
| Flag | Default | Description |
|---|---|---|
--version |
— | Print version and exit |
--dry-run |
— | Probe environment + smoke test, no encode |
--verify-only PATH |
— | Re-verify an existing output file |
--force |
off | Skip ffprobe pre-validation |
--chunk-method METHOD |
auto | Force av1an chunk method (select, hybrid, ffms2, lsmash, bestsource, dgdecnv) |
--max-workers N |
auto | Cap chunk-parallel worker count |
--threads-per-worker N |
auto | Per-encoder thread cap |
--use-av1an |
off | Use av1an chunk-parallel (UI toggle also available) |
--verbose |
off | Full tech-detail log output |
--skip-existing |
on | Skip files whose output has matching codec |
--force-reencode |
off | Re-encode everything |
--timeout SECONDS |
86400 | Per-file encode timeout (24h default) |
Architecture
opentranscode/
├── __init__.py # Package metadata + lazy launch_gui wrapper
├── __main__.py # python -m opentranscode entry point
├── cli.py # argparse + dry-run + verify-only
├── codec_profiles.py # VideoCodecProfile, AudioProfile, ContainerProfile tables
├── encoder_worker.py # QThread-based per-file encode pipeline
├── env_probe.py # Distro + binary + library + av1an detection
├── ffprobe_utils.py # ffprobe_validate, ffprobe_duration, file-type ID
├── temp_manager.py # Per-worker temp directory isolation
├── cpu_topology.py # Physical core / logical thread detection
├── distro_probe.py # Distro family + package manager detection
├── keepawake.py # systemd-inhibit + optional mouse nudge
├── source_builder.py # From-git rebuild for VapourSynth/av1an ABI mismatches
├── license_registry.py # Third-party license attribution
├── ui_window.py # PySide6 main window + launch_gui
├── ui_theme.py # Retro-futuristic QSS theme
└── widgets/ # Custom Qt widgets (radio_knob, etc.)
open-transcode.py # Launcher script (mirrors package, test target)
pyproject.toml # PEP 621 build config
tests/ # 149 tests across 14 files
Encode pipeline
- Environment probe — detects distro, ffmpeg/ffprobe/av1an paths, encoder library availability, VapourSynth + source plugins, CPU topology
- Pre-flight validation — ffprobe scans all input files; reports valid vs invalid counts before encoding starts
- Per-file pipeline:
_validate_file— ffprobe pre-check (skip if invalid)_check_disk_space— warn (verbose) if free space < source size_output_already_encoded— skip if output exists with matching codec_prepare_input— pre-scale (if requested) or symlink to temp_encode_one— dispatch to ffmpeg (default) or av1an (opt-in)_run_with_stop_check— subprocess with STOP-button interrupt support_verify_and_finalize— duration check (≥95%), subtitle mux, source deletion
- Final cleanup — sweep per-worker temp dir, delete verified sources
Thread safety
- Each
EncoderWorkerruns in its ownQThread - Per-worker temp subdirectory (
~/.cache/OpenTranscode/tmp/worker-<pid>/) created with mode 0700 (SEI CERT FIO09-C) - Process-group signaling (
start_new_session=True+os.killpg) reaches av1an's child encoders (SvtAv1EncApp / vpxenc / x265) - Drainer threads read stdout/stderr continuously to prevent pipe-buffer
deadlock (same pattern as
subprocess.run._communicate)
Coding standards
The codebase follows:
- PEP 868 — parameterized type hints (
dict[str, object], notDict[str, object]) - SEI CERT — MSC04-C (single source of truth for diagnostics), FIO09-C (secure temp directory), ERR01-C (narrow exception scope), STR09-C (no substring matches for encoder names)
- POSIX —
start_new_session=Truefor process-group signaling,signal.SIGTERM→SIGKILLescalation,os.killpgfor child cleanup - MISRA (where applicable to Python) — single exit point per function
where practical, no early returns from
tryblocks without cleanup
Testing
python -m pytest tests/ -q # 149 tests, ~10s
python -m pytest tests/ -v # verbose
python -m pytest tests/ -k "skip_existing" # subset
Test categories:
- Smoke tests — av1an VSScript compatibility probe
- Encoder pipeline —
_validate_file,_prepare_input,_verify_and_finalize - Real-encode e2e — generates real test videos with ffmpeg, runs the full pipeline
- Chunk-method retry — y4m pipe break recovery
- Stop button — SIGTERM/SIGKILL on process group
- Concurrent workers — per-PID temp directory isolation
- Skip-existing — codec matching, ffprobe failure, resolution mismatch
- Massive files — timeout flag, 1KB integrity threshold, disk-space checks
- Package structure — public API surface, submodule imports, CLI parser
License
AGPL-3.0-or-later. See LICENSE.
Third-party tools invoked (not bundled): ffmpeg, ffprobe, av1an, VapourSynth, SvtAv1EncApp, vpxenc, x265, mkvmerge. Licenses flow through from upstream.
Project
- Repository: https://git.dcos.net/dcosnet/OpenTranscode
- Issues: https://git.dcos.net/dcosnet/OpenTranscode/issues
- Changelog: CHANGELOG.md