Converting Your iTunes Library To Html

Contents:

1. Introduction

Warning: CCTunes has been changing. Not all of this documentation may correspond to the way CCTunes is working right now. It will be updated in the near future to reflect those changes.

iTunes is keeping track of all the information of your music library, in a proprietary database file and does not provide any interface to manipulate that. You have to go through AppleScript to get or set information in it. This is sometimes nice, some people have nice websites of all sorts of AppleScripts to do whatever they felt useful to do, but I have not found much information on how to publish your whole library in html-format. Therefore I went to search the internet, and found some links which set me on my way to get my own solution.

It might not be perfect, it might not even be nice, but it could do for you. Anyway, it did for me. As of lately, I've bundled everything together in a nice little application which you can find here.

You can see a list of similar packages at the end of this document. Some of them are payware, some of them are freeware. All of them have advantages and disadvantages, and you might well find that mine is not the package that best suits your needs. However, you will find that mine comes with a full explanation of what it does. As a matter of fact, it actually grew out of this documentation and has not had a purpose so far but illustrating this text. That might change in the future, but as for now, it's better to still consider it like that. If you have some success or ideas with the package, I'd be glad to hear!

2. About the XML file

The XML file resides in your home directory, at ~/Music/iTunes/iTunes Music Library.xml. It has been described as braindead and, agreed, it does not look like one would expect an xml file for a music library would look like. It is track based, and every track has an ID and most of the information one would expect to find there, except for some, like the images. There does not seem to be a logical way in which the Track IDs are assigned and they tend to change when f.i. you reencode songs. Where one would expect the name of the tag has information about the information contained in the tag, the XML file just has key tags followed by value tags, like a dictionary streamed out. This is the typical plist approach of Apple, and it's just another example of how not to use XML, imho.

When you are playing tracks in your iTunes application, iTunes seems to update both a private proprietary database file and the xml file. The proprietary database, ~/Music/iTunes/iTunes 4 Music Library, is the master file. Whatever you change in the xml file, if the database file is not updated, the next time you play something in iTunes, the XML file will be updated again. Very annoying.

3. Prerequisites

Organizing The Library

First of all, it was my aim to make a nice list of all the albums I posess in my iTunes library. I am in the ongoing process of converting my whole CD collection, some couple of hundred CDs, to MP3, and while doing so, I try to keep a list. I used to do this by updating some spreadsheet file in GNumeric, which is still a viable approach, but this is nicer. However, I use certain rules to tag my MP3s:

  • I don't keep track of Genres. They are never accurate anyway.
  • I try to keep the Artwork of the tracks to just contain the Album cover art, not any other artwork, like singles cover art or logos.
  • I try to keep the disk numbers up to date. They are often ignored but I want albums consisting of multiple CDs to be treated as one album.
You do not have to organize your library like I do, but just in order for all of this to work and look nicely, I recommend this approach. I recommend this approach anyway, I think it should be forced by law to organize your library like this ;-).

Installing the software used

In earlier versions I used perl modules like MP3::Info to extract the images and ImageMagick command line tools to generate the thumbnails. However, things are much simpler now: I've made a self contained package which is downloadable and has everything you will need.

4. Converting the XML file

Forcing the XML file to make sense

Well, we want a decent xml file to start with, not that plisty file that is not like we would make it ourselves. Luckily there are people that helped me on the way to do this. Searching google gave me a nice little link, http://www.xmldatabases.org/WK/blog/1086?t=item which explained it all. Now that was very useful. Step one finished.

Code listing 4.1: makeProperXml.xsl file makes a nicer XML file

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xsl:version="1.0">
        
  <xsl:template match="/">
    <songlist>
      <xsl:text>
      </xsl:text>
      <xsl:apply-templates select="plist/dict/dict/dict"/>
    </songlist>
    <xsl:text>
    </xsl:text>
  </xsl:template>
  <xsl:template match="dict">
    <song>
      <xsl:text>
      </xsl:text>
      <xsl:apply-templates select="key"/>
      <xsl:text>
      </xsl:text>
    </song>
  </xsl:template>
  <xsl:template match="key">
  <xsl:element name="{translate(text(), ' ', '_')}">
    <xsl:value-of select="following-sibling::node()[1]"/>
  </xsl:element>
    <xsl:text>
    </xsl:text>
  </xsl:template>
</xsl:stylesheet>


To now actually do the conversion, we use xsltproc, which is a tool that comes with the libxml2 library. If you haven't downloaded the package you must install it using fink or compile it from sources yourself. To generate the nicer XML file, we execute

Code listing 4.2: xsltproc command to generate proper XML from plist file

xsltproc --novalid makeProperXml.xsl "~/Music/iTunes/iTunes Music Library.xml" > properiTunes.xml

Sorting the XML file per Album

This was the next thing to do, so I started reading XSLT specs and experimenting, and googling. In the end, it turned out pretty easy to do, and a stylesheet was the result. I am still struggling a bit with the fact that if I have a compilation album, I want it to be listed as "Various Artists". The xml value for this id3 tag is a key which is either present or not in the iTunes xml file, instead of a bool as one would expect. This is a bit confusing, since I have found no way yet to check whether a node is null or not in the XSLT spec. It's probably possible to do this in a more elegant way, but my approach works too. So, I came up with another XSLT file.

Code listing 4.3: makeMainXml.xsl file creates the main XML file

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xsl:version="1.0">
            
<xsl:template match="songlist">
  <xmlLibrary>
    <xsl:for-each select="song[ not(Album = preceding-sibling::song/Album and Artist = preceding-sibling::song/Artist) ]">
      <xsl:sort select="Compilation"/>
      <xsl:sort select="Artist"/>
      <xsl:sort select="Album"/>
      <xsl:variable name="albumname" select="Album"/>
      <xsl:variable name="artistname" select="Artist"/>
      <xsl:variable name="albumid" select="position()"/>
      <xsl:if test="$artistname!='' and $albumname!=''">
      <includeXml><xsl:value-of select="$albumid"/></includeXml>
      <xsl:text>
      </xsl:text>
      <xsl:document href="{$destinationDir}/{$albumid}.xml">
      <AlbumItem>
   <xsl:text>
   </xsl:text>
        <Artist>
          <xsl:variable name="compilationval" select="count(child::Compilation)"/>
          <xsl:choose>
            <xsl:when test="$compilationval=1">
              <xsl:text>Various Artists</xsl:text>
            </xsl:when>
            <xsl:when test="$compilationval=0">
              <xsl:value-of select="Artist"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:text>????</xsl:text>
            </xsl:otherwise>
          </xsl:choose>
        </Artist>
   <xsl:text>
   </xsl:text>
        <Album>
          <xsl:value-of select="Album"/>
        </Album>
   <xsl:text>
   </xsl:text>
        <AlbumID/>
   <xsl:text>
   </xsl:text>
        <Picture>
      <xsl:text>
      </xsl:text>
          <PictureURL><xsl:value-of
               select="Location" /></PictureURL>
     <xsl:text>
   </xsl:text>
        </Picture>
     <xsl:text>
   </xsl:text>
	<TrackList>
          <xsl:variable name="currentArtist" select="Artist"/>
          <xsl:variable name="currentAlbum" select="Album"/>
          <xsl:for-each select="parent::node()[1]/song[Album=$currentAlbum and Artist=$currentArtist]">
            <xsl:sort select="Disk_Number" data-type="text"/>
            <xsl:sort select="Track_Number" data-type="number"/>
      <xsl:text>
      </xsl:text>
	      <Track>
         <xsl:text>
         </xsl:text>
	        <Number><xsl:value-of select="Track_Number"/></Number>
         <xsl:text>
         </xsl:text>
	        <Name><xsl:value-of select="Name"/></Name>
         <xsl:text>
         </xsl:text>
	        <TotalTime><xsl:value-of select="Total_Time"/></TotalTime>
         <xsl:text>
         </xsl:text>
	        <DiscNumber><xsl:value-of select="Disc_Number"/></DiscNumber>
         <xsl:text>
         </xsl:text>
	        <DateAdded><xsl:value-of select="Date_Added"/></DateAdded>
         <xsl:text>
         </xsl:text>
	        <Year><xsl:value-of select="Year"/></Year>
         <xsl:text>
         </xsl:text>
	        <PlayCount><xsl:value-of select="Play_Count"/></PlayCount>
      <xsl:text>
      </xsl:text>
              </Track>
	  </xsl:for-each>
   <xsl:text>
   </xsl:text>
	</TrackList>
<xsl:text>
</xsl:text>
      </AlbumItem>
      </xsl:document>
      <xsl:text>
      </xsl:text>
      </xsl:if>
    </xsl:for-each>
  </xmlLibrary>
</xsl:template>

</xsl:stylesheet>

The main magic in this XSL file is where the preceding-sibling does it's work to create a unique sorting on the Albums. Some other magic is where the comments are generated to get the images out of the mp3 file. But that is explained in the next chapter. The conversion is again done using xsltproc just like we did before.

Code listing 4.4: Making main XML file using xsltproc

xsltproc --novalid makeMainXml.xsl properiTunes.xml > masterXml.xml

5. Getting the images out of the mp3 files

The trickiest part of the job was getting the images. Since the images are not in the XML file we need to get them out of the mp3 files themselves. iTunes uses the correct mp3 v2 tag to store the image. Now, initially I wrote a perl script to extract the images based on a perl module. But, you need to install the perl modules MP3::Info and URI::URL in order for this to work. This is not too hard to do, but is still some effort. I could have packaged the perl modules I guess, but I found it easier to create a little Cocoa command line tool that does the same based on libid3, the library that is distributed by http://www.id3.org. Both approaches are still explained however.

Using perl to get the images out of the mp3 files

Warning: This section is outdated with respect to CCTunes. Read on for the current approach.

A link that got me on the way was on the ever interesting macosxhints website, http://www.macosxhints.com/article.php?story=20030429003250559, in which one of the comments describes almost perfectly what needs to be done. Almost, but for the fact on how to get to open a file based on the url. Of course, this is not hard to do, using some parsing, and I have my "Perl in a Nutshell" book on my desktop, but getting used to this perl thing I thought there must be a module already out there that does this for me. Now, that's the URI::URL module we've installed, and it was mentioned in the book.

There we have our perl script, which takes as argument a unix file path and a filename to write the png file to. If this script does not find the corresponding tag, it just creates a link to a nopic.png file instead of the destination file, so that we can put in a nice icon when the image is missing in the mp3.

Code listing 5.1: The getPicture script gets the image from the mp3 file

#!/usr/bin/perl

no strict 'refs';
use Getopt::Long;
use IO::File;
use MP3::Info;
use URI::URL;

$url1 = new URI::URL shift;
$unixpath = $url1->unix_path();
$e = $ARGV[0];
if ( -e $e || -l $e ) {
    system("/bin/rm " . $e);
}
if (my $mp3tag_id3v2 = get_mp3tag($unixpath, 2, 1)) {
    my $tag = "PIC";
    my $result = $mp3tag_id3v2->{$tag};
    $result =~ s/^(.....).//;
    if (length($result) > 0)
    {
      open $f, "> $e";
      print $f $result;
    }
    else
    {
      system("/bin/ln -s ./nopic.png " . $e);
    }
}
else
{
   system("/bin/ln -s ./nopic.png " . $e);
}

To invoke the correct sequence of calls to this perl command, we need a little hack. If you take a closer look at the XML file we used to sort the original cleaned up XML file per album, you will see that we generated some output in XML comments, which invokes the getPicture script in a way to generate the png file we want in the images directory. So now, with a simple bash command we can invoke the whole set of getPicture's that will generate us the total directory of images:

