MacDevCenter    
 Published on MacDevCenter (http://www.macdevcenter.com/)
 See this if you're having trouble printing code examples


Using Java to Expand iTunes Functionality

by David Miller
09/03/2003

AppleScript fans have had hooks into iTunes for several years, which makes the information in your music library accessible to other scriptable applications. And while AppleScript can be glued together with other components in OS X (via the /usr/bin/osascript command in the terminal and AppleScript's do shell script command), attempting to provide your music information to non-scriptable applications or resources (example: a web site powered by PHP and MySQL) has traditionally been done in hack-and-scratch ways.

MyTunes fills that void by providing an easy-to-use and extensible framework that allows use of your iTunes library via Java. But it's also a case study in integrating open standards with Apple's iLife applications to provide extra functionality. This article contains an introduction to MyTunes, in addition to the basic concepts of how and why it works the way it does.

What's in a Name?

MyTunes has been around for several years in a previous form; it was originally an AppleScript that created an XML-ish file from the iTunes library, which was then parsed by a Perl script to load into a MySQL database (hence the name: MySQL + iTunes = MyTunes). However, iTunes version 4+ creates a file called iTunes Music Library.xml in your music library folder, so the first half of the old version was no longer necessary. And because the Perl script had to be rewritten to parse the new file that is automatically maintained by iTunes, the package was overhauled to:

Contained in the iTunes Music Library.xml file is just about every piece of data about your music collection that you could possibly want:

To see what other information is included, open it up in your favorite text editor and take a peek.

A little bit later on, we'll walk through an example of how the bigger and better MyTunes accomplishes the task previously completed by its predecessor, and in doing so, provides a reusable framework to avoid rewriting the meat and potatoes; instead, we'll be spending our time using the information, rather than retrieving the information. But before we start playing around with MyTunes, we'll need to install the necessary components that comprise the package.

Downloading & Installing MyTunes

As with any other time that you're dealing with XML, the first thing that you'll need is an XML parser. MyTunes uses the Apache Software Foundation's Xerces parser, which is freely available here. You'll also need Apache's XML-RPC library to take advantage of the new "remote control" features available (but more on that later).

The latest version of MyTunes is available here (included in the distribution is the currently incomplete javadoc documentation -- feel free to refer to the appropriate page when we're working our way through the examples). Be sure to add these files to your CLASSPATH (refer to this article at java.sun.com to set up the CLASSPATH environment variable), or place all of the JAR files in the /Library/Java/Extensions/ folder, as all classes located here are automatically made available to your Java applications.

Customizing MyTunes

One of the major goals of the new version of MyTunes is to make it easy for other people (besides myself) to use. One step in accomplishing this goal is to use a configuration file that holds all necessary parameters; all classes in the package that have parameterized variables will look for the configuration file located at ~/.mytunes.xml. A sample file is located here.

Once you've downloaded the sample to your desktop, place it in the proper location with the following command:

mv ~/Desktop/mytunes.xml ~/.mytunes.xml 

You'll notice that it won't show up when you view your home directory in the Finder. This is because every file that has a "." as the first character in its name is hidden. In order to view and edit, you'll have to use the Terminal in conjunction with a text editor such as emacs, vi, or pico:

emacs ~/.mytunes.xml

You should see something similar to the following.

Sample configuration file for the MyTunes package
Sample configuration file for the MyTunes package

As you can see from the above image, there are several groups of properties:

Getting Started

Once you have the necessary components installed and have set the proper values in your configuration file, open a Terminal window and enter the command:

java com.fivevoltlogic.mytunes.MyTunesLibrary

You should see something similar to the following:

Sample output from MyTunesLibrary
Sample output from MyTunesLibrary

If you receive a ClassNotFoundException, then one (or more) of the JAR files aren't located in /Library/Java/Extensions/ folder or another location specified in your CLASSPATH. One other problem that you might run into is a thrown java.io.IOException: this will arise when you are not connected to the Internet when using MyTunesLibrary. This situation stems from the fact that iTunes Music Library.xml's DTD (the guide saying how a particular XML document is constructed) is declared as PUBLIC, and as such, it is located on Apple's servers. Thus, the XML parser attempts to load the document from this location, and throws a java.io.IOException when the file can't be opened.

Looking at the sample output, we can see that I currently have 1839 tracks and 20 playlists in my iTunes library. However, this is the only information that MyTunesLibrary provides on its own; while MyTunesLibrary parses the information in the library (track names, albums, the playlists that songs belong to, etc.), the class doesn't actually do anything with the data. This separation of logic allows us to reuse the class in different applications without rewriting the bulk of the code (which mainly consists of parsing the XML file to extract the desired information).

With that in mind, in order to use the information made available to us, we have to create a class that has appropriate methods invoked by MyTunesLibrary as callbacks. For anyone who has worked with SAX before, the ContentHandler interface should spring immediately to mind; with SAX we receive notifications when we've reached XML elements, text, or other items. While we're indirectly interested in this information, we want to abstract the XML to a higher level than tags and attributes.

Design Considerations

While MyTunes' callback structure is similar to SAX, the XML file is actually parsed with DOM. The reasoning behind this choice is mainly because of the non-semantic markup in iTunes Music Library.xml; the entire library is described by approximately 10 tags, which makes keeping track of our location in the file while parsing problematic. Because we are using DOM, there is a chance that you could run into memory issues if you have a large music library. In the event that you do run into memory errors, try increasing the memory allocated to your JVM to fix the problem.

Extending Chord

The com.fivevoltlogic.mytunes.Chord class is to MyTunes what the ContentHandler interface is to SAX; Chord's methods can be overridden to suit your needs. As mentioned earlier, we'll create a Chord to populate a MySQL database with the information from your iTunes library. While the design could be improved in many ways, it serves as an excellent introduction, and after reading and walking through this tutorial, you'll be confident and knowledgeable enough to use MyTunes in any way that you could wish.

Prerequisites

The walkthrough assumes the reader already has MySQL up and running on his or her Mac, and has a basic level of competency in using and administrating the server. If this is not the case, Marc Liyonage has an excellent walkthrough for installing the MySQL server on OS X, and the online documentation is available here. In addition to xerces.jar and mytunes.jar, we'll need a JDBC driver to connect to the MySQL database that we're populating. MySQL Connector/J will do exactly that. Again, extract and place the JAR file in /Library/Java/Extensions/ and we'll be ready to go.

The first step is to create the database tables that will hold the desired information; here is a MySQL script that will accomplish that task. Three tables will be created (see the script for details on which columns will be created in each table to correspond to the fields of the Track and Playlist beans), two of which are self-explanatory:

To create the tables in the FVL database (as an example), open up the Terminal and type the following command:


mysql FVL < mytunes.mysql

With the database ready to go, the next step is to extend the Chord class to populate the database with the information that it receives. The resulting class is available here, and upon running it, you should receive no output. However, upon checking our database, we see that the information has been properly stored in the database:

Viewing the number of tracks in our iTunes music library
Viewing the number of tracks in our iTunes music library

And now that our information is stored in an SQL database, we can run a variety of queries on our music data:

Wrapping Up

With MySQLImport, we saw how to populate an SQL database with the information from our iTunes library. However, this task is just one example of using MyTunes; if you come up with any clever ideas for other Chords, post your thoughts in the comments section at the bottom of this article.

Remote Control

We just saw how to create a Chord to populate a MySQL database with information from the iTunes library. However, the com.fivevoltlogic.mytunes package contains several utility classes that are not related to the library. As you can probably guess by the heading, MyTunes also allows you to control iTunes via Java.

How Does it Work?

If you open up iTunes' dictionary in Script Editor and compare it to com.fivevoltlogic.mytunes.Remote's API, you'll see there is a similarity between the two. There's good reason for this: the majority of Remote's methods are actually just wrappers around iTunes' available AppleScript commands:

When one of the above methods is invoked, a file is created in the directory indicated by the System property java.io.tmpdir (which, by default, evaluates to /tmp). The contents of this file are merely an AppleScript that will be passed as an argument to the /usr/bin/osascript command. For example, create an instance of Remote and invoke the playPlaylistTrack(playlist, track), as in this example, a file named mytunes.remote will be created containing the following text:


tell application "iTunes"
  play (track 4 of playlist 1)
end tell
	

Related Reading

Java and XML
Solutions to Real-World Problems
By Brett McLaughlin

This file is then passed as an argument to the /usr/bin/osascript command.

Following the completion (successful or not) of the method's execution, the temporary file will be deleted to allow the next command to be executed. Because control of iTunes is done in this manner, the method is required to be synchronized.

The Final Piece of the Puzzle

You have now been introduced to all classes in the com.fivevoltlogic.mytunes package but one. We just saw how to control iTunes from a Java class that is running on the local machine (localhost); the next step is to control iTunes from a different machine. And since we're using Java and XML as our foundation, there are three choices to implement this feature:

All three frameworks are under the same general umbrella: distributed computing. And with three different choices, there are a variety of reasons to choose one over the others in certain situations. RMI's main drawback is that both sides of the communication line must be written in Java. While this isn't necessarily bad, the client, ideally, shouldn't be tied down to just one language. And because XML-RPC is easier than SOAP to get up and running, we'll use it as our transport from to client to server and back again. For a more detailed description and comparison of the protocols, see Java & XML 2nd Edition by Brett McLaughlin, O'Reilly & Associates, 2001.

The first step is to get the server up and running. If you wish, you can specify a port that the server should listen to; by default, it uses the property given in ~/.mytunes.xml:

Sample output when starting the XML-RPC server
Sample output when starting the XML-RPC server

Once BaseStation is listening, we'll need to create a client to talk to the server. Because of XML-RPC's simplicity, this can be done in a matter of minutes (for an introduction to XML-RPC, see Eric Kidd's how-to). The source for RPCTest.java is available here. Because both the server and client are running on the same machine, this example isn't very practical, but if you have two or more Macs on a network, it is trivial to control another Mac to behave as a jukebox. Throw this into a servlet (see this article on Apple's Developer Connection to get Apache Tomcat up and running on OS X) and you've got remote control of iTunes from a web interface.

Roadmap

You've now seen how to use most of the desired features of MyTunes are in place. However, there are several things that remain to be completed:

These changes and additions will be incorporated into the package as time permits. If you're interested in the package, stay tuned to the project's home page to keep abreast of any updates and changes that will be released. If there's anything else that you think should be incorporated, send your suggestions to me via email( ), or post them in the comments section below.

David Miller is combining his passions of photography and working on the web at iStockphoto; when not hacking away with a text editor and a few web browsers in hand, he can be seen huddled over his laptop tweaking levels and curves for his freelance photography. Keep track of David's latest projects over at his home on the web.


Return to the Mac DevCenter

Copyright © 2009 O'Reilly Media, Inc.