For various (bug-related) reasons, I strongly dislike the podcast feature in iTunes. Mainly because, at random, it will mysteriously delete episodes of podcasts (giving "could not be found" errors when trying to play or sync them to my iPod).
I tried a number of different alternatives (
YamiPod,
Floola,
myPodder), but all had their limitations and bugs of their own. After trying a few different podcatchers, I decided that
Juice is my favorite. For a while, I used a combination of Juice,
RockBox (open source alternative firmware for the iPod that allows you to just copy your music directly to the player without iTunes), and
SyncToy to keep my music and podcasts up to date, but this was lacking as well. While I don't like the podcast features of iTunes, I
do like the clean look and feel of the iPod interface, and the way podcast episodes are auto-bookmarked and marked as new or listened automatically—something that RockBox simply can't compete with.
Now, Juice
does support iTunes, but it doesn't put your podcasts into the "Podcasts" section—it just copies them over as regular songs and creates playlists for each show to keep track of the episodes. This is lame. What follows is my hacked solution that lets Juice put your podcasts where they belong in your iTunes library.
In short, this solution involves using Juice's post-download processing feature to run a Perl script that adds the special ID3v2 tags to each downloaded episode that makes iTunes recognize it as a podcast. In order to implement this, you will need a Perl distribution for Windows installed on your computer (I use
ActivePerl), and the
MP3::Tag module (which you can install using the Perl Package Manager).
The Basic Idea
After a few minutes spent with a couple MP3s and a
hex editor, I determined that in order for a song to appear in the Podcasts section of iTunes, it needs a few special ID3v2 tags:
- PCST needs to be set to 1
- WFED needs to be a unique URL for each podcast, but the same for each episode within the podcast
- TGID, TCAT, and TDES need to be defined (though I'm not sure entirely how they are used by iTunes)
The problem with this is that the MP3::Tag module in Perl does not support the WFED and PCST frames in its ID3v2 module. I toyed with the idea of modifying the module itself to support them, but settled on a slightly more hacky (but much more easily implemented) solution. Here's the source code for my FixPodcast.pl script:
#!/usr/bin/perl
use MP3::Tag;
my $file = shift;
if($file =~ /\.mp3$/i){
#### SECTION 1 ####
my $mp3 = MP3::Tag->new($file);
$mp3->get_tags;
$file =~ /\\([^\\]+)\\([^\\]+)$/;
my $title = $2;
my $album = $1;
my $artist = $album;
my $fixed = 0;
if(exists $mp3->{'ID3v1'}){
if($mp3->{'ID3v1'}->title ne ''){
$title = $mp3->{'ID3v1'}->title;
}
if($mp3->{'ID3v1'}->artist ne ''){
$artist = $mp3->{'ID3v1'}->artist;
}
$mp3->{'ID3v1'}->remove_tag;
}
if(exists $mp3->{'ID3v2'}){
if($mp3->{'ID3v2'}->comment eq 'fixed'){
$fixed = 1;
} else {
if($mp3->{'ID3v2'}->title ne ''){
$title = $mp3->{'ID3v2'}->title;
}
if($mp3->{'ID3v2'}->artist ne ''){
$artist = $mp3->{'ID3v2'}->artist;
}
$mp3->{'ID3v2'}->remove_tag;
}
}
if($fixed == 0){
#### SECTION 2 ####
$mp3->new_tag('ID3v2');
$mp3->{'ID3v2'}->add_frame('TIT2', $title);
$mp3->{'ID3v2'}->add_frame('TPE1', $artist);
$mp3->{'ID3v2'}->add_frame('TALB', $album);
$mp3->{'ID3v2'}->add_frame('TCON', 'Podcast');
$mp3->{'ID3v2'}->add_frame('COMM', 'ENG', '', 'fixed');
$mp3->{'ID3v2'}->add_frame('TCAT', 'category', 'podcast');
$mp3->{'ID3v2'}->add_frame('TDES', 'description', $title);
$mp3->{'ID3v2'}->add_frame('TGID', 'id', $album);
$mp3->{'ID3v2'}->add_frame('WXXX', $album, 'http://dev/null');
$mp3->{'ID3v2'}->add_frame('PCNT', 1);
$mp3->{'ID3v2'}->write_tag;
#### SECTION 3 ####
my ($pcst, $wfed) = (0, 0);
open(MP3, "<$file");
open(NEW, ">$file.tmp");
binmode(MP3);
binmode(NEW);
while(){
my $line = $_;
if(!$pcst){
if($line =~ s/PCNT/PCST/){
$pcst++;
}
}
if(!$wfed){
if($line =~ s/WXXX/WFED/){
$wfed++;
}
}
print NEW $line;
}
close(MP3);
close(NEW);
unlink($file);
rename("$file.tmp", $file);
}
}
Because I wrote it quickly, and didn't bother to comment, here's a brief description of what's happening in each of the sections of code (delimited by the #### SECTION #### comments).
Section 1
Here I get the path to the file from the command line, parse out the last directory name, which I use for the artist name and album, and the file name, which I use for the episode title.
I then check if there are any ID3v1 frames, get the real artist and song name if they're provided, and delete the entire ID3v1 tag from the MP3. This is followed by doing the same for any ID3v2 frames that already exist on the file (I let the artist name and song name from the ID3v2 tags overwrite the values received from the ID3v1 tags if there were any).
You will note that I also check the ID3v2 comment frame to see if it says "fixed." This isn't necessary—I only do it because I have another script that looped through my entire podcast directory and ran this script on each file, and I didn't want it to process a file that had already been previously processed. Tagging them with "fixed" in the comment frame would cause this script to skip over them the next time they were processed.
Section 2
This is where I create a new ID3v2 tag for the MP3 (since I just deleted all the existing tags in the file). I add the title, artist. album, genre, and (optional) comment frames first. The TDES, TCAT and TGID frames are already supported by MP3::Tag, so those are straight forward to add (I don't think it matters too much what values you put into those frames as far as iTunes goes).
The WFED and PCST frames cannot be added by MP3::Tag, however, since it doesn't know what those are (they are iTunes specific frames). This is where it gets a bit sneaky/hacky. I add a WXXX frame with the name of the podcast and a meaningless URL (which is supported my MP3::Tag as a custom URL tag), and a PCNT frame with the value "1" (which MP3::Tag supports as a play-count frame).
Section 3
Here, I read the newly-tagged MP3 file in binary mode, and search for the first occurrences of "PCNT" and "WXXX", and replace them with PCST and WFED respectively. In essence, this converts the frames I added but didn't need into the ones that I do need but couldn't add.
Loading the newly processed MP3 into iTunes will now place it in the "Podcasts" section as a new episode of a podcast named the same as whatever you put into the TALB (album name) frame in the script (in this case, the name of the folder that the MP3 was in, which is set to the name of the podcast by Juice).
In order to make Juice process each file it downloads automatically before adding them to your iTunes library, simply go to the "Advanced" tab in its settings, and tell it to run the following command after each download:
c:\path\to\perl.exe c:\path\to\FixPodcast.pl "%f"
Now, every time Juice downloads a new episode of a podcast, it will process it with the Perl script and add the iTunes podcast frames, then copy the file to your iTunes library where it will show up in the Podcast section.
Exciting, huh?
If you're crafty enough, try modifying the above script to preserve more information about each podcast than just the title and artist names (album art would be nice, for example).
steve Says,
Sunday, October 5. 2008 at 11:58 (Reply)
Can't coerce
UNKNOWN to string in leave at c:\perl\bin\FixPodcast.pl line 137.
panic: leave_scope inconsistency at c:\perl\bin\FixPodcast.pl line 137.
panic: leave_scope inconsistency at c:\perl\bin\FixPodcast.pl line 135.
My system contains the following:
ActivePerl 5.10.0 Build 1004
MP3-Tag 0.9710
Windows XP SP3
Juice v2.2
iTunes 7.7.1.11
Do you have any ideas why these errors are occurring?