summaryrefslogtreecommitdiff
path: root/cliparmyknife.sh
diff options
context:
space:
mode:
Diffstat (limited to 'cliparmyknife.sh')
-rwxr-xr-xcliparmyknife.sh299
1 files changed, 299 insertions, 0 deletions
diff --git a/cliparmyknife.sh b/cliparmyknife.sh
new file mode 100755
index 0000000..ca19a44
--- /dev/null
+++ b/cliparmyknife.sh
@@ -0,0 +1,299 @@
+#!/bin/bash
+
+#set -e
+
+get_clip_params () {
+
+ CLIP_PATH=$( dirname "$1" )
+ CLIP_NAME=$( basename "$1" | sed -r "s|\.[^. ]*$||g" )
+ CLIP_EXTENSION=$( sed -r "s|^.*\.||g" <<< "$1" )
+ CLIP_WIDTH=$( ffprobe -v error -select_streams v:0 -show_entries stream=width -of default=noprint_wrappers=1:nokey=1 "$1" )
+ CLIP_HEIGHT=$( ffprobe -v error -select_streams v:0 -show_entries stream=height -of default=noprint_wrappers=1:nokey=1 "$1" )
+ CLIP_SAR=$( ffprobe -v error -select_streams v:0 -show_entries stream=sample_aspect_ratio -of default=noprint_wrappers=1:nokey=1 "$1" )
+ CLIP_FRAMERATE=$( ffprobe -i "$1" 2>&1 | grep -oP "\d{1,5}\.?\d{1,5} fps" | tail -1 | sed -r "s|\ fps||g" )
+
+ MOD=1
+
+ [[ "$CLIP_SAR" != "1:1" ]] && MOD=$( echo "$CLIP_SAR" | sed -r "s|\:|\ \/\ |g; s|^|scale\=5\;\ |g" | bc ) && CLIP_HEIGHT=$( printf "%.0f" $( bc <<< "$CLIP_HEIGHT / $MOD" ) )
+
+}
+
+dump_clip_params () {
+
+ echo -e "\nCLIP NAME: $CLIP_NAME.$CLIP_EXTENSION\nCLIP LOCATION: $CLIP_PATH/\nCLIP DIMENSIONS: ${CLIP_WIDTH}x${CLIP_HEIGHT}\nCLIP FRAMERATE: $CLIP_FRAMERATE\nCLIP SAR: $CLIP_SAR"
+
+}
+
+crop () {
+
+ echo -e "-----out_w is the width of the output rectangle-----\n-----out_h is the height of the output rectangle-----\n-----x and y specify the top left corner of the output rectangle-----"
+ echo -e "-----CURRENT VIDEO DIMENSIONS OF $CLIP_NAME.$CLIP_EXTENSION: ${CLIP_WIDTH}x${CLIP_HEIGHT}"
+
+
+
+ re='^[0-9]+(\ )+[0-9]+(\ )+[0-9]+(\ )+[0-9]+$'
+
+ declare OUT_W OUT_H X Y CROP_PARAMS
+
+ while read -p "Enter new clip dimensions [FORMAT: out_w out_h x y]: " CROP_PARAMS; do
+
+ CROP_PARAMS=${CROP_PARAMS:-"$CLIP_WIDTH $CLIP_HEIGHT 0 0"}
+
+ if ! [[ $CROP_PARAMS =~ $re ]]; then
+ echo "error: Invalid crop parameters" >&2; continue
+ fi
+
+
+
+ OUT_W=$( echo "$CROP_PARAMS" | tr -s ' ' | cut -d ' ' -f 1 )
+ OUT_H=$( echo "$CROP_PARAMS" | tr -s ' ' | cut -d ' ' -f 2 )
+ X=$( echo "$CROP_PARAMS" | tr -s ' ' | cut -d ' ' -f 3 )
+ Y=$( echo "$CROP_PARAMS" | tr -s ' ' | cut -d ' ' -f 4 )
+
+ if [[ $( bc <<< "$OUT_W + $X" ) -gt $CLIP_WIDTH || $( bc <<< "$OUT_H + $Y" ) -gt $CLIP_HEIGHT ]]; then
+ echo "error: Crop parameters go outside clip bounds" >&2; continue
+ fi
+
+ break
+ done
+
+ OUT_H_MOD=$( bc <<< "$OUT_H * $MOD" )
+ Y_MOD=$( bc <<< "$Y * $MOD" )
+
+ ffmpeg -i "$1" -filter:v "crop=$OUT_W:$OUT_H_MOD:$X:$Y_MOD" "$CLIP_NAME [cropped $OUT_W:$OUT_H:$X:$Y].$CLIP_EXTENSION"
+
+}
+
+join_no_reencode () {
+ [[ ! -f "join.$CLIP_EXTENSION" ]] && cp "$1" "join.$CLIP_EXTENSION" && return
+ echo "file 'join.${CLIP_EXTENSION}'" > cliparmyknife_concat.txt
+ echo "file '$1'" >> cliparmyknife_concat.txt
+ ffmpeg -y -f concat -safe 0 -i cliparmyknife_concat.txt -c copy join.$CLIP_EXTENSION
+ rm cliparmyknife_concat.txt
+}
+
+join_yes_reencode () {
+
+
+ [[ ! -f "join.$CLIP_EXTENSION" ]] && cp "$1" "join.$CLIP_EXTENSION" && return
+ JOIN_WIDTH=$( ffprobe -i "join.$CLIP_EXTENSION" 2>&1 | grep -oP "[1-9]\d{1,10}x[1-9]\d{1,10}" | sed -r "s|\ ||g; s|x[0-9]*$||g" )
+ JOIN_HEIGHT=$( ffprobe -i "join.$CLIP_EXTENSION" 2>&1 | grep -oP "[1-9]\d{1,10}x[1-9]\d{1,10}" | sed -r "s|\ ||g; s|^[0-9]*x||g" )
+
+ #ffmpeg -f lavfi -i anullsrc=channel_layout=stereo:sample_rate=44100 -i boximouto.webm -c:v copy -c:a libopus -shortest boximouto2.webm
+ #ffprobe -i ikadiabetes2.gif -show_streams -select_streams a -loglevel error
+
+ [[ -z $(ffprobe -i "$1" -show_streams -select_streams a -loglevel error) ]] && ffmpeg -f lavfi -i anullsrc=channel_layout=stereo:sample_rate=44100 -i "$1" -c:v copy -c:a libopus -shortest "/tmp/clip_treated.$CLIP_EXTENSION" || cp "$1" "/tmp/clip_treated.$CLIP_EXTENSION"
+ [[ -z $(ffprobe -i "join.$CLIP_EXTENSION" -show_streams -select_streams a -loglevel error) ]] && ffmpeg -f lavfi -i anullsrc=channel_layout=stereo:sample_rate=44100 -i "join.$CLIP_EXTENSION" -c:v copy -c:a libopus -shortest "/tmp/join_treated.$CLIP_EXTENSION" || cp "join.$CLIP_EXTENSION" "/tmp/join_treated.$CLIP_EXTENSION"
+
+
+ echo "JOIN DIMENSIONS: $JOIN_WIDTH x $JOIN_HEIGHT"
+
+ [[ $JOIN_WIDTH -gt $CLIP_WIDTH ]] && PAD_WIDTH=$JOIN_WIDTH || PAD_WIDTH=$CLIP_WIDTH
+ [[ $JOIN_HEIGHT -gt $CLIP_HEIGHT ]] && PAD_HEIGHT=$JOIN_HEIGHT || PAD_HEIGHT=$CLIP_HEIGHT
+ PAD_X=$( bc <<< "($JOIN_WIDTH - $CLIP_WIDTH) / 2" )
+ PAD_Y=$( bc <<< "($JOIN_HEIGHT - $CLIP_HEIGHT) / 2" )
+
+ echo "PAD DIMENSIONS: $PAD_X, $PAD_Y"
+
+ if [[ $PAD_X -ge 0 && $PAD_Y -ge 0 ]]; then
+ cp "/tmp/join_treated.$CLIP_EXTENSION" "/tmp/join_temp.$CLIP_EXTENSION"
+ ffmpeg -y -i "/tmp/clip_treated.$CLIP_EXTENSION" -vf "pad=width=$PAD_WIDTH:height=$PAD_HEIGHT:x=$PAD_X:y=$PAD_Y:color=black" "/tmp/clip_temp.$CLIP_EXTENSION"
+ elif [[ $PAD_X -lt 0 && $PAD_Y -ge 0 ]]; then
+ ffmpeg -y -i "/tmp/join_treated.$CLIP_EXTENSION" -vf "pad=width=$PAD_WIDTH:height=$PAD_HEIGHT:x=${PAD_X#-}:y=0:color=black" "/tmp/join_temp.$CLIP_EXTENSION"
+ ffmpeg -y -i "/tmp/clip_treated.$CLIP_EXTENSION" -vf "pad=width=$PAD_WIDTH:height=$PAD_HEIGHT:x=0:y=$PAD_Y:color=black" "/tmp/clip_temp.$CLIP_EXTENSION"
+ elif [[ $PAD_X -ge 0 && $PAD_Y -lt 0 ]]; then
+ ffmpeg -y -i "/tmp/join_treated.$CLIP_EXTENSION" -vf "pad=width=$PAD_WIDTH:height=$PAD_HEIGHT:x=0:y=${PAD_Y#-}:color=black" "/tmp/join_temp.$CLIP_EXTENSION"
+ ffmpeg -y -i "/tmp/clip_treated.$CLIP_EXTENSION" -vf "pad=width=$PAD_WIDTH:height=$PAD_HEIGHT:x=$PAD_X:y=0:color=black" "/tmp/clip_temp.$CLIP_EXTENSION"
+ elif [[ $PAD_X -lt 0 && $PAD_Y -lt 0 ]]; then
+ ffmpeg -y -i "/tmp/join_treated.$CLIP_EXTENSION" -vf "pad=width=$PAD_WIDTH:height=$PAD_HEIGHT:x=${PAD_X#-}:y=${PAD_Y#-}:color=black" "/tmp/join_temp.$CLIP_EXTENSION"
+ cp "/tmp/clip_treated.$CLIP_EXTENSION" "/tmp/clip_temp.$CLIP_EXTENSION"
+ else
+ echo "something fucked up" && exit 1
+ fi
+
+
+ if [[ -n $(ffprobe -i "/tmp/clip_temp.$CLIP_EXTENSION" -show_streams -select_streams a -loglevel error) && -n $(ffprobe -i "/tmp/clip_temp.$CLIP_EXTENSION" -show_streams -select_streams a -loglevel error) ]]; then
+ ffmpeg -y -i "/tmp/join_temp.$CLIP_EXTENSION" -i "/tmp/clip_temp.$CLIP_EXTENSION" -filter_complex "[0:v:0][0:a:0][1:v:0][1:a:0]concat=n=2:v=1:a=1[outv][outa]" -map "[outv]" -map "[outa]" "/tmp/join2.$CLIP_EXTENSION"
+ else
+ ffmpeg -y -i "/tmp/join_temp.$CLIP_EXTENSION" -i "/tmp/clip_temp.$CLIP_EXTENSION" -filter_complex "[0:v:0] [1:v:0]concat=n=2:v=1[outv]" -map "[outv]" "/tmp/join2.$CLIP_EXTENSION"
+ fi
+
+ mv "/tmp/join2.$CLIP_EXTENSION" "join.$CLIP_EXTENSION"
+ rm "/tmp/join_treated.$CLIP_EXTENSION" "/tmp/clip_treated.$CLIP_EXTENSION" "/tmp/join_temp.$CLIP_EXTENSION" "/tmp/clip_temp.$CLIP_EXTENSION"
+}
+
+join () {
+
+ if [[ "$REENCODE_FLAG" = "true" ]]; then
+ join_yes_reencode "$1"
+ elif [[ "$REENCODE_FLAG" = "false" ]]; then
+ join_no_reencode "$1"
+ else
+
+ while true; do
+ read -p "Are the video parameters identical (y/n)? " yn
+
+ case $( tr '[A-Z]' '[a-z]' <<< "$yn" ) in
+ y|yes) REENCODE_FLAG="false"; join_no_reencode "$1" ; break;;
+ n|no) REENCODE_FLAG="true"; join_yes_reencode "$1" ; break;;
+ *) echo "Invalid response." ;;
+ esac
+ done
+ fi
+}
+
+mute () {
+
+ ffmpeg -i "$1" -c copy -an "$CLIP_NAME [no audio].$CLIP_EXTENSION"
+
+}
+
+extract_audio () {
+
+ ffmpeg -i "$1" -codec:a libmp3lame "$CLIP_NAME [audio only].mp3"
+
+}
+
+scale () {
+
+ echo -e "-----CURRENT VIDEO DIMENSIONS OF $CLIP_NAME.$CLIP_EXTENSION: $CLIP_WIDTH x $CLIP_HEIGHT"
+ while read -p "Enter scale coefficient [Default: 2; Recommended: $( bc <<< "scale=2; $CLIP_HEIGHT / 360" )]: " SCALE; do
+
+ SCALE=${SCALE:-2}
+
+ re='^[0-9]*([.][0-9]+)?$'
+ if ! [[ $SCALE =~ $re ]] ; then
+ echo "error: Not a non-zero number" >&2; continue
+ fi
+
+ break
+ done
+
+ ffmpeg -i "$1" -vf scale="-1:$( printf "%.0f" $( bc <<< "scale=5; $CLIP_HEIGHT / $SCALE" ) )" -c:a copy "$CLIP_NAME [$SCALE].$CLIP_EXTENSION"
+
+}
+
+reverse () {
+
+ ffmpeg -i "$1" -vf reverse -af areverse "$CLIP_NAME [reversed].$CLIP_EXTENSION"
+
+}
+
+clip_to_gif () {
+
+ echo -e "-----CURRENT VIDEO DIMENSIONS OF $CLIP_NAME.$CLIP_EXTENSION: $CLIP_WIDTH x $CLIP_HEIGHT"
+ while read -p "Enter scale coefficient [Default: 2; Recommended: $( bc <<< "scale=2; $CLIP_HEIGHT / 360" )]: " SCALE; do
+
+ SCALE=${SCALE:-2}
+
+ re='^[0-9]*([.][0-9]+)?$'
+ if ! [[ $SCALE =~ $re ]] ; then
+ echo "error: Not a non-zero number" >&2; continue
+ fi
+
+ break
+ done
+
+ echo -e "-----CURRENT FRAMERATE OF $CLIP_NAME.$CLIP_EXTENSION: $CLIP_FRAMERATE fps"
+ while read -p "Enter new framerate [Default: 23.98; Recommended: $( bc <<< "scale=2; $CLIP_FRAMERATE / 2" )]: " RATE; do
+
+ RATE=${RATE:-23.98}
+
+ re='^[1-9][0-9]*([.][0-9]+)?$'
+ if ! [[ $RATE =~ $re ]] ; then
+ echo "error: Not a non-zero number" >&2; continue
+ fi
+
+ break
+ done
+
+ filters="fps=$RATE,scale=$( bc <<< "$CLIP_WIDTH / $SCALE" ):-1:flags=lanczos"
+ temp_palette="/tmp/_tmp_palette_$CLIP_NAME.png"
+ temp_gif="/tmp/tmp_out_$CLIP_NAME.gif"
+
+ ffmpeg -v warning -i "$1" -vf "$filters,palettegen=stats_mode=diff" -y $temp_palette
+ ffmpeg -i "$1" -i "$temp_palette" -lavfi "$filters,paletteuse=dither=bayer:bayer_scale=3:diff_mode=rectangle" "$temp_gif"
+ gifsicle --optimize=3 --no-background --output "$CLIP_NAME [$SCALE - $RATE].gif" "$temp_gif"
+
+ rm "$temp_palette" "$temp_gif"
+
+}
+
+clip_to_webm () {
+
+ echo -e "-----CURRENT VIDEO DIMENSIONS OF $CLIP_NAME.$CLIP_EXTENSION: $CLIP_WIDTH x $CLIP_HEIGHT"
+ while read -p "Enter scale coefficient [Default: 2; Recommended: $( bc <<< "scale=2; $CLIP_HEIGHT / 360" )]: " SCALE; do
+
+ SCALE=${SCALE:-2}
+
+ re='^[1-9][0-9]*([.][0-9]+)?$'
+ if ! [[ $SCALE =~ $re ]] ; then
+ echo "error: Not a non-zero number" >&2; continue
+ fi
+
+ break
+ done
+
+ ffmpeg -i "$1" -c:v libvpx-vp9 -crf 30 -b:v 0 -b:a 128k -vf scale="-1:$( bc <<< "$CLIP_HEIGHT / $SCALE" )" -c:a libopus "$CLIP_NAME [$SCALE].webm"
+}
+
+webp_to_gif () {
+ python3 -c "from PIL import Image;Image.open('$1').save('${1%.webp}.gif','gif',save_all=True,optimize=True,background=0)"
+}
+
+FUNCTION=""
+
+while getopts "hicsrjmxgw" opt; do
+ case $opt in
+ h) echo -e "usage: $0 [-h help] [-i get clip info] [-c crop clip] [-s scale/resize clip] [-r reverse clip] [-j join clips] [-m mute clip] [-x extract audio] [-g convert clip to gif] [-w convert clip to webm]"; exit ;;
+ i) FUNCTION="dump_clip_params"; break;;
+ c) FUNCTION="crop"; break ;;
+ s) FUNCTION="scale"; break ;;
+ r) FUNCTION="reverse"; break ;;
+ j) FUNCTION="join"; break ;;
+ m) FUNCTION="mute"; break ;;
+ x) FUNCTION="extract_audio"; break ;;
+ g) FUNCTION="convert"; break ;;
+ w) FUNCTION="clip_to_webm"; break ;;
+ ?) echo "error: option -$OPTARG is not implemented"; exit ;;
+ esac
+done
+
+if [ "$#" -lt 2 ]; then
+ echo "Please insert only one flag and at least one argument"
+ exit
+else
+ echo -e "\c"
+fi
+
+i=0
+
+for file in "${@:2}"
+do
+ re='s|\.[^. ]*$||g'
+
+ ! [[ $file =~ $re ]] && echo "$file NOT A PROPER FILE, IGNORING..." && continue
+ get_clip_params "$file"
+ echo "$CLIP_NAME - $CLIP_EXTENSION - $FUNCTION"
+
+ if [[ "$FUNCTION" == "convert" ]]; then
+
+ if [[ "$CLIP_EXTENSION" == "gif" ]]; then
+ echo -e "$CLIP_NAME.$CLIP_EXTENSION IS ALREADY A GIF. SKIPPING...\n\n-------------------------$file DONE-------------------------\n\n" && continue
+ elif [[ "$CLIP_EXTENSION" == "webp" ]]; then
+ FUNCTION="webp_to_gif"
+ else
+ FUNCTION="clip_to_gif"
+ fi
+
+ fi
+
+ $FUNCTION "$file"
+ [[ "$FUNCTION" =~ ^(clip|webp)_to_gif$ ]] && FUNCTION="convert"
+ echo -e "\n\n------------------------- $file - DONE -------------------------\n\n"
+done
+
+
+# TO ADD: MORE FLEXIBLE FLAG/ARGUMENT PROCESSING, FLAG WHICH MAKES EVERY FUNCTION RESPECT FILEPATHS (I.E. DOESN'T DRAG EVERY OUTPUT TO CURRENT DIRECTORY)
+
+