Sunday, December 2, 2012

How to find more artists on Last.fm

When you've used Last.fm for a while you'll no doubt have found some friends, people with similar tastes or interesting genres (tags). Some of the music inside those libraries might be really good, but if you want to find the ones that you don't know yet it's a lot of work to find those.

In this post I'm going to show you how to make list of artists including the  information if you've played them before and include links to listen to the artists. For this you'll need access to a web server that works with PHP, that can be your hosting provider or your own local WAMP-server. Make sure it supports PHP 5+ since we'll be using the function simplexml_load_file. I'll make a separate post how to install a local server (don't worry, it's really easy).

Luckily there are a couple of methods in the Last.fm API that can help with finding artists and their information. Note that you'll need your own free API key for this (you can get one here). The methods that I'm using here don't require authentication so it'll be a lot easier getting the info.
  • user.getTopArtists - This methods fetches the list of artists that somebody has been playing. You can get a list for different time periods (including overall) and set the number of results per page. This is convenient for restricting the number of things the script has to check, if it's too much then it'll take too long to finish and the server will timeout. Setting it to a maximum of 25 works best for standard servers.
  • tag.getTopArtists - Almost the same as the previous method but without the time periods and with a limit of 1000 artists.
  • artist.getInfo - This one does most of the heavy lifting: for each of the artists that are listed by the previous two methods it checks it for their information and (since we also add a username) how many times we've played if.
There are more methods that provide lists of artists based on charts, location or groups but for now we'll only use these.


user.getTopArtists.php

