Last.fm Widget for WordPress

Whenever I listen to music or podcasts on my phone, I use Pano Scrobbler to notify Last.fm what I’m listening to. Here’s just a quick share of the HTML I use to display that info on my WordPress site.

This widget is adapted from Prashant Shrethra’s Last.fm widget, adding a few tweaks of my own.

Before you jump in, you’ll need to generate a Last.fm API key. Once you’ve generated your key, you can find it here.

Then, you’ll just want to insert the code block below on your WordPress site, wherever you’d like, as a Custom HTML block. Be sure to replace “Your_LastFM_Username” & “Your_LastFM_API_Key” with your corresponding info. (This code will actually also work on Blogger — or really any webpage that doesn’t have restrictions on using JavaScript.)

Screenshot showing the insertion of a Custom HTML block in a WordPress post.
Here’s the Code:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js" type="text/javascript"></script>

<script>
var lastfmJsonURL = {
    baseURL: "https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=",
    // Your Last.fm Username
    user: "Your_LastFM_Username",
    // Your API key
    api_key: "Your_LastFM_API_Key",
    additional: "&format=json&limit=1"
};

// Function to shorten long track titles
function shortenString(str, length) {
  if (str.length > length) {
    let shortenedStr = str.substring(0, length - 3);
    shortenedStr = shortenedStr.substr(0, Math.min(shortenedStr.length, shortenedStr.lastIndexOf(" ")));
    return shortenedStr + '...';
  } else {
    return str;
  }
}

// Construct JSON URL
var lastfmData = function () {
    $.ajax({
		  type: "GET",
		  url:
			lastfmJsonURL.baseURL +
			lastfmJsonURL.user +
			"&api_key=" +
			lastfmJsonURL.api_key +
			lastfmJsonURL.additional,
		  dataType: "json",

	success: function (data) {
	// Reset info (prevents old info from persisting when current entries are blank)
	$("#tracktitle").html("");
	$("#trackartist").html("");
	$("div#playstatus").html("");

    // Check if 'nowplaying' exists in Last.fm JSON
    if (data.recenttracks.track[0]["@attr"] && data.recenttracks.track[0]["@attr"].nowplaying === "true") {
		// Show LIVE badge instead of date & time when nowplaying exists
		var playStatusHtml = "<span style='display:block; background-image: linear-gradient(to top right,#800000,red); color:#ccc; width:32px; font-weight:bold; line-height:12px; font-size:12px; padding:2px; margin-top:2px; border-radius: 0 12px 12px 0; margin-left:-10px;'>LIVE</span>";
	} else {
        // Pull timestamp from Last.fm JSON
        var dateString = data.recenttracks.track[0].date['#text'];
        // Format timestamp to be human readable
        var date = new Date(dateString);
        date.setHours(date.getHours() - 4); // adjust Last.FM time to Eastern
        var currentDate = new Date();
        var timeDiff = Math.floor((currentDate - date) / (1000 * 60 * 60 * 24));
        var formattedTime = date.toLocaleTimeString('en-US', { hour: 'numeric', minute: 'numeric', hour12: true }).replace(/ /g, '').toLowerCase();
        let formattedDate;
        if (timeDiff < 1 && currentDate.getDate() === date.getDate()) {
        	formattedDate = 'Today';
        } else if (timeDiff < 2 && currentDate.getDate() !== date.getDate()) {
        	formattedDate = 'Yesterday';
        } else if (timeDiff <= 7) {
        	formattedDate = date.toLocaleDateString('en-US', { weekday: 'long' });
        } else if (timeDiff <= 14) {
        	formattedDate = 'Last ' + date.toLocaleDateString('en-US', { weekday: 'long' });
        } else {
        	formattedDate = date.toLocaleDateString('en-US', {
        		month: 'long',
        		day: 'numeric',
        		...(date.getDate() > 3 && date.getDate() < 21 && { day: 'numeric' + 'th' }),
        		...(date.getDate() % 10 === 1 && { day: 'numeric' + 'st' }),
        		...(date.getDate() % 10 === 2 && { day: 'numeric' + 'nd' }),
        		...(date.getDate() % 10 === 3 && { day: 'numeric' + 'rd' }),
        	});
        }
		// Create timestamp string for widget
		var playStatusHtml = 'Played ' + formattedDate + ' @ ' + formattedTime;
	}
	
	// Pull remaining track info from Last.fm JSON 
	var trackLink = data.recenttracks.track[0].url;
	var coverArt = data.recenttracks.track[0].image[2]["#text"];
	var trackFormatted = shortenString(data.recenttracks.track[0].name,70);
	var artistFormatted = data.recenttracks.track[0].artist["#text"];
	var albumFormatted = data.recenttracks.track[0].album["#text"];

	// START *** missing coverart & URLs for podcasts ***
	if (artistFormatted == "Fire Escape Cast") {
	coverArt = "https://s3-us-west-2.amazonaws.com/anchor-generated-image-bank/production/podcast_uploaded_nologo400/12427283/12427283-1617024611770-a4fc1c0db4b1.jpg";
	trackLink = "https://anchor.fm/fireescape";
	}

	if (artistFormatted == "Brad & Will Made a Tech Pod.") {
	coverArt = "https://is2-ssl.mzstatic.com/image/thumb/Podcasts113/v4/b2/6c/2b/b26c2b0e-078d-1c3f-7bc1-516ffb0d3873/mza_7993470612435752699.jpg/400x400bb.jpg";
	trackLink = "https://techpod.content.town/";
	}

	if (artistFormatted == "The Nextlander Podcast") {
	coverArt = "https://assets.libsyn.com/secure/content/104904350/?height=200&width=200";
	trackLink = "https://podcasts.apple.com/us/podcast/the-nextlander-podcast/id1570737028";
	}
	// END *** missing coverart & URLs for podcasts ***
		
	// Update html with most recent info from Last.fm
	$("img#trackart").attr("src", coverArt);
	$("a#trackart").attr("href", trackLink);
	$("div#tracktitle").html(trackFormatted);
	$("div#trackartist").html(artistFormatted);	
	$("div#playstatus").html(playStatusHtml);
	
	}, // End of AJAX success function
	  
	error: function () {  // Show error message if Last.fm is down or wrong Last.fm username/API entered 
		console.log("Error fetching Last.fm data.");
		const ErrorCoverartURL="https://4.bp.blogspot.com/-AXYpAfwRnKY/XDejdGyWmnI/AAAAAAAAdmI/Cfi-2VRGvYsrq_8z8qVR8KEA0JkqfbMVwCK4BGAYYCw/s90/rhinokick.jpg";
		$("img#trackart").attr("src", ErrorCoverartURL);
		$("a#trackart").attr("href", "#");
		$("div#tracktitle").html("Silence?");
		$("div#trackartist").html("(something is broken...)");	
		$("div#playstatus").html("");
	}
	   
    });
	
  }

