Unfortunately, none of this data ever winds up in ID3 tags, so copying MP3s from my Mac to my FreeBSD laptop loses information. The trick is to mine the "iTunes Music Library.xml" file for data and populate the ID3 tags using pudge's MP3::Info.
The first step is to glean out the relevant fields from the XML file. I could sit down and write an XML parser handler for the iTunes file. Or I could be lazy and use brian's Mac::PropertyList, since this XML file is simply a property list. But I didn't do either. Instead, I used my favorite XML gleaning tool: XSLT. I started by looking at the specific XML data in the iTunes file (the Tracks dictionary) and converted the information I was looking for into text. Building this stylesheet was an iterative process, and here is the final result:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="text"/>
<xsl:template match="text()"/>
<xsl:template match="plist/dict/dict[preceding-sibling::key[text() = 'Tracks']]">
<xsl:apply-templates select="dict" mode="display"/>
</xsl:template>
<xsl:template match="dict" mode="display">
<xsl:apply-templates select="key[. = 'Location']" mode="display"/>
<xsl:apply-templates select="key[. = 'Name']" mode="display"/>
<xsl:apply-templates select="key[. = 'Artist']" mode="display"/>
<xsl:apply-templates select="key[. = 'Album']" mode="display"/>
<xsl:apply-templates select="key[. = 'Year']" mode="display"/>
<xsl:apply-templates select="key[. = 'Genre']" mode="display"/>
<xsl:apply-templates select="key[. = 'Track Number']" mode="display"/>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="key" mode="display">
<xsl:value-of select="concat(text(), ': ', following-sibling::*/text(), '
')"/>
</xsl:template>
</xsl:stylesheet>
This produces output like this:
Location: file://localhost/Users/ziggy/Music/iTunes/iTunes%20Music/Louis%20Armstrong/Grea
t est%20Hits/01%20Sugar.mp3
Name: Sugar
Artist: Louis Armstrong
Album: Greatest Hits
Genre: Jazz
Track Number: 1
I can then easily parse this output and use set_mp3tag from MP3::Info to add the ID3 tags these files were missing.
Here is the full script for anyone who is interested. Note that the XSLT stylesheet was included in the __DATA__ segment to reduce an external dependency.
#!/usr/bin/perl -w
use strict;
use MP3::Info;
my @id3_fields = qw(Location Name Artist Album Year Comment Genre);
push(@id3_fields, "Track Number");
use_winamp_genres();
sub read_metadata {
my $filename = shift;
use XML::LibXSLT;
use XML::LibXML;
$/ = undef;
my $parser = XML::LibXML->new();
my $xslt = XML::LibXSLT->new();
my $source = $parser->parse_file($filename);
my $style_doc = $parser->parse_string(<DATA>);
my $stylesheet = $xslt->parse_stylesheet($style_doc);
my $results = $stylesheet->transform($source);
return $stylesheet->output_string($results);
}
## Update ID3 tags
print STDERR "Processing 'iTunes Music Library.xml'...";
my $metadata = read_metadata("$ENV{HOME}/Music/iTunes/iTunes Music Library.xml");
print STDERR "done\n";
my @blocks = split("\n\n", $metadata);
foreach my $block (@blocks) {
my (%info) = map {m/^(\w+): (.*)$/} split("\n", $block);
$info{Location} =~ s{^file://localhost}{};
$info{Location} =~ s{%20}{ }g;
print STDERR "$info{Artist}: $info{Album}, $info{Name}\n";
$info{Genre} = 'Dance' if $info{Genre} eq "Electronica/Dance";
$info{Genre} = 'Other' if $info{Genre} eq "World";
$info{Genre} = 'Alternative' if $info{Genre} eq "Alternative & Punk";
set_mp3tag(@info{@id3_fields});
}
__DATA__
## stylesheet, as above
Note (Score:2)
So if you writing ID3v1 is fine with you, you will either want to remove the ID3v2 tag (MP3::Info can do that for y
Re:Note (Score:2)
--Nat
What is iTunes not updating? (Score:1)
pure perl .xml parsing... (Score:1)