You no longer need Windows. Now, it can all be done on your Mac. Everything from fetching the files from the TiVo to the decrypt and playback can be accomplished on the Mac side. This all due to some great work by the guys on the TiVo Decode and Galleon projects. Let’s get started.
A short road map of where we’re going:
- Install Galleon.
- Configure the ToGo feature of Galleon to automatically download selected shows from the TiVo into a download folder.
- Create a script to decode any completed downloads and launch it with
launchd when a file is in the folder.
- Optionally create another script that uses VLC to transcode those decoded files.
This article discusses a method to automate the process of getting the files off the TiVo and into Front Row/iPod format. If you just want to convert a single file then just run tivodecode on the TiVo file and you’ll have an MPEG-2 file that VLC can play. If, however, you want to make a media center that does everything for you and keeps anything from one show to everything the TiVo records, then you should read on.
Note the updated scripts at the end of the article that resolve some issues with catching files in their varying states. These appear to get it right almost all of the time now.
Install and Setup Galleon
We’ve gone over that before, though take note of their new address when downloading it. You can have it running on your Mac or on another machine; it doesn’t matter. What matters is having access to the downloaded files, so whip up a solution for that later on if you use a machine other than the decoding machine to perform this task.
Make the Destination Folders
We’ll need one folder for incoming files and one for decoded files. To make life easier, avoid spaces. If you use spaces, be careful later on with escaping them and using quotes.
If you are using another machine than the one doing the decoding, you’ll want to setup some network shares for remote access to these files. That ball’s in your court.
Setup Auto-Downloading in Galleon
- First enter your Media Access Key into File → Properties.
- Check the program’s
preferences properties and ensure the download folder for such things is the temporary folder, not the final destination.
- Now go into the File → ToGo menu and then to the Rules tab. Here, tell Galleon to automatically download shows matching some information.
Now Galleon should be polling the TiVo to pull the encrypted TiVo files to your local drive regularly. The problem now is that these are good only for sending back to the TiVo, not for watching at a later time on the computer.
You can go to your TiVo in Safari and pull down videos one-by-one as well. I recommend having at least one TiVo file for the rest of this to make sure things are working as you go. Your TiVo’s address is in the network settings area on the TiVo. You’ll need to connect over HTTPS and use the username “tivo” and your MAK as the password to do the actual downloading.
TiVo also supports Bonjour, so if you have that turned on in Safari, you should see the name of your TiVo in that list when you’re on the same network:

After selecting it from that menu, be sure to change the protocol to HTTPS instead of HTTP and login as detailed above.
Build TiVo Decode
Get the program from the project site and build it. It’s extremely simple, really. With the Developer Tools installed (if you’ve been following along on this site at all, they’re installed already) just go into the directory the tarball expanded into and run:
$ make
You’ll find the binary called tivodecode in a directory called objects.dir. Put the binary somewhere useful (I keep things like this in ~/bin).
Setup the Conversion Script
So now we want to look in a folder and decode all the files in that folder, placing them in a different folder. Fairly easy if we don’t want to make it safe. Since the files are on the TiVo still, I see no need to make it safe. The line of hashes is where you put your Media Access Key. It won’t work without it.
#!/bin/bash
SOURCE_DIR=/Users/Shared/TiVo/Incoming/
OUTPUT_DIR=/Users/Shared/TiVo/Converted/
cd “$SOURCE_DIR”
ls | while read FILE
do
if ( [ ! -N “$FILE” ] )
then
#echo Processing $FILE
~/bin/tivodecode -m ######## -o “$OUTPUT_DIR/$FILE.m2v” “$FILE”
rm “$FILE”
else
#echo Skipping $FILE
sleep 5
fi
done
Change the SOURCE_DIR and OUTPUT_DIR items as needed for your system. Remember to quote them if they include spaces.
The reason for the -N part is because Galleon downloads the file to the final destination, and launchd will start the script when the file is created, not finished downloading. The -N flag asks if the file has been modified since its last read access. If so, the script skips it. If not, it processes it. It works well to test for files in the process of being downloaded or copied.
The sleep wait is in there so that launchd will not start the script ten times a second while waiting for the file. There’s the chance that this will take a while to actually get started on the file, but no huge amount of time, and you can change it.
Uncomment the echo lines if you’re having problems and want to see what’s going on.
Setup Launchd
I love launchd. Here, we use the queue directory feature to have launchd start the above script whenever there’s a file to process in the folder. The script then processes and removes the file, so launchd won’t start it again. It’s genius.
Using Launchd Editor I made the following item:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.macgeekery.tivodecoder</string>
<key>Program</key>
<string>/Users/codepoet/bin/tivobatch.sh</string>
<key>ProgramArguments</key>
<array>
<string>/Users/codepoet/bin/tivobatch.sh</string>
</array>
<key>QueueDirectories</key>
<array>
<string>/Users/Shared/TiVo/Incoming/</string>
</array>
<key>ServiceDescription</key>
<string>This item watches a folder for TiVo files and decodes them into another folder.</string>
</dict>
</plist>
You will want to change the paths to match where you put things. The item in QueueDirectories is the incoming folder you made previously. It must match the one you put in the script above.
Save this file in ~/Library/LaunchAgents so it starts when you login and then load it with launchctl load [filename] to get it started (or logout/login).
Viewing
MPEG-2 streams are quite useless, all things considered. The only program on the Mac to support them easily is VLC, but it will display these files quite well if this is where you want to stop.
If, however, you want to get them into Front Row, you’ll need a QuickTime codec for that. It just so happens that Apple sells that for about $20 on the Apple Store so go ahead and get that and off you go.
Of course, you could always transcode the video to MPEG-4.
Transcoding
Using the tricks above and a copy of VLC, you can take the file after decoding and begin a transcode to MPEG-4 for Front Row, or with some careful option sets you could do the same for the iPod.
Below is a script that will transcode that MPEG-2 (or any video file, really) into a file suitable for an iPod using VLC.
#!/bin/bash
~/Applications/Media/VLC.app/Contents/MacOS/clivlc -I dummy -v "$1" :sout="#transcode{vcodec=mp4v,\
vb=1024,acodec=mp4a,ab=128,scale=1,channels=2,deinterlace,audio-sync}:standard{access=file,mux=mp4,\
url=$1.mp4}" vlc:quit --sout-transcode-width=640 --sout-transcode-height=480 --aspect-ratio "4:3"
Just run the script (fixing the location of VLC) and it will convert any passed file into a file that plays on a video iPod with the latest firmware, and that plays on your Mac at a decent resolution.
If you modify the decrypt setup above to have another folder and another script (that calls the command above) and another launchd item, then you could have this run automatically as well. Wouldn’t that just be swell? 
The iPod
Now once the transcode is done, we’ll need this in iTunes to sync to the iPod. This is easy enough, really, as telling iTunes to open a file causes it to be added to the library (and playback to start).
open -a iTunes ThatFile.TiVo.m2v.mp4
When you sync the iPod, the video will go over to it and …

