How To Integrate Last.fm Into Your Hugo Website

| |

This is a quick tutorial to teach you how to integrate your Last.fm feed into your hugo site. To keep things nice and simple I’ve created a demo theme to get you started, which you can see running here.

The demo is basically a one page template. I’ve highlighted the important part.

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 
 4 <head>
 5     <meta charset="utf-8">
 6     <meta name="author" content="Ryan Kes">
 7     <meta name="generator" content="Hugo 0.16" />
 8     <meta name="viewport" content="width=device-width, initial-scale=1">
 9     <meta name="description" content="A page that demonstrates Hugo working with the Last.fm api">
10     <meta name="keywords" content="Last.fm, Hugo, api, tutorial, demo">
11     <meta name="generation-date" content="{{ .Now }}">
12 
13     <title>Last.fm Hugo Demo</title>
14     <link href="/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
15     <link rel="stylesheet" href="/css/style.css" type="text/css"/>
16     {{ template "_internal/google_analytics_async.html" . }}
17 </head>
18 
19 <body>
20 
21 <a href="https://github.com/alrayyes/lastfm-hugo-demo-theme"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/e7bbb0521b397edbd5fe43e7f760759336b5e05f/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677265656e5f3030373230302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_green_007200.png"></a>
22 <div class="container-fluid">
23     <div class="row text-center">
24         <h3 style="color:white;font-family:verdana;">
25             <a href="http://www.last.fm" target="_new">Last.fm</a> <a href="https://gohugo.io/" target="_new">Hugo</a> Demo<br><small><a href="http://ryankes.eu">Ryan Kes</a></small><br>
26             Design inspired by <a href="http://bootsnipp.com/snippets/featured/image-tiles-full-width" target="_new">Snap TightImage Tiles snippet</a><br>
27             See blog post for more information
28         </h3>
29     </div>
30     <div class="row">
31         {{ $dataJ := getJSON (printf "https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=%s&api_key=%s&format=json&limit=%s" .Site.Params.username .Site.Params.apikey (.Site.Params.limit) ) }}
32         {{ range $dataJ.recenttracks.track }}
33         <a href="{{ .url }}" target="_new">
34         <div class="cover-card col-sm-4" style="background: url({{ range last 1 .image }}{{ index . "#text" }}{{ end }}) no-repeat center top;background-size:cover;">
35             <p>
36                 {{ printf "%s - %s" (index .artist "#text") .name }}<br>
37                 -<br>
38                 {{ if isset .date "#text" }}<span class="play-date" data-date="{{ (index .date "uts") }}">{{ index .date "#text" }}</span>{{ else }}<span class="play-date" data-date="">Now playing...</span>{{ end }}
39             </p>
40         </div>
41     </a>
42         {{ end }}
43     </div>
44 </div>
45 
46 <script src="/js/jquery-1.10.2.min.js"></script>
47 <script src="/js/bootstrap.min.js"></script>
48 <script src="/js/moment.min.js"></script>
49 <script>
50     $(document).ready(function() {
51         $('span.play-date').each(function(i, e) {
52             var date = moment.unix($(e).data('date'));
53             $(e).html(date.fromNow());
54         });
55     });
56 </script>
57 </body>
58 </html>

On line 31 we connect to the Last.fm api with 3 parameters:

  1. username
  2. limit (aka how many results do you want with a maximum of 200)
  3. api key (which you can get here)

This returns something like:

{
  "recenttracks": {
    "track": [{
      "artist": {
        "#text": "Years  Years",
        "mbid": ""
      },
      "name": "1977",
      "streamable": "0",
      "mbid": "",
      "album": {
        "#text": "Communion",
        "mbid": ""
      },
      "url": "https://www.last.fm/music/Years++Years/_/1977",
      "image": [{
        "#text": "https://lastfm-img2.akamaized.net/i/u/34s/bcf1058b23fb9871695ac9e57582e095.png",
        "size": "small"
      }, {
        "#text": "https://lastfm-img2.akamaized.net/i/u/64s/bcf1058b23fb9871695ac9e57582e095.png",
        "size": "medium"
      }, {
        "#text": "https://lastfm-img2.akamaized.net/i/u/174s/bcf1058b23fb9871695ac9e57582e095.png",
        "size": "large"
      }, {
        "#text": "https://lastfm-img2.akamaized.net/i/u/300x300/bcf1058b23fb9871695ac9e57582e095.png",
        "size": "extralarge"
      }],
      "date": {
        "uts": "1475601846",
        "#text": "04 Oct 2016, 17:24"
      }
    }],
    "@attr": {
      "user": "alrayyes",
      "page": "1",
      "perPage": "1",
      "totalPages": "175083",
      "total": "175083"
    }
  }
}

Lines 32 - 42 basically just loop through this json and output corresponding html.

The rest of the html is basically styling and using momentjs to generate pretty human readable datetimes. There is one small caveat though, as the generated site is static you need to rebuild it sporadically to keep the scrobbles up to date. I accomplish this using a cron job to trigger wercker.

And that’s all there is to it. The same principle can also be used to integrate Instagram into your site. Hopefully this tutorial was clear and concise enough. If not please let me know in the comments below.

Copyright © by Ryan Kes

comments powered by Disqus