iTunes has many nice features, but Apple's approach to control everything and anything users do and can do, can make data management unnecessarily hard. i just had to move all my music (43GB, 7'500 songs) from one hard drive to another, and iTunes is not very good at dealing with that. essentially, you start all all over with a brand new library.
the processing times for importing the library are not the bad part, that's something i can just leave running. but there does not seem to be a way to easily move playlists, and this is really annoying. there even does not seem to be an export for playlists. if there is one consistent underlying strategy across apple products, it's the black hole strategy
, getting things in works really well, getting them out again is almost impossible.
my first goal was to simply get my playlists out of iTunes. the library is stored in an XML document, but in true black hole style, this is treated as a write-only document by iTunes: you cannot use it to make changes to iTunes data; that would just be too convenient. the data actually managed by iTunes is stored in some opaque .itl
file, which probably contains the same information as the XML, but in some proprietary format.
this means at least for exporting data, the XML is pretty convenient. it also is pretty large, my XML document is 11MB and 242'000 lines. the XML is not all that great, but at least good enough to figure out how things work. to get started, my only goal was to be able to extract my playlists from the XML of my old iTunes installation. this turned out to be fairly simple, the XSLT for that is just a couple of lines, here it is:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="UTF-8"/>
<xsl:key name="trackID" match="dict[key[1] = 'Track ID']" use="key[text() = 'Track ID']/following-sibling::integer[1]/text()"/>
<xsl:template match="/">
<ul>
<xsl:for-each select="/plist/dict/key[text() = 'Playlists']/following-sibling::array[1]/dict">
<li>
<xsl:value-of select="key[text() = 'Name']/following-sibling::string[1]/text()"/>
<ul>
<xsl:for-each select="array/dict">
<xsl:variable name="track" select="key('trackID', integer/text())"/>
<li>
<xsl:value-of select="$track/key[text() = 'Artist']/following-sibling::string[1]"/>
<xsl:text>: </xsl:text>
<xsl:value-of select="$track/key[text() = 'Name']/following-sibling::string[1]"/>
<xsl:text> (</xsl:text>
<xsl:value-of select="$track/key[text() = 'Album']/following-sibling::string[1]"/>
<xsl:text>)</xsl:text>
</li>
</xsl:for-each>
</ul>
</li>
</xsl:for-each>
</ul>
</xsl:template>
</xsl:stylesheet>
this could be more sophisticated, but it is mainly meant as a starting point. please keep in mind that iTunes stores the entire library and all media types such as Music
and Podcasts
as playlists, so if you don't filter them (the above code does not do that), your output will contain these long playlists. for my library, this unfiltered output is 1MB or 16'000 lines of HTML.
one interesting observation when running that code was the following: i always thought all XSLT processors would not just use xsl:key
as a syntax shorthand, but for some performance optimization (they don't have to, but it is kind of logical to do it) to accelerate access to the nodes in the key. when i ran the above code in XML Spy using its built in XSLT processor, it took forever to complete, which makes me believe that XML Spy does not build an index for xsl:key
. Saxon, on the other hand, processed the code much faster. so i guess i finally found an example for an XSLT processor which really treats xsl:key
as a syntax shorthand only.
Illuminating, looks like a good building block.
FWIW, you can export individual playlists as XML by rightclicking on them in the iTunes source list and choosing Export...
Posted by: Tim Jarrett | Sunday, June 28, 2009 at 18:00
Moving of iTunes files, at least on OSX, is easy. Most of the time iTunes can track fileststem changes. And whenit doesn't, there's iTunes Fixer app: http://itunes.pornel.net
Posted by: kldj | Saturday, February 06, 2010 at 05:14