// Call the function to fetch & populate initial Last.fm dataset
lastfmData();

// Refresh every 30 seconds
setInterval(lastfmData, 30 * 1000);

</script>

<div id="lastfmcontainer" style="display:inline-block;">
  <div style="display:inline-block; width:100px; float:left;">
    <a href="#" id="trackart"><img decoding="async" id="trackart" src="#" style="width:100%;"></a>
  </div>
  <div style="display:inline-block; width:180px; float:left; padding-left:10px;">
    <div id="tracktitle" style="font-size:0.95em; line-height:1em; margin-bottom:5px;"></div>
    <div id="trackartist" style="font-size:0.8em; line-height:1em; margin-bottom:5px; padding-top:5px;"></div>
    <div id="playstatus" style="font-size:0.8em; line-height:1em; margin-bottom:5px; padding-top:5px;"></div>
  </div>
</div>

In the code, you’ll notice a few entries that follow this format:

if (artistFormatted == "Fire Escape Cast") {
coverArt = "https://s3-us-west-2.amazonaws.com/anchor-generated-image-bank/production/podcast_uploaded_nologo400/12427283/12427283-1617024611770-a4fc1c0db4b1.jpg";
trackLink = "https://anchor.fm/fireescape";
}

This is for podcasts which Last.fm doesn’t have art or URLs for. You can duplicate/remove/modify these entries however you please, filling in the appropriate links to your podcasts’ cover art & websites.

Also, different podcast & scrobbler apps tend to publish data differently to Last.fm. If your particular setup happens to publish the podcast names as albums instead of artists, you’ll want to format these additional entries like this, instead:

if (albumFormatted == "Fire Escape Cast") {
coverArt = "https://s3-us-west-2.amazonaws.com/anchor-generated-image-bank/production/podcast_uploaded_nologo400/12427283/12427283-1617024611770-a4fc1c0db4b1.jpg";
trackLink = "https://anchor.fm/fireescape";
artistFormatted = albumFormatted;
}

Leave a Comment