I'm not going to paste the complete file here, just the important parts. At the end of the post there's a link to a zip-file that contains the needed files (including a header, footer and stylesheet).
<?php
//URL: user.getTopArtists.php?user=<OtherUser>&page=<Page#>
Although this is just a comment in the code, it shows you the template how to start the script: open the URL in a browser and replace the name of the person that you want to "hunt" and the starting page (usually 1).
//Get data from the URL
$Other_User = $_GET['user'];
$page = $_GET['page'];
$nextpage = $page + 1;
This part grabs the info from the URL and pastes them into our own variables. We'll use $nextpage to create a link at the top of the page to create the next page while we're looking through this one.
//Declare variable settings
$lastfmapi = 'INSERT YOUR API KEY HERE';
$echonestapi = 'FILDTEOIK2HBORODV';
$user = 'YOUR LAST.FM USERNAME';
$limit = '25';
$format = 'xml';
This part is where you enter your settings, change the values for the API key and your username here. In this example I'm restricting the number of results per page ($limit) to 25 since most servers stop building the page after 30 seconds. You can get more results per page by changing the settings in your php.ini for the max_execution_time (I've set mine to 500 seconds).
$echonestapi and $format are not needed to communicate with Last.fm but to get further information from Echonest (a bit like Last.fm but it only contains data about music, not the files)
//Building the Artist list
$CompleteGetTopArtists_url = "http://ws.audioscrobbler.com/2.0/?method=user.gettopartists&api_key=$lastfmapi&user=$Other_User&limit=$limit&page=$page";
$TopArtists = simplexml_load_file($CompleteGetTopArtists_url);
In this part we build the URL that we want out of the settings we've previously made and load it into a new variable: $CompleteGetTopArtists_url. That variable is then fetched by a function in PHP called simplexml_load_file, and fed into another variable called $TopArtists. This variable now contains the list of artists for a user, the artist ranking, the user's amount of plays and some other data.
//This.UsersStats data
$totalPages = $TopArtists->topartists->attributes()->totalPages;
$total = $TopArtists->topartists->attributes()->total;
We also extract some extra data about the list: how many pages are there in total and the total amount of artists in the user's library. It does this by following the XML-structure that Last.fm uses: from the attributes of topartists we want the number of total pages and store it in the variable $totalPages, we also get the total number of artists and store it in $total.
//Display on screen
echo "<h1>Get the Top artists that are in $Other_User's library and see if they're in the library of $user</h1>";
This creates the first line of text we'll get to see, it's the header 1 containing some of the information from the settings.
echo "<p>Get the <a href='user.getTopArtists.php?user=$Other_User&page=$nextpage' target='_blank'>next page</a></p><br />\n";
This creates the link for getting the next page in a new tab (target='_blank'). You could extend it by doing some math on how long it will take to build.
echo "<p>User: $Other_User perPage: $limit Page: $page totalPages: $totalPages total: $total</p><br />\n";
echo "<p>Fetching data from the following URL: $CompleteGetTopArtists_url</p><br />\n";
Some more details about what we're fetching, not really needed but handy while debugging.
for ($this_artist = 0; $this_artist < $limit; $this_artist++) {
This starts a loop for getting more information for every artist in the list and it'll stop once it has reached the number we've set in $limit since that is all the artists that are available. Every time an artist has been processed the number for the next artist is added by one. Since we'll be making multiple loops in the script it is better to use a different name for the variable so you don't mix up the information.
//Store the info from getTopArtists into local variables
$artistrank = $TopArtists->topartists->artist[$this_artist]->attributes()->rank;
$artistname = $TopArtists->topartists->artist[$this_artist]->name;
$artisturl = $TopArtists->topartists->artist[$this_artist]->url;
$Other_User_Artist_Playcount = $TopArtists->topartists->artist[$this_artist]->playcount;
Here we fetch the information for a single artist from the list by stating [$i] (the current artist) and feed them into our own variables to build the page with: the rank of the artist, name, URL, the other user's playcount for this artist and the overall playcount. You could extend the script by also getting the picture and the MusicBrainz id to get a page similar to the Recommendations-section.
//Wait one fifth of a second to comply with the Last.fm API TOS
 usleep(300000);
Since you're only allowed one request per second, and we want to get more information, we'll pause the script for one second.
//Build the url for this artist.getInfo
$CompleteGetInfo_url = "http://ws.audioscrobbler.com/2.0/?method=artist.getInfo&api_key=$lastfmapi&username=$user&artist=" . urlencode($artistname);
$GetInfo = simplexml_load_file($CompleteGetInfo_url);
Now we're building the URL for getting our own information (artist playcount) and some of the information not available when using the user.gettopartists-method. For this we use the artist.getInfo-method and place it in the variable $GetInfo.
//Store the info from getInfo into local variables
$listeners = $GetInfo->artist->stats->listeners;
$playcount = $GetInfo->artist->stats->playcount;
$tag_count = $GetInfo->artist->tags->tag->count();
$userplaycount = $GetInfo->artist->stats->userplaycount;
Storing the data into variables: number of listeners, overall playcount, the number of tags for this artist (0 - 5) and the number of times I've played this artist.
//Display the information for this artist
echo "<h2><a href='$artisturl' target='_blank'>$artistname</a></h2>";
echo "<p>$artistrank | <font color='red'>$userplaycount</font> | $playcount plays ($listeners listeners) | $Other_User played this $Other_User_Artist_Playcount times</p><br />";
This will display the information from the variables on screen: The artist name as a header 2, rank, my playcount in red (if I've never played the artist it will be empty, if I've manually added the artist it will be 0), overall playcount and listeners and how many times the other user has played them.
echo "<p>Tags: ";
for ($this_tag = 0; $this_tag < $tag_count; $this_tag++) {
            $ArtistTopTags = $GetInfo->artist->tags->tag[$this_tag]->name;
            $ArtistTopTagURL = $GetInfo->artist->tags->tag[$this_tag]->url;
            echo "<a href='" . $ArtistTopTagURL . "' target='_blank'>" . $ArtistTopTags . "</a>&nbsp;&nbsp;&nbsp;&nbsp;";
            }
 echo "</p><br />";
On a new line we use a new loop to get the tags for this artist with a maximum of the number of tags that are available.
echo "<p>Sources: <a href='http://grooveshark.com/#!/search?q=" . urlencode($artistname) . "' target='_blank'>Grooveshark</a> <a href='http://toma.hk/artist/" . urlencode($artistname) . "' target='_blank'>Tomahawk</a> <a href='http://developer.echonest.com/api/v4/artist/search?api_key=$echonestapi&format=$format&name=" . urlencode($artistname) . "' target='_blank'>Echonest</a> <a href='http://www.google.com/cse?cx=014209395896937011977:ouczate7qiu&ie=UTF-8&q=" . urlencode($artistname) . "&sa=Search&siteurl=www.google.com/cse/home?cx=014209395896937011977:ouczate7qiu&ref=somesite.com?topic=7447.msg43038&ss=1999j973825j7#gsc.tab=0&gsc.q=" . urlencode($artistname) . "&gsc.page=1' target='_blank'>OMB</a></p><br /><br />\n";
On the next line the links to other music sites are displayed with the artist name inside the link, but instead of their standard name it will be an encoded version. The reason for this is that some artists contain characters that can "break" the link. An example of this would be "Martin O'Donnell & Michael Salvatori": the apostrophe (') and ampersand (&) are both likely to cause problems. When they're encoded these characters are made "safe" again.
}
?>
And we're done with this artist, next please.

Screenshot of the output


Note: since this has become a lot lengthier then I thought it would be, the tutorial for searching the top artists for a tag will be done in another post.

Some possible tweaks

New! #requests - Contrary to what it says in the default documentation according to the TOS you can make 5 calls per second (1500 requests / 5 min). A really ugly edit would be to set the limit to 5 and delete the sleep-line. A better way would be to do a check on $this_artist to see if it's a multiple of five and if so let it sleep for a second. Changed it from sleep to usleep(300000) to make it wait for a smaller unit of time.


maximum_execution_time - If you can't/don't want to change the php.ini: add set_time_limit(120); at the top of the files and it should have plenty of time to finish doinf 100 results now ^_^

OMG - If you're a member of the Obscure Music Game and want to explore the new artists but don't want to get kicked out, then you could add another artist.getInfo-call to the API and display any results in Big Epic Red if the kicker has listened to this as well. The downside of doing that is that it'll take twice as long.

Display - The resulting page doesn't look all that fancy, you could tweak the stylesheet so it'll look like you want it.

Glitch - There's an odd glitch/bug: while I was testing the script against my own library some of the artists would be displayed as not having been played by me before. Kinda strange since their name wasn't "difficult" (example: The Ventures) or contained a redirect. Just keep that in mind.

2 comments: