George Schneeloch

Blog | Projects | Contact

Echo's Nest plugin for Rhythmbox

| categories: uncategorized

I wrote a Echo's Nest plugin for Rhythmbox! It's hosted on github. It was a pain sometimes. I've written before about how Rhythmbox is a pain to work with. The basic issues here were sparse documentation and broken features, although I shouldn't really complain too hard about something that people contribute to in their spare time.

I've been meaning to work with Echo's Nest for a little while. I first heard about the Davis square company when reading about them in the Phoenix, and then again when reading about Boston's Music Hack day. They have a large database of information about artists and songs, and their API provides easy (and free, for noncommercial use) access to things like artist similarity. They also have Echoprint, a music fingerprinting C++ library similar in idea to Shazam, although currently in beta.

The plugin basically looks up information about the currently playing artist, gets similar artists, matches them to music you have in your library, and populates a similar artists playlist. I started by googling "echo nest rhythmbox" to see what else was there. I found this plugin but it didn't work for me, probably due to API changes within Rhythmbox. I also wanted to do something that operated more or less independently once you got it going. And I figured it would be a good experience to do something that was all my own. I also looked at the Last FM plugins on the Rhythmbox plugins page, specifically looking at this one, which I borrowed a lot of code from. The official plugins in /usr/lib/rhythmbox/plugins were also very helpful, especially the Magnatune one.

There's an interesting balance between writing new code and borrowing existing code. I usually borrow to get started, because I like the gratification of getting started quickly, and because I'm better at iterating over code than creating new code from scratch if I'm unfamiliar with it. Since this plugin is open source I felt perfectly fine with that.

My first iteration was a plugin which printed out the currently playing song to the debug log. This wasn't too difficult once I got through some activation/deactiviation issues.

The next iteration was to make a playlist and show the currently playing song in it. In Rhythmbox this is done by implementing RB.BrowserSource. It has a RhythmDBQueryModel which holds a list of RhythmDBEntrys for each song. I struggled for a little while trying to figure out how to create a RhythmDBEntry from scratch, but then I realized that the current song is provided as a RhythmDBEntry so I could just use that variable. It would make sense to make this difficult to create from scratch since these entries on the playlist correspond to actual songs, which could be a artist, song title, and location on a file system, or something more complicated like an internet radio stream.

In the next (and current) iteration I wanted to contact Echo's Nest, get a list of songs, match them with the library on my laptop, and populate the playlist with them. The most frustrating part of this was a bug with querying for songs, which somehow affected my laptop even though it's a 64-bit laptop. Other examples showed people using db.query_new() but that function was removed for some reason. Even GLib.PtrArray didn't have much documentation associated with it, or why someone would want to use it to replace db.query_new(), to hold query results. I found that many query functions aren't supported in Python due to variable length arguments, which might explain why the documentation is so sparse for do_full_query_parsed. I worked around this eventually by iterating over the library myself, but this took a decent chunk of time, roughly the same amount of time as it took to get the rest of this working.

The other hurdle was contacting Echo's Nest. The API is pretty simple and I elected to just craft the URL myself instead of using pyechonest. The plugin writing guide recommends that people use asynchronous communication whenever doing anything that could take time, like network communication. They recommend two ways of doing it, using GIO or their Loader class, which uses GIO underneath. I got an error message when using either method, "The specified location is not supported", which didn't make much sense since it was a plain old http url. Currently it just uses urllib2 to do it synchronously, eventually I'll switch to Twisted to do it correctly.

So far I'm pretty happy with it. It does tend to get stuck in certain ways though. I currently ask for 100 similar artists, but there's no guarantee that the artists exist in my own library, especially if my library is small, or if a certain artist is overrepresented. Currently I just set it on shuffle mode to pick randomly from the given playlist, but I could weigh it to increase the chance of switching to a different artist each time. It would also be nice to have a similar feature on a song by song level, but I'm not sure the Echo Nest API provides anything for that.