Update June 7, 2007: this script is obsolete. Read about the updated version.
Despite my recent switch to Ubuntu, I still own a video iPod 5G, and I have been searching for a nice replacement for Handbrake and iSquint. I’ve tried every available script and program that purports to encode iPod-compatible video (including Handbrake under Linux), but each falls short on one of several counts:
- Doesn’t produce high-quality video
- Doesn’t support H.264/AAC
- Doesn’t always produce files the iPod can actually read (less of a problem with firmware 1.2, but still)
- Can’t encode directly from a DVD device
- Can’t encode from a pre-ripped DVD directory
- Can’t encode individual video files
- Doesn’t support all video formats
- Can’t batch process multiple tracks
- Doesn’t take advantage of multiple processors
So I wrote my own: podencoder
Prerequisites:
lsdvdmplayer, compiled with MPEG-2 and AC3 supportffmpeg, compiled with H.264 and AAC supportmp4creatorzenity, if you want graphical dialogs and progress bars
Please don’t ask me for help installing these prerequisites. Consider it a character-building exercise.
You can run the script from the command line:
$ podencoder # interactively select tracks from default DVD device
$ podencoder -t longest # auto-select longest track and encode it
$ podencoder ./BBSDOC/ # encode from a pre-ripped directory instead
$ podencoder -t "2,3,4" ./BBSDOC/ # encode multiple tracks
$ podencoder -t "2,3,4,5" -i 7 -n "WEEDS1%i" \
-o ~/Videos/ipod/Weeds/ ./WEEDS_SEASON_1_DISC_2/
# encode tracks 2-5 from a DVD directory,
# store them in ~/Videos/ipod/Weeds/
# and name them WEEDS107.mp4, WEEDS108.mp4,
# WEEDS109.mp4, and WEEDS110.mp4 respectively
You can set defaults in the configuration file (~/.podencoderrc) like a default DVD device (mine is /dev/scd0), a default output directory, and a default scratch directory.
outputdir=/home/mark/Videos/ipod
scratchdir=/home/tmp
device=/dev/scd0
I use /home/tmp as a scratch directory for video encoding because my /tmp directory is on a small partition. If you’re encoding from a DVD or DVD directory, podencoder dumps the entire track to disk before encoding it, which can require up to 8 GB of free space.
On the other hand, if you dislike the command line, you can double-click podencoder or set up a shortcut in your favorite window manager or desktop environment, and it will graphically prompt you to select tracks from the default DVD device (screenshot — note that it automatically selects the longest track for you). Or you can put a shortcut to it in your Nautilus scripts directory, like this:
$ ln -s /usr/local/bin/podencoder ~/.gnome2/nautilus-scripts/"Encode for iPod"
Then you can right-click a mounted DVD, or a DVD directory, or even a random video file, and select “Scripts → Encode for iPod”.
This is my first shell script longer than 3 lines, so please take this opportunity to berate me for my newbie shell scripting mistakes.
Update: script updated with some feedback from the comments. Now uses /bin/sh for greater portability, and a new test to autodetect console-vs-graphical environment (thanks Bob).
Update June 7, 2007: this script is obsolete. Read about the updated version.


Just a quick question - why bash, and not sh?
Comment by Anonymous — Wednesday, August 30, 2006 @ 5:20 pm
No idea, really. Would it work in sh? As I said, this is my first non-trivial shell script (in any shell), so I really have no idea which of the things I’ve learned so far are Bash-specific.
Comment by Mark — Wednesday, August 30, 2006 @ 6:31 pm
First shell script, and your catching signals? Impressive. Heh.
The Bourne Shell is more portable than Bash. You can check for Bash specific extensions using the application ‘checkbashisms’ that should be in the Ubuntu repositories. If you switch to the Bourne Shell you should start your script with ‘#!/bin/sh -e’ as well.
Oh, very trivial and just a personal preference, but some of your lines quite long - why not limit them to 79 characters + newline using the ‘\’ character to escape newlines. It makes it much easier to read your code on a small terminal.
Weird also, you use lower case variable names - I have always been used to shell script variables to be ALL_UPPER_CASE. But I must admit, it doesn’t look half bad like this.
Comment by Noah Slater — Thursday, August 31, 2006 @ 2:46 am
A few double-brackets and double-equals, and a let statement, and it runs under /bin/sh. Thanks.
Comment by Mark — Thursday, August 31, 2006 @ 10:02 am
See http://www.shelldorado.com/goodcoding/tempfiles.html for a useful example of how to use the “program termination” pseudo-signal to clean up temporary files. In this relatively simple script, it may not be necessary, but nonetheless it is a good technique to know.
Comment by Isaac Lin — Thursday, August 31, 2006 @ 11:23 am
Hmm. This GUI-vs-console thing is pissing me off. I was sitting at my computer last night and wanted to test some changes, and the GUI dialogs kept coming up, and currently the only way to turn them off is to change the configuration file. But doing that means that my right-click-as-Nautilus-script thing no longer shows the GUI dialogs (or any progress whatsoever, since stdout isn’t displayed anywhere). That’s stupid design. The question is, what’s a better design?
- An additional command-line flag?
- A better magical test to determine whether the program is running in what I would call a graphical context? (My current magical test is to check for the existence of the DISPLAY variable, which seemed like a clever hack considering I had no idea what I was doing. But it turns out that it doesn’t actually solve the right problem, because I want to be able to sit at my computer and type “podencoder” in a terminal window and get output in the terminal window, or right-click in Nautilus and select something from the Scripts menu and get graphical progress dialogs.)
- Fork the script into two separate scripts, one for GUI, one for console, and have them share code by sourcing common files?
- Make a symlink called “guipodencoder” or “gpodencoder” or something, and key off `basename $0`?
@Noah: yeah, I figured out traps on my first script. Zenity has an annoying habit of sending SIGHUP to the parent process when you click Cancel, which resulted in the script dying but mplayer/ffmpeg processes continuing. I spent a lot of time testing this script and canceling it halfway, and I finally realized (when both CPUs spiked to 100% and my computer slowed to a crawl) that the background progresses were still running. This led to a whole detour of learning about traps and signals and $! and wait and — for reasons not worth explaining — variable scoping. Bleah.
More generally, I am good at acquiring deep technical knowledge quickly. I am much more interested in feedback like “why not change 3 lines and use sh?” and “scripts should hard-wrap at 79 columns.” The aesthetics of shell scripting. Example: I think the script has pretty good command-line usage notes and unsurprising command-line flags. They work the way you would expect a shell script to work. Then how do I solve my GUI-vs-console problem? Any of the solutions (and probably many others) could be made to work. But which one *feels* better? I don’t know yet.
Comment by Mark — Thursday, August 31, 2006 @ 2:24 pm
Mark, have you tried testing for the $TERM variable? On Debian testing nautillus sets it to “dumb” when run from a gui, and the name of your terminal when run from the CLI. A possible avenue for exploration.
Comment by Noah Slater — Friday, September 1, 2006 @ 2:59 am
I think the $TERM variable differs a lot between the desktop systems/file managers.
Application links to scripts in KDE have $TERM=linux ?!?
Comment by Filip — Friday, September 1, 2006 @ 6:23 am
You can use the tty program to detect whether the script is connected to a terminal or not. If it is being run from Nautilus then there will be no terminal connected.
Short test script:
#!/bin/bash
tty –quiet
if [ "$?" == 0 ]; then
echo “Is a terminal” >> afile
else
echo “Not a terminal” >> afile
fi
Comment by Bob Atkey — Friday, September 1, 2006 @ 9:00 am
I think that the answer is keeping strict in line with KISS principle — make two scripts. One strictly running from command line (or xterm), which could be well tested as such. The other would be just GUI candy around that script. In this way you can be sure you can separate and test real processing part in xterm.
And by the way, I have no idea, why in the world you haven’t wrote all this in Python (which you know much better than shell apparently, and which has good GUI extensions — at least for KDE, but I guess that Gnome-Python exists as well).
Comment by Matej Cepl — Friday, September 1, 2006 @ 9:47 am
Just if you like to read — http://www.catb.org/~esr/writings/taoup/html/ — ESR has some crazy comments lately, but this thing is pretty smart and ESR is here doing what he can do the best — recording Unix tradition.
Comment by Matej Cepl — Friday, September 1, 2006 @ 9:53 am
@Bob: that’s exactly what I was looking for. I’ve updated the script accordingly, and removed the (rather useless) gui= option in the configuration file. (Sorry your comment took so long to appear; it fell into my spam filters!)
@Noah: script is now wrapped at 78 characters.
@Isaac, @Matej: thanks for the links, I’ll read them this weekend.
(Comments temporarily closed until I return from vacation.)
Comment by Mark — Friday, September 1, 2006 @ 3:08 pm
Hi, nice script, but i’ts possible burn subtitles??
like .srt, .sub files ????
:(
Comment by Harold — Sunday, September 10, 2006 @ 11:53 pm