Jouons avec l'API de Last.fm

10 minutes to read

Dans le cadre des cours que je donne à Les Gobelins, une de mes stagaires a voulu peupler son projet de fin de formation en tapant dans l’API de the MovieDB. N’étant pas developpeur front de métier, je suis donc parti en exploration pour pouvoir l’aider et aussi pour apprendre quelque chose de nouveau.

Histoire de rendre cette exploration plus interessante pour moi, je me suis interessé à l’API de Last.fm pour créer une page listant les XX derniers morceaux que j’ai scrobbé.

TL;DR

La démo live est par ici : http://labs.nicolas-birckel.fr/music/

Le code sur Github : Last 20 track played

Oui ma clé API est visible dans le dépot Github ¯\_(ツ)_/¯.

L’objectif :

Lister sous forme de cards les derniers 20 morceaux scrobbés sur Last.fm.

Chaque card comprendra le titre du morceau, le nom de l’artiste et si elle est disponible sur Last.fm l’image du morceau en background de la card.

On pourra bien sur cliquer sur chaque card pour accéder à la page last.fm du morceau.

Un peu de html et de SCSS

<html>
  <head>
    <title>Music Tracking</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="assets/css/styles.css">
    <link rel="icon" type="image/png" href="assets/img/favicon.png" />
  </head>
  <body>
    <svg class="logo" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M1 13h-1v-1h1v1zm22-1h-1v1h1v-1zm-20-1h-1v3h1v-3zm18 0h-1v3h1v-3zm-14 0h-1v3h1v-3zm10-1h-1v5h1v-5zm-12 0h-1v5h1v-5zm14-1h-1v7h1v-7zm-10 0h-1v7h1v-7zm2-2h-1v10h1v-10zm4 0h-1v10h1v-10zm-2-2h-1v14h1v-14z"/></svg>
    <h1>Music</h1>
    <main id="content">
          <div id="loading"><img src="assets/img/loader.gif" alt=""></div>

    </main>
    <script src="assets/js/main.js"></script>
  </body>
</html>
@import "variables";
body {
  margin:0;
  font-family:Helvetica, Arial, sans-serif;
  background:$background;
}
.logo{ display:block; margin: 0 auto;width:100px; height:100px; fill:rgba(91, 134, 229, 1);}
[id="content"] {
  display:flex;
  flex-wrap:wrap;
  width:$container-width;
  margin:0 auto;
  justify-content:space-around;
}
[id="loading"] {
  display:flex;
  justify-content:center;
  align-items:center;
}
.card{
width:300px;
height:300px;
flex:auto;
margin:.5em;
padding:.5em;
box-sizing:border-box;
display:flex;
justify-content:center;
align-items:center;
text-decoration:none;
box-shadow:0px 2px 4px darken($background,30%);
  & h2, & p {
    color:$primary;
    text-align:center;
    text-shadow: 1px 1px 5px $shadow;
  }
}
h1{
  text-transform:uppercase;
  text-align:center;
  margin:0 0 1em;
  color:rgba(91, 134, 229, 1)
}

Rien d’extraordinaire coté html.

La balise <main id="content"> sera la balise que je vais remplir en javascript. Elle contient par défaut un gif à afficher en attendant qu’elle soit rempli.

Coté CSS rien de bien méchant non plus, du flex et les propriétés css des “cards” que je vais générer en javascript.

Le javascript

C’est là où cela s’est compliqué pour moi :)

Pour taper dans l’API de Last.fm, il m’a fallu comprendre comment utiliser XMLHttpRequest(), décortiquer la réponse de la requete et trouver un moyen de construire mon markup html des cards en javascript.

var key ="50ad8f29fba1727d9f76646a56242eb0"; // oui encore ma clé en clair
var limit="5";
var user= "nbirckel"; // et mon username aussi ¯\_(ツ)_/¯

var xhr = new XMLHttpRequest();
xhr.open("GET", "https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user="+user+"&api_key="+key+"&format=json&limit="+limit+"", true);
xhr.send();
console.log(xhr.response);

ce qui retourne dans la console ce magnifique JSON :

{"recenttracks":{"track":[{"artist":{"#text":"Luigi Madonna","mbid":"8353a6a9-815a-4c65-b495-c45fed04568f"},"name":"Unconditional Beauty","streamable":"0","mbid":"","album":{"#text":"Background","mbid":""},"url":"https://www.last.fm/music/Luigi+Madonna/_/Unconditional+Beauty","image":[{"#text":"https://lastfm-img2.akamaized.net/i/u/34s/f79a1ca07bae4deecdacce40d43b515d.png","size":"small"},{"#text":"https://lastfm-img2.akamaized.net/i/u/64s/f79a1ca07bae4deecdacce40d43b515d.png","size":"medium"},{"#text":"https://lastfm-img2.akamaized.net/i/u/174s/f79a1ca07bae4deecdacce40d43b515d.png","size":"large"},{"#text":"https://lastfm-img2.akamaized.net/i/u/300x300/f79a1ca07bae4deecdacce40d43b515d.png","size":"extralarge"}],"date":{"uts":"1513253273","#text":"14 Dec 2017, 12:07"}},{"artist":{"#text":"Pan-Pot","mbid":"cca5fd04-e583-4a84-b8a6-607d485dc187"},"name":"Sleepless - Stephan Bodzin Remix","streamable":"0","mbid":"","album":{"#text":"The Other Remixes","mbid":""},"url":"https://www.last.fm/music/Pan-Pot/_/Sleepless+-+Stephan+Bodzin+Remix","image":[{"#text":"https://lastfm-img2.akamaized.net/i/u/34s/9b59f11d7034589b920b3d96d240c38b.png","size":"small"},{"#text":"https://lastfm-img2.akamaized.net/i/u/64s/9b59f11d7034589b920b3d96d240c38b.png","size":"medium"},{"#text":"https://lastfm-img2.akamaized.net/i/u/174s/9b59f11d7034589b920b3d96d240c38b.png","size":"large"},{"#text":"https://lastfm-img2.akamaized.net/i/u/300x300/9b59f11d7034589b920b3d96d240c38b.png","size":"extralarge"}],"date":{"uts":"1513252734","#text":"14 Dec 2017, 11:58"}},{"artist":{"#text":"Mark Knight","mbid":"e507d880-398e-40fe-bda9-aa93920c7d2b"},"name":"Your Love - Original Club Mix","streamable":"0","mbid":"","album":{"#text":"Your Love","mbid":"343004e4-19af-425e-a59f-93c11454c90b"},"url":"https://www.last.fm/music/Mark+Knight/_/Your+Love+-+Original+Club+Mix","image":[{"#text":"https://lastfm-img2.akamaized.net/i/u/34s/d0a0dca431fe4ccba6dbd9a92d0e673c.png","size":"small"},{"#text":"https://lastfm-img2.akamaized.net/i/u/64s/d0a0dca431fe4ccba6dbd9a92d0e673c.png","size":"medium"},{"#text":"https://lastfm-img2.akamaized.net/i/u/174s/d0a0dca431fe4ccba6dbd9a92d0e673c.png","size":"large"},{"#text":"https://lastfm-img2.akamaized.net/i/u/300x300/d0a0dca431fe4ccba6dbd9a92d0e673c.png","size":"extralarge"}],"date":{"uts":"1513252337","#text":"14 Dec 2017, 11:52"}},{"artist":{"#text":"Paul Ritch","mbid":"446a6b51-84c1-4318-83d8-2e7fcd492b0f"},"name":"Run Baby Run","streamable":"0","mbid":"","album":{"#text":"Run Baby Run","mbid":""},"url":"https://www.last.fm/music/Paul+Ritch/_/Run+Baby+Run","image":[{"#text":"https://lastfm-img2.akamaized.net/i/u/34s/0e24b188287945eeb2afe8a1cbce80e0.png","size":"small"},{"#text":"https://lastfm-img2.akamaized.net/i/u/64s/0e24b188287945eeb2afe8a1cbce80e0.png","size":"medium"},{"#text":"https://lastfm-img2.akamaized.net/i/u/174s/0e24b188287945eeb2afe8a1cbce80e0.png","size":"large"},{"#text":"https://lastfm-img2.akamaized.net/i/u/300x300/0e24b188287945eeb2afe8a1cbce80e0.png","size":"extralarge"}],"date":{"uts":"1513251839","#text":"14 Dec 2017, 11:43"}},{"artist":{"#text":"Dr Peacock","mbid":""},"name":"Muzika","streamable":"0","mbid":"","album":{"#text":"Muzika","mbid":""},"url":"https://www.last.fm/music/Dr+Peacock/_/Muzika","image":[{"#text":"","size":"small"},{"#text":"","size":"medium"},{"#text":"","size":"large"},{"#text":"","size":"extralarge"}],"date":{"uts":"1513192172","#text":"13 Dec 2017, 19:09"}}],"@attr":{"user":"nbirckel","page":"1","perPage":"5","totalPages":"4739","total":"23693"}}}"

