aboutsummaryrefslogtreecommitdiff

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 PATH to /bin:/usr/bin:/usr/local/bin for security. If your ffmpeg lives 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>