CCCPaste

Batch transcoder — transcode imgs & vids...

#!/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 ###"