Saving Pandora Songs to Rdio

I flew to Austin the other weekend for the Austin Music Hackathon and built a quick Chrome extension called “Pandora to Rdio“. (Lame name, I know.) It adds any track you “thumbs up” in Pandora to an Rdio playlist. Take the pain out of creating playlists by letting Pandora do the heavy lifting of finding music you like. Download the extension/grab its source code on my Github page.


Pandora is great at finding music I like, but it streams one track after another without any ability to repeat a song. Rdio is great at letting me pick and choose songs I want to listen to and repeat them as often as I like, but—despite its name—does a lousy job of finding me new music. I wanted to get a peanut butter chocolate situation going and combine the good parts of each service.

Revisiting Pandora

I created a somewhat popular Firefox extension called Harmony a few years back that automatically scrobbled songs you listened to on Pandora to It used an undocumented Pandora Javascript API that ended up being removed during one of Pandora’s redesigns (subsequently breaking my extension). I’d been meaning to tinker around with Pandora again since then, and what better time than at a music hackathon?

The latest version of the Pandora web player is in HTML, so hooking into it is straightforward. The Chrome Developer Tools came in handy, I simply right-clicked on the “thumbs up” button, hit “Inspect”, and had its CSS class name. I did the same for the song’s name and artist. With those, I used a few lines of jQuery to add an event listener to the “thumbs up” button and extract the values for song title and artist.

A screenshot of the Chrome Developer Tools inspecting the Pandora website

The trickier part was working with oAuth in a browser extension. The Chrome Extension documentation has a page dedicated to it, and provides a helper library that makes the oAuth dance a breeze. Well, except that if you use the code as provided, you’ll be presented with an error in your Javascript Console:

Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' chrome-extension-resource:". - chrome_ex_oauth.html:19

The reason for this is that the Chrome security policy doesn’t allow for HTML pages in extensions to have inline Javascript. The workaround is to move the small amount of Javascript code in chrome_ex_oauth.html into a new Javascript file. I called mine oauth_helper.js:

$(document).ready(function() { 

Here’s my updated chrome_ex_oauth.html and oauth_helper.js for reference.

Invisible Interfaces and Room for Improvement

When you install the extension, one of the things you’ll notice is that there isn’t anything to notice. Unlike most other extensions, Pandora to Rdio doesn’t add buttons or any other elements to the browser’s chrome. This was a conscious decision. The interface is Pandora itself. Like a song? Hit the thumbs up button in Pandora, the extension will silently take care of adding the song to your Rdio playlist. It’ll even create the special “Pandora Favorites” playlist automatically if it doesn’t exist. An ideal invisible interface only disturbs the user for errors the application couldn’t deal with on its own, so you won’t find any “successfully added track” type notifications. Success is the default, not something that needs to be called out every time. Unfortunately, Pandora to Rdio isn’t an ideal invisible interface and while it doesn’t disturb you with “successfully added track” type notifications, it also doesn’t call out errors properly. This is something I wanted to work on, but ended up running out of time.

Unfortunately, it’s also not packaged for distribution to non-technical users since each person needs to sign up for their own (free) Rdio Developer account and enter their Rdio API credentials in the background.js file. I wrote up step-by-step install instructions that should help make things easier.

Despite a few rough edges, I think the project is still a useful starting point for more elaborate hackathon projects. You can grab the source code on my Github page. If you end up using it to build something cool, let me know all about it, @rahims or!