#!/bin/bash #Is a 'slave' script to run an ffmpeg (and ffprobe) command. Generally it will #take a file specified on the 'host' machine that has a list of files; copy a #version down locally, transcode it, then replace the original file on the #'host', and remove the temporary files. # #You will need to have both FFMPEG and FFPROBE installed (and will need #to pray to St. Isidore... good luck if you encounter issues. #Default operations, change if necessary FFBIN="/usr/bin/" #if unusual install or a specific version HostName="localhost" #if localhost then will do locally (removes ssh/scp cmds) hostFile="/tmp/transcode/list.txt" workDIR="$PWD/transcode" tmpDIR="$PWD/tmpTranscode" #DEFINE FUNCTIONS function encode { local inFL=$1 local outFL=$2 local burn=$3 if [ $burn -gt -1 ] then inline="-i "$tmpDIR/dummy.mkv"" vidline="-map 1:v:0" subline="-map -0:s:$burn" else unset inline vidline="-map 0:v:0" unset subline fi $FFBIN/ffmpeg -hide_banner -loglevel error -stats \ -i "$inFL" $inline \ -map_metadata 0 \ $vidline \ -c:v libsvtav1 -crf 28 -preset 6 -pix_fmt yuv420p10le \ -svtav1-params \ "enable-overlays=1:\ tune=0:\ keyint=10s" \ -map 0:a? -acodec aac \ -map 0:s? -scodec copy $subline \ -use_wallclock_as_timestamps 1 -max_interleave_delta 0 \ -map_chapters 0 -max_muxing_queue_size 9999 -y "$outFL" } function burnSubs { local inFL=$1 local outFL=$2 local track=$3 #EVIDTNELY NECESSARY SO THAT FILES AREN'T 198+ HRS IN LEN. AT THE END local DURATION=$( $FFBIN/ffprobe -loglevel error -show_entries format=duration \ -of default=noprint_wrappers=1:nokey=1 "$inFL" ) $FFBIN/ffmpeg -hide_banner -stats -i "$inFL" \ -map_metadata -1 \ -filter_complex "[0:v][0:s:$track]overlay[v]" -map "[v]" \ -filter_complex_threads 1 \ -an -sn \ -max_muxing_queue_size 4096 \ -t $DURATION -y "$tmpDIR/dummy.mkv" if ! [ $? -eq 0 ] then echo "Sub Burn Failed" return 10 else encode "$inFL" "$outFL" $track fi } #MAIN FUNCTION BEGINS mkdir -p "$tmpDIR" mkdir -p "$workDIR" if [ -n "$1" ] then numIter=$1 iterLim="true" else numIter=20 iterLim="false" fi if ! command -v $FFBIN/ffprobe &> /dev/null then echo "Need to have ffprobe (and ffmpeg) installed" exit 1 fi if ! command -v $FFBIN/ffmpeg &> /dev/null then echo "Need to have ffmpeg (and ffprobe) installed" exit 1 fi if ! command -v bc &> /dev/null then echo "Need to have bc installed (foreign audio scan)" exit 1 fi i=0 #Total number of loops j=0 #Failed to obtain job k=0 #ffmpeg failed declare -a langs #Subtitle Languages while [ $numIter -gt 0 ] && [ $j -lt 50 ] && [ $k -lt 60 ] do if [ $( echo "$HostName"|tr [a-z] [A-Z] ) != "LOCALHOST" ] then filePATH=$( ssh $HostName "head -n1 $hostFile;sed -i -e '1d' $hostFile" ) else filePATH=$( head -n1 $hostFile;sed -i -e '1d' $hostFile ) fi if [ -n "$filePATH" ] then j=0 fileNAME=$( basename "$filePATH" ) #PULL DOWN FILE if [ $( echo "$HostName"|tr [a-z] [A-Z] ) != "LOCALHOST" ] then scp $HostName:"$( echo $filePATH | sed "s/[][!@#$%^&*( ;)]/\\\&/g" )" "$tmpDIR" else rsync -a --progress "$filePATH" "$tmpDIR/" fi #RUN FFMPROBE/FFMPEG echo "DOING $fileNAME" unset langs #DETERMINE THE NUMBER OF ENGLISH SUBTITLES (BURNING BEHAVIOUR) for line in $( $FFBIN/ffprobe -loglevel error -select_streams s -show_entries\ stream=index:stream_tags=language -of csv=p=0 \ "$tmpDIR"/"$fileNAME" ) do langs+=("$line") done unset line engCounter=0 if [ ${#langs[@]} -gt 1 ] then #DETERMINE NUMBER OF ENGLISH-CONTAINING SUBTITLES unset streamNUMBERS declare -a streamNUMBERS for language in "${langs[@]}" do if grep -iq "eng" <<< $language then let ++engCounter delim="," unset addNumber addNumber=( "${language%%"$delim"*}" ) streamNUMBERS+=($addNumber) fi done if [ $engCounter -lt 2 ] then #ONLY ONE ENG. SUB TRACK echo "ONE ENG. SUB TRACK" encode "$tmpDIR/$fileNAME" "$workDIR/$fileNAME" -1 else #TEST SUBTITLE TYPE; IF NOT PGS SKIP IT; FEEL FREE TO FILL IN if grep -qi "pgs" <<< $( $FFBIN/ffprobe -loglevel error -select_streams s \ -show_entries stream=codec_name \ -of csv=p=0 "$tmpDIR"/"$fileNAME" ) then # MORE THAN ONE SUB TRACK; HAVE TO FIGURE OUT WHICH TO BURN unset streamFRAMES declare -a streamFRAMES #Presuming the one to burn-in is the one with less frames unset minFrames unset maxFrames unset indexITER unset minINDEX minFrames=0 maxFrames=0 indexITER=0 minINDEX=0 for index in ${streamNUMBERS[@]} do SUBINDEX=$(expr $index - ${streamNUMBERS[0]}) currFrames=$( $FFBIN/ffprobe -loglevel error -select_streams s:$SUBINDEX \ -show_entries stream_tags=NUMBER_OF_FRAMES-eng -of csv=p=0 \ "$tmpDIR/$fileNAME") if [ $indexITER -lt 1 ] then minFrames=$currFrames maxFrames=$currFrames minINDEX=$index let ++indexITER elif [ $currFrames -lt $minFrames ] then minFrames=$currFrames minINDEX=$index let ++indexITER elif [ $currFrames -gt $maxFrames ] then maxFrames=$currFrames let ++indexITER fi done unset SUBTITLEINDEX SUBTITLEINDEX=$(expr $minINDEX - ${streamNUMBERS[0]}) #TEST FRAMES IN SUB TRACK, IF < 15% MAX MOST LIKELY ISN'T FOR. AUD. #15% as LOTR dir. comm included, blows up the max number for them... currFrames=$( $FFBIN/ffprobe -loglevel error -select_streams s:$SUBTITLEINDEX \ -show_entries stream_tags=NUMBER_OF_FRAMES-eng -of csv=p=0 \ "$tmpDIR/$fileNAME") if [ $( echo "($currFrames / $maxFrames) < 0.25"|bc -l ) -gt 0 ] then echo "BURNING STREAM $SUBTITLEINDEX (STREAM $minINDEX) from $fileNAME" burnSubs "$tmpDIR/$fileNAME" "$workDIR/$fileNAME" $SUBTITLEINDEX else echo "MIN. SUB TRACK ($SUBTITLEINDEX [$minINDEX])) DUR. > 25% FILM, NOT BURNING" encode "$tmpDIR/$fileNAME" "$workDIR/$fileNAME" -1 fi else #TODO expand foreign scan for more than pgs subtitles Need ass and #subtitles filters; don't know how to differentiate at present time. #I'm actually kinda missing a good example; I'm sure they're in there #but I don't know which ones they are lol; lmk if you know one. echo "NOT A PGS SUBTITLE TYPE; PASSING ALL THROUGH, FUTURE DEV." encode "$tmpDIR/$fileNAME" "$workDIR/$fileNAME" -1 fi fi else #ONE OR FEWER SUB TRACKS encode "$tmpDIR/$fileNAME" "$workDIR/$fileNAME" -1 fi if [ $? != 0 ] || [ $( stat -c%s "$workDIR/$fileNAME" ) -eq 0 ] then #RUN FAILED (EITHER NONZERO EXIT OR THE FILE IS 0 BYTES LARGE) if [ $( echo "$HostName"|tr [a-z] [A-Z] ) != "LOCALHOST" ] then ssh $HostName <<< "echo $( echo $filePATH | sed "s/[!@#$%^&*( ;)-]/\\\&/g" )>>$hostFile" else echo "$filePATH">>$hostFile fi let ++k echo "RUN ($fileNAME) FAILED ($k/60)" if [ $( stat -c%s "$workDIR/$fileNAME" ) -eq 0 ] then rm "$workDIR"/"$fileNAME" fi else #UPLOAD AND REMOVE THE TRANSCODED FILE if [ $( echo "$HostName"|tr [a-z] [A-Z] ) != "LOCALHOST" ] then #scp "$workDIR/$fileNAME" $HostName:"$( echo $filePATH | sed "s/[][!@#$%^&*( ;)]/\\\&/g" )" scp "$workDIR/$fileNAME" $HostName:"$filePATH" else rsync -a --progress "$workDIR/$fileNAME" "$filePATH" fi if [ $? != 0 ] then echo "UPLOAD OF $filePATH FAILED; EXITING" if [ $( echo "$HostName"|tr [a-z] [A-Z] ) != "LOCALHOST" ] then ssh $HostName <<< "echo $( echo $filePATH | sed "s/[!@#$%^&*( ;)-]/\\\&/g" )>>$hostFile" else echo "$filePATH">>$hostFile fi exit 1 else rm "$workDIR"/"$fileNAME" fi k=0 fi #REMOVE THE TEMP FILE (if necessary) if ls "$workDIR"/"$fileNAME" then rm "$workDIR"/"$fileNAME" else echo "Workdir already cleaned" fi if ls "$tmpDIR"/"$fileNAME" then rm "$tmpDIR"/"$fileNAME" elif ls "$tmpDIR"/"dummy.mkv" then rm "$tmpDIR"/"dummy.mkv" else echo "Already removed temp. file(s)" fi else echo "OUT OF FILES!? (try number $j/50) ... sleeping 1 min" let ++j sleep 60 fi #Increment if required if [ $iterLim == "true" ] then let --numIter fi let ++i echo "Done LOOP NUMBER $i!" done exit 0