BrettSheleski / comchap

Commercial detection script to add chapters into video file
MIT License
138 stars 26 forks source link

handbrakecli post-processing #21

Closed nambrosch closed 6 years ago

nambrosch commented 6 years ago

i updated comcut to include handbrakecli post-processing, any objections if i submitted these updates to your project? the reason is mainly for decombing and compressing. this would be disabled by default and require a command line argument to enable.

BrettSheleski commented 6 years ago

I would rather keep these tools as standalone tools that do one job only.

What would make sense to me is instead of writing the resulting video to a file, instead provide a way to output to stdout. Therefore it could be piped in to other tools like handbrake or whatever you want.

I am curious how you did what you did though.

Perhaps you could update the readme.md file as a usage example instead.

nambrosch commented 6 years ago

this is the first time i've touched plex post-processing so i'm not familiar with the capabilities of any tools that people normally use. if using a pipe is more efficient then i could look into that.

here's what i have so far, i did a little cleanup (sorry) and added the handbrake bits in there. i'll test it with plex tomorrow but i forsee it working fine.

to test:

$ time sh ./comcut-new --use-handbrake video_file.ts video_file_processed.mp4

and the script:

#!/usr/bin/env bash

# LD_LIBRARY_PATH is set and will mess up ffmpeg, unset it, then re-set it when done
ldPath=${LD_LIBRARY_PATH}
unset LD_LIBRARY_PATH

exitcode=0

comskipPath="comskip"
ffmpegPath="ffmpeg"
handbrakePath="HandBrakeCLI"

if [[ $# -lt 1 ]]; then
  exename=$(basename "$0")

  echo "Remove commercial from video file using EDL file"
  echo "     (If no EDL file is found, comskip will be used to generate one)"
  echo ""
  echo "Usage: $exename infile [outfile]"

  exit 1
fi

comskipini=$HOME/.comskip.ini

deleteedl=true
deletelog=true
deletelogo=true
deletemeta=true
deletetxt=true
lockfile=""
usehandbrake=false
workdir=""

while [[ $# -gt 1 ]]; do
  key=$1
  case $key in
    --comskip=*)
      comskipPath="${key#*=}"
      shift
      ;;
    --comskip-ini=*)
      comskipini="${key#*=}"
      shift
      ;;
    --ffmpeg=*)
      ffmpegPath="${key#*=}"
      shift
      ;;
    --handbrake=*)
      handbrakePath="${key#*=}"
      shift
      ;;
    --keep-edl)
      deleteedl=false
      shift
      ;;
    --keep-meta)
      deletemeta=false
      shift
      ;;
    --lockfile=*)
      lockfile="${key#*=}"
      shift
      ;;
    --use-handbrake)
      usehandbrake=true
      shift
      ;;
    --work-dir=*)
      workdir="${key#*=}"
      shift
      ;;
    *)
      break
      ;;
  esac
done

# make sure comskip exists
if [[ ! $(type "$comskipPath") ]]; then
  "error: ${comskipPath} does not exist, exiting"
  exit 1
fi

# make sure ffmpeg exists
if [[ ! $(type "$ffmpegPath") ]]; then
  "error: ${ffmpegPath} does not exist, exiting"
  exit 1
fi

# make sure handbrakecli exists (if enabled)
if [[ ! $(type "$handbrakePath") && $usehandbrake ]]; then
  "error: ${handbrakePath} is not executable, exiting"
  exit 1
fi

if [[ ! -z "$lockfile" ]]; then
  echo "lockfile: $lockfile"
  while [[ -f "$lockfile" ]]; do
    echo "Waiting"
    sleep 5
  done

  touch "$lockfile"
fi

if [[ ! -f "$comskipini" ]]; then
  echo "output_edl=1" > "$comskipini"
elif [[ ! $(grep -q "output_edl=1" "$comskipini") ]]; then
  echo "output_edl=1" >> "$comskipini"
fi

infile=$1
outfile=$infile

if [[ -z "$2" ]]; then
  outfile="$infile"
else
  outfile="$2"
fi

outdir=$(dirname "$outfile")

outextension="${outfile##*.}"
comskipoutput=""

if [[ ! -z "$workdir" ]]; then
  case "$workdir" in
    */)
      ;;
    *)
      comskipoutput="--output=$workdir"
      workdir="$workdir/"
      ;;
  esac
fi

edlfile="$workdir${infile%.*}.edl"
metafile="$workdir${infile%.*}.ffmeta"
logfile="$workdir${infile%.*}.log"
logofile="$workdir${infile%.*}.logo.txt"
txtfile="$workdir${infile%.*}.txt"

if [[ ! -f "$edlfile" ]]; then
  $comskipPath $comskipoutput --ini="$comskipini" "$infile"
fi

start=0
i=0
hascommercials=false

concat=""

tempfiles=()
totalcutduration=0

echo ";FFMETADATA1" > "$metafile"

