summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDennis Fink2026-05-08 19:41:14 +0200
committerDennis Fink2026-05-08 19:41:14 +0200
commit7e73a76bba167c2a6d5bd9afc145690c5049ebf2 (patch)
treee3d6f0596f3d7083c5b9c2141649590a2931a5d3
parentaea6e655e1c572c2a11c5438c57634f15649bf5e (diff)
downloadtranscode.sh-7e73a76bba167c2a6d5bd9afc145690c5049ebf2.tar.gz
transcode.sh-7e73a76bba167c2a6d5bd9afc145690c5049ebf2.zip
feat(cli)!: add TOML config file support
Add a structured config file so common defaults can live in one place instead of being repeated in aliases or shell wrappers. CLI flags keep precedence, which keeps one-off overrides predictable. Use tomlq only when a config file is present, and fail early on missing dependencies or invalid TOML so configuration mistakes do not silently change encode behavior. Move the persistent skip-codec list from the legacy skip.conf file to [skip] codecs in config.toml. Config codecs are merged with --skip-codec so global defaults and per-run skips can be combined. Document the new format in the README and man page, update dependency notes, and extend bash completion for config-file paths and the new negative boolean flags. BREAKING CHANGE: skip.conf is no longer read for codec skips. Migrate its entries to [skip] codecs in config.toml and remove the old file. BREAKING CHANGE: --encodefile and --encodefile= are replaced by --encode-file and --encode-file=. Update scripts, aliases, and completion usage to use the hyphenated option name.
-rw-r--r--README.md59
-rwxr-xr-xtranscode.sh282
-rw-r--r--transcode.sh.198
-rw-r--r--transcode.sh.bash-completion28
4 files changed, 409 insertions, 58 deletions
diff --git a/README.md b/README.md
index 64dd6c0..6d6c74d 100644
--- a/README.md
+++ b/README.md
@@ -33,6 +33,7 @@ replaces the original atomically on success.
| `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
@@ -114,22 +115,56 @@ ffargs=(
> **Security:** presets are executed as shell code. Only use presets from
> trusted sources.
-### Codec skip list
+### Configuration file
-To permanently skip files that are already in a particular codec, add codec
-names (one per line) to:
+An optional TOML configuration file can be placed at:
```
-${XDG_CONFIG_HOME:-$HOME/.config}/transcode.sh/skip.conf
+${XDG_CONFIG_HOME:-$HOME/.config}/transcode.sh/config.toml
```
-**Example — skip files already encoded as AV1 or HEVC:**
+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.
-```
-av1
-hevc
+**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` |
+| `[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.
+
+### 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
```
@@ -139,13 +174,16 @@ 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) |
+| `--no-continue` | Do not continue with the next file if ffmpeg fails |
+| `--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) |
| `-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 |
@@ -170,6 +208,9 @@ transcode.sh --preset av1_fast --only-if-smaller --skip-codec av1 /media/films/*
# 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
diff --git a/transcode.sh b/transcode.sh
index 61ecc5e..092fbb9 100755
--- a/transcode.sh
+++ b/transcode.sh
@@ -23,8 +23,28 @@
#
# Note: Presets are sourced as shell code. Only use trusted presets.
#
-# A per-user list of video-codecs to skip can be provided in:
-# ${XDG_CONFIG_HOME:-$HOME/.config}/transcode.sh/skip.conf
+# An optional TOML configuration file may be placed at:
+# ${XDG_CONFIG_HOME:-$HOME/.config}/transcode.sh/config.toml
+#
+# It may set defaults for any option, for example:
+# [encoding]
+# preset = "av1"
+# nice = 10
+#
+# [skip]
+# codecs = ["av1", "hevc"]
+#
+# [saving]
+# enabled = false
+# file = "transcode_savings"
+#
+# CLI flags always take precedence over config file values.
+# Requires tomlq (https://github.com/nicowillis/tomlq) when config.toml
+# is present.
+#
+# NOTE: skip.conf is deprecated. If it exists alongside config.toml the
+# script will warn and ignore it. Migrate codecs to [skip] codecs in
+# config.toml.
#
# NOTES
# This script respects the NO_COLOR standard (https://no-color.org/).
@@ -130,8 +150,22 @@ REMAINING_ARGS=()
TMP_FILES=()
readonly PRESET_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/transcode.sh/presets"
+CONFIG_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/transcode.sh/config.toml"
+# Kept for deprecation warning only — no longer read for codec data.
readonly SKIP_CODECS_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/transcode.sh/skip.conf"
+# Sentinel flags: set to 1 by the CLI option parser so that load_config()
+# knows which values have already been provided and must not be overridden.
+_CLI_CONFIG_FILE=0
+_CLI_PRESET=0
+_CLI_NICE=0
+_CLI_ONLY_IF_SMALLER=0
+_CLI_CONTINUE=0
+_CLI_SAVING=0
+_CLI_SAVING_FILE=0
+_CLI_QUIET=0
+_CLI_VERBOSE=0
+
###############################################################################
# EXIT CODES
###############################################################################
@@ -201,10 +235,13 @@ ${BOLD}${BLUE}Encoding options:${ALL_OFF}
${BOLD}${YELLOW}-N, --nice${ALL_OFF} ${BOLD}${GREEN}VALUE${ALL_OFF} nice value for ffmpeg (default: ${BOLD}${RED}%d${ALL_OFF})
${BOLD}${YELLOW}-n, --dry-run${ALL_OFF} show what would be done, do not run ffmpeg
${BOLD}${YELLOW}-l, --only-if-smaller${ALL_OFF} only replace original if new file is smaller
+ ${BOLD}${YELLOW}--no-only-if-smaller${ALL_OFF} do replace original even if new file is bigger
${BOLD}${YELLOW}-c, --continue${ALL_OFF} continue with the next file even if ffmpeg fails
+ ${BOLD}${YELLOW}--no-continue${ALL_OFF} do not continue with the next file if ffmpeg fails
${BOLD}${BLUE}Input options:${ALL_OFF}
- ${BOLD}${YELLOW}-f, --encodefile${ALL_OFF} ${BOLD}${GREEN}FILE${ALL_OFF} file containing a list of files to encode
+ ${BOLD}${YELLOW}--config-file${ALL_OFF} ${BOLD}${GREEN}FILE${ALL_OFF} load configuration from FILE instead of the default path
+ ${BOLD}${YELLOW}-f, --encode-file${ALL_OFF} ${BOLD}${GREEN}FILE${ALL_OFF} file containing a list of files to encode
${BOLD}${YELLOW}-S, --skip-codec${ALL_OFF} ${BOLD}${GREEN}LIST${ALL_OFF} skip files with these codecs;
${BOLD}${GREEN}LIST${ALL_OFF} is comma, space, or colon separated
@@ -224,20 +261,18 @@ ${BOLD}${BLUE}General options:${ALL_OFF}
${BOLD}${BLUE}Configuration files:${ALL_OFF}
${BOLD}${CYAN}%s/<NAME>.sh${ALL_OFF}
- preset file; must define a bash array ${BOLD}${GREEN}ffargs${ALL_OFF} with
- the ffmpeg encoding arguments. the following
- variables are available to presets:
- ${BOLD}${GREEN}input_codec${ALL_OFF} e.g. h264, hevc
- ${BOLD}${GREEN}input_pixel_format${ALL_OFF} e.g. yuv420p
- ${BOLD}${GREEN}output_pixel_format${ALL_OFF} derived from input
- presets are sourced as shell code; only use
- trusted presets from trusted directories.
+ preset file; must define a bash array ${BOLD}${GREEN}ffargs${ALL_OFF} with the ffmpeg encoding arguments.
+ the following variables are available to presets:
+ ${BOLD}${GREEN}input_codec${ALL_OFF} e.g. h264, hevc
+ ${BOLD}${GREEN}input_pixel_format${ALL_OFF} e.g. yuv420p
+ ${BOLD}${GREEN}output_pixel_format${ALL_OFF} derived from input
+
+ ${BOLD}${RED}presets are sourced as shell code; only use trusted presets from trusted
+ directories.${ALL_OFF}
${BOLD}${CYAN}%s${ALL_OFF}
- skip file; one codec name per line, lines
- starting with ${BOLD}${YELLOW}#${ALL_OFF} are treated as comments.
- codecs listed here are skipped in addition to
- any codecs passed via ${BOLD}${YELLOW}--skip-codec${ALL_OFF}.
+ Use ${BOLD}${YELLOW}--config-file${ALL_OFF} ${BOLD}${GREEN}FILE${ALL_OFF} to load an alternative path.
+ CLI flags always take precedence. Requires ${BOLD}tomlq${ALL_OFF} when the file is present.
${BOLD}${BLUE}Examples:${ALL_OFF}
%s video.mp4
@@ -246,7 +281,7 @@ ${BOLD}${BLUE}Examples:${ALL_OFF}
"$SCRIPTNAME" "$VERSION" "$DESCRIPTION" \
"$SCRIPTNAME" \
"$PRESET_NAME" "$NICE_VALUE" "$SAVING_FILE" \
- "$PRESET_DIR" "$SKIP_CODECS_FILE" \
+ "$PRESET_DIR" "$CONFIG_FILE" \
"$SCRIPTNAME" "$SCRIPTNAME" "$SCRIPTNAME"
}
@@ -365,6 +400,129 @@ setup_colors
# MAIN EXECUTION
###############################################################################
+###############################################################################
+# CONFIGURATION FILE
+#
+# Load defaults from config.toml if it exists. Values are only applied when
+# the corresponding CLI flag has NOT already been set (CLI takes precedence).
+# Requires tomlq when config.toml is present.
+###############################################################################
+
+# Query a single scalar value from config.toml via tomlq.
+# Usage: _tomlq_get <key> (e.g. ".encoding.preset")
+# Outputs the raw string on stdout; returns non-zero if the key is absent
+# or tomlq fails for any reason.
+_tomlq_get() {
+ tomlq -r "$1" "$CONFIG_FILE" 2>/dev/null
+}
+
+# Load configuration from config.toml, respecting CLI precedence.
+# Called once, after option parsing, so all _CLI_* sentinels are already set.
+load_config() {
+ # Warn about the legacy skip.conf here (not at startup) so --quiet is
+ # respected; by this point the CLI has been fully parsed.
+ if [[ -f "$SKIP_CODECS_FILE" ]]; then
+ warn "skip.conf is deprecated." "Migrate codecs to [skip] codecs in config.toml and remove $SKIP_CODECS_FILE"
+ fi
+
+ # We use -r and not -f to allow things like /dev/null
+ [[ -r "$CONFIG_FILE" ]] || {
+ if [[ $_CLI_CONFIG_FILE -eq 1 ]]; then
+ error "Config file not found:" "$CONFIG_FILE"
+ exit $EXIT_CONFIG_ERROR
+ fi
+ return 0
+ }
+
+ command -v tomlq >/dev/null 2>&1 || {
+ error "tomlq not found but $CONFIG_FILE exists. Install tomlq or remove the config file."
+ exit $EXIT_MISSING_DEPENDENCY
+ }
+
+ # Validate that the config file is well-formed TOML before reading any keys.
+ # tomlq exits non-zero and prints to stderr if the file cannot be parsed.
+ if ! tomlq '.' "$CONFIG_FILE" >/dev/null 2>&1; then
+ error "Config file is not valid TOML:" "$CONFIG_FILE"
+ exit $EXIT_CONFIG_ERROR
+ fi
+
+ debug "Loading config file:" "$CONFIG_FILE"
+
+ local val
+
+ # [encoding] preset
+ if [[ $_CLI_PRESET -eq 0 ]]; then
+ val=$(_tomlq_get '.encoding.preset // empty') && [[ -n "$val" ]] && {
+ debug "Config: preset =" "$val"
+ PRESET_NAME="$val"
+ }
+ fi
+
+ # [encoding] nice
+ if [[ $_CLI_NICE -eq 0 ]]; then
+ val=$(_tomlq_get '.encoding.nice // empty') && [[ -n "$val" ]] && {
+ debug "Config: nice =" "$val"
+ NICE_VALUE="$val"
+ }
+ fi
+
+ # [encoding] only_if_smaller
+ if [[ $_CLI_ONLY_IF_SMALLER -eq 0 ]]; then
+ val=$(_tomlq_get '.encoding.only_if_smaller // empty') && [[ -n "$val" ]] && {
+ debug "Config: encoding.only_if_smaller =" "$val"
+ [[ "$val" == "true" ]] && ONLY_IF_SMALLER=1 || ONLY_IF_SMALLER=0
+ }
+ fi
+
+ # [encoding] continue
+ if [[ $_CLI_CONTINUE -eq 0 ]]; then
+ val=$(_tomlq_get '.encoding.continue // empty') && [[ -n "$val" ]] && {
+ debug "Config: encoding.continue =" "$val"
+ [[ "$val" == "true" ]] && CONTINUE_ON_FAIL=1 || CONTINUE_ON_FAIL=0
+ }
+ fi
+
+ # [skip] codecs (TOML array → one element per line via tomlq -r '.skip.codecs[]')
+ # CLI --skip-codec entries are additive, so we always load config codecs
+ # regardless of _CLI_SKIP_CODEC. Both sources merge into SKIP_CODECS.
+ local codec
+ while IFS= read -r codec; do
+ [[ -n "$codec" ]] && add_skip_codec_token "$codec"
+ done < <(tomlq -r '.skip.codecs[]?' "$CONFIG_FILE" 2>/dev/null)
+
+ # [saving] enabled
+ if [[ $_CLI_SAVING -eq 0 ]]; then
+ val=$(_tomlq_get '.saving.enabled // empty') && [[ -n "$val" ]] && {
+ debug "Config: saving.enabled =" "$val"
+ [[ "$val" == "true" ]] && SAVING=1 || SAVING=0
+ }
+ fi
+
+ # [saving] file
+ if [[ $_CLI_SAVING_FILE -eq 0 ]]; then
+ val=$(_tomlq_get '.saving.file // empty') && [[ -n "$val" ]] && {
+ debug "Config: saving.file =" "$val"
+ SAVING_FILE="$val"
+ }
+ fi
+
+ # [output] verbose
+ if [[ $_CLI_VERBOSE -eq 0 ]]; then
+ val=$(_tomlq_get '.output.verbose // empty') && [[ -n "$val" ]] && {
+ debug "Config: output.verbose =" "$val"
+ [[ "$val" == "true" ]] && VERBOSE=1 || VERBOSE=0
+ }
+ fi
+
+ # [output] quiet
+ if [[ $_CLI_QUIET -eq 0 ]]; then
+ val=$(_tomlq_get '.output.quiet // empty') && [[ -n "$val" ]] && {
+ debug "Config: output.quiet =" "$val"
+ [[ "$val" == "true" ]] && QUIET=1 || QUIET=0
+ }
+ fi
+}
+
# Add a codec name to the SKIP_CODECS associative array.
# - Normalizes to lowercase
# - Ignores empty tokens
@@ -390,13 +548,6 @@ parse_skip_codec_parameter() {
done
}
-if [[ -f "$SKIP_CODECS_FILE" ]]; then
- while read -r line || [[ -n "$line" ]]; do
- [[ -z "$line" || "${line:0:1}" == "#" ]] && continue
- parse_skip_codec_parameter "$line"
- done <"$SKIP_CODECS_FILE"
-fi
-
# Load ffmpeg argument preset by name.
#
# Security:
@@ -434,8 +585,10 @@ load_preset() {
# readlink -f (GNU) with a fallback to realpath for macOS/BSD.
local resolved_file
resolved_file=$(readlink -f -- "$file" 2>/dev/null || realpath -- "$file")
+
local preset_perms
preset_perms=$(stat -c '%a' -- "$resolved_file" 2>/dev/null || stat -f '%OLp' -- "$resolved_file")
+
if [[ "${preset_perms: -1}" =~ [2367] ]]; then
error "Preset file is world-writable, refusing to source:" "$resolved_file"
exit $EXIT_CONFIG_ERROR
@@ -476,10 +629,12 @@ while [[ $# -gt 0 ]]; do
;;
-q | --quiet)
QUIET=1
+ _CLI_QUIET=1
shift
;;
-v | --verbose)
VERBOSE=1
+ _CLI_VERBOSE=1
shift
;;
--color)
@@ -494,9 +649,42 @@ while [[ $# -gt 0 ]]; do
;;
-c | --continue)
CONTINUE_ON_FAIL=1
+ _CLI_CONTINUE=1
+ shift
+ ;;
+ --no-continue)
+ CONTINUE_ON_FAIL=0
+ _CLI_CONTINUE=1
+ shift
+ ;;
+ --config-file)
+ [[ $# -ge 2 ]] || {
+ error "Missing value for $1"
+ exit $EXIT_USAGE_ERROR
+ }
+ [[ -n "$2" ]] || {
+ error "Value for $1 must not be empty"
+ exit $EXIT_USAGE_ERROR
+ }
+ CONFIG_FILE="$2"
+ _CLI_CONFIG_FILE=1
+ shift 2
+ ;;
+ --config-file=*)
+ [[ -n "${1#*=}" ]] || {
+ error "Value for --config-file must not be empty"
+ exit $EXIT_USAGE_ERROR
+ }
+ CONFIG_FILE="${1#*=}"
+ _CLI_CONFIG_FILE=1
+ shift
+ ;;
+ --no-config)
+ CONFIG_FILE="/dev/null"
+ _CLI_CONFIG_FILE=1
shift
;;
- -f | --encodefile)
+ -f | --encode-file)
[[ $# -ge 2 ]] || {
error "Missing value for $1"
exit $EXIT_USAGE_ERROR
@@ -508,9 +696,9 @@ while [[ $# -gt 0 ]]; do
ENCODE_FILE="$2"
shift 2
;;
- --encodefile=*)
+ --encode-file=*)
[[ -n "${1#*=}" ]] || {
- error "Value for --encodefile must not be empty"
+ error "Value for --encode-file must not be empty"
exit $EXIT_USAGE_ERROR
}
ENCODE_FILE="${1#*=}"
@@ -526,14 +714,22 @@ while [[ $# -gt 0 ]]; do
exit $EXIT_USAGE_ERROR
}
NICE_VALUE="$2"
+ _CLI_NICE=1
shift 2
;;
--nice=*)
NICE_VALUE="${1#*=}"
+ _CLI_NICE=1
shift
;;
-s | --saving)
SAVING=1
+ _CLI_SAVING=1
+ shift
+ ;;
+ --no-saving)
+ SAVING=0
+ _CLI_SAVING=1
shift
;;
--saving-file)
@@ -547,6 +743,7 @@ while [[ $# -gt 0 ]]; do
}
SAVING_FILE="$2"
SAVING_FILE_EXPLICIT=1
+ _CLI_SAVING_FILE=1
shift 2
;;
--saving-file=*)
@@ -556,6 +753,7 @@ while [[ $# -gt 0 ]]; do
}
SAVING_FILE="${1#*=}"
SAVING_FILE_EXPLICIT=1
+ _CLI_SAVING_FILE=1
shift
;;
-S | --skip-codec)
@@ -572,6 +770,12 @@ while [[ $# -gt 0 ]]; do
;;
-l | --only-if-smaller)
ONLY_IF_SMALLER=1
+ _CLI_ONLY_IF_SMALLER=1
+ shift
+ ;;
+ --no-only-if-smaller)
+ ONLY_IF_SMALLER=0
+ _CLI_ONLY_IF_SMALLER=1
shift
;;
-p | --preset)
@@ -584,6 +788,7 @@ while [[ $# -gt 0 ]]; do
exit $EXIT_USAGE_ERROR
}
PRESET_NAME="$2"
+ _CLI_PRESET=1
shift 2
;;
--preset=*)
@@ -592,10 +797,12 @@ while [[ $# -gt 0 ]]; do
exit $EXIT_USAGE_ERROR
}
PRESET_NAME="${1#*=}"
+ _CLI_PRESET=1
shift
;;
--)
shift
+ REMAINING_ARGS+=("$@")
break
;;
-*)
@@ -609,6 +816,25 @@ while [[ $# -gt 0 ]]; do
esac
done
+# Apply config file defaults. Must run after CLI parsing so _CLI_* sentinels
+# are set, but before validation so config-supplied values are validated too.
+load_config
+
+printf -v joined ' %s' "${!SKIP_CODECS[@]}"
+
+debug "Effective configuration:"
+debug " preset:" "$PRESET_NAME"
+debug " nice:" "$NICE_VALUE"
+debug " dry-run:" "$DRY_RUN"
+debug " only-if-smaller:" "$ONLY_IF_SMALLER"
+debug " saving:" "$SAVING"
+debug " saving-file:" "$SAVING_FILE"
+debug " skip-codecs:" "${joined# }"
+debug " verbose:" "$VERBOSE"
+debug " quiet:" "$QUIET"
+debug " config-file:" "$CONFIG_FILE"
+debug " encode-file:" "$ENCODE_FILE"
+
command -v ffmpeg >/dev/null 2>&1 || {
error "ffmpeg not found."
exit $EXIT_MISSING_DEPENDENCY
@@ -640,10 +866,10 @@ if [[ ! "$NICE_VALUE" =~ ^-?[0-9]+$ ]] || ((NICE_VALUE < -20 || NICE_VALUE > 19)
exit $EXIT_USAGE_ERROR
fi
-if [[ ${#SKIP_CODECS[@]} -gt 0 ]]; then
- printf -v joined ' %s' "${!SKIP_CODECS[@]}"
+if [[ ! -z "${joined# }" ]]; then
verbose "Skipping files with codecs:" "${joined# }"
fi
+unset joined
# Remove temporary output files created during encoding.
# Registered via trap EXIT so it runs on normal exit and on failures
diff --git a/transcode.sh.1 b/transcode.sh.1
index a22b802..bcbc0fb 100644
--- a/transcode.sh.1
+++ b/transcode.sh.1
@@ -42,7 +42,21 @@ Continue processing the next file even if
fails on the current one. Without this flag the script exits immediately
on the first failure.
.TP
-.BR \-f " \fIPATH\fR, " \-\-encodefile " \fIPATH\fR, " \-\-encodefile= \fIPATH\fR
+.BR \-\-no\-continue
+Do not continue processing next file even if
+.BR ffmpeg (1)
+fails on the current one.
+.TP
+.BR \-\-config\-file " \fIPATH\fR, " \-\-config\-file= \fIPATH\fR
+Load configuration from \fIPATH\fR instead of the default location
+.RI ( ${XDG_CONFIG_HOME:-$HOME/.config}/transcode.sh/config.toml ).
+If the specified file does not exist the script exits with a configuration
+error. CLI flags always take precedence over values in the config file.
+Requires
+.BR tomlq (1)
+when the file is present.
+.TP
+.BR \-f " \fIPATH\fR, " \-\-encode-file " \fIPATH\fR, " \-\-encode-file= \fIPATH\fR
Read the list of files to encode from \fIPATH\fR (one file per line)
instead of taking files from the command line.
.TP
@@ -78,17 +92,20 @@ in the current working directory.
.BR \-S " \fILIST\fR, " \-\-skip\-codec " \fILIST\fR, " \-\-skip\-codec= \fILIST\fR
Comma-, space- or colon-separated list of video codec names to skip.
Files whose detected input codec appears in this list are left untouched.
-The per-user skip list from
-.B skip.conf
-(see
-.BR FILES )
-is always loaded in addition to codecs supplied here.
+Codecs specified here are merged with any codecs listed under
+.B [skip] codecs
+in
+.B config.toml
+\(em both sources contribute to the skip list.
.TP
.BR \-l ", " \-\-only\-if\-smaller
After encoding, replace the original only when the new file is strictly
smaller. If the transcoded file is larger the temporary file is removed
and the original is kept unchanged.
.TP
+.BR \-\-no\-only\-if\-smaller
+After encoding, replace the original also if the new file is bigger.
+.TP
.BR \-p " \fINAME\fR, " \-\-preset " \fINAME\fR, " \-\-preset= \fINAME\fR
Load the preset named \fINAME\fR from the preset directory. Defaults to
.BR default .
@@ -134,6 +151,8 @@ or terminal detection.
Set to
.B 1
to enable structured debug messages (high-level state reporting).
+After configuration loading, a summary of all effective option values
+is printed.
.TP
.B TRACE
Set to
@@ -207,12 +226,51 @@ ffargs=( \-c:v:0 libsvtav1 \-crf 30 \-preset 6 \-pix_fmt "$output_pixel_format"
presets are executed as shell code. Only use presets from trusted
sources.
.TP
+.IR "${XDG_CONFIG_HOME:-$HOME/.config}/transcode.sh/config.toml"
+Optional TOML configuration file. Provides defaults for all options.
+CLI flags always take precedence. The file must be valid TOML; an
+unparseable file is a hard error. Requires
+.BR tomlq (1).
+Supported sections and keys:
+.RS
+.TP
+.B [encoding] preset
+String. Default preset name. Equivalent to
+.BR \-p .
+.TP
+.B [encoding] nice
+Integer. Default niceness value. Equivalent to
+.BR \-N .
+.TP
+.B [skip] codecs
+Array of strings. Merged with any codecs from
+.BR \-S .
+.TP
+.B [saving] enabled
+Boolean. Equivalent to
+.BR \-s .
+.TP
+.B [saving] file
+String. Equivalent to
+.BR \-\-saving\-file .
+.TP
+.B [output] verbose
+Boolean. Equivalent to
+.BR \-v .
+.TP
+.B [output] quiet
+Boolean. Equivalent to
+.BR \-q .
+.RE
+.TP
.IR "${XDG_CONFIG_HOME:-$HOME/.config}/transcode.sh/skip.conf"
-Optional per-user codec skip list. Each line should contain one codec
-name (as reported by
-.BR ffprobe (1)).
-Files whose input video codec matches an entry are skipped without
-encoding.
+.B Deprecated.
+This file is no longer read. If it exists, a warning is printed.
+Migrate its contents to
+.B [skip] codecs
+in
+.B config.toml
+and delete the file.
.SH EXIT STATUS
.TP
.B 0
@@ -234,11 +292,14 @@ A required dependency
.RB ( ffmpeg ,
.BR ffprobe ,
.BR nice ,
-or
.B bc
when
.B \-\-saving
-is active) was not found.
+is active, or
+.B tomlq
+when
+.B config.toml
+is present) was not found.
.SH EXAMPLES
Transcode a single file using the default preset:
.PP
@@ -280,6 +341,14 @@ transcode.sh \-N 10 \-S av1 input.mkv
.fi
.RE
.PP
+Load an alternative configuration file:
+.PP
+.RS
+.nf
+transcode.sh \-\-config\-file ~/profiles/fast.toml input.mp4
+.fi
+.RE
+.PP
Use
.BR ffmpeg (1)
installed via Homebrew (macOS):
@@ -305,7 +374,8 @@ before invoking
.BR ffmpeg (1),
.BR ffprobe (1),
.BR nice (1),
-.BR bc (1)
+.BR bc (1),
+.BR tomlq (1)
.SH AUTHOR
Dennis Fink <dennis.fink@c3l.lu>
.SH LICENSE
diff --git a/transcode.sh.bash-completion b/transcode.sh.bash-completion
index ce153dd..59a9c8d 100644
--- a/transcode.sh.bash-completion
+++ b/transcode.sh.bash-completion
@@ -6,8 +6,8 @@
#
# Each option that takes an argument is supported in all three forms:
# short -f list.txt
-# long space --encodefile list.txt
-# long equals --encodefile=list.txt
+# long space --encode-file list.txt
+# long equals --encode-file=list.txt
_transcode_sh() {
local cur prev words cword
@@ -40,7 +40,12 @@ _transcode_sh() {
# This must be checked before the $prev dispatch below.
# ---------------------------------------------------------------------------
case "$cur" in
- --encodefile=*)
+ --config-file=*)
+ cur="${cur#*=}"
+ _filedir
+ return
+ ;;
+ --encode-file=*)
cur="${cur#*=}"
_filedir
return
@@ -69,11 +74,15 @@ _transcode_sh() {
# ---------------------------------------------------------------------------
# Handle VALUE after short form (-f, -N, -S, -p) and
- # long space form (--encodefile, --nice, --saving-file, --skip-codec, --preset).
+ # long space form (--encode-file, --nice, --saving-file, --skip-codec, --preset).
# $prev is the option word, $cur is the value being typed.
# ---------------------------------------------------------------------------
case "$prev" in
- -f|--encodefile)
+ --config-file)
+ _filedir
+ return
+ ;;
+ -f|--encode-file)
_filedir
return
;;
@@ -106,17 +115,22 @@ _transcode_sh() {
--*)
local longopts='
--continue
- --encodefile
- --encodefile=
+ --no-continue
+ --config-file
+ --config-file=
+ --encode-file
+ --encode-file=
--dry-run
--nice
--nice=
--saving
+ --no-saving
--saving-file
--saving-file=
--skip-codec
--skip-codec=
--only-if-smaller
+ --no-only-if-smaller
--preset
--preset=
--help