# transcode.sh > Batch transcode helper for media files using [ffmpeg](https://ffmpeg.org/). `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](https://no-color.org/)**. ## Requirements | Tool | Required | Notes | |------|----------|-------| | `ffmpeg` | always | encoding | | `ffprobe` | always | codec/format detection | | `nice` | always | process priority control | | `bc` | only with `--saving` | percentage calculation | | `tomlq` | only when `config.toml` is present | TOML config parsing | ## Installation ```sh # 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 ``` ## Configuration ### Presets Presets live in: ``` ${XDG_CONFIG_HOME:-$HOME/.config}/transcode.sh/presets/.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 | | `input_frame_rate` | Raw frame rate fraction of the input file as reported by `ffprobe` (e.g. `30000/1001`, `25/1`) | | `input_fps` | `input_frame_rate` rounded to the nearest integer (e.g. `30`, `25`, `60`) | | `output_gop_size` | Recommended GOP size derived from `input_fps` (≈ 5 s of video, capped at 300 frames) | 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`:** ```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. A preset may carry an optional one-line description as a shell comment: ```sh # description: Encode to AV1 with SVT-AV1 at a balanced quality/speed trade-off ffargs=( -c:v:0 libsvtav1 -crf 30 -preset 6 -pix_fmt "$output_pixel_format" ) ``` Run `transcode.sh --list-presets` to enumerate all presets in the preset directory and display their descriptions. ### Configuration file An optional TOML configuration file can be placed at: ``` ${XDG_CONFIG_HOME:-$HOME/.config}/transcode.sh/config.toml ``` Use `--config-file FILE` to load an alternative path. CLI flags always take precedence over values in the config file. The file must be valid TOML — an unparseable file is a hard error. Requires `tomlq` when the file is present. **Example `config.toml`:** ```toml [encoding] preset = "av1" nice = 10 [skip] codecs = ["av1", "hevc"] [saving] enabled = true file = "transcode_savings" [output] verbose = false quiet = false ``` | Section | Key | Type | CLI equivalent | |---------|-----|------|----------------| | `[encoding]` | `preset` | string | `-p` | | `[encoding]` | `nice` | integer | `-N` | | `[encoding]` | `verify_output` | boolean | `--verify-output` | | `[encoding]` | `hwaccel` | string or boolean | `--hwaccel` / `--no-hwaccel` | | `[skip]` | `codecs` | array of strings | `-S` (merged with CLI) | | `[saving]` | `enabled` | boolean | `-s` | | `[saving]` | `file` | string | `--saving-file` | | `[output]` | `verbose` | boolean | `-v` | | `[output]` | `quiet` | boolean | `-q` | > **Note:** `[skip] codecs` from the config file and `--skip-codec` from the > CLI are **merged** — both sources contribute to the skip list. > **Note:** `[encoding] hwaccel` accepts `false` (disable), `true` (use `auto`), > or a method string such as `"vaapi"` or `"cuda"`. CLI `--hwaccel`/`--no-hwaccel` > always takes precedence. ### Codec skip list (deprecated) The legacy `skip.conf` file is no longer read. If it still exists, a deprecation warning is printed at startup. Migrate its contents to `[skip] codecs` in `config.toml` and delete the old file. ## Usage ``` transcode.sh [OPTION] [--] FILE... ``` | Option | Description | |--------|-------------| | `-c`, `--continue` | Continue to next file if ffmpeg fails | | `--no-continue` | Do not continue with the next file if ffmpeg fails | | `--verify-output` | Probe output with ffprobe before replacing original (default) | | `--no-verify-output` | Skip the post-encode integrity check | | `--config-file FILE`, `--config-file=FILE` | Load configuration from FILE instead of the default path | | `-f PATH`, `--encode-file 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) | | `--hwaccel [METHOD]`, `--hwaccel=METHOD` | Enable hardware acceleration; METHOD defaults to `auto` if omitted (e.g. `cuda`, `vaapi`, `videotoolbox`); enabled by default | | `--no-hwaccel` | Disable hardware acceleration (omit `-hwaccel` from ffmpeg invocation) | | `-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 | | `--no-only-if-smaller` | Do replace original even if new file is bigger | | `-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 | | `--list-presets` | List available presets (name + description) and exit | | `-h`, `-?`, `--help` | Show help and exit | ### Examples ```sh # 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 # Load an alternative config file transcode.sh --config-file ~/profiles/fast.toml input.mp4 ``` ## Debugging ```sh # 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](LICENSES/BSD-3-Clause.txt) © 2025 Dennis Fink \