1. Skip to Navigation
  2. Skip to Content
our blog

Display Recent Spotify Track with Album Cover using PHP

Updated: 1/19/2013

Edit: Per Bob’s comment below, I had a typo in my code. I have also updated the code to take some special characters into account and search results with multiple authors.

Edit 2: See Jan’s code in the comments for a simple function to pull album art from a Spotify URL.

Many of our clients show their personalities on their websites – from Facebook to Twitter feeds, photo streams and more, they like to integrate a bit of themselves. While hooking up a Social Media feed is no problem, recently we had a unique request to develop a widget that displays the most recently listened to Spotify track with artist information and album cover.

It turns out there isn’t a straightforward way to do this; after a little digging around on the internet, and the help of the Spotify API, we were able to put together a solution that I wanted to share.

There are several steps to this process:

  1. Create a Lastfm account and setup Spotify to scrobble to Last.fm
  2. Pull the Lastfm Recent Tracks RSS feed
  3. Look-up the desired Track using the Spotify Metadata API and load the data


First, we need a basic cURL function:

function fetchUrl($url){
     $ch = curl_init();
     curl_setopt($ch, CURLOPT_URL, $url);
     curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
     curl_setopt($ch, CURLOPT_TIMEOUT, 20);
 
     $retData = curl_exec($ch);
     curl_close($ch); 
 
     return $retData;
}

With that in hand, it’s time to get down to business.

We start by loading the Last.fm RSS feed to get the latest track:

//load Spotify Tracks
function loadSpotifyTracks($username, $count = 1){
	$lastfm_url = "ws.audioscrobbler.com/2.0/user/".$username."/recenttracks.rss";
 
	$trackAttributes = array();
 
	$rss = fetchUrl($lastfm_url);
	$rss = simplexml_load_string($rss);
 
	for($i=0; $i<$count; $i++) {
		$title = $rss->channel->item[$i]->title;
 
		//sometimes characters are formatted funny, so lets be sure that isn't the case here
 
		// First, replace UTF-8 characters.		
		$title = str_replace(
		array("\xe2\x80\x98", "\xe2\x80\x99", "\xe2\x80\x9c", "\xe2\x80\x9d", "\xe2\x80\x93", "\xe2\x80\x94", "\xe2\x80\xa6"),
		array("'", "'", '"', '"', '-', '--', '...'),
		$title);
 
		// Next, replace their Windows-1252 equivalents.
		$title = str_replace(
		array(chr(145), chr(146), chr(147), chr(148), chr(150), chr(151), chr(133)),
		array("'", "'", '"', '"', '-', '--', '...'),
		$title);
 
		//format is Artist - Track
		$artistData = explode(" - ", $title);
 
		//get track album cover and info
		$trackAttributes[] = getTrackAttributes($artistData[1], $artistData[0]);
	}
 
	return $trackAttributes;
}

The above function will load the Most Recent RSS Feed of the Lastfm account passed, and will loop through $count of them, loading the attributes.

You need to include the following function to be able to load the attributes from Spotify:

//get attributes of track from Spotify
function getTrackAttributes($trackname, $artistnicename){	
	//format artist name, keep the original around for output
	$artistname = strtolower(str_replace("-"," ",$artistnicename));
 
	//default return data
	$retData = array(
		'title' => "",
		'artist' => "",
		'cover' => "",
		'album' => "",
		'trackUrl' => ""
	);
 
	//search spotify, find match by artist and trackname, then pull that data
	$first_url = 'http://ws.spotify.com/search/1/track?q=' . urlencode($trackname);
	$result = fetchUrl($first_url);		
	$xmlObjs = simplexml_load_string($result);
 
	if ($xmlObjs) {
		foreach($xmlObjs as $xmlObj){			
 
			//if there are many artists, need to build an array of names
			if(isset($xmlObj->artist[0])){
				$artists = array();			
				foreach($xmlObj->artist as $artist){	
					$json = json_encode($artist);
					$artist = json_decode($json,TRUE);
					$artists[] = strtolower($artist['name']);
				}
			}
 
			//check to see if the Track and Album name both match,
			//if they do, use the data		
			//sometimes tracks have multiple artists, so we need to check arrays, too
			if(strtolower($xmlObj->name) == strtolower($trackname) && ($xmlObj->artist->name == $artistname || in_array($artistname,$artists))){
				$albumName = "";
				$trackURL = "#";
				$albumName = $xmlObj->album->name;
				$trackURL = $xmlObj->attributes()->href;
				$uri = end(explode(":", $trackURL));
				$trackURL = 'http://open.spotify.com/track/' . $uri;				
				$coverResults = fetchUrl($trackURL);//load album cover info from track
 
				if($coverResults){
					$parser = xml_parser_create();
					$vals = $index = array();
 
					xml_parse_into_struct($parser, $coverResults, $vals, $index);
					xml_parser_free($parser);
 
					if(count($vals)){
						foreach($vals as $val){
							if($val['tag'] == 'META' && substr($val['attributes']['PROPERTY'], 0, 14) == 'og:image') {
								$coverImage = $val['attributes']['CONTENT'];
							}
						}
					}
				}
 
				//we want the fullsized image, not the thumbnail
				$coverImage = str_replace('thumb', 'image', $coverImage);	
 
				//no image? use the default data
				//you can remove this check if you're ok not having an album cover
				if(strlen($coverImage) > 4){
					//has cover image, use the returned data
					$retData['title'] = $trackname;
					$retData['artist'] = $artistnicename;
					$retData['cover'] =  $coverImage;
					$retData['album'] = $albumName;
					$retData['trackUrl'] = $trackURL;
				}
			}
		}		
	}
 
	return $retData;
}

Here’s what that wall-of-text does:

  • Searches for the Track using the Spotify API
  • If a matching track/album is found, generates a Track URL from the match’s URI
  • Loads that track’s info from the Spotify API and grabs the image from the result

A few notes:

  1. You will want to cache your requests. It’s polite (and they’ll probably cut you off, otherwise).
  2. You’ll probably want some fallback display data. Sometimes an exact match isn’t found. The above code isn’t perfect.
  3. You might want to think about loading this via AJAX – the page can hang a second while waiting for the info to load.

Here is the complete code, in one box:

function fetchUrl($url){
     $ch = curl_init();
     curl_setopt($ch, CURLOPT_URL, $url);
     curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
     curl_setopt($ch, CURLOPT_TIMEOUT, 20);
 
     $retData = curl_exec($ch);
     curl_close($ch); 
 
     return $retData;
}
 
