Announcement
I've posted a new tutorial thread in the Apple TV and Home Theater forum:
How-To: Automating DVD & Blu-Ray (Backup, Encoding & Tagging) for Mac OS X 10.6
I hoping this one will die a graceful death.![]()
Enjoy!
Part 3: Automating DVD Backup with FairMount, HandBrake and iTunes
Parts to this tutorial:
Part 1: Introduction
Part 2: Using AppleScript to Automate Ripping
Part 3: Using a Batch Script to Automate HandBrake
Part 4: TV Show Renamer Droplet
Part 5: Add to iTunes and Auto-tag Script
Using a Batch Script to Automate HandBrake
What you need to get started
To complete this tutorial you will need to download and install the following applications:
HandBrakeCLI
HandBrake is an open-source, GPL-licensed, multiplatform, multithreaded DVD to MPEG-4 converter, available for MacOS X, Linux and Windows. The Command Line Interface version of HandBrake is a lot faster and more flexible than the MacGui. Using scripting you can automate HandBrakeCLI to encode your files as a background process using the same parameters you'd set in the GUI.
Script Editor
Script Editor is located at /Applications/AppleScript/Script Editor.
Install HandBrakeCLI
- Download and open the HandBrakeCLI disk image.
- Next, drag the HandBrakeCLI application from the disk image to your applications folder.
Creating the HandBrakeCLI Batch Script:
This shell script will batch encode an entire directory full of TS_Video source media to any desired output HandBrakeCLI can handle, including presets. It will also encode every track that is longer than the length you specify in the batch encode script, which is a great time saver for batch encoding TV Shows.
- Open TextEdit and create a new PLAIN TEXT document.
- Next, select, copy and paste the shell script below into your text file.
Code:#!/bin/sh # UPDATED: 01-22-2009 # HandBrakeCLI-batch.sh is a script to batch execute the HandBrakeCLI. # This script is based upon the MediaForkCLI-batch.sh. # Usage: HandBrakeCLI-batch.sh [options] # # arg1: inputSearchDir=<string> Set input directory to process all DVDs in it (example: ~/Movies/Batch Rip) " # arg2: outputDir=<string> Set output directory for all output files (example: ~/Movies/Batch Encode) " # arg3: fileType=<string> Set the file type: .mp4, .avi, .omg, .mkv, .m4v (example: .m4v) " # arg4: minTrackTime=<number> Set the minimum time (mins) of the track/s to encode (example: 30) " # arg5: toolArgs=<string> Set the HandBrakeCLI encode settings (example: -2 -w 640) " # Revision history: # 0.20070329.0 - initial release # 0.20081125.0 - revised for HandBrakeCLI # 0.20090122.0 - fixed problem with spaces in the directory tree ############################################################################# # globals # const global variables scriptName=`basename "$0"` scriptVers="2.0" scriptPID=$$ saveDVDinfo=1 # the dvd track info is also saved as txt file when on skipDuplicates=1 # if this option is off (-0), # the new output files will overwrite existing files E_BADARGS=65 # set the global variables to default toolName="HandBrakeCLI" toolPath="/Applications/$toolName" toolTrackArgs="-t 0" # scans dvd title list for all tracks inputSearchDir=$1 # set input search directory: "~/Movies/Batch Rip" outputDir="$2" # set output directory: "~/Movies/Batch Encode" fileType="$3" # set output container: .m4v, .mp4, .avi, .omg, .mkv minTrackTime="$4" # this is in minutes: "30" toolArgs="$5" # set handbrakecli encode settings: "-2 -w 640" ############################################################################# # functions 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 " arg1: inputSearchDir=<string> Set input directory to process all DVDs in it (example: ~/Movies/Batch Rip) " echo " arg2: outputDir=<string> Set output directory for all output files (example: ~/Movies/Batch Encode) " echo " arg3: fileType=<string> Set the file type: .mp4, .avi, .omg, .mkv, .m4v (example: .m4v) " echo " arg4: minTrackTime=<number> Set the minimum time (mins) of the track/s to encode (example: 30) " echo " arg5: toolArgs=<string> Set the HandBrakeCLI encode settings (example: -2 -w 640) " if [ -x "$toolPath" ]; then echo " $toolName possible options" mForkHelp=`$toolPath --help 2>&1` mForkHelpPt=`printf "$mForkHelp" | egrep -v '( --input| --output| --help|Syntax: |^$)'` printf "$mForkHelpPt\n" else echo " The options available to HandBrakeCLI except -o and -i" if [ -e "$toolPath" ]; then echo " ERROR: $toolName command tool is not setup 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 install 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 then 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 # fix input and output paths to be full paths inputSearchDir=`makeFullPath "$inputSearchDir"` outputDir=`makeFullPath "$outputDir"` # 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 displayUsageExit fi # display the basic setup information echo "$scriptName $scriptVers" echo " Start: `date`" echo " Input directory: $inputSearchDir" echo " Output directory: $outputDir" echo " File type: $fileType" echo " Minimum get track time: $minTrackTime mins" echo " Tool path: $toolPath" echo " Tool args: $toolArgs" echo " - - - - - - - - - - - - - - - -" # find all the DVD videos in the input search directory tree # spaces in file path temporarily become /008 and paths are separ 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 then 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}$fileType" else outputFilePath="$outputDir/${dvdName}$fileType" fi cmd="$toolPath -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 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 # End Script
- Save the file in your ~/Movies/BatchScripts folder as: HandBrakeCLI-batch.sh. Save using the .sh extension.
- Next we need to set the file permissions, open Terminal.app, type: chmod 777 ~/Movies/BatchScripts/HandBrakeCLI-batch.sh
- Hit enter. Alternatively, you can just drag the file to the terminal window after the chmod 777 with a space before the path.
Creating Your Batch Encode Script:
This AppleScript can be triggered by an iCal Alarm to run the HandBrakeCLI-batch.sh file with defined encoding parameters for HandBrakeCLI.
- Select, copy and paste the script below into a new Script Editor document.
Code:---------- BatchEncode.scpt ---------- ---------- Updated: 01-22-2009 ---------- global batchScript1, batchScript2 ---------- Properties ---------- -- The input directory to process all DVDs (TS Folders) in it. Default is: ~/Movies/BatchRip. This folder will be automatically created on first run, but the script will fail since there isn't anything in the folder. property inputPath1 : "~/Movies/BatchRip" -- The output directory for all output files. Default is: ~/Movies/BatchEncode. This folder will be automatically created on first run. property outputPath : "~/Movies/BatchEncode" -- The path to your HandBrakeCLI-batch.sh script. Default is: ~/Movies/BatchScripts/HandBrakeCLI-batch.sh property scriptPath : "~/Movies/BatchScripts/HandBrakeCLI-batch.sh" -- Insert the path to HandBrakeCLI-batch.sh. -- Set your HandBrakeCLI encode settings. Default is the Universal preset, HandBrake's universally compatible, full resolution settings for all current Apple devices: iPod, iPhone, AppleTV, and Macs). For more info in setting parameters, visit http://handbrake.fr and read the CLI Guide Wiki. property encodeSettings : "--preset=\"Universal\"" -- Note: you must escape quotes for presets. -- Set the minimum time (minutes) of the track/s to encode (default: 20) property minGetTime : "20" ---------- Optional Properties ---------- -- Dual-Encode: If you have a fast multi-core processor and hard disk, you can setup two batch folders to encode two RIPs at once. property batchFolders : 1 -- If adding a second input directory, change number of batch folders to 2. property inputPath2 : "~/Movies/BatchRip2" -- Set second input directory to process all DVDs in it. Default is: ~/Movies/BatchRip2. This folder will be automatically created if the batchFolders property is set to 2. -- Output Container: This script's default output container is .m4v(mp4). HandBrake can also save files in: .mkv, .avi, and .ogm. property fileType : ".m4v" -- Setup Growl Support: Set useGrowl to yes and set the path to the GrowlNotifyLib.scpt. property useGrowl : no -- If you have growl, set to yes. property growlTitle : "Batch Encode" property growlMessage : "Your HandBrake Encodes Are Done!" property growlLibraryPath : POSIX file "Users/userloginname/Movies/BatchScripts/GrowlNotifyLib.scpt" -- Set the path to the GrowlNotifyLib.scpt. ---------- Encode Script ---------- try if useGrowl is yes then setGrowl(growlTitle) of (load script growlLibraryPath) end try try createBatchScript() mkDir(inputPath1) & mkDir(outputPath) if batchFolders is 2 then mkDir(inputPath2) with timeout of 36000 seconds doScript() growlMe(growlTitle, growlMessage) of (load script growlLibraryPath) end timeout end try ---------- Sub-routines ---------- to doScript() tell application "Terminal" activate tell application "System Events" to keystroke "n" using {command down} if batchFolders is 2 then repeat 1 times tell application "System Events" to keystroke "t" using {command down} end repeat do script batchScript1 in tab 1 of window 1 if batchFolders is 2 then do script batchScript2 in tab 2 of window 1 repeat until tab 1 of window 1 is not busy delay 4 end repeat if batchFolders is 2 then repeat until tab 2 of window 1 is not busy delay 4 end repeat end tell end doScript to setInput(batchFolder) set inputDir to quoted form of batchFolder end setInput to createBatchScript() set spc to " " set inputDir1 to setInput(inputPath1) set inputDir2 to setInput(inputPath2) set logFile to " | tee " & escapePath(outputPath) set outputDir to quoted form of outputPath set batchScript1 to escapePath(scriptPath) & spc & inputDir1 & spc & outputDir & spc & quoted form of fileType & spc & quoted form of minGetTime & spc & quoted form of encodeSettings & logFile & "/BatchEncode.log" set batchScript2 to escapePath(scriptPath) & spc & inputDir2 & spc & outputDir & spc & quoted form of fileType & spc & quoted form of minGetTime & spc & quoted form of encodeSettings & logFile & "/BatchEncode2.log" end createBatchScript on escapePath(outputPath) set the search_string to " " set the replacement_string to "\\ " set AppleScript's text item delimiters to the " " set the text_item_list to every text item of the outputPath set AppleScript's text item delimiters to the replacement_string set the new_item_name to the text_item_list as string set AppleScript's text item delimiters to "" set the outputPath to new_item_name end escapePath on mkDir(somepath) do shell script "mkdir -p " & escapePath(somepath) end mkDir ---------- End Script ---------
- Choose Script > Compile (CMD-K)
- Follow the steps in the script to set the properties, if needed.
- Save the script in your ~/Movies/BatchScripts folder as "batchEncode.scpt"
Setting An iCal Batch Encode Alarm
- Create a Calendar and name it (ex. Scripts)
- Select your new calender, create a New Event and name it (ex. Encode Alarm)
- Set the Start Time to the time you'd like your encodes to begin (ex. 12:00AM)
- Set the End Time to the Start Time + 1 minute (ex. 12:01AM)
- Set Repeat to "Every Day" (if you want it to run every night)
- Set End to "Never"
- Set Alarm to "Run Script"
- Below "Run Script", change "None" to "Other" and navigate to select your batchEncode.scpt
- Set Notification to "0 minutes before"
- Click Done.
Note: If you sync your iCal calendars with other Macs, you will need to select the calendar containing your Batch Encode Alarm on the other Macs and choose File > Get Info > and check the box next to "Ignore Alarms".
Go To Part 4: TV Show Renamer Droplet