Back to Posts

Echoes of me

I've been working at Shazam for 4 years, between 2012 and 2016, and I always found annoying that we didn't provide much integration with third party services or any public API.

There were of course many reasonable business reasons why, but since the website offered a way to download the history of the tracks discovered with Shazam, I thought it could have been a nice experiment to figure out what I could do with the data publicly available.

I ended up creating Echoes of me starting from the code of the Spotify web api auth example, a repository I was looking at to figure out exactly how to handle the Spotify login.

I didn't focus too much on the code quality or the UX to be honest, I just wanted something working quickly.

I had 2 problems to solve:
* The data available really terse * The Spotify APIs

The data

The main struggle I had was the terseness of the data available: Shazam's track pages come with all the information needed for the job, but the download file did not.

All I got was something on these lines

<tr>  
    <td><a href="http://shz.am/t230989543" target="_blank">Circles Out Of Salt</a></td>
    <td>Snow Ghosts</td>
    <td class="time">13-Mar-2016 09:37</td>
    <td class="map">
        <a href="https://google.com/maps/?q=51.55120086669922,-0.12947963178157806" target="_blank">
            <svg class="icon"><use xlink:href="#icon-map"></use></svg>
        </a>
    </td>
</tr>  

Full text search was really the only option available. Of course this is less than ideal as sometimes the results might be not really accurate, but it was good enough for my goal.

The Spotify APIs

Spotify API are not too difficult to work with and the documentation is pretty good. I had a couple of issues I needed to solve, tho.

As I mentioned before the login was simple enough thanks to the example repository, the full text search was pretty straight forward too, but when it came to adding the songs to the playlist I had a couple of issue.

First of all I wanted to make sure I was not creating a new playlist every single time.

I didn't want to have to setup a data store either (like the id of the playlist, for example). I decided to use a specific name for the playlist and check if there was one with that name.

I called the playlist "Shazam to Spotify", showing off all the creativity I carry with me (only right at this moment, while writing, it came to my mind that "Echoes of Me" would have probably been more appropriate).

If I found a "Shazam to Spotify" playlist for the current user I would have picked the last 3 songs and checked against the list, in order to be able to pick up from where I left. That's a dumb check, but the chances you shazamed the same three songs in the same exact order are quite slim.

Another tricky parts were that adding to many songs to a playlist in one go would end up being really slow and by default Spotify would append track at the end of a playlist, while I wanted the most recent at the top.

I ended up chunking the songs in groups of 30, reversing them and adding them with a position=0 (please bear in mind that I know the code sucks, that's not the point)

function addToPlaylist( tracksUrl, queue, callback ) {  
    var set = queue.splice( 0, 30 ).reverse();

    var fillPlaylistOptions = config( {
        url: tracksUrl + '?position=0&' + querystring.stringify( {
            uris: set.join( ',' )
        } )
    } );

    request.post( fillPlaylistOptions, function() {
        queue.length
            ? addToPlaylist( tracksUrl, queue, callback )
            : callback();
    } );
}

And here's an example of the end result

After Spotify was sorted, I did the same for Youtube and Deezer.

Follow up

Since then, Shazam removed from the website the ability to download your history. And while writing this article I figured out that my algorithm was broken (not sure exactly what went wrong, as the code not tested, but despite having ~500 shazams, the playlists contain more than 900 tracks).

Hence I'm kicking off the second phase: Making it right :tm:

Making it right :tm:

Echoes of me is far from dead. Right at the moment I created a bookmarlet to grab all I need to call the shazam APIs (nothing more than you can find in the developer console if you inspect the network tab on Shazam's website, the only way to do it without access to an history file/API), which is scary. In fact I plan to logout the session after grabbing the data, to invalidate the tokens.

I'm fetching the tracks and store them to the local storage, to avoid having to fetch the whole history every time.
I plan to offer the users to create a playlist in any of the services available (Youtube, Deezer and Spotify). This involves a shift in terms of UX as well: before you had 3 different landing pages, one per service, and you'd login the service upfront, upload the history file and let the app work its (broken) magic; now the bookmarklet would trigger the page to provide all the options and let you pick where to store the data.
Last important bit I need to take care of, beside fixing the logic to create playlists, is to give the user a logout button which would delete that local data, even tho there isn't sensitive data, and prompt to logout from the selected service.

Pixel pusher, javascript-something, front end developer since able to grow a beard, father of two, meteoropathic and with an insane passion for lo-fi music.