Subscribe to RSS
Download
Album highlight:
Since their inception in late 2006, Tuesday Spoils have been catching eyes and ears with their unique, vibrant sound and enigmatic live shows. They have recently released their new EP as a free download along with an iTunes LP.
 

Controlling iTunes

Playing tracks in iTunes is pretty easy. Tracks are exposed as a TrackObject which has a method play which starts playback. How you get access to these track objects however is a bit more difficult.

The window.iTunes object is the interface to communicating with iTunes. It exposes several interesting functions and properties but for this tutorial we will focus on getting access to tracks from the music library and playing them.

There are currently four methods that I know of to get access to track objects which can be played:

By playlist

Using the Javascript function findPlaylistsByName you can return a playlist object containing all the tracks in that list. You can search for these playlists by passing the name of the playlist to the function as a string.

For example if I had a playlist called iTunesLP containing three songs of which I wanted to play the third, I could do something like this:

var thePlaylistArray = window.iTunes.findPlaylistsByName("iTunesLP");

// First check if we found anything
if (thePlaylistArray && thePlaylistArray.length > 0) {

	// We assume that the first playlist we found is the correct one
	var thePlaylist = thePlaylistArray[0];

	// The list is zero-indexed so to play the third track we tell it to play index 2
	thePlaylist.play(2);

}

All the search functions return arrays. In the example above an array of playlists. This is because under certain circumstances you might get multiple playlists that match your query.

This works very well, but not ideal, because it would require that for every album you use this method with, you have to create a playlist. There are better ways:

By cnID

This is the method Apple's iTunes LPs use. Every track that you purchase from the iTunes store has a unique track identifier (cnID) inside. In the iTunes LP's manifest.xml these identifiers are matched to so-called XIDs. These are unique strings that are used within the iTunes LP to reference tracks with the unique track identifiers.

You can request these tracks using the findTracksByXID.

var theTrackArray = window.iTunes.findTracksByXID("Vendor:Artist:Album:001");

// First check if we found anything
if (theTrackArray && theTrackArray.length > 0) {
	
	// We assume that the first track we found is the correct one
	var theTrack = theTrackArray [0];
	
	theTrack.play();
}

It's reasonably easy to invent a few XIDs of which you can be sure that they won't be used for official iTunes LPs. However because they are linked to a numerical cnID, it can become a bit trickier. If your music is already published on the iTunes store you can still use your unique track ids in this way if you wish. However if you publish your music in another way you risk using cnIDs that Apple will use later on.

Another disadvantage is that there are no good tools to set the cnID atom tag. Atomic Parsley is able to do this with a few minor code modifications. However out-of-the-box it can't write the Apple specific mp4 atoms.

A final problem is that this is hard linked to specific mp4 atoms. It won't work for mp3's or other file formats that you may want to use.

By XID

Yes we had that one already, sort of. Another method however is to skip the manifest.xml and instead of using a "cnID" mp4 atom we use a "xid " mp4 atom (note the space in there).

This avoids the problem of using cnIDs that aren't issued by Apple, but has all the other problems. If you however control how the music files are distributed, and are able to modify Atomic Parsley, this might not be a problem though.

By tags

Probably the most useful way to find and play tracks for indie-developers is by searching for tags in the mp3 or mp4 files. This is very versatile because it is independant of the file format in which the files are distributed. There are however some quirks you have to take into account.

Searching by tags is done using the findTracksByTextFields function. This function requires a very particular structured object containing the tags to look for. The search query will only match with files which completely match the tags you are looking for. Also note that it will also include bonus features such as iTunes LPs, booklets, video files, that also match the query.

Let's take our first example of this tutorial, and change it so it finds all the tracks from that album without needing to make a playlist for it, and then play it's third track again. To do this we will need to do some special stuff:

var theTrackArray = window.iTunes.findTracksByTextFields({album: "iTunesLP"});
var theTrackList = [];

// First check if we found anything
if (theTrackArray && theTrackArray.length > 0) {
	
	// Now we have everything that matches, which is too much
	
	for (var i = 0; i < theTrackArray.length; i++) {
		
		// We don't want the iTunes LP itself to end up in our playlist
		if (theTrackArray[i].name.indexOf("iTunes LP") == -1) {
			theTrackList.push(theTrackArray[i]);
		}
	}

	// Our new tracklist is zero-indexed so to play the third track we tell it to play index 2
	var theTrack = theTrackList[2];
	theTrack.play();

}

The findTracksByTextFields function requires an object as a parameter. The object supports at least the following properties:

album
The name of the album which the track is from.

title
The name of the track. This is curious because the API itself uses the name property in most other locations.

artist
The name of the artist.

People can be pretty stubborn about their mp3 tagging habbits. Some people insist on putting collaborations in the track name such as Some track (feat. Some Guy). While others will use the artist field for these collaborations and use the album artist field for the overall artist. However more desirable the second option is, many users won't have their tunes tagged this way. So the easiest way to get your iTunes LP working with their music library is to search by album name. It has a relatively low impact on users to ensure that the album name matches your desires. Then if the rest of the album is tagged properly, which means that at the least every track has a correct tracknumber, you can use that to match the tracks in the music library to your LP.

In our iTunes LPs we use the following implementation:

	var albumTracks = null;
	var albumPlaylist = null;

	Array.prototype.sortOn = function(aField) {
		var ArraySortField = "";

		function fieldSort(a, b) {
			if (a[ArraySortField] > b[ArraySortField])
				return 1;

			if (a[ArraySortField] < b[ArraySortField])
				return -1;

			return 0;
		}

		ArraySortField = aField;
		this.sort(fieldSort)
	}

	function findAlbumTracks(aAlbumName) {
		var theTracks = window.iTunes.findTracksByTextFields({album: aAlbumName});
		var theTrackList = [];

		if (theTracks != null)
		for (var i = 0; i < theTracks.length; i++) {
			var theTrack = theTracks[i];

			if ((theTrack != null) && (theTrack.name.indexOf("iTunes LP") == -1))
				theTrackList.push(theTrack);
		}

		theTrackList.sortOn("trackNumber");
		theTrackList.sortOn("discNumber");

		albumPlaylist = window.iTunes.createTempPlaylist();
		albumTracks = theTrackList;

		albumPlaylist.addTracks(albumTracks);
	}

	findAlbumTracks("iTunes LP");

This will search for all tracks in the album iTunes LP and filter out the iTunes LP itself. Then it will sort them by track number and then by disc number, which makes sure they are in the order they should be. Then we create a new temporary playlist which we add the tracks to. We make a new playlist because you can then start playing a track and it will continue playing the rest of the tracks from the album in sequence. Playing a specific track now becomes as easy as:

function playTrack(aTrackNumber, aFromPlaylist) {
	// If we want to play the track from the playlist (ie. continue with the rest of the songs after this one has finished playing), we use the playlist's play function
	
	if (aFromPlaylist) {
		albumPlaylist.play(aTrackNumber);
	} else {
		
		// Otherwise just play a single track
		var theTrack = albumTracks[aTrackNumber];
		
		if (theTrack) theTrack.play();
	}
}

Other functions

There are a few more functions to search through your music library.

findTracksByStoreID(<number>)
Finds all tracks which were purchased on a specific iTunes store. You can find out the store you wan't to search for by either looking at the mp4 atoms for a couple .m4a files you purchased from that store, or by using some kind of proxy-tool to sniff the data iTunes sends to the store web-page. For example the US store uses id: 143441.

findTracksByPersistentID(<number>)
Finds all tracks which match with the requested persistent-id. Every item in your library has a persistent id. This id is however unique to your music library. When you import one mp3 in two different music libraries they will both have a different persistent-id. This makes it not very useful for iTunes LPs which you wish to distribute. You can find these persistent-ids by looking at the iTunes Music Library.xml file in your iTunes folder.

findPlaylistsByPersistentID(<number>)
Finds all playlists which match with the requested persistent-id. See findTracksByPersistentID for more information.