diff options
| -rw-r--r-- | README.md | 3 | ||||
| -rwxr-xr-x | transcode.sh | 49 | ||||
| -rw-r--r-- | transcode.sh.1 | 27 | ||||
| -rw-r--r-- | transcode.sh.bash-completion | 2 |
4 files changed, 76 insertions, 5 deletions
@@ -153,6 +153,7 @@ quiet = false |---------|-----|------|----------------| | `[encoding]` | `preset` | string | `-p` | | `[encoding]` | `nice` | integer | `-N` | +| `[encoding]` | `verify_output` | boolean | `--verify-output` | | `[skip]` | `codecs` | array of strings | `-S` (merged with CLI) | | `[saving]` | `enabled` | boolean | `-s` | | `[saving]` | `file` | string | `--saving-file` | @@ -178,6 +179,8 @@ transcode.sh [OPTION] [--] FILE... |--------|-------------| | `-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 | diff --git a/transcode.sh b/transcode.sh index 9d66f49..9125627 100755 --- a/transcode.sh +++ b/transcode.sh @@ -149,6 +149,7 @@ PRESET_NAME="default" SAVING=0 SAVING_FILE="transcode_savings" SAVING_FILE_EXPLICIT=0 +VERIFY_OUTPUT=1 declare -A SKIP_CODECS REMAINING_ARGS=() @@ -163,14 +164,15 @@ readonly SKIP_CODECS_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/transcode.sh/skip.c # 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_CONTINUE=0 _CLI_NICE=0 _CLI_ONLY_IF_SMALLER=0 -_CLI_CONTINUE=0 +_CLI_PRESET=0 +_CLI_QUIET=0 _CLI_SAVING=0 _CLI_SAVING_FILE=0 -_CLI_QUIET=0 _CLI_VERBOSE=0 +_CLI_VERIFY_OUTPUT=0 ############################################################################### # EXIT CODES @@ -244,6 +246,8 @@ ${BOLD}${BLUE}Encoding options:${ALL_OFF} ${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}${YELLOW}--verify-output${ALL_OFF} probe output with ffprobe before replacing original (default) + ${BOLD}${YELLOW}--no-verify-output${ALL_OFF} skip the post-encode integrity check ${BOLD}${BLUE}Input options:${ALL_OFF} ${BOLD}${YELLOW}--config-file${ALL_OFF} ${BOLD}${GREEN}FILE${ALL_OFF} load configuration from FILE instead of the default path @@ -491,6 +495,14 @@ load_config() { } fi + # [encoding] verify_output + if [[ $_CLI_VERIFY_OUTPUT -eq 0 ]]; then + val=$(_tomlq_get '.encoding.verify_output // empty') && [[ -n "$val" ]] && { + debug "Config: encoding.verify_output =" "$val" + [[ "$val" == "true" ]] && VERIFY_OUTPUT=1 || VERIFY_OUTPUT=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. @@ -667,6 +679,16 @@ while [[ $# -gt 0 ]]; do _CLI_CONTINUE=1 shift ;; + --verify-output) + VERIFY_OUTPUT=1 + _CLI_VERIFY_OUTPUT=1 + shift + ;; + --no-verify-output) + VERIFY_OUTPUT=0 + _CLI_VERIFY_OUTPUT=1 + shift + ;; --config-file) [[ $# -ge 2 ]] || { error "Missing value for $1" @@ -837,6 +859,7 @@ debug " preset:" "$PRESET_NAME" debug " nice:" "$NICE_VALUE" debug " dry-run:" "$DRY_RUN" debug " only-if-smaller:" "$ONLY_IF_SMALLER" +debug " verify-output:" "$VERIFY_OUTPUT" debug " saving:" "$SAVING" debug " saving-file:" "$SAVING_FILE" debug " skip-codecs:" "${joined# }" @@ -1082,6 +1105,26 @@ encode_one() { if nice -n "$NICE_VALUE" "${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 + # streams, so we treat any non-zero exit as a fatal encode failure. + # 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" + if [[ $CONTINUE_ON_FAIL -eq 1 ]]; then + return $EXIT_RUNTIME_FAILURE + else + exit $EXIT_RUNTIME_FAILURE + fi + fi + verbose "Integrity check passed:" "$tmp" + fi + local original_filesize new_filesize original_filesize=$(filesize "$file") new_filesize=$(filesize "$tmp") diff --git a/transcode.sh.1 b/transcode.sh.1 index 9a4c894..92cefa2 100644 --- a/transcode.sh.1 +++ b/transcode.sh.1 @@ -34,8 +34,12 @@ default (\fB\-map 0 \-map_metadata 0 \-map_chapters 0 \-c copy\fR); the preset only needs to override the streams it cares about (typically video). .PP -Encoding is performed into a temporary file in the same directory as the -input, which ensures an atomic rename on completion. +After encoding completes, +.BR ffprobe (1) +is run against the temporary output file to verify it is a valid, +playable media file before the original is replaced. If the integrity +check fails the temporary file is removed and the encode is treated as a +runtime failure, leaving the original untouched. .SH OPTIONS .TP .BR \-c ", " \-\-continue @@ -49,6 +53,19 @@ Do not continue processing next file even if .BR ffmpeg (1) fails on the current one. .TP +.BR \-\-verify\-output +After encoding, probe the temporary output file with +.BR ffprobe (1) +to confirm it is a valid, playable media file before replacing the +original. Enabled by default. +.TP +.BR \-\-no\-verify\-output +Skip the post-encode integrity check. The original is replaced as long +as +.BR ffmpeg (1) +exits successfully. Useful for large batch jobs on trusted encoders +where the additional probe round-trip is undesirable. +.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 ). @@ -270,6 +287,12 @@ String. Default preset name. Equivalent to Integer. Default niceness value. Equivalent to .BR \-N . .TP +.B [encoding] verify_output +Boolean. Equivalent to +.BR \-\-verify\-output / \-\-no\-verify\-output . +Defaults to +.BR true . +.TP .B [skip] codecs Array of strings. Merged with any codecs from .BR \-S . diff --git a/transcode.sh.bash-completion b/transcode.sh.bash-completion index 558cf09..9fc4701 100644 --- a/transcode.sh.bash-completion +++ b/transcode.sh.bash-completion @@ -120,6 +120,8 @@ _transcode_sh() { local longopts=' --continue --no-continue + --verify-output + --no-verify-output --config-file --config-file= --encode-file |