Code listing 5.2: Bash command to generate directory of images

bash-2.05a$ cat mymusic.xml | grep getPicture | /bin/bash

I have understood that a Microsoft implementation of the XSLT parsing implements something like callbacks, in which you can call a script from within your XSL file. That's a good idea, nice, but since it isn't in the XSLT specification it is not very compatible to depend upon it. But if you do, you might want to check that out.

Using a custom command line tool to get the image out of the mp3 file

Another approach is taken in the downloadable package however. The getPicure command is not a perl command, but a compiled tool, based on id3v2 which already uses the libid3 library and has a great set of functionalities, except... extracting images.

Adapting the sources to eliminate all we don't need and adding the extra little bit of functionality to extract the image was easy however, since we basically know how to do this in perl already. In order to compile it you will need to have the libid3 headers in place and have a libid3 library that you can link to. The commands needed to compile it with the statically linked library are included in comments in the source code.

Code listing 5.3: Source code to getPicture command line tool

/**
 * Based on id3v2 ... compile something like
 *
 * g++ -I/usr/local/include/ -O3   -c -o getpic.o getpic.cpp
 * c++ -L/usr/local/lib/ -O3 -pedantic -Wall -framework CoreFoundation -lz -liconv
 *                       -g -o id3v2 getpic.o ./libid3.a
 * 
 * Part of CCTunes, distributed under the GPL.
 * details at http://www.coin-c.com/CCTunes/
 */

#include <CoreFoundation/CoreFoundation.h>

#include <id3/misc_support.h>
#include <id3/tag.h>

#include "frametable.h"
#include "genre.h"

int main( int argc, char *argv[]);
int main( int argc, char *argv[])
{
  CFURLRef theSourceUrl = CFURLCreateWithBytes(kCFAllocatorDefault,
					       (unsigned char*)argv[1],
					       strlen(argv[1]),
					       kCFStringEncodingUTF8,
					       NULL);
  unsigned char sourceFilePath[500];
  if (CFURLGetFileSystemRepresentation(theSourceUrl,true,sourceFilePath,500))
    {
      bool tags = false;

      ID3_Tag myTag;
      myTag.Link((char*)sourceFilePath, ID3TT_ID3V2);

      const ID3_Frame * myFrame = 0;
      const ID3_Tag myTagConstRef = myTag;
      ID3_Tag::ConstIterator *Iter = myTagConstRef.CreateIterator();
      for (size_t nFrames = 0; nFrames < myTag.NumFrames(); nFrames++)
	{
	  myFrame = Iter->GetNext();
	  if (NULL != myFrame && myFrame->GetID() == ID3FID_PICTURE)
	    {
	      myFrame->Field(ID3FN_DATA).ToFile(argv[2]);
	      tags = true;
	      break;
	    }
	}

      if(!tags)
	std::cout << (unsigned char*)argv[1] << ": No ID3 tag" << std::endl;
    }
}


Generating Thumbnails

This might be all you need if you are on your own private server, have lots of disk space and don't really care on how fast your collection loads in the browser. Since I have been using this, I found that the size of the images tends to be too big to view all of the images for all of the albums at once, so it would be nice to generate thumbnails for the overview.

If you have a utility like GraphicConverter, which came for free with my laptop, you can easily generate 60 by 60 thumbnails from the images by using the batch mode to set the Max Size. Also ImageMagick can do things like this. You will need to install it using fink or compile it yourself, both should be easy and with a simple set of commands on the prompt you convert all images like this:

Code listing 5.4: Generating thumbnails with ImageMagick convert

mv images images_high
mkdir images
cd images_high; find . -type f -exec convert -resize 60x60 {} ../images/{} \;
cd -

