Skip to content

Instantly share code, notes, and snippets.

@FractalHQ
Last active May 25, 2023 00:15
Show Gist options
  • Save FractalHQ/67eb9e134e8b6a9362d3d1d7b0008245 to your computer and use it in GitHub Desktop.
Save FractalHQ/67eb9e134e8b6a9362d3d1d7b0008245 to your computer and use it in GitHub Desktop.
Simple batch video processing.
#!/bin/zsh
e() {
echo "$1"
}
fmt() {
if [ -t 1 ]; then
printf "%b\n" "$1"
else
e "$1"
fi
}
bb() { # Bold
fmt "\033[1m$1\033[0m"
}
r() { # Red
fmt "\033[31m$1\033[0m"
}
g() { # Green
fmt "\033[32m$1\033[0m"
}
b() { # Blue
fmt "\033[34m$1\033[0m"
}
dim() { # Dim Grey
fmt "\033[2m$1\033[0m"
}
printHelp() {
e "⌜$(dim "-------------------------")⌝"
e "$(dim "|")$(b " _ _ ")$(dim "|")"
e "$(dim "|")$(b " |_| | | ")$(dim "|")"
e "$(dim "|")$(b " ___ _ _______| |_ ")$(dim "|")"
e "$(dim "|")$(b " | . | | | |_ -| | ")$(dim "|")"
e "$(dim "|")$(b " |_ |___|_|___|_|_| ")$(dim "|")"
e "$(dim "|")$(b " |_| ")$(dim "|")"
e "$(dim "|")$(b " ")$(dim "|")"
e "$(dim "|")$(dim " simple video processing ")$(dim "|")"
e "⌞$(dim "-------------------------")⌟"
e ""
bb Usage
e " $(b quish) [FILE|DIRECTORY] [OPTIONS]"
e ""
bb Options
e " -r, --resolution $(dim "width:height") Set the output resolution. $(dim default) $(b "1280:1280")"
e " -b, --bitrate $(dim "kbps") Set the output bitrate. $(dim default) $(b 3000)"
e " -f, --fps $(dim "fps") Set the output framerate. $(dim default) $(b 24)"
e " -t, --type $(dim "type") Set the output file type. $(dim default) $(b webm)"
e " -s, --formats $(dim "ext1,ext2,... ") Set the input formats. $(dim default) $(b \"mp4,mov\")"
e " -d, --depth $(dim "n") Set the search depth. $(dim default) $(b 1)"
e " -h, --help Print this help message."
e ""
bb Examples
e " Process all videos in the current directory:"
b " quish ."
e ""
e " Process all videos in a subfolder called 'media':"
b " quish ./my/media/"
e ""
e " Process a single video called 'input.mov':"
b " quish input.mov $(bb '')$(bb '-r') $(b '1920:1080') $(bb '-b') $(b '3000') $(bb '-f') $(b '20')"
}
# Print help if no arguments are provided or if --help or -h flag is provided.
if [[ $# -eq 0 ]] || [[ "$1" == "--help" ]] || [[ "$1" == "-h" ]]; then
printHelp
exit 0
fi
# Default settings.
resolution="1280:1280"
bitrate=3000
fps=24
type="webm"
formats="mp4,mov"
depth=2
# Parse CLI flags.
input_set=0
while [[ "$#" -gt 0 ]]; do
case "$1" in
-r | --resolution)
resolution="$2"
shift
;;
-b | --bitrate)
bitrate="$2"
shift
;;
-f | --fps)
fps="$2"
shift
;;
-t | --type)
type="$2"
shift
;;
-s | --formats)
formats="$2"
shift
;;
-d | --depth)
depth="$2"
shift
;;
*)
if ((input_set == 0)); then
input="$1"
input_set=1
fi
;;
esac
shift
done
if [[ -z "$input" ]]; then
r "$(bb '\nNo input file or directory specified.')"
e "Use $(b 'quish --help') to see usage instructions."
exit 1
elif [[ ! -e "$input" ]]; then
r "$(bb '\nFile or directory not found.')"
e "Are you sure it exists at the specified path?"
exit 1
fi
function printSettings() {
e ""
e " $(bb Resolution): $(b $resolution)"
e " $(bb Bitrate): $(b $bitrate kbps)"
e " $(bb FPS): $(b $fps)"
e " $(bb "Inputs"): $(b $formats)"
e " $(bb "Output"): $(b $type)"
e ""
}
process_video() {
local file=$1
local filename=$(basename "$file")
local dirname=$(dirname "$file")
local output="${dirname}/${filename%.*}"
local name="${filename%%.*}"
local ext="${filename##*.}"
local output_type="${filename##*.}"
((video_count++))
e "\n$video_count$(dim /)$num_videos $(dim $name).$(b $ext) --> $(dim $name).$(b $type)\n"
ffmpeg -y -v quiet -stats -i "$file" -vf "scale=$resolution:force_original_aspect_ratio=decrease,pad=1280:1280:(ow-iw)/2:(oh-ih)/2" \
-r "$fps" -b:v "$bitrate"k "${output}.${type}" </dev/null
e "$(g "Complete:") $output.$(b $type)"
}
count_videos() {
local dir=$1
local count=$(find "$dir" -type f \( -name "*.mp4" -o -name "*.mov" \) -maxdepth "$depth" 2>/dev/null | wc -l)
bb $(b $count)
}
if [[ -d "$input" ]]; then
num_videos=$(count_videos "$input")
e "$num_videos videos found. Settings:"
printSettings
printf "Continue? (Y/n): "
read confirm
confirm=${confirm:-Y} # Default value to "Y"
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
e "k.."
exit 1
fi
e "Processing $(b $num_videos) video files in the directory: $(realpath $input)"
video_count=0 # Initialize video count
# Convert formats string to array
IFS=',' read -r -a format_array <<<"$formats"
# Generate string of -name arguments for find command
name_args=""
for format in "${format_array[@]}"; do
name_args+="-name \"*.$format\" -o "
done
# Remove the trailing '-o' from the name arguments.
name_args=${name_args%'-o '}
# Find all video files in the directory and process them.
eval "find \"$input\" -type f \( $name_args \) -maxdepth \"$depth\"" | while IFS= read -r file; do
process_video "$file"
done
elif [[ -f "$input" ]]; then
# Convert formats string to array.
IFS=',' read -r -a format_array <<<"$formats"
for format in "${format_array[@]}"; do
# Check if the input file's extension matches the current format.
if [[ "$input" == *."$format" ]]; then
e "\nVideo found: $(b "$(bb "$(basename "$input")")")"
e "\nCurrent settings:"
printSettings
read -p "Continue? (y/n): " confirm
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
e "k..."
exit 1
fi
e "Processing the video file: $input"
process_video "$input"
break
fi
done
else
e "Invalid input. Please provide a valid directory or file path."
exit 1
fi
e "\n$(g "All done! 🎉")"
if [[ "$OSTYPE" == "darwin"* ]]; then
osascript -e "display notification \"All done! 🎉\" with title \"Quish\" sound name \"Glass\""
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment