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