transcode.sh
Batch transcode helper for media files using ffmpeg.
transcode.sh re-encodes media files in place using user-defined presets —
small shell snippets that supply the ffmpeg arguments. It probes each input
file to detect the video codec and pixel format, skips files whose codec is on
the skip list, selects an appropriate output pixel format automatically, and
replaces the original atomically on success.
Features
- Preset-driven — encoding parameters live in plain shell files, not hard-coded flags.
- Batch processing — pass files on the command line or via a list file
(
-f). - Codec skip list — per-user and per-invocation lists of codecs to leave untouched (e.g. skip files already encoded as AV1).
- Only-if-smaller mode (
-l) — discard the re-encoded file if it is larger than the original. - Savings log (
-s) — append filename, original size, new size and percentage saved to a log file after each successful encode. - Dry-run (
-n) — preview what would happen without touching any files. - Nice — runs ffmpeg at niceness 19 by default to avoid starving other
processes; configurable with
-N. - Respects NO_COLOR.
Requirements
| Tool | Required | Notes |
|---|---|---|
ffmpeg |
always | encoding |
ffprobe |
always | codec/format detection |
nice |
always | process priority control |
bc |
only with --saving |
percentage calculation |
Installation
# Clone the repo
git clone https://codeberg.org/yourname/transcode.sh.git
cd transcode.sh
# Make the script executable
chmod +x transcode.sh
# Optional: install to a directory in your PATH
install -Dm755 transcode.sh ~/.local/bin/transcode.sh
# Optional: install the man page
install -Dm644 transcode.1 ~/.local/share/man/man1/transcode.1
# Optional: install bash completion
install -Dm644 transcode.sh.bash-completion \
/etc/bash_completion.d/transcode.sh
# or for your user only (requires bash-completion ≥ 2.2):
install -Dm644 transcode.sh.bash-completion \
~/.local/share/bash-completion/completions/transcode.sh
PATH note: the script resets
PATHto/bin:/usr/bin:/usr/local/binfor security. If yourffmpeglives elsewhere (e.g. Homebrew on macOS, Nix), prepend its directory:
sh PATH="/opt/homebrew/bin:$PATH" transcode.sh input.mp4
Configuration
Presets
Presets live in:
${XDG_CONFIG_HOME:-$HOME/.config}/transcode.sh/presets/<name>.sh
Each preset is sourced as Bash and must define an array named ffargs.
The following variables are available to presets at source time:
| Variable | Description |
|---|---|
input_codec |
Video codec of the input file (e.g. h264, hevc) |
input_pixel_format |
Pixel format of the input after yuvj* normalisation (e.g. yuv420p) |
output_pixel_format |
Recommended output pixel format, derived from the input to preserve chroma subsampling and bit depth |
Before the preset's ffargs are appended, the script always passes
these base arguments to ffmpeg:
-map 0 # include all streams from the input
-map_metadata 0 # copy all container-level metadata
-map_chapters 0 # copy all chapter markers
-c copy # default to stream copy for every stream
A preset therefore only needs to specify the streams it wishes to
re-encode (typically -c:v:0) and any associated encoder options.
All other streams — audio, subtitles, attachments — are passed through
unchanged unless the preset explicitly overrides them.
Example — ~/.config/transcode.sh/presets/default.sh:
ffargs=(
-c:v:0 libsvtav1
-crf 30
-preset 6
-pix_fmt "$output_pixel_format"
)
Security: presets are executed as shell code. Only use presets from trusted sources.
Codec skip list
To permanently skip files that are already in a particular codec, add codec names (one per line) to:
${XDG_CONFIG_HOME:-$HOME/.config}/transcode.sh/skip.conf
Example — skip files already encoded as AV1 or HEVC:
av1
hevc
Usage
transcode.sh [OPTION] [--] FILE...
| Option | Description |
|---|---|
-c, --continue |
Continue to next file if ffmpeg fails |
-f PATH, --encodefile PATH, --encodefile=PATH |
Read file list from PATH (one per line) |
-n, --dry-run |
Show what would be done; don't run ffmpeg |
-N VALUE, --nice VALUE, --nice=VALUE |
Niceness value for ffmpeg (default: 19) |
-s, --saving |
Log filesize savings after each encode |
--saving-file PATH, --saving-file=PATH |
Savings log path (default: transcode_savings) |
-S LIST, --skip-codec LIST, --skip-codec=LIST |
Codecs to skip (comma/space/colon separated) |
-l, --only-if-smaller |
Only replace original if new file is smaller |
-p NAME, --preset NAME, --preset=NAME |
Preset to use (default: default) |
-q, --quiet |
Suppress all output (overrides -v) |
-v, --verbose |
More detailed output |
--color / --no-color |
Force or disable colored output |
--version |
Print version information |
-h, -?, --help |
Show help and exit |
Examples
# Transcode a single file with the default preset
transcode.sh video.mp4
# Transcode a list of files and record size savings
transcode.sh --saving --encodefile list.txt
# Dry-run with verbose output to inspect detected formats
transcode.sh --dry-run --verbose my_movie.mkv
# Use a custom preset, only keep result if smaller, skip AV1 inputs
transcode.sh --preset av1_fast --only-if-smaller --skip-codec av1 /media/films/*.mkv
# Run at a moderate priority and continue past failures
transcode.sh --nice 10 --continue *.mp4
Debugging
# High-level state messages
DEBUG=1 transcode.sh input.mkv
# Full bash execution trace
TRACE=1 transcode.sh input.mkv
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Runtime failure (ffmpeg error on one or more files) |
| 2 | Usage error (unknown option, missing argument) |
| 3 | Configuration error (missing or invalid preset) |
| 127 | Missing dependency |
License
BSD-3-Clause © 2025 Dennis Fink \<dennis.fink@c3l.lu>
