A couple weeks ago I moved from Barcelona to Stockholm... by car! 3000km and 4 days later, I arrived at my new home, but not without consuming a large amount of music along the way. Here is a semi-automatically generated collection of my road-trip music data.
The Route
The Album Art Collage
The Playlist
The Code
First I retrieved my scrobbled tracks from Last.FM for the desired date range (in UTC UNIX timestamp format) using their API function user.getRecentTracks.
http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=hiqlokey&api_key=YOUR_API_KEY&from=1288439460&to=1288720800&limit=400&page=1
Next I parsed the XML data to extract each track's artist, album, name, URL, and date. Artist, track name, and date are displayed in the playlist above, with links to the track's Last.FM page. Here's a snippet of the XML document:
<?xml version="1.0" encoding="utf-8"?> <lfm status="ok"> <recenttracks user="hiqlokey" page="1" perPage="400" totalPages="1"> <track> <artist mbid="7754905b-8bf7-48e2-935a-03d566fec464">Moderat</artist> <name>Berlin</name> <streamable>0</streamable> <mbid></mbid> <album mbid=""></album> <url>http://www.last.fm/music/Moderat/_/Berlin</url> <image size="small"></image> <image size="medium"></image> <image size="large"></image> <image size="extralarge"></image> <date uts="1288720541">2 Nov 2010, 17:55</date> </track> <track> ... </track> ... </recenttracks></lfm>
Using the Artist and Album data from the previous step, I searched the Discogs API to retrieve album art and release information for my album art collage. I found some python bindings to get me started, but the code lacked access to the search request.
http://www.discogs.com/search?type=all&q=QUERY&f=xml&api_key=YOUR_API_KEY
However, it was easy enough to extend with a few lines of code. Here's what I added:
def search(self, query, qtype='all', xml=False): response = self.open(qtype, query, search=True) if not response: return elif not xml: data = {} exact_response = lxml.etree.fromstring(response).xpath('./exactresults') if exact_response: exact_response = exact_response[0] data['exact_results'] = { 'num':map(lambda e: Search(e.attrib), exact_response.xpath('./result'))[0].num, 'type':map(lambda e: Search(e.attrib), exact_response.xpath('./result'))[0].type, 'result':map(lambda e: tree2object(e, Result, type='Search'), exact_response.xpath('./result')) } else: data['exact_results'] = {} search_response = lxml.etree.fromstring(response).xpath('./searchresults') if search_response: search_response = search_response[0] data['search_results'] = { 'start':search_response.get('start'), 'end':search_response.get('end'), 'numResults':search_response.get('numResults'), 'results':map(lambda e: tree2object(e, Result, type='Search'), search_response.xpath('./result')) } else: data['search_results'] = {} return Search(data) else: return response
(Obviously I had to modify some of the other functions in the code, so contact me if you would like the complete source.)
I was then able to browse the search results from within python to choose the best candidate (usually the master release), and grab the URL to the release and the primary image. This step was bit bug-prone, as the API would often return incorrect image URLs if none were present for the master release. So a bit of manual verification was needed to ensure that the correct image URL was in fact retrieved :P
With all the data on hand, it was only a matter of generating the HTML code to bring it all together, as you can see above!
No comments:
Post a Comment