#!/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
HostName="Clementine" #if localhost then will do locally (removes ssh/scp cmds)
hostFile="/tmp/transcode/list.txt"
tmpDIR="$PWD/tmpTranscode"
workDIR="$PWD/transcode"

#DEFINE FUNCTIONS
function encode {
  local tmpFL=$1
  local outFL=$2
  ffmpeg -hide_banner -loglevel error -stats -re -i "$tmpFL" -map 0 \
          -map_metadata 0 -c copy \
          -c:v libx265 -preset slow -x265-params crf=23 \
          -use_wallclock_as_timestamps 1 \
          -map_chapters 0 -max_muxing_queue_size 9999 "$outFL"
}

function burnSubs {
  local inFL=$1
  local tmpFL=$2
  local outFL=$3
  local track=$4

  encode "$inFL" "$tmpFL"

  rm "$inFL" #SAVE SPACE

  #EVIDTNELY NECESSARY SO THAT FILES AREN'T 198+ HRS IN LEN. AT THE END
  local DURATION=$( ffprobe -loglevel error -show_entries format=duration \
                -of default=noprint_wrappers=1:nokey=1 "$tmpFL" )

  ffmpeg -hide_banner -loglevel error -stats -i "$tmpFL" \
          -filter_complex "[0:v][0:s:$track]overlay[v]" -map "[v]" \
          -map 0:a -c:a copy -map 0:s -map -0:s:$track -c:s copy \
          -map_metadata 0 -map_chapters 0 -max_muxing_queue_size 9999 \
          -t $DURATION "$outFL"
}

#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 ffprobe &> /dev/null
then
  echo "Need to have ffprobe (and ffmpeg) installed"
  exit 1
fi
if ! command -v 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 $( 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"
      else
        #TEST SUBTITLE TYPE; IF NOT PGS SKIP IT; FEEL FREE TO FILL IN
        if grep -qi "pgs" <<< $( 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=$( 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=$( 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.15"|bc -l ) -gt 0 ]
          then
            echo "BURNING STREAM $SUBTITLEINDEX (STREAM $minINDEX) from $fileNAME"
            burnSubs "$tmpDIR/$fileNAME" "$tmpDIR/TMP$fileNAME" "$workDIR/$fileNAME" $SUBTITLEINDEX
          else
            echo "MIN. SUB TRACK ($SUBTITLEINDEX [$minINDEX])) DUR. > 15% FILM, NOT BURNING"
            encode "$tmpDIR/$fileNAME" "$workDIR/$fileNAME"
          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"
        fi
      fi
    else
      #ONE OR FEWER SUB TRACKS
      encode "$tmpDIR/$fileNAME" "$workDIR/$fileNAME"
    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" )"
      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"/"TMP$fileNAME"
    then
      rm "$tmpDIR"/"TMP$fileNAME"
    else
      echo "Already removed $tmpDIR/$fileNAME?"
    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