//get attributes of track from Spotify
function getTrackAttributes($trackname, $artistnicename){	
	//format artist name, keep the original around for output
	$artistname = strtolower(str_replace("-"," ",$artistnicename));
 
	//default return data
	$retData = array(
		'title' => "",
		'artist' => "",
		'cover' => "",
		'album' => "",
		'trackUrl' => ""
	);
 
	//search spotify, find match by artist and trackname, then pull that data
	$first_url = 'http://ws.spotify.com/search/1/track?q=' . urlencode($trackname);
	$result = fetchUrl($first_url);		
	$xmlObjs = simplexml_load_string($result);
 
	if ($xmlObjs) {
		foreach($xmlObjs as $xmlObj){			
 
			//if there are many artists, need to build an array of names
			if(isset($xmlObj->artist[0])){
				$artists = array();			
				foreach($xmlObj->artist as $artist){	
					$json = json_encode($artist);
					$artist = json_decode($json,TRUE);
					$artists[] = strtolower($artist['name']);
				}
			}
 
			//check to see if the Track and Album name both match,
			//if they do, use the data		
			//sometimes tracks have multiple artists, so we need to check arrays, too
			if(strtolower($xmlObj->name) == strtolower($trackname) && ($xmlObj->artist->name == $artistname || in_array($artistname,$artists))){
				$albumName = "";
				$trackURL = "#";
				$albumName = $xmlObj->album->name;
				$trackURL = $xmlObj->attributes()->href;
				$uri = end(explode(":", $trackURL));
				$trackURL = 'http://open.spotify.com/track/' . $uri;				
				$coverResults = fetchUrl($trackURL);//load album cover info from track
 
				if($coverResults){
					$parser = xml_parser_create();
					$vals = $index = array();
 
					xml_parse_into_struct($parser, $coverResults, $vals, $index);
					xml_parser_free($parser);
 
					if(count($vals)){
						foreach($vals as $val){
							if($val['tag'] == 'META' && substr($val['attributes']['PROPERTY'], 0, 14) == 'og:image') {
								$coverImage = $val['attributes']['CONTENT'];
							}
						}
					}
				}
 
				//we want the fullsized image, not the thumbnail
				$coverImage = str_replace('thumb', 'image', $coverImage);	
 
				//no image? use the default data
				//you can remove this check if you're ok not having an album cover
				if(strlen($coverImage) > 4){
					//has cover image, use the returned data
					$retData['title'] = $trackname;
					$retData['artist'] = $artistnicename;
					$retData['cover'] =  $coverImage;
					$retData['album'] = $albumName;
					$retData['trackUrl'] = $trackURL;
				}
			}
		}		
	}
 
	return $retData;
}
 
//load Spotify Tracks
function loadSpotifyTracks($username, $count = 1){
	$lastfm_url = "ws.audioscrobbler.com/2.0/user/".$username."/recenttracks.rss";
 
	$trackAttributes = array();
 
	$rss = fetchUrl($lastfm_url);
	$rss = simplexml_load_string($rss);
 
	for($i=0; $i<$count; $i++) {
		$title = $rss->channel->item[$i]->title;
 
		//sometimes characters are formatted funny, so lets be sure that isn't the case here
 
		// First, replace UTF-8 characters.		
		$title = str_replace(
		array("\xe2\x80\x98", "\xe2\x80\x99", "\xe2\x80\x9c", "\xe2\x80\x9d", "\xe2\x80\x93", "\xe2\x80\x94", "\xe2\x80\xa6"),
		array("'", "'", '"', '"', '-', '--', '...'),
		$title);
 
		// Next, replace their Windows-1252 equivalents.
		$title = str_replace(
		array(chr(145), chr(146), chr(147), chr(148), chr(150), chr(151), chr(133)),
		array("'", "'", '"', '"', '-', '--', '...'),
		$title);
 
		//format is Artist - Track
		$artistData = explode(" - ", $title);
 
		//get track album cover and info
		$trackAttributes[] = getTrackAttributes($artistData[1], $artistData[0]);
	}
 
	return $trackAttributes;
}
 
//do something!
$trackAttributes = loadSpotifyTracks("username");
 
$trackAttributes = $trackAttributes[0];
 
//only output data if a match is found
if(strlen($trackAttributes['trackUrl']) > 2){
	$output .= '<a href="' . $trackAttributes['trackUrl'] . '" target="_blank"><img src="'.$trackAttributes['cover'].'" />';
	$output .= '<div class="info">
			<span class="title">' . $trackAttributes['title'] . '</span>
			<span class="artist">by ' . $trackAttributes['artist'] . '</span>
			<span class="album">on ' . $trackAttributes['album'] . '</span>
		</div></a>';
 
	echo $output;
}

And that’s it! A solution to display recent Spotify track with album cover using PHP.

I hope you find it helpful!

