Wednesday, September 22, 2010

RageTube: Using APIs to Bring Music Video Playlists Online

As I've mentioned in other posts, I have a thing for music videos. Whenever I visit a new foreign country, I try to find the local music videos channel and learn about their culture entirely through hours glued to the channel (it kind of works!). In Australia, I eventually discovered Rage, a 6-hour long stretch of music videos that plays every Friday night, Saturday morning, and Saturday night (midnight to 6am!). I did my best to reorganize my life so that I could catch atleast one of the 6-hour stretches each weekend, but alas, I eventually acquired hobbies and the semblance of a social life. At the same time, I've been increasingly disappointed with my worktime music options here — I can't use Pandora (US only), can't use Spotify (Europe only), and have had mixed success with Grooveshark. So, I decided to kill two birds with one stone, and make a mashup that'd let me watch Rage during the day in the form of Youtube music videos: RageTube!

To use RageTube, you provide a URL to any playlist from the Rage archives and it will take care of finding music videos on Youtube for all of the songs and playing through the list one at-a-time. You can skip through songs you don't like with the "Next" button, or you can click ahead or back to videos that you want to watch immediately. Plus, if you want to remember what songs you liked (or hated!), you can click the "Yay", "Meh", or "Nay" ratings widget and see your rating displayed in the playlist. That makes it even easier to find favorite videos to skip to later.

I wrote RageTube on a Monday morning and after sharing it on Twitter, I've discovered that there are quite a few Rage fans that are happy to have a way to enjoy Rage at work, including a few of my Australian colleagues abroad that never get the chance to watch Rage on TV anymore. I've added a few features since the initial version, like the ratings widgets, share-by-URL, and support for older playlists, but I still have much room to improve it, like by adding a playlist picker and playlist ratings.

Now, I have to admit I didn't write RageTube just because I needed a better music option at work — I also plan to use it as a demo in my upcoming GDD Tokyo Talk to showcase how much is possible with just the Google JavaScript APIs. The entire mashup is client-side - one HTML and 2 JS files - and it relies on 3 different JS APIs as well as some HTML5 functionality.

For those interested, here's how it works behind the scenes:

  • When you enter a URL, I send that URL through open.dapper.net, service that screen-scrapes websites and gives you the desired nodes in the format of your choice. I fetch the JSON format using a lightweight JSONP library. I store the song and title in an internal JS array and render them out as a scrollable list.
    var dappUrl = 'http://open.dapper.net/transform.php';
    var params = {'dappName': dappName, 'transformer': 'JSON', 'applyToUrl': playlistUrl}
    JSONP.get(dappUrl, params, function(json) {
      ....
    });
    
  • I then try to play the first song, and when I discover that there's no youtube ID stored in in the internal array (as will be the case for the first song), I use the Youtube JSON-C API to find the first matching video.
    var query = song.artist + ' ' + song.title;
    var searchUrl = 'http://gdata.youtube.com/feeds/api/videos';
    var params = {'v': '2',  'alt': 'jsonc',  'q': query}
    JSONP.get(searchUrl, params, function(json) {
      song.results = json.data.items;
      song.youtubeId = json.data.items[0].id;
      ...
    });
    
  • The first time that I play a video, I embed the Youtube player SWF in the page using SWFObject, specifying a few parameters that will make it possible for me to use JavaScript to interact with that player later using the Youtube Player API.
    var params = {allowScriptAccess: 'always', allowFullScreen: 'true'};
    var atts = {id: 'youtubeplayer'};
    swfobject.embedSWF('http://www.youtube.com/v/' + song.youtubeId +
     '?autoplay=1&fs=1&enablejsapi=1&playerapiid=ytplayer',
     'videoBlock', '425', '356', '8', null, null, params, atts);
    
  • Once I get programmatic access to the player, I register a callback so that I know whenever the player changes status (started/paused/ended).
    function onYouTubePlayerReady(playerId) {
      youtubePlayer = document.getElementById(playerId);
      youtubePlayer.addEventListener('onStateChange', 'onYouTubePlayerStateChange');
    }
    
  • When the video ends, I play the next song. (I've already got the Youtube ID for the next song because I always search for the next video whenever I start playing a video.)
    function onYouTubePlayerStateChange(newState) {
      if (newState == 0) {
        currentSong++;
        playNext();
      }
    }
    
  • To let users rate each video, I use a localStorage-based doodad that I wrote for another music video mashup.
    likerCol.appendChild(LIKER.createLikerMini(song.id));
    ...
    likerBlock.appendChild(LIKER.createLiker(song.id));
    

Happy RageTube'ing!

No comments: