From b1b8ce749b9d42cd270531e66a7d00b5264b006c Mon Sep 17 00:00:00 2001 From: dabruh <11458706-dabruh@users.noreply.gitlab.com> Date: Mon, 17 Nov 2025 20:06:18 +0100 Subject: [PATCH] refactor: standardize indentation in videncode script --- .local/bin/videncode | 328 ++++++++++++++++++++++++------------------- 1 file changed, 187 insertions(+), 141 deletions(-) diff --git a/.local/bin/videncode b/.local/bin/videncode index 3c7ee89..e6d1d0c 100755 --- a/.local/bin/videncode +++ b/.local/bin/videncode @@ -18,7 +18,7 @@ log_error() { echo -e "${RED}ERROR:${NC} $1" >&2; } # Gets the duration of a video file in seconds using ffprobe. get_duration() { - ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$1" 2>/dev/null || echo "0" + ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$1" 2>/dev/null || echo "0" } # Default encoding parameters tuned for quality and compatibility. @@ -48,7 +48,7 @@ AUDIO_MODE="transcode" # Prints usage information to stdout. usage() { - cat <&2; usage; exit 1 ;; - esac - done + while [[ $# -gt 0 ]]; do + case "$1" in + -i | --input) + INFILE="$2" + shift 2 + ;; + -o | --output) + OUTFILE="$2" + shift 2 + ;; + -c | --crf) + CRF="$2" + shift 2 + ;; + -p | --preset) + PRESET="$2" + shift 2 + ;; + -m | --maxrate) + MAXRATE="$2" + shift 2 + ;; + -b | --bufsize) + BUFSIZE="$2" + shift 2 + ;; + --extra) + EXTRA_ARGS+=("$2") + shift 2 + ;; + --max-w) + MAX_W="$2" + shift 2 + ;; + --max-h) + MAX_H="$2" + shift 2 + ;; + --overwrite-same) + OVERWRITE_SAME=true + shift + ;; + --overwrite-different) + OVERWRITE_DIFFERENT=true + shift + ;; + --audio-mode) + AUDIO_MODE="$2" + shift 2 + ;; + -h | --help) + usage + exit 0 + ;; + --) + shift + break + ;; + *) + echo "Unknown argument: $1" >&2 + usage + exit 1 + ;; + esac + done } # Validates input and determines output path based on rules to avoid overwriting. validate_input() { - if [[ -z "${INFILE:-}" ]]; then - log_error "input (-i/--input) is required." - usage - exit 1 - fi + if [[ -z "${INFILE:-}" ]]; then + log_error "input (-i/--input) is required." + usage + exit 1 + fi - if [[ -d "$INFILE" ]]; then - IS_BATCH=true - INPUT_DIR="$INFILE" - log_info "Processing directory: $INFILE" - # Find video files in the directory - while IFS= read -r -d '' file; do - INPUT_FILES+=("$file") - done < <(find "$INPUT_DIR" -type f \( -iname "*.mp4" -o -iname "*.avi" -o -iname "*.mkv" -o -iname "*.mov" -o -iname "*.flv" -o -iname "*.wmv" -o -iname "*.webm" -o -iname "*.m4v" \) -print0) - if [[ ${#INPUT_FILES[@]} -eq 0 ]]; then - log_error "no video files found in directory: $INFILE" - exit 1 - fi - log_info "Found ${#INPUT_FILES[@]} video file(s) to process" - elif [[ -f "$INFILE" ]]; then - IS_BATCH=false - INPUT_FILES=("$INFILE") - log_info "Processing single file: $INFILE" - else - log_error "input not found or not a file/directory: $INFILE" - exit 1 - fi + if [[ -d "$INFILE" ]]; then + IS_BATCH=true + INPUT_DIR="$INFILE" + log_info "Processing directory: $INFILE" + # Find video files in the directory + while IFS= read -r -d '' file; do + INPUT_FILES+=("$file") + done < <(find "$INPUT_DIR" -type f \( -iname "*.mp4" -o -iname "*.avi" -o -iname "*.mkv" -o -iname "*.mov" -o -iname "*.flv" -o -iname "*.wmv" -o -iname "*.webm" -o -iname "*.m4v" \) -print0) + if [[ ${#INPUT_FILES[@]} -eq 0 ]]; then + log_error "no video files found in directory: $INFILE" + exit 1 + fi + log_info "Found ${#INPUT_FILES[@]} video file(s) to process" + elif [[ -f "$INFILE" ]]; then + IS_BATCH=false + INPUT_FILES=("$INFILE") + log_info "Processing single file: $INFILE" + else + log_error "input not found or not a file/directory: $INFILE" + exit 1 + fi } # Validates single input file and determines output path. validate_input_single() { - # Determine output path: use _videncode.mp4 suffix in same dir, original name in different dir. - if [[ -n "$OUTFILE" && -d "$OUTFILE" ]]; then - if [[ "$OUTFILE" == "$INPUT_DIR" ]]; then - OUTFILE="${OUTFILE%/}/$(basename "${INFILE%.*}")_videncode.mp4" - else - OUTFILE="${OUTFILE%/}/$(basename "$INFILE")" - fi - elif [[ -z "$OUTFILE" ]]; then - OUTFILE="${INFILE%.*}_videncode.mp4" - fi + # Determine output path: use _videncode.mp4 suffix in same dir, original name in different dir. + if [[ -n "$OUTFILE" && -d "$OUTFILE" ]]; then + if [[ "$OUTFILE" == "$INPUT_DIR" ]]; then + OUTFILE="${OUTFILE%/}/$(basename "${INFILE%.*}")_videncode.mp4" + else + OUTFILE="${OUTFILE%/}/$(basename "$INFILE")" + fi + elif [[ -z "$OUTFILE" ]]; then + OUTFILE="${INFILE%.*}_videncode.mp4" + fi - log_info "Output will be: $OUTFILE" + log_info "Output will be: $OUTFILE" - # Prevent accidental overwriting of the input file. - if [[ "$OUTFILE" == "$INFILE" ]]; then - log_error "output file would overwrite input file." - exit 1 - fi + # Prevent accidental overwriting of the input file. + if [[ "$OUTFILE" == "$INFILE" ]]; then + log_error "output file would overwrite input file." + exit 1 + fi } # Assembles the full ffmpeg command array with all configured options. build_ffmpeg_cmd() { - # Sets audio flags to transcode to Opus or copy streams unchanged. - local audio_flags - if [[ "$AUDIO_MODE" == "transcode" ]]; then - audio_flags=("-c:a" "libopus") - else - audio_flags=("-c:a" "copy") - fi + # Sets audio flags to transcode to Opus or copy streams unchanged. + local audio_flags + if [[ "$AUDIO_MODE" == "transcode" ]]; then + audio_flags=("-c:a" "libopus") + else + audio_flags=("-c:a" "copy") + fi - # Constructs the ffmpeg video scaling filter to fit within max dimensions while preserving aspect ratio. - # Scale logic: for landscape (w>h), constrain width to MAX_W and height to MAX_H; - # for portrait (h>w), constrain width to MAX_H and height to MAX_W. - # Commas are escaped for safe shell passing; force_original_aspect_ratio=decrease avoids upscaling. - local vf="scale='if(gt(iw\\,ih)\\,min(iw\\,${MAX_W})\\,min(iw\\,${MAX_H}))':'if(gt(iw\\,ih)\\,min(ih\\,${MAX_H})\\,min(ih\\,${MAX_W}))':force_original_aspect_ratio=decrease" + # Constructs the ffmpeg video scaling filter to fit within max dimensions while preserving aspect ratio. + # Scale logic: for landscape (w>h), constrain width to MAX_W and height to MAX_H; + # for portrait (h>w), constrain width to MAX_H and height to MAX_W. + # Commas are escaped for safe shell passing; force_original_aspect_ratio=decrease avoids upscaling. + local vf="scale='if(gt(iw\\,ih)\\,min(iw\\,${MAX_W})\\,min(iw\\,${MAX_H}))':'if(gt(iw\\,ih)\\,min(ih\\,${MAX_H})\\,min(ih\\,${MAX_W}))':force_original_aspect_ratio=decrease" - CMD=(ffmpeg -y -i "$INFILE" -map_metadata 0 -c:v libx264 -preset "$PRESET" -crf "$CRF" -maxrate "$MAXRATE" -bufsize "$BUFSIZE" -vf "$vf" "${audio_flags[@]}" -movflags +faststart) + CMD=(ffmpeg -y -i "$INFILE" -map_metadata 0 -c:v libx264 -preset "$PRESET" -crf "$CRF" -maxrate "$MAXRATE" -bufsize "$BUFSIZE" -vf "$vf" "${audio_flags[@]}" -movflags +faststart) - # Safely append user-provided extra arguments by splitting on whitespace. - if [[ ${#EXTRA_ARGS[@]} -gt 0 ]]; then - for ea in "${EXTRA_ARGS[@]}"; do - read -r -a split <<< "$ea" - CMD+=("${split[@]}") - done - fi + # Safely append user-provided extra arguments by splitting on whitespace. + if [[ ${#EXTRA_ARGS[@]} -gt 0 ]]; then + for ea in "${EXTRA_ARGS[@]}"; do + read -r -a split <<<"$ea" + CMD+=("${split[@]}") + done + fi - CMD+=("$OUTFILE") + CMD+=("$OUTFILE") } # Executes the constructed ffmpeg command after displaying it for transparency. run_ffmpeg() { - log_info "Processing: $INFILE -> $OUTFILE" - if [[ -f "$OUTFILE" ]]; then - input_duration=$(get_duration "$INFILE") - output_duration=$(get_duration "$OUTFILE") - if [[ "$input_duration" == "$output_duration" && "$output_duration" != "0" ]]; then - if [[ "$OVERWRITE_SAME" == false ]]; then - log_warn "Skipping existing file with matching duration: $OUTFILE" - return - fi - elif [[ "$IS_BATCH" == false ]]; then - if [[ "$OVERWRITE_DIFFERENT" == false ]]; then - read -p "Output file exists with different duration, overwrite? (y/N) " -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - log_warn "User chose not to overwrite: $OUTFILE" - return - fi - fi - else - if [[ "$OVERWRITE_DIFFERENT" == false ]]; then - log_warn "Skipping existing file with different duration: $OUTFILE" - return - fi - fi - fi - log_info "Starting encoding..." - log_info "Command:" - printf ' %q' "${CMD[@]}" - echo - "${CMD[@]}" - log_success "Encoding completed: $OUTFILE" + log_info "Processing: $INFILE -> $OUTFILE" + if [[ -f "$OUTFILE" ]]; then + input_duration=$(get_duration "$INFILE") + output_duration=$(get_duration "$OUTFILE") + if [[ "$input_duration" == "$output_duration" && "$output_duration" != "0" ]]; then + if [[ "$OVERWRITE_SAME" == false ]]; then + log_warn "Skipping existing file with matching duration: $OUTFILE" + return + fi + elif [[ "$IS_BATCH" == false ]]; then + if [[ "$OVERWRITE_DIFFERENT" == false ]]; then + read -p "Output file exists with different duration, overwrite? (y/N) " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + log_warn "User chose not to overwrite: $OUTFILE" + return + fi + fi + else + if [[ "$OVERWRITE_DIFFERENT" == false ]]; then + log_warn "Skipping existing file with different duration: $OUTFILE" + return + fi + fi + fi + log_info "Starting encoding..." + log_info "Command:" + printf ' %q' "${CMD[@]}" + echo + "${CMD[@]}" + log_success "Encoding completed: $OUTFILE" } # Main entry point: orchestrates parsing, validation, command building, and execution. main() { - parse_args "$@" - validate_input - if [[ "$IS_BATCH" == true ]]; then - # Save the output directory for batch processing - local OUTPUT_DIR="$OUTFILE" - local total=${#INPUT_FILES[@]} - local count=1 - for file in "${INPUT_FILES[@]}"; do - log_info "Processing file $count of $total: $(basename "$file")" - INFILE="$file" - OUTFILE="$OUTPUT_DIR" # Reset OUTFILE for each iteration - validate_input_single - build_ffmpeg_cmd - run_ffmpeg - ((count++)) - done - log_success "Batch processing completed: $total file(s) processed" - else - validate_input_single - build_ffmpeg_cmd - run_ffmpeg - fi + parse_args "$@" + validate_input + if [[ "$IS_BATCH" == true ]]; then + # Save the output directory for batch processing + local OUTPUT_DIR="$OUTFILE" + local total=${#INPUT_FILES[@]} + local count=1 + for file in "${INPUT_FILES[@]}"; do + log_info "Processing file $count of $total: $(basename "$file")" + INFILE="$file" + OUTFILE="$OUTPUT_DIR" # Reset OUTFILE for each iteration + validate_input_single + build_ffmpeg_cmd + run_ffmpeg + ((count++)) + done + log_success "Batch processing completed: $total file(s) processed" + else + validate_input_single + build_ffmpeg_cmd + run_ffmpeg + fi } main "$@"