mirror of
https://gitlab.com/dabruh/dotfiles.git
synced 2025-12-07 10:46:43 +01:00
refactor: standardize indentation in videncode script
This commit is contained in:
parent
530c9307d4
commit
b1b8ce749b
1 changed files with 187 additions and 141 deletions
|
|
@ -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 <<EOF
|
||||
cat <<EOF
|
||||
Usage: $(basename "$0") -i input.mp4 [options]
|
||||
|
||||
Options:
|
||||
|
|
@ -70,173 +70,219 @@ EOF
|
|||
|
||||
# Parses command-line arguments, supporting both short and long options.
|
||||
parse_args() {
|
||||
if [[ $# -eq 0 ]]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
if [[ $# -eq 0 ]]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
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
|
||||
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 "$@"
|
||||
|
|
|
|||
Loading…
Reference in a new issue