silverorange labs is the playground of silverorange slice logo
silverorange labs

Working with XML Feeds - Last.fm

by Mike Gauthier  []  -  8 replies

While working on a weblog I had a friend ask me if she could have her recently played Last.fm tracks appear in her sidebar. Last.fm has a feature to add your recent tracks to your blog but unfortunately, the recent track list is implemented as a dynamically generated image. Last.fm does have a nifty chart designing tool but for a number of reasons I wanted to work with styleable XHTML markup. Luckily for me, Last.fm also provides a set of XML data feeds for all sorts of things including recently played tracks. For example, these are the recently played tracks in the silverorange office.

Having data feeds is great, but as you can see, the data is not an XHTML unordered list. There are several things that can be done at this point.

PHP/XSLT

Using XSLT (eXtensible Stylesheet Transformation), it is possible to transform this standalone markup into an XHTML document. Depending on your environment and level of XSLT experience this may or may not be easy to do. At silverorange, we heavily use PHP and PHP's XSLT support is not great. XSLT is available as a separate extension and is not enabled by default. Other platforms may have better support for XSLT.

PHP/DOM

PHP 5 contains an implementation of the DOM API that is similar to the one used in JavaScript. Using the DOM extension it is fairly easy to load the Last.fm data feed, parse the various XML nodes and display an unordered list. The code to do this looks something like this:

<?php

$uri = 'http://ws.audioscrobbler.com/1.0/user/so_office/recenttracks.xml';
$document = new DOMDocument();
$document->load($uri);
$tracks = $document->getElementsByTagName();
if ($tracks->length == 0) {
    echo '&lt;none recently&gt;';
} else {
    echo '<ul id="last_fm_tracks">';
    foreach ($tracks as $track) {
        echo '<li>';
        // further processing of DOMNode objects goes here ...
        echo '</li>';
    }
    echo '</ul>';
}
?>

Initially on my friend's blog I implemented the above PHP/DOM-based solution. Everything worked great until a few days later I noticed her blog was taking about thirty seconds to load. The problem, of course, is that the $document->load(); call was downloading the XML feed from Last.fm and Last.fm's servers were, at the time, overloaded. The rest of the PHP script responsible for generating the page was blocked waiting for an external resource to download.

Both the PHP/DOM solution and the PHP/XSLT solution will incur this blocking effect if a third-party web-server is experiencing problems. With the increasing prevalence and use of XML feeds for all kinds of data, the problem of relying on other people's web servers will only become greater. There are two primary ways to work around this problem.

1. Caching

  • write a script to periodically check the XML feed
  • if the content has changed, save the content locally
  • PHP using either the DOM or XSLT loads the local, cached XML

2. Asynchronous Requests

Using an asynchronous request, it is possible to load the XML feed separately from your main PHP script. This brings up the third way of doing things:

Asynchronous HTTP Request using JavaScript/DOM

This technique is frequently referred to as AJAX. Though the technique is a little more complicated, asynchronous requests mean no matter how slow Last.fm's servers are, my friend's blog loads as fast as our server can serve it up. Using AJAX to load Last.fm feeds turned out to be the best solution for my friend's blog. Since the DOM is used, the code is quite similar to the above PHP. For those interested, here is the Last.fm AJAX loader I wrote.

One important thing to keep in mind using AJAX to load remove XML feeds is that browsers will not allow loading documents from different domains than the domain the JavaScript is hosted from. To work around this, I created a simple proxy script in PHP that downloads the Last.fm feed. This proxy script also handles the UTF-8-to-ISO-8859-1 character set conversion since my friend's blog is using ISO-8859-1. Here is the full source code for the demo including my PHP proxy script.

The code is released under a BSD type license that gives users full rights to copy and use the function in programs, provided the copyright notice remains intact. If you have improvements to the function, feel free to post them here.

Replies to Mike Gauthier’s post: Working with XML Feeds - Last.fm
Thanks for this, very usefull. Only thing you might mention is that the sourcecode contains a base url in the metadata and that caused the following error: Error: uncaught exception: Permission denied to call method XMLHttpRequest.open.
Once I removed this it worked like a charm.

I took quite some while for me to figure this out. No biggie, though.

Mike Gauthier []
Pascal,

Glad you found the script useful. Thanks for noticing the problem with base hrefs in the demo script. I've updated the PHP and JavaScript so it now works if you just unzip all the files in a folder on your webserver.

Thanks! This script was just what I'm looking for. Great work.
Just a heads up that the demo doesn't appear to be functional:
http://labs.silverorange.com/local/solabs/last-fm/demo.php

I haven't given the code a test yet but I suspect if the demo isn't functional, neither will the code. Feel free to e-mail me if there is an update but I'll check back! :)

David J,

It's possible Last.fm was having service problems when you tried the demo. The demo works as of my writing and I havn't changed any code.

I can't get this script to work either. =( what lines in the script am I supposed to remove?
Not working for me either? The PHP gets the XML ok, but I think the AJAX call isn't processing the returned XML. I've had a browse though the JS but nothing is jumping out at me? The JS is loading ok, and the throbber spins so I know it's modifying the page element accordingly, if that's of any help.

Also- the demo at the top of the page is down, maybe it's being cached on your end??

Got it:

request_object.open('GET', uri, true);
request_object.send(null);
} }

Changed the last few lines of the script. Send should be Null, open should be after statechange (seems buggy before it).

Hope that helps! Thanks for the great script.

Post a Reply
:
:
(optional)
:
(optional)
Your email address will not be displayed with your reply.
:
Are You Human?: