Developing a Last.fm shortcode for Hugo
By egoebelbecker
I moved this blog to Hugo from Ghost over the holidays. This was after about a year of dissatisfaction with (very expensive) hosted Ghost, after many years on WordPress.
While Hugo has fewer bells and whistles, it suits my purposes perfectly. I can write in Markdown, which I prefer over any other format. Ghost claims to support Markdown, but you can only input Markdown to its editor. You can’t see it.)
With Hugo, I can use git to manage my content, which I feel very comfortable with. And, I can host my content on Github, where I am already paying for premium so that I can have private repos. (I think it’s free with their basic plan now, too?)
And, unlike WordPress, I never have to look at PHP.
As I familiarized myself with Hugo, I found that it’s a fantastic blogging platform for developers. So, I decided to play a little.
I listen to a lot of music, but I’d never gotten around to using Last.fm. So I decided to kill two birds with one stone and figure out how to share my Last.fm status to this blog. You can see the finished product over here.
Last.fm API
API Account
The first step is to get yourself an API key. You can do that here. In the Application Description I said I was working on a display app for my personal website and was immediately approved.
API Interface
Last.fm has a REST API. You can do a lot with it, but there’s only one API call you need to get this job done. It’s called getrecentracks. (Although you will be using a couple of others.)
Here’s the fully formed URL, where USER is your Last.fm username, and API_KEY is your key.
The last two parameters get us a JSON response and limits the answer to one entry.
You can enter this URL (with your username and key) into your browser or run it with curl or wget. Here’s a response I received.
{
"recenttracks": {
"@attr": {
"page": "1",
"total": "1385",
"user": "egoebelbecker",
"perPage": "1",
"totalPages": "1385"
},
"track": [
{
"artist": {
"mbid": "b76c700e-eac3-4fc2-a166-6b9b5b2b2a18",
"#text": "Christina Giannone"
},
"album": {
"mbid": "de280561-227e-40f4-887c-e1f6bac334a4",
"#text": "Redemption"
},
"image": [
{
"size": "small",
"#text": "https://lastfm.freetls.fastly.net/i/u/34s/c671434cb49acdb7c811613af8fc79c3.jpg"
},
{
"size": "medium",
"#text": "https://lastfm.freetls.fastly.net/i/u/64s/c671434cb49acdb7c811613af8fc79c3.jpg"
},
{
"size": "large",
"#text": "https://lastfm.freetls.fastly.net/i/u/174s/c671434cb49acdb7c811613af8fc79c3.jpg"
},
{
"size": "extralarge",
"#text": "https://lastfm.freetls.fastly.net/i/u/300x300/c671434cb49acdb7c811613af8fc79c3.jpg"
}
],
"streamable": "0",
"date": {
"uts": "1609701677",
"#text": "03 Jan 2021, 19:21"
},
"url": "https://www.last.fm/music/Christina+Giannone/_/Infinite+Closure",
"name": "Infinite Closure",
"mbid": "8815115b-f497-4809-8ea8-af71bf66905f"
}
]
}
}
So, getting the information you need from Last.fm is simple.
Formatting the Response
I’m not a fan of HTML or CSS, so I put the song, album, and artist information in divs and let the browser figure out how to put them on the screen.
<div class="nowplayingcard">
<div class="nowplayingcontainer-inner">
<div class="trackInfo">
<p>Track: <a id="tracktitle"></a></p>
<p>Album: <a id="album"></a></p>
<p><a id="artist"></a></p>
</div>
<img id="trackart" src="#">
</div>
</div>
The css is equally simple, at least as simple as a <sarcasm>delightful</sarcasm> “language” like CSS can be.
If you have the time (and patience) to improve on this, please do. I’d love to see a pull request.
Getting the Data Onto the Page
I decided it wasn’t worth pull JQuery into the page, so I used native Javascript. It’s more verbose than a JQuery script would look, but it’s fast and simple. Like Hugo.
|
|
The function takes the username and API key as parameters.
Line 1 -5 are variables for building the URLs. You’ll be reusing them later.
On line 9, you assemble the request. Lines 12 - 16 create an HTTP request object. You need to do some extra work there because Microsoft.
Finally, lines 18 - 40 define a callback for the HTTP request object. You’re using an asynchronous request to retrieve the data. When the request returns, it calls this function.
- First, it retrieves the divs with ids of tracktitle, trackart, artist, and album from the document the script is embedded in.
- Then, if the request succeeded, it parses the response into a Javascript object.
- It places the data in the appropriate node.
- If the request failed, it sets the HTML to reflect an error.
This works, and it’s ready to be put into a shortcode. But it’s not good enough for me. It gives you a link to the track, but why not add links for the album and artist?
Slight Tweaks
The beauty of Javascript async requests is that we can add multiple requests without slowing down the page load. It may mean that some sections take a little longer to render, but the browser doesn’t wait for them.
So, time to add a couple more requests.
Both the album and the artist have fields named “mbid” in their responses to getrecenttrack. This is the MusicBrainz identification field. You can use these ids to request details from Last.fm.
Here are a new function and a modified getLastTrack() that calls it.
|
|
The new function takes a JSON object, the type of the object (“artist” or “album”), and the API key.
If you swap user.getrecenttracks for album.getinfo or album.getinfo and supply an mbid instead of a user, you get details for the requested id. So, you can use the requestType parameter to build the request. Then you can use it again to request the proper node from the web page and pull the required data out of the response.
This gives you a link for the album and artist. If there’s no mbid (I’ve come across albums on Bandcamp with incomplete data), the script adds the name.
Hugo Shortcode
You could take the CSS, Javascript, and HTML and make a post or page from this. But that’s not the Hugo way.
Javascript and CSS
Hugo gives you a static directory in your directory tree. Anything you place in there ends up in the root of your website. So, I created static/js and static/ css directories. Then I put nowplaying.js. and nowplaying.css in their respective locations.
Next, the shortcode itself.
<script src="/js/nowplaying.js"></script>
<link rel="stylesheet" href="/css/nowplaying.css">
<div class="nowplayingcard">
<div class="nowplayingcontainer-inner">
<div class="trackInfo">
<p>Track: <a id="tracktitle"></a></p>
<p>Album: <a id="album"></a></p>
<p><a id="artist"></a></p>
</div>
<img id="trackart" src="#">
</div>
</div>
<script>
setInterval(getLastTrack({{ .Get "user" }},{{ .Get "key" }}), 10 * 1000);
</script>
The Javascript and CSS are included at the top. You already saw the HTML. All that’s left is the shortcode definition at the bottom.
Since the user and the API key parameters, you can add the shortcode to your site without making any changes to the source:
{{< nowplaying user=“USER” key=“API KEY” >}}
If you have a Hugo site, give it a shot and let me know how it goes.
Back to Raspberry Pi and pigpio next week.
Did you enjoy this post? Did you find it useful? Sign up for my newsletter. There's more content like this coming!