#!/bin/sh
# HandBrakeCLI-batch.sh is a script to batch execute the HandBrakeCLI
# Copyright (C) 2007 Brian Beardmore
# Copyright (C) 2008 Chris Pepper
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# This script requires HandBrakeCLI .
# This script searches the entire input directory directory tree looking
# for TS_VIDEO directories looking for DVDs to encode. The output mp4
# filename is the DVD volume name. When multiple tracks are to encoded
# from a DVD, the output filename is appended with the track number.
# For example, 'Monsters, Inc.' DVD has both standard and wide screen tracks
# so there are 'Monsters, Inc.1.mp4' & 'Monsters, Inc.10.mp4' files created.
# For addition information on this script contact the script's original author,
# Brian Beardmore at medfk(at)realisticsoftware(dot)com or
# http://www.realisticsoftware.com
# Or contact Chris Pepper .
# Examples
# $ ./appletv.sh
# [ encodes all DVDs mounted and encodes any tracks longer than 60min ]
# [ to the mp4 folder in the users Movies folder ]
# $ appletv.sh -i ~/Movies -o toPod -b 1000 -w 576 --minGetTime 50
# [ encodes all DVDs found in the users Movies folder and encodes any ]
# [ tracks longer than 50min, with a 1000kb/s bitrate and scales the output ]
# [ movie to a width of 576 pixels and puts the resulting mp4 files in the ]
# [ toPod folder in the current directory that the command was executed from ]
# Syntax: HandBrakeCLI [options] -i -o
#
# ### General Handbrake Options------------------------------------------------
#
# -h, --help Print help
# -u, --update Check for updates and exit
# -v, --verbose Be verbose
# -C, --cpu Set CPU count (default: autodetected)
# -Z. --preset Use a built-in preset. Capitalization matters, and
# if the preset name has spaces, surround it with
# double quotation marks
# -z, --preset-list See a list of available built-in presets
#
# ### Source Options-----------------------------------------------------------
#
# -i, --input Set input device
# -t, --title Select a title to encode (0 to scan only,
# default: 1)
# -L, --longest Select the longest title
# -c, --chapters Select chapters (e.g. "1-3" for chapters
# 1 to 3, or "3" for chapter 3 only,
# default: all chapters)
#
# ### Destination Options------------------------------------------------------
#
# -o, --output Set output file name
# -f, --format Set output format (avi/mp4/ogm/mkv, default:
# autodetected from file name)
# -4, --large-file Use 64-bit mp4 files that can hold more than
# 4 GB. Note: Breaks iPod, @TV, PS3 compatibility.
# -O, --optimize Optimize mp4 files for HTTP streaming
# -I, --ipod-atom Mark mp4 files so iPods will accept them
#
# ### Picture Settings---------------------------------------------------------
#
# -w, --width Set picture width
# -l, --height Set picture height
# --crop Set cropping values (default: autocrop)
# -Y, --maxHeight <#> Set maximum height
# -X, --maxWidth <#> Set maximum width
# -s, --subtitle Select subtitle (default: none)
# -U, --subtitle-scan Scan for subtitles in an extra 1st pass, and choose
# the one that's only used 10 percent of the time
# or less. This should locate subtitles for short
# foreign language segments. Best used in conjunction
# with --subtitle-forced.
# -F, --subtitle-forced Only display subtitles from the selected stream if
# the subtitle has the forced flag set. May be used in
# conjunction with --subtitle-scan to auto-select
# a stream if it contains forced subtitles.
# -N, --native-language Select subtitles with this language if it does not
# match the Audio language. Provide the language's
# iso639-2 code (fre, eng, spa, dut, et cetera)
# -m, --markers Add chapter markers (mp4 output format only)
#
# ### Video Options------------------------------------------------------------
#
# -e, --encoder Set video library encoder (ffmpeg,xvid,
# x264,x264b13,x264b30 default: ffmpeg)
# -q, --quality Set video quality (0.0..1.0)
# -Q, --cqp Use with -q for CQP instead of CRF
# -S, --size Set target size
# -b, --vb Set video bitrate (default: 1000)
# -r, --rate Set video framerate (5/10/12/15/23.976/24/25/29.97)
#
# -2, --two-pass Use two-pass mode
# -d, --deinterlace Deinterlace video with yadif/mcdeint filter
# (default 0:-1:-1:1)
# or
#
# -7, --deblock Deblock video with pp7 filter
# (default 0:2)
# -8, --denoise Denoise video with hqdn3d filter
# (default 4:3:6:4.5)
# or
#
# -9, --detelecine Detelecine video with pullup filter
# (default 1:1:4:4:0:0)
# -g, --grayscale Grayscale encoding
# -p, --pixelratio Store pixel aspect ratio in video stream
# -P, --loosePixelratio Store pixel aspect ratio with specified width
# Takes as optional argument what number you want
# the dimensions to divide cleanly by (default 16)
#
# ### Audio Options-----------------------------------------------------------
#
# -E, --aencoder Audio encoder (faac/lame/vorbis/ac3/aac+ac3)
# ac3 meaning passthrough, aac+ac3 meaning an
# aac dpl2 mixdown paired with ac3 pass-thru
# (default: guessed)
# -B, --ab Set audio bitrate (default: 128)
# -a, --audio Select audio channel(s), separated by commas
# ("none" for no audio, "1,2,3" for multiple
# tracks, default: first one,
# max 8 normally, max 4 with aac+ac3)
# -6, --mixdown Format for surround sound downmixing
# (mono/stereo/dpl1/dpl2/6ch, default: dpl2)
# -R, --arate Set audio samplerate (22.05/24/32/44.1/48 kHz)
# -D, --drc Apply extra dynamic range compression to the audio,
# making soft sounds louder. Range is 1.0 to 4.0
# (too loud), with 1.5 - 2.5 being a useful range.
#
# ### Advanced Options---------------------------------------------------------
#
# -x, --x264opts Specify advanced x264 options in the
# same style as mencoder:
# option1=value1:option2=value2
# -T, --turbo When using 2-pass use the turbo options
# on the first pass to improve speed
# (only works with x264, affects PSNR by about 0.05dB,
# and increases first pass speed two to four times)
# -V, --vfr Perform variable framerate detelecine on NTSC video
# Revision history:
# 1.0.3, 2008/05 -- Add basic argument processing, so "appletv" and "iphone" as argument #1 produce different output.
# 1.0.2, 2008/05 -- hacks by Pepper to use HandBrakeCLI and optimize for AppleTV.
# 1.0.1, 2008/02/24 -- hacks by Pepper to use HandBrakeCLI and optimize for iPhone.
# 0.20070329.0 - initial release
#############################################################################
# globals
# const global variables
scriptName=`basename "$0"`
scriptVers="1.0.2"
scriptPID=$$
saveDVDinfo=1 # the dvd track info is also saved as txt file when on
skipDuplicates=1 # if this option is off,
# the new output files will overwrite existing files
E_BADARGS=65
# set the global variables to default
toolName="HandBrakeCLI"
toolPath="$HOME/bin/$toolName"
toolTrackArgs="-t 0"
toolArgs="-v" # also of interest -v
inputSearchDir="$HOME/tivo-inspector/input"
outputDir="$HOME/tivo-inspector/output"
minTrackTime="3" # this is in minutes
#### Recognize format argument, 2008/05
if [[ $# = 0 ]]
then myArgs="$HANDBRAKE_ARGS -2 -T -w 720 -p -b 2400 -V -I -N eng -6 stereo -E faac -f mp4 -m -e x264 -x bframes=3:ref=1:subq=5:me=umh:no-fast-pskip=1:trellis=1:cabac=0"
elif [[ $1 = "iphone" ]]
then myArgs="$HANDBRAKE_ARGS -2 -T -w 480 -b 960 -V -I -N eng -6 stereo -E faac -f mp4 -m -e x264 -x level=30:keyint=300:keyint-min:30:bframes=0:cabac=0:ref=1:vbv-maxrate=1500:vbv-bufsize=2000:analyse=all:me=umh:subq=6:no-fast-pskip=1"
elif [[ $1 = "iPhone" ]]
then myArgs="$HANDBRAKE_ARGS -2 -T -w 480 -b 960 -V -I -N eng -6 stereo -E faac -f mp4 -m -e x264 -x level=30:keyint=300:keyint-min:30:bframes=0:cabac=0:ref=1:vbv-maxrate=1500:vbv-bufsize=2000:analyse=all:me=umh:subq=6:no-fast-pskip=1"
elif [[ $1 = "appletv" ]]
then myArgs="$HANDBRAKE_ARGS -2 -T -w 720 -p -b 2400 -V -I -N eng -6 stereo -E faac -f mp4 -m -e x264 -x bframes=3:ref=1:subq=5:me=umh:no-fast-pskip=1:trellis=1:cabac=0"
elif [[ $1 = "AppleTV" ]]
then myArgs="$HANDBRAKE_ARGS -2 -T -w 720 -p -b 2400 -V -I -N eng -6 stereo -E faac -f mp4 -m -e x264 -x bframes=3:ref=1:subq=5:me=umh:no-fast-pskip=1:trellis=1:cabac=0"
else
echo "Unknown format -- aborting!"
exit 1
fi
# Pepper's sometime options:
# -s 1 # first subtitle
# -a 2 # second audio track ??
# For example (best within screen): HANDBRAKE_ARGS="-s1 -t11" iphone.sh
#############################################################################
# functions
parseProcessInArgs()
{
if [ -z "$1" ]; then
return
fi
toolArgs=""
while [ ! -z "$1" ]
do
case "$1" in
-h) displayUsageExit ;;
--help) displayUsageExit ;;
-i) inputSearchDir=$2
shift ;;
--input) inputSearchDir=$2
shift ;;
-o) outputDir=$2
shift ;;
--output) outputDir=$2
shift ;;
--minGetTime) minTrackTime=$2
shift ;;
*) toolArgs="$toolArgs $1" ;;
esac
shift
done
}
verifyFindCLTool()
{
# attempt to find the HandBrakeCLI if the script toolPath is not good
if [ ! -x "$toolPath" ];
then
toolPathTMP=`PATH=.:/Applications:/:/usr/bin:/usr/local/bin:$HOME:$PATH which $toolName | sed '/^[^\/]/d' | sed 's/\S//g'`
if [ ! -z $toolPathTMP ]; then
toolPath=$toolPathTMP
fi
fi
}
displayUsageExit()
{
echo "Usage: $scriptName [options]"
echo ""
echo " -h, --help Print help"
echo " -i, --input Set input directory to process all DVDs in it (default: /Volumes/)"
echo " -o, --output Set output directory for all output files (default: ~/Movies/mp4/)"
echo " --minGetTime Set the minimum time (mins) of the track/s to encode (default: 60)"
if [ -x "$toolPath" ];
then
echo " $toolName possible options"
hBrakeHelp=`$toolPath --help 2>&1`
hBrakeHelpPt=`printf "$hBrakeHelp" | egrep -v '( --input| --output| --help|Syntax: |^$)'`
printf "$hBrakeHelpPt\n"
else
echo " The options available to HandBrakeCLI except -o and -i"
if [ -e "$toolPath" ];
then
echo " ERROR: $toolName command tool is not set up to execute"
echo " ERROR: attempting to use tool at $toolPath"
else
echo " ERROR: $toolName command tool could not be found"
echo " ERROR: $toolName can be installed in ./ /usr/local/bin/ /usr/bin/ ~/ or /Applications/"
fi
fi
echo ""
exit $E_BADARGS
}
getTrackListLongerThan()
{
# Two input arguments are are need.
# arg1 is the time in minutes selector
# arg2 is the raw text stream from the track 0 call to HandBrake
# returns: a list of track numbers of tracks longer than the selector
if [ $# -lt 2 ]; then
return ""
fi
minTime="$1"
shift
allTrackText="$*"
aReturn=""
trackList=`eval "echo \"$allTrackText\" | egrep '(^\+ title |\+ duration\:)' | sed -e 's/^[^+]*+ //'g -e 's/title \([0-9]*\):/\1-/'g -e 's/duration: //'g"`
trackNumber=""
for aline in $trackList
do
trackLineFlag=`echo $aline | sed 's/[0-9]*-$/-/'`
if [ $trackLineFlag = "-" ];
then
trackNumber=`echo $aline | sed 's/\([0-9]*\)-/\1/'`
else
set -- `echo $aline | sed -e 's/(^[:0-9])//g' -e 's/:/ /g'`
if [ $3 -gt 29 ];
then let trackTime=($1*60)+$2+1
else let trackTime=($1*60)+$2
fi
if [ $trackTime -gt $minTime ];
then aReturn="$aReturn $trackNumber"
fi
fi
done
echo "$aReturn"
}
makeFullPath()
{
aReturn=""
currentPath=`pwd`
if [ $# -gt 0 ]; then
inPath="$*"
# put full path in front of path if needed
aReturn=`echo "$inPath" | sed -e "s!~!$currentPath/!" -e "s!^./!$currentPath/!" -e "s!^\([^/]\)!$currentPath/\1!" -e "s!^../!$currentPath/../!"`
# remove ../ from path - only goes 4 deep
aReturn=`echo "$aReturn" | sed -e 's!/[^\.^/]*/\.\./!/!g' | sed -e 's!/[^\.^/]*/\.\./!/!g' | sed -e 's!/[^\.^/]*/\.\./!/!g' | sed -e 's!/[^\.^/]*/\.\./!/!g'`
# cleanup by removing //
aReturn=`echo "$aReturn" | sed -e 's!//!/!g'`
fi
echo "$aReturn"
}
isPIDRunning()
{
aResult=0
if [ $# -gt 0 ]; then
txtResult="`ps ax | egrep \"^[ \t]*$1\" | sed -e 's/.*/1/'`"
if [ -z "$txtResult" ];
then aResult=0
else aResult=1
fi
fi
echo $aResult
}
#############################################################################
# MAIN SCRIPT
# initialization functions
verifyFindCLTool
parseProcessInArgs $*
# see if the output directory needs to be created
if [ ! -e $outputDir ]; then
mkdir -p "$outputDir"
fi
# sanity checks
if [[ ! -x $toolPath || ! -d $inputSearchDir || ! -d $outputDir || -z "$toolArgs" ]]
then
if [[ ! -x $toolPath ]]
then echo "ERROR: $toolPath is not executable!"
fi
if [[ ! -d $inputSearchDir ]]
then echo "ERROR: $inputSearchDir is not a valid input directory!"
fi
if [[ ! -d $outputDir ]]
then echo "ERROR: $outputDir is not a valid output directory!"
fi
if [[ ! -z "$toolArgs" ]]
then echo "ERROR: $toolArgs is unset!"
fi
displayUsageExit
fi
# fix input and output paths to be full paths
inputSearchDir=`makeFullPath $inputSearchDir`
outputDir=`makeFullPath $outputDir`
# display the basic setup information
echo "$scriptName v$scriptVers"
echo " Start: `date`"
echo " Input directory: $inputSearchDir"
echo " Output directory: $outputDir"
echo " Minimum get track time: $minTrackTime mins"
echo " Tool path: $toolPath"
echo " Tool args: $toolArgs"
echo " My args: $myArgs"
echo " - - - - - - - - - - - - - - - -"
# find all the DVD videos in the input search directory tree
# spaces in file path temporarily become /008 and paths are separated with spaces
dvdTSVidList=`find $inputSearchDir -name VIDEO_TS -print0 | tr ' ' '\007' | tr '\000' ' '`
# process each DVD video found
for dvdTSDir in $dvdTSVidList
do
# correct the tmp char back to spaces in the DVD file paths
dvdTSDir=`echo $dvdTSDir | tr '\007' ' '`
# get the DVD's name and path to root of the DVD
dvdVolPath=`dirname "$dvdTSDir"`
dvdName=`basename "$dvdVolPath"`
dvdNameALNUM=`basename "$dvdVolPath" | sed 's/[^[:alnum:]^-^_]//g'`
# display information
echo " * Processing DVD '$dvdName'"
# create tmp link to the dvdVolPath to workaround a problem that the
# HandBrakeCLI tool has a problem with the input file paths with space
# when in a script
tmpNoSpacePath="/tmp/dvdVol-$dvdNameALNUM-$scriptPID"
ln -s "$dvdVolPath" $tmpNoSpacePath
# get the track list information from the DVD
cmd="$toolPath -i $tmpNoSpacePath $toolTrackArgs /dev/null 2>&1"
dvdTrackInfo=`eval $cmd`
# save the DVD info
outputFilePath="$outputDir/${dvdName}_Info.txt"
if [ $saveDVDinfo -eq 1 ]; then
if [[ ! -e $outputFilePath || skipDuplicates -eq 0 ]]; then
echo "$dvdTrackInfo" | egrep '[ \t]*\+' > "$outputFilePath"
fi
fi
# get the track number of tracks which are longer then the time desired
trackFetchList=`getTrackListLongerThan $minTrackTime "$dvdTrackInfo"`
if [ ! -z "$trackFetchList" ];
then
echo " Will encode the following tracks: `echo $trackFetchList | sed 's/ /, /g'` "
else
echo " No tracks on this DVD are longer than the minimum track time setting"
fi
trackCount=`echo $trackFetchList | wc -w`
for aTrack in $trackFetchList
do
if [ $trackCount -gt 1 ]
then outputFilePath="$outputDir/${dvdName}-${aTrack}.m4v"
else outputFilePath="$outputDir/${dvdName}.m4v"
# .m4v is important for AppleTV
fi
cmd="$toolPath $myArgs -i $tmpNoSpacePath $toolArgs -t $aTrack -o \"$outputFilePath\" > /tmp/${dvdNameALNUM}Results.txt 2>&1"
if [[ ! -e $outputFilePath || skipDuplicates -eq 0 ]];
then
# simple command execution
#ripResult=`eval $cmd`
# background command execution with some status
echo "Command is: $cmd"
eval $cmd &
cmdPID=$!
while [ `isPIDRunning $cmdPID` -eq 1 ]; do
cmdStatusTxt="`tail -n 1 /tmp/${dvdNameALNUM}Results.txt | grep 'Encoding: '`"
if [ ! -z "$cmdStatusTxt" ]; then
echo -n "$cmdStatusTxt"
fi
sleep 1s
done
echo ""
wait $cmdPID
else
echo " Output file SKIPPED because it ALREADY EXISTS"
fi
if [ -e /tmp/${dvdNameALNUM}Results.txt ]; then
rm /tmp/${dvdNameALNUM}Results.txt
fi
done
rm $tmpNoSpacePath
done
echo " - - - - - - - - - - - - - - - -"
echo " End: `date`"
exit 0