# Reads in from $edlfile, see end of loop.
while IFS=$'\t' read -r -a line; do
  end="${line[0]}"
  startnext="${line[1]}"

  if [ `echo "$end" | awk '{printf "%i", $0 * 1000}'` -gt `echo "$start" | awk '{printf "%i", $0 * 1000}'` ]; then
    ((i++))

    hascommercials=true

    echo [CHAPTER] >> "$metafile"
    echo TIMEBASE=1/1000 >> "$metafile"
    echo START=`echo "$start  $totalcutduration" | awk  '{printf "%i", ($1 - $2) * 1000}'` >> "$metafile"
    echo END=`echo "$end $totalcutduration" | awk  '{printf "%i", ($1 - $2) * 1000}'` >> "$metafile"
    echo "title=Chapter $i" >> "$metafile"

    chapterfile="${infile%.*}.part-$i.ts"

    if [[ ! -z "$workdir" ]]; then
      chapterfile=`basename "$chapterfile"`
      chapterfile="$workdir$chapterfile"
    fi

    tempfiles+=("$chapterfile")
    concat="$concat|$chapterfile"

    duration=`echo "$end" "$start" | awk  '{printf "%f", $1 - $2}'`
    $ffmpegPath -hide_banner -loglevel error -nostdin -i "$infile" -ss "$start" -t "$duration" -c copy -y "$chapterfile"

    totalcutduration=`echo "$totalcutduration" "$startnext" "$end" | awk  '{print $1 + $2 - $3}'`

  fi

  start=$startnext
done < "$edlfile"

if $hascommercials ; then

  #dont forget to add the final part from last commercial to end of file
  end=`$ffmpegPath -hide_banner -nostdin -i "$infile" 2>&1 | grep Duration | awk '{print $2}' | tr -d , | awk -F: '{ printf "%f", ($1*3600)+($2*60)+$3 }'`

  if [ `echo "$end" | awk '{printf "%i", $0 * 1000}'` -gt `echo "$start" | awk '{printf "%i", $0 * 1000}'` ]; then

    ((i++))

    echo [CHAPTER] >> "$metafile"
    echo TIMEBASE=1/1000 >> "$metafile"
    echo START=`echo "$start  $totalcutduration" | awk  '{printf "%i", ($1 - $2) * 1000}'` >> "$metafile"
    echo END=`echo "$end $totalcutduration" | awk  '{printf "%i", ($1 - $2) * 1000}'` >> "$metafile"
    echo "title=Chapter $i" >> "$metafile"

    chapterfile="${infile%.*}.part-$i.ts"

    if [[ ! -z "$workdir" ]]; then
      chapterfile=`basename "$chapterfile"`
      chapterfile="$workdir$chapterfile"
    fi

    tempfiles+=("$chapterfile")
    concat="$concat|$chapterfile"

    duration=`echo "$end" "$start" | awk  '{printf "%f", $1 - $2}'`
    $ffmpegPath -hide_banner -loglevel error -nostdin -i "$infile" -ss "$start" -t "$duration" -c copy -y "$chapterfile"
  fi

  $ffmpegPath -hide_banner -loglevel error -nostdin -i "$metafile" -i "concat:${concat:1}" -c copy -map_metadata 0 -y "pre-${outfile}"
fi

# if we're cleaning up the video using handbrake
if [[ $usehandbrake ]]; then
  echo;echo "Encoding video with HandbrakeCLI..."
  $handbrakePath --preset "Apple 1080p30 Surround" --encoder-preset medium -i "pre-${outfile}" -o "${outfile}"
  rm -f "pre-${outfile}"
else
  mv "pre-${outfile}" "${outfile}"
fi

for i in "${tempfiles[@]}"; do
  rm -f "$i"
done

if [[ $deleteedl ]]; then
  rm -f "$edlfile";
fi

if [[ $deletemeta ]]; then
  rm -f "$metafile";
fi

if [[ $deletelog ]]; then
  rm -f "$logfile";
fi

if [[ $deletelogo ]]; then
  rm -f "$logofile";
fi

if [[ $deletetxt ]]; then
  rm "$txtfile";
fi

if [[ ! -z $ldPath ]]; then
  # re-set LD_LIBRARY_PATH
  export LD_LIBRARY_PATH="$ldPath"
fi

if [[ ! -z "$lockfile" ]]; then
  rm -f "$lockfile"
fi
BrettSheleski commented 6 years ago

Unfortunately I'm not going to add that to Comchap/Comcut. As stated previously Comchap/Comcut are to be a standalone tools to do one thing (flagging/removing commercials).

I understand both scripts can be used directly as a Plex post-processing script, but it is not limited to that. Adding this as suggested would introduce a new dependency to the project as well. Also, the quality settings for calling handbrake are hard-coded, making it difficult for someone to re-encode it to their liking.

If one wants to re-encode the video I'd suggest making a wrapper-script that does it instead. Something like the following:

recording = "$1"

comcut "$recording"
handbrakecli --preset "Apple 1080p30 Surround" --encoder-preset medium -i "$recording" -o "encoded-$recording"

rm "$recording"
mv "encoded-$recording" "$recording"

Then one can setup Plex to call this wrapper-script instead of calling Comchap/Comcut directly. This also makes it easier for the user to change their desired target quality.