#!/usr/bin/env bash
DEBUG=0 # set to 1 for dry running with stdout verbosity
# will still populate INTERFOLDER
### README
## DEPENDENCIES
# ffmpeg
# libheif-examples
# renrot
# INSTALL VIA apt install ffmpeg libheif-examples renrot -y
#################################################################
### START OF CONFIG #############################################
### CONFIG OF FOLDERS
# tmp folder
INTERFOLDER="~/.mediatmp"
# drop folder, if not specified FULL PATH in SECOND parameter
DROPFOLDER="~/Videos/transcode/in"
# out folder, can be specified FULL PATH by FIRST parameter
OUTFOLDER="~/Videos/transcode/out"
### CONFIG OF FFMPEG (OBLIGATORY)
## VIDEOS
# minimum byte-size (not kB/MB/GB!) of file to process (smaller will be skipped)
SMALLVID=0
# api for scaling. "scale" if on CPU, "scale_vaapi" if on GPU via VAAPI, check documentation for other apis scaling hook
VIDSCALEAPI="scale_vaapi"
# video target bitrate. you can use e.g. "512k" or "4M"
VIDEOBITS="4M"
# target size in pixels on LONGER side
VIDEOSIZE=1920
# main function to tune
VIDFFMPEG() {
ffmpeg -hide_banner \
-hwaccel vaapi -hwaccel_output_format vaapi \
-i "$v" $VF \
-c:v hevc_vaapi \
-extbrc 1 -look_ahead_depth 40 \
-b:v "$VIDEOBITS" \
-c:a copy \
"$OUTFOLDER/$VIDNEWNAME.mp4"
}
## IMAGES
# api for scaling. "scale" if on CPU, "scale_vaapi" if on GPU via VAAPI, check documentation for other apis scaling hook
PICSCALEAPI="scale"
# quality factor - lower is better (i.e. bigger byte-size)
IMAGECRF=22
# target size in pixels on SHORTER side
IMAGESIZE=2000
# main function to tune
PICFFMPEG() {
ffmpeg -hide_banner -i "$i" $VF \
-c:v libaom-av1 -crf "$IMAGECRF" \
-cpu-used 0 "$OUTFOLDER/${i%.*}.avif"
}
##### OPTIONAL config below #####################################
### FILETYPE ARRAYS
## generally no need to change that.
# "good" are usually well-compressed to begin with and will be COPIED
goodVariant=( "avif" "AVIF" "webp" "WEBP" )
# ffmpeg chokes on HEIF, thus conversion out of that format
heifVariant=( "heic" "heif" "HEIC" "HEIF" )
# jpegs, due to poor renrot compatibility
jpegVariant=( "jpeg" "jpg" "JPEG" "JPG" )
# the rest, compatible with ffmpeg
otherVariant=( "jxl" "JXL" "tif" "tiff" "TIF" "TIFF" "png" "PNG" "bmp" "BMP" )
# videos
vidVariant=( "mov" "MOV" "mp4" "MP4" "mkv" "MKV" )
### END OF CONFIG ###############################################
#################################################################
# PREP
if [[ ! -d "$INTERFOLDER" ]]; then mkdir -p "$INTERFOLDER" ; fi
if [[ ! -z $1 ]]; then OUTFOLDER="$1" ; fi
if [[ ! -z $2 ]]; then DROPFOLDER="$2" ; fi
if [[ `ls "$DROPFOLDER" | wc -c` -eq 0 ]]; then
echo "Drop folder empty or absent. (Be sure to use FULL PATHs, or tilde expansion.)"
exit
fi
shopt -s extglob
# DEFINE FUNCTIONS
function IMAGETRANSCODE {
VIDDATA=`ffprobe "$i" 2>&1 | grep Video:`
if [[ $VIDDATA =~ ([0-9]{3,4})x([0-9]{3,4}) ]]; then
width="${BASH_REMATCH[1]}"
height="${BASH_REMATCH[2]}"
# SET UP SIZING
#check if vertical
if [[ $width -le $IMAGESIZE ]] && [[ $height -le $IMAGESIZE ]]; then
test $DEBUG = 1 && echo "# resizing $i not necessary"
VF=""
else
if [[ $height -gt $width ]]; then # vertical aspect
VF="-vf $PICSCALEAPI=$IMAGESIZE:-2"
else # horizontal aspect
VF="-vf $PICSCALEAPI=-2:$IMAGESIZE"
fi
fi
if [[ $DEBUG = 1 ]]; then echo "FFMPEG $i with res '$VF'"
else
PICFFMPEG || echo "$i crashed ffmpeg!" >> "$DROPFOLDER/.errors.log"
fi
else # error logging
if [[ ${i:0:1} -ne "*" ]]; then
if [[ $DEBUG = 1 ]]; then echo "# resolution of $i failed to be established"
else
echo "$i couldn't be processed." >> "$DROPFOLDER/.errors.log"
fi
fi
fi
}
function VIDEOTRANSCODE {
VIDDATA=`ffprobe "$v" 2>&1 | grep Video:`
# Read MOV metadata from iPhones
if [[ ${v:0:4} -eq "IMG_" ]]; then
test $DEBUG = 1 && echo "# renaming $v"
VIDSTAMP=`ffprobe "$v" 2>&1 | grep com.apple.quicktime.creationdate | cut -c39-`
if [[ -z $VIDSTAMP ]]; then
test $DEBUG = 1 && echo "# unable to find creationdate"
VIDNEWNAME="${v%.*}"
else
VIDNEWNAME="${VIDSTAMP:0:4}${VIDSTAMP:5:2}${VIDSTAMP:8:2}_${VIDSTAMP:11:2}${VIDSTAMP:14:2}${VIDSTAMP:17:2}"
echo "Renaming $v to $VIDNEWNAME.mp4."
fi
else
VIDNEWNAME="${v%.*}"
fi
# determine scale
if [[ $VIDDATA =~ ([0-9]{3,4})x([0-9]{3,4}) ]]; then
width="${BASH_REMATCH[1]}"
height="${BASH_REMATCH[2]}"
if [[ $width -le $VIDEOSIZE ]] && [[ $height -le $VIDEOSIZE ]]; then
test $DEBUG = 1 && echo "# resizing $v not necessary"
VF=""
else
if [[ $height -gt $width ]]; then # vertical aspect
VF="-vf $VIDSCALEAPI=-2:$VIDEOSIZE"
else # horizontal aspect
VF="-vf $VIDSCALEAPI=$VIDEOSIZE:-2"
fi
fi
if [[ $DEBUG = 1 ]]; then echo "FFMPEG $v with res '$VF' and new name $VIDNEWNAME"
else
VIDFFMPEG || echo "$v crashed ffmpeg!" >> "$DROPFOLDER/.errors.log"
fi
else # error logging
if [[ ${v:0:1} -ne "*" ]]; then
if [[ $DEBUG = 1 ]]; then echo "# resolution of $v failed to be established"
else
echo "$v couldn't be processed." >> "$DROPFOLDER/.errors.log"
fi
fi
fi
}
cd "$DROPFOLDER"
### START ACTUAL WORK
## copy good images without processing
test $DEBUG = 1 && echo "### START ###"
for img in "${goodVariant[@]}" ; do
test $DEBUG = 1 && echo "# copying $img files"
cp *.$img "$OUTFOLDER/"
done
## HEVC imgs convert
for heimg in "${heifVariant[@]}"; do
for h in *.$heimg ; do
test $DEBUG = 1 && echo "# converting HEIC $h"
heif-convert -q 100 "$h" "$INTERFOLDER/$h.jpg"
done
done
## copy images to actually transcode
for jpimg in "${jpegVariant[@]}" "${otherVariant[@]}" ; do
cp *.$jpimg "$INTERFOLDER/"
done
cd "$INTERFOLDER"
### WORK ON PICTURES
## rename files
for n in !([0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]_[0-9][0-9][0-9][0-9][0-9][0-9]*) ; do
# jpegs via renrot
if [[ ${jpegVariant[@]} =~ ${n##*.} ]]; then
test $DEBUG = 1 && echo "# attempting renrot of $n"
renrot -n %Y%m%d_%H%M%S "$n"
fi # TODO: some universal FFPROBE®EX method of doing that not just for JPEGs
done
## transcode all
for i in * ; do
test $DEBUG = 1 && echo "# function image transcode $i"
IMAGETRANSCODE
done
## cleanup
rm "$INTERFOLDER/"*
cd "$DROPFOLDER"
## VIDEOS
for vid in "${vidVariant[@]}" ; do
for v in *.$vid ; do
# if small file then not worth it
SIZE=`stat "$v" | grep Size: | cut -c9-19`
if [[ $SIZE -le $SMALLVID ]]; then
if [[ $DEBUG -eq 1 ]]; then echo "# just moving $v (small size)"
else cp "$v" "$OUTFOLDER/"
fi
else
test $DEBUG = 1 && echo "# function video transcode $v"
VIDEOTRANSCODE
fi
done
done
echo "### FINISHED ###"