More permanent stuff at http://www.dusbabek.org/~garyd

14 April 2009

Hacking the Yahoo! Media Player

I've been using the Yahoo! Media Player to stream Mp3s at Tagfriendly.  Minor glitches aside*, the only real complaints I have is that it isn't skinnable and there is no public API.

About a month ago I started prodding it to see what would make it squeak.  This post documents some of what I've found.

Disclaimer: I skimmed over the YMP terms of service and don't believe I'm breaking any of the rules.  You should also know that YMP is a) beta software, and b) a hosted application.  This means that any code you write or use that relies on specific methods or objects will be brittle and prone to breaking when Yahoo! releases updates.  That said, you're on your own; I didn't make you do anything.

YMP is one of those nifty internet tools you can use by simply embedding a <script> element in your markup.  If the page you've embedded it in contains links to mp3s, or if it links to an XSPF playlist, YMP picks it up and makes those MP3s streamable.  This is powerful if you're a non-technical blogger and want to have an embedded player on your site.

My aim is to leverage all of that, but to take the Yahoo! face off and give it my own.  I use jQuery to manipulate the Tagfriendly DOM, but really, any decent JS toolkit should allow you to get the same results.

The first thing you need to do is know when YMP is finished loading so that you can tell the UI to go away.  Due to the fact that the Javascript you embed includes a bootstrap that downloads other things, you can't count on YMP to be ready when your document is.  This is easily accomplished with a JS timer that polls to check whether or not YAHOO.MediaPlayer.setPlayerViewState is defined.  When it is, call YAHOO.MediaPlayer.setPlayerViewState(YAHOO.mediaplayer.View.DisplayState.HIDDEN); to make the YMP user interface go away.

Now you are in the drivers set to start working with the MediaPlayer object.  Here are some useful methods:

YAHOO.MediaPlayer.getTrackPosition()
     Gets the position offset (in seconds) of the currently playing (or paused) track.

YAHOO.MediaPlayer.getTrackDuration()
     Gets the track duration (in seconds).  YMP appears to grab this from ID3 tags in the mp3, so you can't always count on this piece of data to be there.

YAHOO.MediaPlayer.play()
     Tells the player to play the currently queued song.

YAHOO.MediaPlayer.pause()
     Tells the player to pause the currently playing song.

YAHOO.MediaPlayer.previous()
     Go back to the previous song.

YAHOO.MediaPlayer.controller.EventManager.onNextRequest.fire()
     I found it odd that there was no YAHOO.MediaPlayer.next() method.  This method does what you expect the non-existent next() would.  There are corresponding fire() objects for play, pause and previous as well.

YAHOO.MediaPlayer.getMetaData()   
     Returns an object that describes the currently queued song.  Useful properties there, gathered from ID3 and the XSPF, include 'title' and 'artistName'.  There're more if you care to look.

That's basically it--all you need to subvert the YMP UI and handle things your own way.  This really is a guerrila API hack, as I don't think the YMP designers intended a public API.  You can see the results  on the front page of Tagfriendly.  I went as far as providing a progress bar that gets updated as the song plays.  The Javascript source is freely available too.

In the future I plan on displaying cover art and linking the songs to their Tagfriendly description pages as well as to music stores.



* I suppose not so minor because it really bothers me:  YMP breaks down when you try to use a locally hosted XSPF file or the machine it is hosted on is stuck behind NAT.  Firebug reports that YMP executes a GET with the URI to the XSPF.  The backend of that GET does some data-munging of the XSPF contents.  The results contain a basic JSONified playlist.  The only problem is that if the [development] server that hosts the XSPF is behind NAT, that backend can't fetch anything.

3 comments:

Lucas Gonze said...

These are valuable hacks. If you posted a note to the mailing list at http://groups.yahoo.com/group/yhoomediaplayer it would help other people to reuse your experience.

About skinnability, the intent from the beginning was to empower developers to use CSS rather than Flash. Also you can replace the standard image sprite master with your own.

Lucas Gonze said...

These are valuable hacks. If you posted a note to the mailing list at http://groups.yahoo.com/group/yhoomediaplayer it would help other people to reuse your experience.

About skinnability, the intent from the beginning was to empower developers to use CSS rather than Flash. Also you can replace the standard image sprite master with your own.

Terry Thorsen said...

As a public service I hacked up a function that allows you to programatically play tracks by title. This is useful when trying to make images play, which the player doesn't seem to support.

img src="...jpg" style="cursor:pointer" onClick="play('Song Title')">

function play(id){
for(var i=0;i<10;i++){ // Rewind to beginning of playlist. I use a for counter just to avoid a possible infinite loop.
// If you have more than 10 tracks on page then increase this
YAHOO.MediaPlayer.previous();
var media=YAHOO.MediaPlayer.getMetaData();
if(media.title=="First Song Title") break; // Not totally necessary but if you know the title of the first song then this is clean
}
for(var i=0;i<10;i++){ // This advances the player until the track title matches the title you passed in. Then it plays the song.

var media=YAHOO.MediaPlayer.getMetaData();
if(media.title==id){
YAHOO.MediaPlayer.play();
break;
}
YAHOO.MediaPlayer.controller.EventManager.onNextRequest.fire();
}
}