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