Writing an iTunes playlist generator in Python

The following is some quick & dirty code on how one might approach the idea of writing a playlist generator in Python.

The language almost doesn’t matter but ActivePython does ship with bindings that allow you easily interact with any Windows COM object. It worked nicely in conjunction with iTunes Windows SDK.

In the end it’s fairly trivial code hacked together in not much more than an hour or so but I’ve been quite happy with the playlists it creates.

Basic Design:

1) Provide a means for the caller to specify basic criteria for playlist generation. ie) # of songs, list of genre’s, propotion of songs per genre, etc.

2) Retrieve a list of songs on an iPod and their metadata relevant to generation (name, genre, play count, etc.).

3) Implement a simple algorithm to choose the appropriate # of songs from each genre as per the original criteria.

4) Create the playlist in iTunes

Code: (this was actually part of my last hack day project, so excuse the python inadequacies)

#!/usr/bin/env python

import pythoncom, win32com.client, time
import sys, os
import csv
from random import Random, shuffle

SOURCE_ID = "Adam's iPod"
EXPORT_FILE = "ipod.data"
CRITERIA = {"name":"Test Playlist", 
            "count":25, 
            "genres":{"Alternative":.15, "Other":.25, "Rock":.60}, 
             "minPlayCount":5}

def main():
    print 'attaching to iTunes...'
    iTunes = win32com.client.gencache.EnsureDispatch("iTunes.Application")
    
    print 'connecting to ' + SOURCE_ID + '...'
    source = iTunes.Sources.ItemByName(SOURCE_ID)
    
    iPodTracks = source.Playlists.ItemByName("Music").Tracks
    libraryTracks = iTunes.LibraryPlaylist.Tracks
    
    name = CRITERIA["name"]
    count = CRITERIA["count"]
    minPlayCount = CRITERIA["minPlayCount"]
    genres = CRITERIA["genres"]

    print 'creating a playlist called "' + name + '" with ' + str(count) + ' songs'
    
    # Create a map of genre -> song where each song satisfies the minPlayCount criteria
    songsByGenre = {}
    for i in range(1, libraryTracks.Count):
        track = libraryTracks.Item(i)        
        if track.Genre in genres and track.PlayedCount > minPlayCount:
            tracks = songsByGenre.get(track.Genre, [])
            tracks.append(track)
            songsByGenre[track.Genre] = tracks

    # For each applicable genre, pull a quasi random set of tracks
    shuffle = Random(None).shuffle
    playlistTracks = []
    for genre in songsByGenre.keys():
        numSongs = int(round(genres[genre] * count))
        tracks = songsByGenre[genre]
        shuffle(tracks)
        print genre + ': ' + str(len(tracks[0:numSongs])) + ' songs'
        for track in tracks[0:numSongs]:
            playlistTracks.append(track)
            
    # Remove any pre-existing playlists that exist with the same name
    librarySource = iTunes.Sources.ItemByName("Library")
    for i in range(1, librarySource.Playlists.Count + 1):
        try:
            playlist = librarySource.Playlists.Item(i)
            playlist = win32com.client.CastTo(playlist, 'IITLibraryPlaylist')
            if playlist.Name == name:
                playlist.Delete()
        except:
            pass
        
    # Create the playlist and add the tracks.
    playlist = win32com.client.CastTo(iTunes.CreatePlaylist(name), 'IITLibraryPlaylist')    
    for track in playlistTracks:        
        playlist.AddTrack(track)
    
if __name__ == '__main__':
    main()

Interesting Wired Article on the Search for Jim Gray

There’s an interesting article on Wired detailing the significant search for computing pioneer Jim Gray, lost at sea since January.

He was intense, I was intense, and we were both raised by single parents. Jim was like a mountain man who was also a brilliant scientist.” She liked to call him Mr. Database. (from Wired)

For those that don’t recognized the name, see Jim’s Wikipedia page. Jim is/was nothing short of a computing legend and many billions of personal worth have resulted from his contributions to database and transaction processing systems.

It’s truely amazing to what lengths the searchers went, it’s unfortunate that more progress wasn’t made.

Multiple Machines : Synergy to the rescue

This past week I’ve started using Synergy again. For those that haven’t heard of it before, Synergy is a software-based KVM application that allows you easily share a keyboard and mouse amongst multiple computers using a TCP/IP connection I first used the tool a couple years back but the most recent release was in April of 2006.

In the past I’ve tended to have nothing but problems with cheap (perhaps that’s the problem) hardware KVMs (power outages or system shutdowns). Instead of using cables, Synergy tunnels everything over TCP/IP and generally works as expected. I had a few inconsistencies in some applications when sharing from Linux -> Windows but vice-versa works like a charm. No problems crossing between computers from within Warcraft either :)

I’ve been running the server on a Windows XP laptop and the client on Ubutu ‘Fiesty Fawn’. In this configuration my laptop’s keyboard and mouse are shared transparent with Ubuntu.

It’s really quite nice so if you happen to be messing around with KVM-cables and the like, stop and check out Synergy. It’s open-source, available via Sourceforge and might just be exactly what you need.