Mimsy Were the Borogoves

Hacks: Articles about programming in Python, Perl, Swift, BASIC, and whatever else I happen to feel like hacking at.

Copying an iTunes playlist

Jerry Stratton, May 15, 2011

When you give, you receive. For my Pioneer 3200BT review I needed to copy 32 gigabytes of music to an SD card, to see how fast the unit would load that many tracks. Making a giant playlist in iTunes was easy. But iTuneMyWalkman, an otherwise very useful application, was taking forever just to collect the list of songs from iTunes—it had taken over two hours and hadn’t even started a copy yet. And iTunes itself doesn’t let you drag that many items to the Finder.

But the iTunes script I wrote as a joke for Palm does almost everything I needed: it loops through all tracks in a named playlist. All I had to do was add a function to copy each track to a specified folder.

At the top, along with the other imports, add:

  • import urllib, shutil

The urllib library is necessary because iTunes stores track locations in its XML file as file:// encoded URLs. The shutil library will copy a file.

In the OptionParser section, add a new option:

  • parser.add_option('-c', '--copy', help='Copy tracks')

Because --copy means it has to loop through all tracks, just like --space and --tracks, change the “if options.showTracks or options.tallySpace:” line to:

  • if options.showTracks or options.tallySpace or options.copy:

Underneath the other two track-based options, add an “if” for copying:

[toggle code]

  • if options.tallySpace and 'Size' in track:
    • spaceUsed = spaceUsed + int(track['Size'])
  • if options.copy:
    • copyTrack(track)

And, finally, among the other functions, add a copyTrack function:

[toggle code]

  • def copyTrack(track):
    • source = track['Location']
    • if not source.startswith('file://localhost'):
      • #probably an online stream, but report it just in case
      • print 'Ignoring', source
      • return
    • #determine source path
    • source = source[16:]
    • source = urllib.url2pathname(source)
    • #determine destination path
    • albumFolder, fileName = os.path.split(source)
    • artistFolder, albumName = os.path.split(albumFolder)
    • musicFolder, artistName = os.path.split(artistFolder)
    • destination = os.path.join(options.copy, artistName, albumName, fileName)
    • destinationDir = os.path.join(options.copy, artistName, albumName)
    • #copy file
    • if os.path.exists(destination):
      • newStats = os.stat(source)
      • oldStats = os.stat(destination)
      • if newStats.st_mtime == oldStats.st_mtime:
        • return
      • elif newStats.st_mtime < oldStats.st_mtime:
        • print '\nThere is a newer version of', artistName, albumName, fileName, 'at', options.copy
        • return
    • if not os.path.exists(destinationDir):
      • os.makedirs(destinationDir)
    • status = 'Copying ' + str(currentItem) + ' of ' + str(itemCount)
    • print '\b%s%s'%(status,'\b' * len(status)),
    • sys.stdout.flush()
    • shutil.copy2(source, destination)

That will copy to whatever folder you specify. So, for example, to copy the playlist “Big Road Trip” to an SD card named “ROAD TRIP”, use something like:

  • itunesLists "Big Road Trip" --copy /Volumes/ROAD\ TRIP/Music/

The resulting tracks will be copied into /Volumes/ROAD TRIP/Music, and organized into folders by artist and then by album. Compilations will be in a “Compilations” folder.

Note that this script has been tested all of one time, so be careful out there!

In response to A present for Palm: Palm needs a little help understanding XML.