Last active
May 25, 2023 00:15
-
-
Save FractalHQ/67eb9e134e8b6a9362d3d1d7b0008245 to your computer and use it in GitHub Desktop.
Simple batch video processing.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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