En le parcourant, on repère assez vite ce qui va nous interesser : l’array track que l’on va devoir parcourir et pour chaque entrée de cet array : la valeur de #text pour artist,celle de name, et la valeur de url dans l’array image.

On remarque également que #text dans l’array image est vide pour certaines tracks, il va donc falloir gérer un placeholder pour ce cas là.

La requete vers l’API est en asynchrone, on va donc écouter les changements d’état jusqu’à ce qu’elle soit faite.

Dès que c’est le cas, on transforme le JSON en objet dans une variable et on définit quelques variables utiles pour la suite : la balise dans laquelle on va injecter nos cards et celle qui contient le loader que l’on supprime vu que nous avons récuperer nos données.

xhr.addEventListener('readystatechange', function() {
    if (xhr.readyState === 4) {
    var lasttrack = JSON.parse(xhr.response);
    var track = lasttrack.recenttracks.track;
    var theDiv = document.getElementById("content");
    var loader = document.getElementById("loading");
    loader.parentNode.removeChild(loader);

On parcourt notre tableau avec une boucle et pour chaque itération on récupère les valeurs qui nous interesse :

for (var i = 0;  i <=track.length - 1; i++) {
       trackArtist =track[i].artist["#text"];
       trackName =track[i].name;
       trackurl = track[i].url;

On teste ensuite si la valeur de #text n’existe pas dans l’array image pour proposer un placeholder, sinon on recupère l’url de l’image :

 if (track[i].image[3]["#text"]==""){
        trackImgLarge="assets/img/nocover.png";
       }
       else {
      trackImgLarge = track[i].image[3]["#text"];
      }

Maintenant que nous avons tout ce qui nous interesse, on construit la card du morceau :

  var card = document.createElement('a');
    var att = document.createAttribute("class");      
    att.value = "card";   
    card.setAttributeNode(att);                       
    var link = document.createAttribute("href");      
    link.value = trackurl;                          
    card.setAttributeNode(link);
    var style = document.createAttribute("style");
    style.value = "background-image : linear-gradient(135deg, rgba(54, 209, 220, .1) 0%,rgba(91, 134, 229, .9) 100%), url("+trackImgLarge+");background-size: cover; background-position: 50% 50%;";
    card.setAttributeNode(style);
    var h2 = document.createElement('h2');
    var h2Content = document.createTextNode(trackName)
    h2.appendChild(h2Content);
    var p = document.createElement('p');
    var pContent = document.createTextNode(trackArtist);
    p.appendChild(pContent);
    var box=document.createElement('div');
    //card.appendChild(cardImg);
    box.appendChild(h2);
    box.appendChild(p);
    card.appendChild(box);

Puis on injecte notre card dans la balise main :

theDiv.appendChild(card);

Tada, ça fait le job !

Le script complet sur github

Pour conclure

Oui mon code est ultra verbeux, oui la construction de ma card à coup de createElement,createAttribut,setAttributeNode et de appendCHild est clairement pas optimisée.

Mais j’ai pu apprendre quelque chose et surtout j’ai été en mesure d’aider ma stagiaire ce qui était mon but initial.

Arnaud Lemaire m’a proposé de jolies refacto de mon script, sur lesquelles je me suis appuyé pour que ma stagaire puisse produire un code plus “propre” que le mien. Elles sont disponibles sur un Gist ici. Un grand merci à Lui !