15 Responses to Display Recent Spotify Track with Album Cover using PHP

  1. bob says:

    Code does not work and contains a few errors. To start with line 105 LoadSpotify doesn’t exist. I assume is a simple typo and meant LoadSpotifyTracks

    I fixed that but i get the following errors:
    Notice: Undefined offset: 1 in C:\wamp\www\index.php on line 98

    Notice: Undefined index: trackUrl in C:\wamp\www\index.php on line 108

    • Hey Bob,

      Thanks for pointing out the typo. I’ve updated the code to take into account tracks with multiple authors and authors with special characters in their names. I’ve tested and verified the code is working – let me know if you have any more trouble.

      Thanks!
      Richard

  2. Peter says:

    hey,

    hopefully someone still looks at this, because i have a question. I want something like this, a sort of screen full of images based on recent songs i listened. But not only the album covers, also other image sources. What i have in mind is like the website Lifeandtimes.com, a page full of images which are being influenced by recent music i listened. Is there a way to make a sort of algorith which makes this possible?

    • Hey Peter,

      That’s definitely something I can envision as being possible. I don’t think I’ll be able to give you the help you would need via our blog, as that may require quite a bit of custom development, but you’re welcome to contact us via e-mail and we can discuss it further.

      Thanks!
      Richard

  3. Eric says:

    Richard could you please tell me how to get this working? I am somewhat of a novice when it comes to PHP but how do I get it to pull the info from my username on last.FM? I have to scrobbling working but I am unsure how to load the page with my username.

    Thanks!

    • Hey Eric,

      There are several other steps to get the above code working – it’s not just copy and paste. Have you followed all of the steps above, and how have you added the code to your website?

      Thanks,
      Richard

  4. Jan says:

    Here are my simple functions used to fetch album art from an spotify url.

    function fetch($url) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, 20);

    $data = curl_exec($ch);
    curl_close($ch);

    return $data;
    }

    function getAlbumArt($url) {
    $html = fetch($url);
    if ($html == true) {
    $doc = new DOMDocument();
    @$doc->loadHTML($html);

    $tags = $doc->getElementsByTagName(‘img’);

    foreach ($tags as $tag) {
    if ($tag->getAttribute(‘id’) == “big-cover”) {
    $src = trim($tag->getAttribute(‘src’));
    $src = explode(“:large”, $src);
    return $src[0];
    }
    }
    }
    return false;
    }

  5. Jan says:

    Here are my simple functions used to fetch album art from an spotify url.

    (Second try to post properly…)


    function fetch($url) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, 20);

    $data = curl_exec($ch);
    curl_close($ch);

    return $data;
    }

    function getAlbumArt($url) {
    $html = fetch($url);
    if ($html == true) {
    $doc = new DOMDocument();
    @$doc->loadHTML($html);

    $tags = $doc->getElementsByTagName('img');

    foreach ($tags as $tag) {
    if ($tag->getAttribute('id') == "big-cover") {
    $src = trim($tag->getAttribute('src'));
    $src = explode(":large", $src);
    return $src[0];
    }
    }
    }
    return false;
    }

  6. James Taylor says:

    I’ve tried and followed up all your instruction and it simply doesn’t work at all.

    Your instructions are very vague it feels like you’re not ready to give some more details, why don’t you just delete this post instead.

    I followed up all your instruction by the letter and all I get is error message although I’m signed up to both services and allowed spotify to scrobbble to lastfm and implemented the phg code correctly to my website. I believe it requires more work.

    I also tried to edit the rss feed url which work on a browser just not over your php function.

    There is no instruction whatsoever on how to add edit our username for any of our spotify or lastfm account so I wonder how this can fetch our data if it’s so.

    Simply put, your post is useless to a junior or mid-weight php dev and most certainly to a senior as he wouldn’t want be here checking your code if he is senior.

    Judging for your reply to previous inquiry it’s pretty clear that this post will get as a sterile response as the previous attempts have.

    • Hey James,

      Let me start by saying that when you say “it’s pretty clear that this post will get as a sterile response as the previous attempts have”, that doesn’t motivate me to want to help! Same goes for “why don’t you just delete this post instead.” :-)

      Regardless, it would be helpful were you to send the error message you are receiving. Your above comment is not formed in a way in which I could help you. This code is definitely not copy and paste, it does require more work.

      Anyways, I wish you the best of luck in the future!

      Thanks,
      Richard

  7. Colin says:

    Not working too! I suggest putting a LIVE example to show it works?

    Ta

    • Hey Colin!

      I’ve sent you an e-mail with some more information.

      Due to silly reasons — I don’t use Spotify or Lastfm — I don’t have a live example of my own to add here. However, I can assure you the code works.

      Are you able to provide more info about the difficulty you’re having?

      Thanks!
      Richard

  8. Colin says:

    Tried using this:
    function fetchUrl($url){
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, 20);
    $retData = curl_exec($ch);
    curl_close($ch);
    return $retData;
    }

    //get attributes of track from Spotify
    function getTrackAttributes($trackname, $artistnicename){
    //format artist name, keep the original around for output
    $artistname = strtolower(str_replace(“-”,” “,$artistnicename));
    //default return data
    $retData = array(
    ‘title’ => “”,
    ‘artist’ => “”,
    ‘cover’ => “”,
    ‘album’ => “”,
    ‘trackUrl’ => “”
    );
    //search spotify, find match by artist and trackname, then pull that data
    $first_url = ‘http://ws.spotify.com/search/1/track?q=’ . urlencode($trackname);
    $result = fetchUrl($first_url);
    $xmlObjs = simplexml_load_string($result);
    if ($xmlObjs) {
    foreach($xmlObjs as $xmlObj){
    //if there are many artists, need to build an array of names
    if(isset($xmlObj->artist[0])){
    $artists = array();
    foreach($xmlObj->artist as $artist){
    $json = json_encode($artist);
    $artist = json_decode($json,TRUE);
    $artists[] = strtolower($artist['name']);
    }
    }
    //check to see if the Track and Album name both match,
    //if they do, use the data
    //sometimes tracks have multiple artists, so we need to check arrays, too
    if(strtolower($xmlObj->name) == strtolower($trackname) && ($xmlObj->artist->name == $artistname || in_array($artistname,$artists))){
    $albumName = “”;
    $trackURL = “#”;
    $albumName = $xmlObj->album->name;
    $trackURL = $xmlObj->attributes()->href;
    $uri = end(explode(“:”, $trackURL));
    $trackURL = ‘http://open.spotify.com/track/’ . $uri;
    $coverResults = fetchUrl($trackURL);//load album cover info from track
    if($coverResults){
    $parser = xml_parser_create();
    $vals = $index = array();
    xml_parse_into_struct($parser, $coverResults, $vals, $index);
    xml_parser_free($parser);
    if(count($vals)){
    foreach($vals as $val){
    if($val['tag'] == ‘META’ && substr($val['attributes']['PROPERTY'], 0, 14) == ‘og:image’) {
    $coverImage = $val['attributes']['CONTENT'];
    }
    }
    }
    }
    //we want the fullsized image, not the thumbnail
    $coverImage = str_replace(‘thumb’, ‘image’, $coverImage);
    //no image? use the default data
    //you can remove this check if you’re ok not having an album cover
    if(strlen($coverImage) > 4){
    //has cover image, use the returned data
    $retData['title'] = $trackname;
    $retData['artist'] = $artistnicename;
    $retData['cover'] = $coverImage;
    $retData['album'] = $albumName;
    $retData['trackUrl'] = $trackURL;
    }
    }
    }
    }
    return $retData;
    }

    $trackAttributes = getTrackAttributes(“1999″, “prince”);

    Erroring at $uri = end(explode(“:”, $trackURL));

    Nothing is defined line is the main cause???
    if($val['tag'] == ‘META’ && substr($val['attributes']['PROPERTY'], 0, 14) == ‘og:image’) {

  9. Colin says:

    Bypassing loadSpotifyTracks for test purposes and since I do not need Lastfm.

  10. Colin says:

    Ignore my posts for now….think it is working.

Leave a Reply

Your email address will not be published. Required fields are marked *


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>