However, for your ease of use, the currently downloadable package comes with a little command line tool programmed in cocoa and based on pdf2png by Evan Jones which does all of this for you. It simply takes an image and draws it in an offline buffer of the specified size and saves that as a png file. I must say however, you will see that the quality is not good. It is the same quality you can see if you use iPhoto to look at the images you've taken with your digital camera. Uuuugly. I must provide some feedback to Apple on that someday.

Code listing 5.5: Little Cocoa command line tool to resize images, based on pdf2png

// A tiny program that resizes images to PNG images with certain dimensions.
// based on pdf2png by Evan Jones, http://evanjones.ca/pdf2png.m
// modified by Kristof Van Landschoot, Coin-C to fit the purpose of resizing.
//
// gcc --std=c99 -g -o resize resize.m -framework Cocoa
//
// Written originally by Evan Jones <ejones@uwaterloo.ca> Februrary, 2004
// http://www.eng.uwaterloo.ca/~ejones/
//
// Released under the GNU Public License

#include <Cocoa/Cocoa.h>

int main( int argc, char* argv[] )
{
	int destinationSize = 36; // in DPI

	int page = 1;
	
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	
	// Package all arguments as NSStrings in an NSArray
	NSMutableArray* args = [NSMutableArray arrayWithCapacity: argc - 1];
	for ( int i = 1; i < argc; ++ i )
	{
		[args addObject: [NSString stringWithCString: argv[i]] ];
	}
	
	// If we have a "--dpi" along with a corresponding argument ...
	unsigned int index = NSNotFound;
	if ( (index = [args indexOfObject: @"--dpi"]) != NSNotFound && index + 1 < [args count] )
	{
		// Parse it as an integer
		destinationSize = [[args objectAtIndex: index + 1] intValue];
		[args removeObjectAtIndex: index + 1];
		[args removeObjectAtIndex: index];
	}
	
	if ( [args count] != 2 || [args indexOfObject: @"--help"] != NSNotFound || destinationSize <= 0 )
	{
		fprintf( stderr, "resizePicture [options] file\n" );
		fprintf( stderr, "\t--dpi dpi\tSpecifies the destination size of the image in pixels\n" );
		fprintf( stderr, "\t--help\tPrint this help message\n" );
		return 1;
	}
	
	NSString* sourcePath = [args objectAtIndex: 0];
	NSImage* source = [ [NSImage alloc] initWithContentsOfFile: sourcePath ];
	[source setScalesWhenResized: YES];
	
	// Tip from http://www.omnigroup.com/mailman/archive/macosx-dev/2002-February/023366.html
	// Allows setCurrentPage to do anything
	[source setDataRetained: YES];
	
	if ( source == nil )
	{
		fprintf( stderr, "Source image '%s' could not be loaded\n", argv[1] );
		return 2;
	}
	
	// The output file name
	NSString* outputFilePath = [args objectAtIndex: 1];
	
	NSSize sourceSize = [source size];
		
	NSSize size = NSMakeSize( destinationSize, destinationSize );
	
	[NSApplication sharedApplication];
	[[NSGraphicsContext currentContext] setImageInterpolation: NSImageInterpolationHigh];
	
	[source setSize: size];
	NSRect destinationRect = NSMakeRect( 0, 0, size.width, size.height );
	
	NSImage* image = [[NSImage alloc] initWithSize:size];
	[image lockFocus];
	    
	NSEraseRect( destinationRect );
	[source drawInRect: destinationRect
		fromRect: destinationRect
		operation: NSCompositeCopy fraction: 1.0];
	
	NSBitmapImageRep* bitmap = [ [NSBitmapImageRep alloc]
				     initWithFocusedViewRect: destinationRect ];
	
	NSData* data = [bitmap representationUsingType:NSPNGFileType properties:nil];
	[bitmap release];
	
	[[NSFileManager defaultManager]
	  createFileAtPath: outputFilePath
	  contents: data
	  attributes: nil ];
	
	[image unlockFocus];
	[image release];
	[pool release];
}

6. Making nice XHTML layout

One little stage is still left, and that is formatting the XML files so that they make nice XHTML files that we can render in a browser. We will generate an HTML document per album that shows the bigger image and a track listing, and have a small scrollable DIV-frame with all of the thumbnails as an overview of the album database.

Code listing 6.1: makeFinalXHtml.xsl makes final XHTML file

<xsl:stylesheet
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xsl:version="1.0">
            
  <xsl:template match="xmlLibrary">
    <html>
      <head>
        <title>Xml Library - Exported from iTunes</title>
        <meta name="GENERATOR" content="CCTunes"/>
        <meta name="DESCRIPTION" content="Music Library published with CCTunes.  Find out all about CCTunes at
          http://www.coin-c.com/CCTunes/"/>
        <link rel="stylesheet" href="default.css"/>
      </head>
      <body bgcolor="#000000"
        style="background-color: rgb(0, 0, 0); color: rgb(153, 153, 153);">
        <xsl:variable name="librarywidth" select="count(AlbumItem) * 64"/>
        <table style="width: 100%; height: 100%; text-align: left; margin-left: auto; margin-right: auto;">
        <tbody>
          <tr>
            <td style="height: 100px; vertical-align: top;" class="albumTitle"><br/>
              CCTunes Exported Library.
            </td>
            <td style="width: 360px; height: 100px;">
            <div style="position: relative; left: 0px; width: 355px; top: 0px; height: 90px; overflow: auto; background-color: rgb(0, 0, 0);">
              <div style="position: relative; top: 0px; left: 0px; height: 60px; width: {$librarywidth}px;">
                <xsl:for-each select="AlbumItem">
                <xsl:sort select="Artist"/>
                <xsl:sort select="Album"/>
                <xsl:variable name="albumname" select="Album"/>
                <xsl:variable name="artistname" select="Artist"/>
                <xsl:variable name="albumid" select="AlbumID"/>
                  <xsl:if test="$artistname!='' and $albumname!=''">
                    <xsl:text disable-output-escaping="yes">&lt;a href=&quot;</xsl:text>
                    <xsl:value-of select="$albumid"/>
                    <xsl:text disable-output-escaping="yes">.html&quot; target=&quot;inlineframe&quot;&gt;
                      &lt;IMG WIDTH=&quot;60&quot; HEIGHT=&quot;60&quot; border=&quot;0&quot;
                      SRC=&quot;</xsl:text>
                    <xsl:value-of select="AlbumID"/>
                    <xsl:text disable-output-escaping="yes">-low.png&quot;</xsl:text>
                        <xsl:text>ALT=&quot;</xsl:text>
                        <xsl:value-of select="$artistname"/><xsl:text> -- </xsl:text><xsl:value-of select="$albumname"/>
                        <xsl:text disable-output-escaping="yes">&quot; TITLE=&quot;</xsl:text>
                        <xsl:value-of select="$artistname"/><xsl:text> -- </xsl:text><xsl:value-of select="$albumname"/>
                        <xsl:text disable-output-escaping="yes">&quot;/&gt;</xsl:text>
                    <xsl:text disable-output-escaping="yes">&lt;/a&gt;</xsl:text>
                  </xsl:if>
              </xsl:for-each>
            </div>
            </div>
          </td>
        </tr>
        <tr>
          <td colspan="2"> <iframe height="100%" width="100%"
              scrolling="auto" src="" frameborder="1"
              name="inlineframe"></iframe> <br/>
          </td>
        </tr>
        <tr>
          <td align="right" height="15" colspan="2" class="cctunesLink">
            Published Using
            <a class="cctunesLink" href="http://www.coin-c.com/CCTunes/">CCTunes</a>
          </td>
    </tr>
      </tbody>
    </table>
  </body>
</html>
</xsl:template>
</xsl:stylesheet>

7. One Simple Package to Rule Them All