Postscript
It’s not what one would call “easy” really, but once setup, it sure is seamless. I set this up on the Mac mini in the living room so that it will pull the files from the TiVo, decode them, transcode them, and add them to iTunes. Now I have all my shows via Front Row within several hours of them being recorded on the TiVo. What’s more, they’re smaller, work on the iPod, and let me stop worrying about if the TiVo is running out of space. Now I just add drives to the Mac mini and move on (since my Mac mini can use all my connected media in Front Row).
After working with this for a while, I tweaked the scripts a little to be more reliable. The main problem is that the
-N operator worked
most of the time, but not
all the time for detecting a file in the right state. It works great for the file coming out of Galleon, but not for the one from
tivodecode or
VLC. So here’s the two scripts that I’m using now that work perfectly on my system. You’ll need to clean out the Processed directory now and again, but it helps while getting this going to not lose the intermediate files.
tivodecoder.sh
#!/bin/bash
SOURCE_DIR=/Users/Shared/TiVo/Incoming/
OUTPUT_DIR=/Users/Shared/TiVo/Decoded/
PROCESSED_DIR=/Users/Shared/TiVo/Processed/
cd “$SOURCE_DIR”
ls *.TiVo | while read FILE
do
echo Checking $FILE
if ( [ ! -N “$FILE” ] )
then
echo Processing $FILE
touch “$OUTPUT_DIR/$FILE.m2v.lock”
~/bin/tivodecode -m 3262041634 -o “$OUTPUT_DIR/$FILE.m2v” “$FILE”
mv “$FILE” $PROCESSED_DIR
rm “$OUTPUT_DIR/$FILE.m2v.lock”
sleep 5
file “$OUTPUT_DIR/$FILE” > /dev/null
else
echo Skipping $FILE
fi
file “$FILE” > /dev/null
sleep 5
done
tivotranscoder.sh
#!/bin/bash
SOURCE_DIR=/Users/Shared/TiVo/Decoded/
OUTPUT_DIR=/Users/Shared/TiVo/Transcoded/
PROCESSED_DIR=/Users/Shared/TiVo/Processed/
cd “$SOURCE_DIR”
ls *.m2v | while read FILE
do
echo Checking $FILE
if [ ! -e “$FILE.lock” ]
then
echo Processing $FILE
touch “$OUTPUT_DIR/$FILE.lock”
~/Applications/VLC.app/Contents/MacOS/clivlc -I dummy “$FILE” \
:sout=”#transcode{vcodec=mp4v,vb=768,acodec=mp4a,ab=128,scale=1,channels=2,deinterlace,audio-sync}\
:standard{access=file,mux=mp4,url=$OUTPUT_DIR/$FILE.mp4}” vlc:quit \
—sout-transcode-width=640 —sout-transcode-height=480 —aspect-ratio “4:3”
mv “$FILE” $PROCESSED_DIR
rm “$OUTPUT_DIR/$FILE.lock”
sleep 5
file “$OUTPUT_DIR/$FILE” > /dev/null
else
echo Skipping $FILE
sleep 5
fi
done
I am missing something, I can’t seem to find the mac os x package
on the galleon website, only the zip file.
The TGZ file works fine. They once had a package, but there’s no need for it anymore. To start it just cd into the bin directory of the resulting folder and execute the run.sh script to get it going and gui.sh to configure it.
It’s working well for me. I run Galleon using the Mac OS X bundle, and I successfully set up tivodecode, and VLC to run from launchd and do their conversions. It took me a solid 2 hours to set up, but I’m kinda rusty at scripting and launchd is brand new to me.
This is a nice setup, Adam. Thanks for the information.
Glad to hear it’s working for you.
In the final set of scripts I used, instead of removing the files I had the scripts move them to a “Processed” folder so I could restart things later if I wanted. That worked out well, so you may want to consider it.
In that transcode line for VLC I also moved the bitrate for video down to 768 and saw the filesize drop dramatically with little loss in quality, so that’s another one to look at.
I found it much easier to transcode my MPEG-2 file using ffmpegx . It has a much easier to use GUI interface instead of running terminal scripts for VLC.
If you want to do the transcode manually, sure. The VLC portion here is done such that you don’t have to intervene for it to work. It’s all about automation.
I can't seem to get the part that waits for the file to be downloaded to work correctly. It starts converting the file as soon as it begins to download the *.TiVo file. I tried a variety of syntax variations but had no luck. Any ideas? My basic script is below.
Knowing when a file is done being used by another program is a little tricky. That flag compares the last access time to the last modification time and if the last mod. is newer then it returns false (it’s still being written to). To actually make it return true means that something needs to read the file. That’s a problem I’ve been having sometimes, and it looks like you’re seeing the opposite.
I’ll look into a way to use the modification time of the file against the current date and see what I can find.
Edit: I actually just decided to use a lockfile to indicate progress and lack thereof to indicate completion. This works a lot better for this.
Has anybody had any audio/video sync issues once the muxed mpeg2 file is converted?
My TiVo files also playback as mono although I believe the other track is actually there.
any help appreciated
I’ve not noticed any sync issues at all, no. I haven’t tested for stereo/mono, but can in a bit.
Edit: Nope, mine are in sync and in stereo at all stages. Sorry. :\
Adam,
For some reason, after plugging in my own directories and MAK (and un-commenting the “echo” lines for troubleshooting), all the tivobatch.sh does is very politely tell me that it’s skipping each file in my source dir. Help…?
Thanks,
Sonya
Try the updated scripts at the bottom of the article.
Actually, I did pay for the quicktime MPEG2 decoder, but alas it doesn’t work. VLC is the only option if you want to see the decoded tivo files (or convert using something like iSquint) to something that is much smaller and looks better like H.264.
If you have gotten your MPEG2 for quicktime to work, and have any ideas why mine only plays sound, shoot me a response. Cheers.
/a
No luck here on that particular issue either. VLC works great, though. Make sure to get the latest VLC for the transcoding part — much less buggy.
Requires: VLC 0.8.5, tivodecode 0.1.4.
I crop the edges to get rid of some annoying video noise and encode to H264 512x384 at 384kbps which gives very small file sizes with good quality. I'm not sure this format works on a video iPod, though, as I'm not lucky enough to have one of those yet
tivobatch.sh
tivotranscode.sh
Hey There-
I got the download part working with no problem. I’ve installed the tools, but when I go to $ make the tivo encode, it errors?
Am I missing something?
UPDATE
Never mind, I clearly forgot to install the tools correctly.. got it working.
Gotta tell you this is very cool. I combined it with rsyncx to get shows from my gf’s tivo cuz i got no cable/tivo since i am a bum/mooch
i wonder, though is there a way to add itunes metadata to get the shows to show up in the tv menu of the ipod video, rather than the movie menu
the only way i have been able to do it so far is manually, which i think defeats the point, any insights or ideas would be greatly appreciated
here is a very primitive script to do what you asked, it checks yahoo!tv and IMDB for show descriptions and season numbers. it is not very efficient and could probably use much improvement, but it is working pretty well for me.
#!/bin/bash SOURCE_DIR=/untitledfolder/ OUTPUT_DIR=/untitledfolder/NEW/ cd "$SOURCE_DIR" ls *.mp4 | while read FILE do title=`echo $FILE | sed -e 's/ (Recorded.*//'` showname=`echo $FILE | sed -e 's/ -.*//'` #Show=`echo $title | sed -e 's/ - .*//'` #Episode=`echo $title | sed -e 's/.* - //'` web="http://www.google.com/search?btnI=I'm+Feeling+Lucky&q=$title site:tv.yahoo.com" web2=`echo $web | sed -e 's/ /%20/g'` desc=`curl -L $web2 -A "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)" | grep -A 3 '<div class="airdate">' | sed -e 's/.*<p>//' -e 's/<.*//'` season=`curl -L $web2 -A "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)" | grep '">Season ' | sed -e 's/.*">Season //' -e 's/,.*//'` desc=`echo ${desc:5}` if [ "$desc" = "" ]; then web="http://www.google.com/search?btnI=I'm+Feeling+Lucky&q=$title site:www.imdb.com" web2=`echo $web | sed -e 's/ /%20/g'` desc=`curl -L $web2 -A "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)" | grep -A 1 '<h5>Plot' | sed -e 's/.*h5>//' -e 's/<a.*//' -e 's/--//'` season=`curl -L $web2 -A "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)" | grep '(Season ' | sed -e 's/.*(Season //' -e 's/, Ep.*//'` desc=`echo ${desc:1}` fi /Users/nikbarge/Desktop/Stuff/test/untitledfolder/AtomicParsley "$SOURCE_DIR$FILE" -o "$FILE.m4v" --stik "TV Show" --TVShowName "$showname" --TVSeasonNum "$season" --description "$desc" mv "$SOURCE_DIR$FILE.m4v" "$OUTPUT_DIR$FILE" done I wanted to thank everyone for posting. Using this site and http://jeremysimmons.com/zen_vision/, I have successfully got my setup to work the way I want. I am running Ubuntu for my OS, my wife is using XP. My script decodes the TiVo file, changes it into a format that my wifes Zen can use and copies the file to a Infrant RAID storage device. An email is sent to me describing what was done, and an email is sent to my wife letting her know that a show is on the Infrant waiting for her to upload to her Zen. Finally the script removes files over a week old to conserve hard drive space. The script is run from cron nightly.
I had a lot of problems dealing with spaces in the file names until I found a script to replace them with underscores:
http://www.pigstye.net/articles/2006/09/18/perl-script-to-remove-spaces-from-file-dir-names
Thanks again for all the help!
#!/bin/bash
TIVODECODE=/usr/local/bin/tivodecode
SOURCE_DIR=~/Media/TiVo
OUTPUT_DIR1=~/Media/Decoded
OUTPUT_DIR2=~/Media/Zen
TO_DEL_DIR=~/Media/2Delete
/usr/bin/nospace $SOURCE_DIR
echo “ Starting Stats” > /tmp/tivohead.tmp
echo “************************************************************” >> /tmp/tivohead.tmp
cd “$SOURCE_DIR”
echo “The Tivo Directory contains `ls | wc -l` files totalling `du -h`” >> /tmp/tivohead.tmp
cd “$OUTPUT_DIR1”
echo “The Decoded Directory contains `ls | wc -l` files totalling `du -h`” >> /tmp/tivohead.tmp
cd “$OUTPUT_DIR2”
echo “The Zen Directory contains `ls | wc -l` files totalling `du -h`” >> /tmp/tivohead.tmp
echo “************************************************************” >> /tmp/tivohead.tmp
echo “” > /tmp/tivostat.tmp
cd “$SOURCE_DIR”
ls | egrep “.TiVo$” | while read FILE
do if ( [ ! -N “$FILE” ] ) then if [ ! -e “$OUTPUT_DIR1/$FILE.mpg” ] then echo Decoding $FILE >> /tmp/tivostat.tmp echo Starting Decode `date +%H:%M:%S` >> /tmp/tivostat.tmp # put TiVo MAK in ~/.tivodecode_mak nice -n 5 $TIVODECODE -o “$OUTPUT_DIR1/$FILE.mpg” “$FILE” echo Finished Decode `date +%H:%M:%S` >> /tmp/tivostat.tmp #echo File size is `ls -lh $OUTPUT_DIR1/”$FILE” | awk ‘{ print $5 }’` >> /tmp/tivostat.tmp echo “” >> /tmp/tivostat.tmp if [ ! -e “$OUTPUT_DIR2/$FILE.avi” ] then echo Translating $FILE >> /tmp/tivostat.tmp echo Starting Zen 4:3 Translation `date +%H:%M:%S` >> /tmp/tivostat.tmp nice -n 5 mencoder $OUTPUT_DIR1/$FILE.mpg -ovc lavc -oac mp3lame -lameopts vbr=0:cbr:br=128 -af volume=20 -lavcopts vcodec=mpeg4:vbitrate=500 -vf pp=fd,softskip,format=yv12,scale=340:272 -ofps 30000/1001 -ffourcc XVID -of avi -noodml -o $OUTPUT_DIR2/$FILE.avi echo Finished Zen 4:3 Translation `date +%H:%M:%S` >> /tmp/tivostat.tmp #
- echo Starting Zen 16:9 Translation `date +%H:%M:%S` >> /tmp/tivostat.tmp
- nice -n 5 mencoder $OUTPUT_DIR1/$FILE.mpg lavc -oac mp3lame -lameopts \
- vbr=0:cbr:br=128 -lavcopts -af volume=30 vcodec=mpeg4:vbitrate=750 -vf \
- pp=fd,softskip,format=yv12,scale=340:272 -ofps 30000/1001 -ffourcc \
- XVID -of avi -noodml -o $OUTPUT_DIR2/$FILE.avi
- echo Finished Zen 16:9 Translation `date +%H:%M:%S` >> /tmp/tivostat.tmp
# echo “” >> /tmp/tivostat.tmp fi fi mv “$FILE” “$TO_DEL_DIR/” mv $OUTPUT_DIR1/”$FILE.mpg” “$TO_DEL_DIR/” cd $OUTPUT_DIR2/ /usr/bin/smbclient //192.168.X.X/Zen PASSWORD -c “prompt off; mput *.avi” mv $OUTPUT_DIR2/”$FILE.avi” “$TO_DEL_DIR/” else echo Skipping $FILE >> /tmp/tivostat.tmp echo “” >> /tmp/tivostat.tmp fi file “$FILE” > /dev/null sleep 61done
echo “” > /tmp/tivotail.tmp
echo “ Summary” >> /tmp/tivotail.tmp
echo “************************************************************” >> /tmp/tivotail.tmp
cd “$SOURCE_DIR”
echo “The Tivo Directory now contains `ls | wc -l` files totalling `du -h`” >> /tmp/tivotail.tmp
cd “$OUTPUT_DIR1”
echo “The Decoded Directory now contains `ls | wc -l` files totalling `du -h`” >> /tmp/tivotail.tmp
cd “$OUTPUT_DIR2”
echo “The Zen Directory now contains `ls | wc -l` files totalling `du -h`” >> /tmp/tivotail.tmp
cd “$TO_DEL_DIR”
echo “The ToBeDeleted Directory now contains `ls | wc -l` files totalling `du -h`” >> /tmp/tivotail.tmp
echo “” >> /tmp/tivotail.tmp
echo “ Hard Drive Space” >> /tmp/tivotail.tmp
echo “************************************************************” >> /tmp/tivotail.tmp
df -h | head -n 2 >> /tmp/tivotail.tmp
cat /tmp/tivohead.tmp > /tmp/tivomail.tmp
cat /tmp/tivostat.tmp >> /tmp/tivomail.tmp
cat /tmp/tivotail.tmp >> /tmp/tivomail.tmp
TiVoMAIL=`cat /tmp/tivostat.tmp | wc -l`
NUM=2
if [ $NUM -lt $TiVoMAIL ]
then cat /tmp/tivomail.tmp | /usr/bin/mailx
s “TiVo Decode for `date +%k%Mhrs%Y%m%d`” bob@dev.nullfi
echo “” > /tmp/zentail.tmp
echo “ These are the Shows available for you to transfer to your Zen” >> /tmp/zentail.tmp
echo “****************************************************************” >> /tmp/zentail.tmp
/usr/bin/smbclient //192.168.X.X/Zen PASSWORD -c ls | egrep “*.avi *” >> /tmp/zentail.tmp
echo “” >> /tmp/zentail.tmp
echo “” >> /tmp/zentail.tmp
RSYNCFILE2=`cat /tmp/zentail.tmp | wc -l`
NUM=5
if [ $NUM -lt $RSYNCFILE2 ]
then cat /tmp/zentail.tmp | /usr/bin/mailx
s “rsync [Zen Files]: `date +%k%Mhrs%Y%m%d`” mrsbob@dev.nullfi
for i in `find $TO_DEL_DIR -maxdepth 1 -mtime +7 -print`; do rm -rf $i; done
exit 0