aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDennis Fink2026-05-14 09:04:48 +0200
committerDennis Fink2026-05-14 09:04:48 +0200
commit1674b2212e3606c3e74f454f066d5253c53b274d (patch)
tree7fbbe157cad08ac4f367837a5817bf0252d05681
parent077ed51e254b9a6af2259b1952e6134ead98afa2 (diff)
downloadtranscode.sh-1674b2212e3606c3e74f454f066d5253c53b274d.tar.gz
transcode.sh-1674b2212e3606c3e74f454f066d5253c53b274d.zip
refactor(script): improve readability of encoding flow
Expand compact shell conditionals into explicit if blocks throughout config loading, codec parsing, cleanup, and file-list handling. Rename short local variables in encode_one to more descriptive names, including the input file, temporary output path, command array, and progress counters. Also align preset-list output with the shared msg helper and update colors for preset names. This makes the main transcoding path easier to read and maintain without changing the intended encoding behaviour.
Diffstat (limited to '')
-rwxr-xr-xtranscode.sh203
1 files changed, 121 insertions, 82 deletions
diff --git a/transcode.sh b/transcode.sh
index 0501974..08ed14e 100755
--- a/transcode.sh
+++ b/transcode.sh
@@ -148,7 +148,7 @@ declare -A SKIP_CODECS
REMAINING_ARGS=()
-TMP_FILES=()
+TEMPORARY_FILES=()
readonly PRESET_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/transcode.sh/presets"
CONFIG_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/transcode.sh/config.toml"
@@ -472,47 +472,65 @@ load_config() {
# [encoding] preset
if [[ $_CLI_PRESET -eq 0 ]]; then
- val=$(_tomlq_get '.encoding.preset // empty') && [[ -n "$val" ]] && {
+ val=$(_tomlq_get '.encoding.preset // empty')
+ if [[ -n "$val" ]]; then
debug "Config: preset =" "$val"
PRESET_NAME="$val"
- }
+ fi
fi
# [encoding] nice
if [[ $_CLI_NICE -eq 0 ]]; then
- val=$(_tomlq_get '.encoding.nice // empty') && [[ -n "$val" ]] && {
+ val=$(_tomlq_get '.encoding.nice // empty')
+ if [[ -n "$val" ]]; then
debug "Config: nice =" "$val"
NICE_VALUE="$val"
- }
+ fi
fi
# [encoding] only_if_smaller
if [[ $_CLI_ONLY_IF_SMALLER -eq 0 ]]; then
- val=$(_tomlq_get '.encoding.only_if_smaller // empty') && [[ -n "$val" ]] && {
+ val=$(_tomlq_get '.encoding.only_if_smaller // empty')
+ if [[ -n "$val" ]]; then
debug "Config: encoding.only_if_smaller =" "$val"
- [[ "$val" == "true" ]] && ONLY_IF_SMALLER=1 || ONLY_IF_SMALLER=0
- }
+ if [[ "$val" == "true" ]]; then
+ ONLY_IF_SMALLER=1
+ else
+ ONLY_IF_SMALLER=0
+ fi
+ fi
fi
# [encoding] continue
if [[ $_CLI_CONTINUE -eq 0 ]]; then
- val=$(_tomlq_get '.encoding.continue // empty') && [[ -n "$val" ]] && {
+ val=$(_tomlq_get '.encoding.continue // empty')
+ if [[ -n "$val" ]]; then
debug "Config: encoding.continue =" "$val"
- [[ "$val" == "true" ]] && CONTINUE_ON_FAIL=1 || CONTINUE_ON_FAIL=0
- }
+ if [[ "$val" == "true" ]]; then
+ CONTINUE_ON_FAIL=1
+ else
+ CONTINUE_ON_FAIL=0
+ fi
+ fi
fi
# [encoding] verify_output
if [[ $_CLI_VERIFY_OUTPUT -eq 0 ]]; then
- val=$(_tomlq_get '.encoding.verify_output // empty') && [[ -n "$val" ]] && {
+ val=$(_tomlq_get '.encoding.verify_output // empty')
+ if [[ -n "$val" ]]; then
debug "Config: encoding.verify_output =" "$val"
- [[ "$val" == "true" ]] && VERIFY_OUTPUT=1 || VERIFY_OUTPUT=0
- }
+ if [[ "$val" == "true" ]]; then
+ VERIFY_OUTPUT=1
+ else
+ VERIFY_OUTPUT=0
+ fi
+ fi
fi
# [encoding] hwaccel
if [[ $_CLI_HWACCEL -eq 0 ]]; then
- val=$(_tomlq_get '.encoding.hwaccel // empty') && [[ -n "$val" ]] && {
+ val=$(_tomlq_get '.encoding.hwaccel // empty')
+ if [[ -n "$val" ]]; then
debug "Config: encoding.hwaccel =" "$val"
if [[ "$val" == "false" ]]; then
HWACCEL=0
@@ -523,7 +541,7 @@ load_config() {
HWACCEL_VALUE="$val"
fi
fi
- }
+ fi
fi
# [skip] codecs (TOML array → one element per line via tomlq -r '.skip.codecs[]')
@@ -531,7 +549,9 @@ load_config() {
# 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"
+ if [[ -n "$codec" ]]; then
+ add_skip_codec_token "$codec"
+ fi
done < <(tomlq -r '.skip.codecs[]?' "$CONFIG_FILE" 2>/dev/null)
# [saving] enabled
@@ -552,25 +572,36 @@ load_config() {
# [output] verbose
if [[ $_CLI_VERBOSE -eq 0 ]]; then
- val=$(_tomlq_get '.output.verbose // empty') && [[ -n "$val" ]] && {
+ val=$(_tomlq_get '.output.verbose // empty')
+ if [[ -n "$val" ]]; then
debug "Config: output.verbose =" "$val"
- [[ "$val" == "true" ]] && VERBOSE=1 || VERBOSE=0
- }
+ if [[ "$val" == "true" ]]; then
+ VERBOSE=1
+ else
+ VERBOSE=0
+ fi
+ fi
fi
# [output] quiet
if [[ $_CLI_QUIET -eq 0 ]]; then
- val=$(_tomlq_get '.output.quiet // empty') && [[ -n "$val" ]] && {
+ val=$(_tomlq_get '.output.quiet // empty')
+ if [[ -n "$val" ]]; then
debug "Config: output.quiet =" "$val"
- [[ "$val" == "true" ]] && QUIET=1 || QUIET=0
- }
+ if [[ "$val" == "true" ]]; then
+ QUIET=1
+ else
+ QUIET=0
+ fi
+ fi
fi
}
# Add a codec name to the SKIP_CODECS associative array.
# - Normalizes to lowercase
# - Ignores empty tokens
-# - Used both for CLI --skip-codec and for skip.conf lines
+# - Used both for CLI --skip-codec and for codecs defined
+# in the config file
add_skip_codec_token() {
local token="${1,,}" # lowercase
if [[ ! -v SKIP_CODECS["$token"] ]]; then
@@ -587,8 +618,9 @@ parse_skip_codec_parameter() {
local arg="$1"
local IFS=',: ' token
for token in $arg; do
- [[ -z "$token" ]] && continue
- add_skip_codec_token "$token"
+ if [[ ! -z "$token" ]]; then
+ add_skip_codec_token "$token"
+ fi
done
}
@@ -694,7 +726,7 @@ list_presets() {
local canonical_preset_dir
canonical_preset_dir=$(resolve_path "$PRESET_DIR")
- printf "${BOLD}${BLUE}Available presets in${ALL_OFF} ${BOLD}${CYAN}%s${ALL_OFF}${BOLD}${BLUE}:${ALL_OFF}\n" "$PRESET_DIR"
+ msg "Available presets in:" "$PRESET_DIR"
local target_path target_name description
for f in "${preset_files[@]}"; do
@@ -706,16 +738,16 @@ list_presets() {
if [[ "$target_path" == "$canonical_preset_dir"/*.sh ]]; then
target_name="${target_path##*/}"
target_name="${target_name%.sh}"
- printf " ${BOLD}${GREEN}%-${max_name_len}s${ALL_OFF} ${BOLD}${YELLOW}-${ALL_OFF} Symlinked to ${BOLD}${GREEN}%s${ALL_OFF}\n" "$name" "$target_name"
+ printf "${BOLD}${BLUE}%-${max_name_len}s${ALL_OFF} ${BOLD}${YELLOW}-${ALL_OFF} Symlinked to ${BOLD}${BLUE}%s${ALL_OFF}\n" "$name" "$target_name"
continue
fi
fi
description=$(sed -n '1s/^[[:space:]]*#[[:space:]]*[Dd]escription:[[:space:]]*//p' "$f")
if [[ -n "$description" ]]; then
- printf " ${BOLD}${GREEN}%-${max_name_len}s${ALL_OFF} ${BOLD}${YELLOW}-${ALL_OFF} %s\n" "$name" "$description"
+ printf "${BOLD}${BLUE}%-${max_name_len}s${ALL_OFF} ${BOLD}${YELLOW}-${ALL_OFF} %s\n" "$name" "$description"
else
- printf " ${BOLD}${GREEN}%-${max_name_len}s${ALL_OFF}\n" "$name"
+ printf "${BOLD}${BLUE}%-${max_name_len}s${ALL_OFF}\n" "$name"
fi
done
}
@@ -1023,9 +1055,11 @@ unset joined
cleanup() {
# "${TMP_FILES[@]+"${TMP_FILES[@]}"}" expands to nothing when the array is
# empty (safe under nounset), unlike [@]:-} which yields one empty iteration.
- for t in "${TMP_FILES[@]+"${TMP_FILES[@]}"}"; do
+ for t in "${TEMPORARY_FILES[@]+"${TEMPORARY_FILES[@]}"}"; do
debug "Cleanup temp file:" "$t"
- [[ -f "$t" ]] && rm -f -- "$t" || true
+ if [[ -f "$t" ]]; then
+ rm -f -- "$t"
+ fi
done
}
trap cleanup EXIT
@@ -1049,28 +1083,30 @@ trap cleanup EXIT
# Return:
# EXIT_OK on success or if skipped; EXIT_RUNTIME_FAILURE on failure.
encode_one() {
- local file="$1"
- local idx="$2"
- local total="$3"
+ local input_file="$1"
+ local current_file_index="$2"
+ local amount_total_files="$3"
- [[ -z "$file" ]] && return $EXIT_OK
+ if [[ -z "$input_file" ]]; then
+ return $EXIT_OK
+ fi
- msg "Processing [$idx/$total]:" "$file"
- [[ ! -f "$file" ]] && {
- error "Not found:" "$file"
+ msg "Processing [$current_file_index/$amount_total_files]:" "$input_file"
+ if [[ ! -f "$input_file" ]]; then
+ error "Not found:" "$input_file"
return $EXIT_RUNTIME_FAILURE
- }
- [[ ! -r "$file" ]] && {
- error "File is not readable:" "$file"
+ fi
+ if [[ ! -r "$input_file" ]]; then
+ error "File is not readable:" "$input_file"
return $EXIT_RUNTIME_FAILURE
- }
+ fi
# Probe codec and pixel format in a single ffprobe invocation to halve
# the startup overhead on large batches.
local probe_out
probe_out=$(ffprobe -v error -select_streams v:0 \
-show_entries stream=codec_name,pix_fmt,r_frame_rate \
- -of default=nk=1:nw=1 "file:$file" 2>/dev/null)
+ -of default=nk=1:nw=1 "file:$input_file" 2>/dev/null)
local input_codec input_pixel_format input_frame_rate input_fps
@@ -1081,15 +1117,15 @@ encode_one() {
input_frame_rate=$(printf "%s\n" "$probe_out" | sed -n '3p')
if [[ -z "$input_codec" ]]; then
- error "Could not determine input video codec:" "$file"
+ error "Could not determine input video codec:" "$input_file"
return $EXIT_RUNTIME_FAILURE
fi
if [[ -z "$input_pixel_format" ]]; then
- error "Could not determine input pixel format:" "$file"
+ error "Could not determine input pixel format:" "$input_file"
return $EXIT_RUNTIME_FAILURE
fi
if [[ -z "$input_frame_rate" ]]; then
- error "Could not determine input frame rate:" "$file"
+ error "Could not determine input frame rate:" "$input_file"
return $EXIT_RUNTIME_FAILURE
fi
@@ -1114,8 +1150,9 @@ encode_one() {
*422*) subsampling="422" ;;
*)
subsampling="420"
- [[ "$input_pixel_format" != *420* ]] &&
+ if [[ "$input_pixel_format" != *420* ]]; then
verbose "Unrecognised chroma subsampling in '$input_pixel_format', falling back to yuv420p"
+ fi
;;
esac
@@ -1153,32 +1190,32 @@ encode_one() {
load_preset "$PRESET_NAME"
- local extension directory tmp base stem
+ local extension directory temporary_output_file base stem
# filename (path without extension) is used only for the savings log.
# Note: only the last extension is stripped, so multi-dot names like
# "my.show.s01e01.mkv" are logged as "my.show.s01e01".
local filename
- base="${file##*/}"
+ base="${input_file##*/}"
if [[ "$base" == *.* ]]; then
extension="${base##*.}"
stem="${base%.*}"
- filename="${file%.*}"
+ filename="${input_file%.*}"
else
extension=""
stem="$base"
- filename="$file"
+ filename="$input_file"
fi
- directory=$(dirname -- "$file")
+ directory=$(dirname -- "$input_file")
# Temp file is created in the same directory as the input so that `mv` is
# atomic on the same filesystem (avoid cross-device rename issues).
# mktemp provides cryptographically random names and atomic creation,
# unlike $$.$RANDOM which has limited entropy and is predictable.
- tmp=$(mktemp -- "$directory/.${stem}_${PRESET_NAME}.XXXXXX${extension:+.$extension}")
- TMP_FILES+=("$tmp")
- debug "Temporary output path:" "$tmp"
+ temporary_output_file=$(mktemp -- "$directory/.${stem}_${PRESET_NAME}.XXXXXX${extension:+.$extension}")
+ TEMPORARY_FILES+=("$temporary_output_file")
+ debug "Temporary output path:" "$temporary_output_file"
# Build ffmpeg command:
# - Map all streams, metadata, and chapters
@@ -1191,20 +1228,20 @@ encode_one() {
hwaccel_args=(-hwaccel "$HWACCEL_VALUE")
fi
- local cmd=(ffmpeg
+ local encode_cmd=(ffmpeg
-nostdin
-y
-hide_banner
-v error
-stats
"${hwaccel_args[@]}"
- -i "file:$file"
+ -i "file:$input_file"
-map 0
-map_metadata 0
-map_chapters 0
-c copy
"${ffargs[@]}"
- "file:$tmp")
+ "file:$temporary_output_file")
if [[ $DRY_RUN -eq 1 ]]; then
msg "DRY RUN:" "Would encode with preset $PRESET_NAME"
@@ -1214,15 +1251,13 @@ encode_one() {
verbose "Input frame rate:" "$input_frame_rate"
verbose "Input FPS:" "$input_fps"
verbose "Output GOP size:" "$output_gop_size"
- verbose "Command:" "${cmd[*]}"
+ verbose "Command:" "${encode_cmd[*]}"
return $EXIT_OK
fi
- msg "Encoding:" "$file"
- debug "Nice value:" "$NICE_VALUE"
-
- if nice -n "$NICE_VALUE" "${cmd[@]}"; then
+ msg "Encoding:" "$input_file"
+ if nice -n "$NICE_VALUE" "${encode_cmd[@]}"; then
# Integrity verification: confirm the encoded output is a valid, playable
# file before discarding the original. ffprobe exits non-zero and emits
# nothing useful if the container is corrupt or contains no readable
@@ -1230,48 +1265,50 @@ encode_one() {
# Skip with --no-verify-output when trust in the encoder is high and the
# extra probe round-trip is undesirable.
if [[ $VERIFY_OUTPUT -eq 1 ]]; then
- debug "Verifying output integrity:" "$tmp"
- if ! ffprobe -v error "file:$tmp" >/dev/null 2>&1; then
- error "Output failed integrity check:" "$tmp"
- rm -f -- "$tmp"
+ debug "Verifying output integrity:" "$temporary_output_file"
+ if ! ffprobe -v error "file:$temporary_output_file" >/dev/null 2>&1; then
+ error "Output failed integrity check:" "$temporary_output_file"
+ rm -f -- "$temporary_output_file"
if [[ $CONTINUE_ON_FAIL -eq 1 ]]; then
return $EXIT_RUNTIME_FAILURE
else
exit $EXIT_RUNTIME_FAILURE
fi
fi
- verbose "Integrity check passed:" "$tmp"
+ verbose "Integrity check passed:" "$temporary_output_file"
fi
local original_filesize new_filesize
- original_filesize=$(filesize "$file")
- new_filesize=$(filesize "$tmp")
+ original_filesize=$(filesize "$input_file")
+ new_filesize=$(filesize "$temporary_output_file")
# If requested, keep the original if the new file is larger.
if [[ $ONLY_IF_SMALLER -eq 1 && $new_filesize -gt $original_filesize ]]; then
- msg "Did not replace (new file is larger):" "$file"
- rm -f -- "$tmp"
+ msg "Did not replace (new file is larger):" "$input_file"
+ rm -f -- "$temporary_output_file"
return $EXIT_OK
fi
- if [[ $SAVING -eq 1 ]]; then
- local pct
+ local filesize_percentage filesize_difference
+ filesize_difference=$((original_filesize - new_filesize))
+
if [[ $original_filesize -gt 0 ]]; then
- pct=$(((original_filesize - new_filesize) * 100 / original_filesize))
+ filesize_percentage=$((filesize_difference * 100 / original_filesize))
else
- pct=0
+ filesize_percentage=0
fi
# Write a header line if the savings file does not yet exist.
if [[ ! -f "$SAVING_FILE" ]]; then
printf "filename\toriginal_bytes\tnew_bytes\tsaved_pct\n" >"$SAVING_FILE"
+ else
+ printf "%s\t%s\t%s\t%s%%\n" "$filename" "$original_filesize" "$new_filesize" "$filesize_percentage" >>"$SAVING_FILE"
fi
- printf "%s\t%s\t%s\t%s%%\n" "$filename" "$original_filesize" "$new_filesize" "$pct" >>"$SAVING_FILE"
fi
- mv -f -- "$tmp" "$file"
- msg "Replaced:" "$file"
+ mv -f -- "$temporary_output_file" "$input_file"
+ msg "Replaced:" "$input_file"
else
- error "Encode FAILED:" "$file"
+ error "Encode FAILED:" "$input_file"
if [[ $CONTINUE_ON_FAIL -eq 1 ]]; then
return $EXIT_RUNTIME_FAILURE # fail this but keep going
else
@@ -1289,7 +1326,9 @@ if [[ -n $ENCODE_FILE ]]; then
# Build the file list into an array first so we know the total count.
mapfile -t FILES_TO_ENCODE < <(
while IFS= read -r file || [[ -n "$file" ]]; do
- [[ -n "$file" ]] && printf '%s\n' "$file"
+ if [[ -n "$file" ]]; then
+ printf '%s\n' "$file"
+ fi
done <"$ENCODE_FILE"
)
total=${#FILES_TO_ENCODE[@]}