To have an easy to use interface, I've linked everything together in a package. To create a package that triggers a bash script that executes everything we use a little AppleScript. This AppleScript we compile as a package and then we add the bash script and all the binaries in it.

To provide for some interactivity with the user, a nice little application is used that gives a wide variety of dialogs to be called from the command line, CocoaDialog.

The final bash script that does everything is not so simple anymore as it used to be, but I think it's still readable enough to include here:

Code listing 7.1: generate.sh bash script to do it all

#!/bin/bash

if [ "${1}" ]; then
  musicXmlFile="$1"
else
  musicXmlFile=~/Music/iTunes/iTunes\ Music\ Library.xml
fi

export set WORKINGDIR=`dirname "$0"`
export set destinationDir=$2
export set destinationDirUrl=`"${WORKINGDIR}/urlencode.sh" "$2"`

"${WORKINGDIR}/xml" tr "${WORKINGDIR}/../xml/makeProperXml.xsl" \
	"$musicXmlFile" \
	> /tmp/mymusic$$.xml

"${WORKINGDIR}/xml" tr "${WORKINGDIR}/../xml/makeMainXml.xsl" \
    -s "destinationDir=$destinationDirUrl" \
    /tmp/mymusic$$.xml > /tmp/mymusic2$$.xml

OLDIDS=`"${WORKINGDIR}/xml" sel -T -t -m xmlLibrary/includeXml -v "concat(node(),' ')" /tmp/mymusic2$$.xml`

echo "<?xml version=\"1.0\"?>" > "${destinationDir}/mymusic.xml"
echo "<xmlLibrary xmlns:xi=\"http://www.w3.org/2001/XInclude\">" >> "${destinationDir}/mymusic.xml"

for oldit in $OLDIDS; do
    file="${oldit}.xml"
    PICTUREURL=`"${WORKINGDIR}/../bin/xml" sel -T -t -v /AlbumItem/Picture/PictureURL "${destinationDir}/${file}"`
    NEWID=`"${WORKINGDIR}/createId.command" "${destinationDir}/$file"`
    "${WORKINGDIR}/getPicture" "${PICTUREURL}" "${destinationDir}/${NEWID}-high"
    "${WORKINGDIR}/resizePicture" --dpi 60 "${destinationDir}/${NEWID}-high" "${destinationDir}/${NEWID}-low.png"
    "${WORKINGDIR}/xml" ed -u /AlbumItem/AlbumID -v "$NEWID" "${destinationDir}/$file" > "${destinationDir}/${NEWID}.xml"
    rm -f "${destinationDir}/$file"
    "${WORKINGDIR}/xml" tr --xinclude "${WORKINGDIR}/../../Resources/English.lproj/makeFinalXHtmlDetail.xsl" \
	"${destinationDir}/${NEWID}.xml" > "${destinationDir}/${NEWID}.html"
    echo "<xi:include href=\"${NEWID}.xml\" />" >> "${destinationDir}/mymusic.xml"
done

echo "</xmlLibrary>" >> "${destinationDir}/mymusic.xml"

# for every xml file, apply stylesheet to obtain corresponding html file

# for mymusic.xml file, apply main stylesheet to obtain index file

"${WORKINGDIR}/xml" tr --xinclude "${WORKINGDIR}/../../Resources/English.lproj/makeFinalXHtml.xsl" \
   "${destinationDir}/mymusic.xml" > "${destinationDir}/mymusic.html"

cp "${WORKINGDIR}/../../Resources/default.css" "${destinationDir}/default.css"

open "${destinationDir}/mymusic.html"

rm /tmp/mymusic$$.xml
rm /tmp/mymusic2$$.xml

8. Conclusion

So, if you want to do a quick XHTML list of your mp3 collection, you need not do much. If you want, there is a package that you can download, and hopefully with the explanations given here you are even able to adapt everything to your needs.

9. Future Wishlist and Todo

In the beginning I was doing this to get to know something about XSLT and about perl scripting etc... However, things seem to be evolving towards Cocoa and I think in the near future I will try and make everything simply one Cocoa application. The basis for the whole application are here, I don't think much will change about that, at least not in the forseeable future.

A short list of the things I want to do however:

  • A way to show the play count, which is usually a good measure on how much I like an album I bought. Related to this, a way to merge the xml files, since I listen to albums both at work as home.
  • You still need the iTunes xml file. It would be nice if it could take extra information from this XML file, but get the main information from the mp3 files itself.
  • Nicer layout of the html by means of css.
  • A speedup with Muenchian Sorting
  • One XSL template to do the whole transformation.
  • Make everything into a nice little Cocoa Application with a nice GUI.
  • Probably even more...

And a short list of the things I need to do:

  • Update documentation to reflect the current state of CCTunes.
  • Make sure versioning is in place.
  • Provide older versions online.

10. Resources

Downloading all the scripts

These scripts can be found in the package of the application at http://www.coin-c.com/downloads/CCTunes.dmg.gz. Surely you could copy and paste everything from this file, but downloading and untarring is a bit safer, since sometimes the whitespace is important.

Resources used in this article

Link Description
http://www.w3.org/TR/xslt#key The XSLT specification. It is the dry stuff, the spec and just the spec.
http://www.jenitennison.com/xslt/grouping/muenchian.html This could be a helpful technique to aid in speeding up the XML parsing we are doing. To be investigated.
http://www.xmldatabases.org/WK/blog/1086?t=item This is the article that tells about how to clean up the property list that iTunes has as an XML file.
http://www.oreillynet.com/lpt/wlg/3130 Nice article about what's in iTunes, and how.
http://www.macosxhints.com/article.php?story=20030429003250559 A tip about how to get images from iTunes, in which the perl hint occurs.

Other Resources

Name Link Description
mp3report http://mp3report.sourceforge.net/ Suppose you have no iTunes XML file, mp3report could be used to generate one. It runs over your collection of mp3 files and does about the same as all these scripts.
AudioHiJack http://www.rogueamoeba.com/audiohijack/ Very good application to record online radio with. Shareware at a nice price.
Doug's AppleScript for iTunes http://www.malcolmadams.com/itunes/index.php Ultimate resource for everything AppleScriptable in iTunes.
ImageMagick http://www.imagemagick.org/ Whatever you need to convert images if GraphicConverter does not seem to do the trick. Available in a weird license, which I think is as free as possible for an image manipulation program that supports a wide range of formats like this one.
Clutter http://www.sprote.com/clutter/ Nice application to get the Artwork in your iTunes. It has not been updated for a while however.
iCatalog http://www.kavasoft.com/iTunesCatalog/ Shareware application that does the same as the scripts in this article. Except, without paying you only get albums from artists starting with letters A-E. Easy workaround: put an A-E in front of all your artists or so? Naaah, just pay or use the scripts here.
iPlaylist http://iplaylist.knownworld.net/index.html Donationware application to publicize your library on a web page that looks like you're in iTunes itself. Does not extract images at all.
itunes2html http://disobey.com/detergent/code/itunes2html.txt Somebody took the perl script approach for converting iTunes playlists to HTML. iPlaylist is based on this.

Document Layout

This document was made with the excellent guide style sheet also used to make the gentoo documentation, with some minor adaptations done by myself.

The XSLT file to generate all of this, is at http://dev.gentoo.org/~swift/local/guide.xsl.



Creative Commons License This work is licensed under a Creative Commons License.
line
Updated $LastChangedDate: 2004-11-18 23:41:16 +0100 (Thu, 18 Nov 2004) $
line
Kristof Van Landschoot
Author

line
Summary: This is a short description of the things I did to get my library published from the XML file that iTunes keeps to a simple HTML file, album based.
line
Multi-page Version
Copyright 2003-2004 Coin-C bvba. Questions, Comments, Corrections? Email cctunes@coin-c.com.