Creating a web widget to get currently playing tracks using Last.FM.
It all started with my cousin asking what kind of songs I listen to, a simple question with simple answer. This is where the idea of Now Playing integration on my website came from. Considering I already used Last.fm which is the last stop for music enthusiasts, I kid you not; it is amazing to create and compile a library with an amazing and accurate recommendation. I am currently using it to Scrobble my Spotify’s Now Playing tracks and loving it so far. I noticed Last.fm provides free-to-use API, although requires an API key generated specifically for you to access the API Endpoints, it is free nonetheless. I was occasionally fiddling around with my music clients, namely CMUS, Spotify and QMMP and noticed they have Last.FM integration, built-in or in plugin form.
Fast forward; I started researching on Last.fm’s API Endpoints and noticed a section named User
with bunch of sub-endpoints which includes User.getRecentTracks
, the one which our widget will solely rely on.
For the development purposes, I will be using HTML, (S)CSS, and jQuery/JavaScript. HTML is fairly simple; it is a layout of your choice and preference.
<div class="nowplayingcard">
<div class="nowplayingcontainer-inner">
<img id="trackart" src="#">
<div class="trackInfo">
<a id="tracktitle"></a>
<a href="#" id="trackartist"></a>
</div>
</div>
</div>
What we did here is to create a foundation of what data goes where and such. Nothing complicated!
Our next step is to give our foundation a look, style if you will.
@import url("https://fonts.googleapis.com/css?family=Source+Sans+Pro");
$globalFontSize: 13px;
$globalFontFamily: "Source Sans Pro", sans-serif;
$globalBorderRadius: 3px;
@mixin border-left-radius($radius) {
-webkit-border-top-left-radius: $radius;
-moz-border-top-left-radius: $radius;
-ms-border-top-left-radius: $radius;
-o-border-top-left-radius: $radius;
border-top-left-radius: $radius;
-webkit-border-bottom-left-radius: $radius;
-moz-border-bottom-left-radius: $radius;
-ms-border-bottom-left-radius: $radius;
-o-border-bottom-left-radius: $radius;
border-bottom-left-radius: $radius;
}
body {
padding: 0;
margin: 0;
.nowplayingcard {
width: 20%;
margin: 0 auto;
margin-top: 3%;
font-family: $globalFontFamily;
font-size: $globalFontSize;
.nowplayingcontainer-inner {
width: 100%;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
transition: 0.3s;
display: inline-block;
@include border-left-radius($globalBorderRadius);
&:hover {
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2);
}
img#trackart {
max-width: 30%;
float: left;
left: 0;
@include border-left-radius($globalBorderRadius);
}
.trackInfo {
width: 70%;
float: left;
display: block;
a {
max-width: 90%;
display: block;
font-size: 14px;
text-align: left;
text-decoration: none;
vertical-align: middle;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
&:nth-child(odd) {
img {
width: 15px;
height: 15px;
vertical-align: middle;
margin: -2% 3px 0 0;
}
color: black;
font-weight: bold;
vertical-align: middle;
line-height: 15px;
letter-spacing: 0.2px;
padding: 10% 0 0 5%;
}
&:nth-child(even) {
img {
width: 15px;
height: 15px;
vertical-align: middle;
margin: -2% 3px 0 0;
}
color: gray;
font-size: $globalFontSize - 1px;
letter-spacing: 0.1px;
padding: 5% 0 0 5%;
}
}
}
}
}
}
Our final step is to make our widget functional, why?! .. because we are fiddling around with dynamic data, and not just any data, data that we pull from a different source. If we were to plan this, we could say, we need a loop to constantly check for data change.
For this very purpose, we will use Last.fm’s API’s sub-endpoint, User.getRecentTracks
.
Get a list of the recent tracks listened to by this user. Also includes the currently playing track with the nowplaying=”true” attribute if the user is currently listening.
The good thing is it has various customizable parameters, limit
, from
, to
. We won’t be using the last two but the limit
, the reason behind is that User.getRecentTracks
, if executed as default fetches 50 recent tracks, that’s 49 useless tracks data. This API Endpoint works in a Stack
theory; any good API endpoint should use Stack
theory if returning multiple data, the last one first, first one last. That should clarify why I called 49/50 useless tracks data because, for our purposes, we only need 1.
Using JavaScript/jQuery, we have.
/**
Developed by Prashant Shrestha
+ https://prashant.me
*/
var lastfmData = {
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"
};
var getSetLastFM = function() {
$.ajax({
type: "GET",
url:
lastfmData.baseURL +
lastfmData.user +
"&api_key=" +
lastfmData.api_key +
lastfmData.additional,
dataType: "json",
success: function(resp) {
var recentTrack = resp.recenttracks.track[0];
var formatted =
"<img src='https://i.imgur.com/EgWjJry.png'>" + recentTrack.name;
$("a#tracktitle")
.html(formatted)
.attr("href", recentTrack.url)
.attr("title", recentTrack.name + " by " + recentTrack.artist["#text"])
.attr("target", "_blank");
var artistFormatted =
"<img src='https://i.imgur.com/fae5XZA.png'>" +
recentTrack.artist["#text"];
$("a#trackartist")
.html(artistFormatted)
.attr("title", "Artist : " + recentTrack.artist["#text"]);
$("img#trackart").attr("src", recentTrack.image[2]["#text"]);
},
error: function(resp) {
$("a#tracktitle").html(
"<img src='https://i.imgur.com/EgWjJry.png'>" + "Silence!"
);
$("img#trackart").attr("src", "https://i.imgur.com/Q6cCswP.jpg");
var artistFormatted =
"<img src='https://i.imgur.com/fae5XZA.png'>Prashant Shrestha";
$("a#trackartist")
.html(artistFormatted)
.attr("href", "www.prashant.me/");
}
});
};
// Get the new one.
getSetLastFM();
// Start the countdown.
setInterval(getSetLastFM, 10 * 1000);
Simple enough, those hardcoded images hosted in Imgur are from Icon8, a great website for icons.
The reason I added getSetLastFM();
and again called setInterval(getSetLastFM, ..);
is because we don’t want the users to wait 10 seconds with no data at the very first time. After completion, the widget should look something like this.
There’s a responsiveness problem with this, and we can fix it with either creating a responsive layout or a going the fixed
width and height way.
Below is the embedded Codepen. Please view it in a new tab to see the proper layout.
See the Pen Last.FM Now Playing! by Prashant M. Shrestha (@intern0t) on CodePen.
Enjoy & Happy Coding!