Thursday, February 1, 2018

How Old Am I, According to Spotify

I'll explain this down below.

Spotify facilitates very convenient access to lots of great music on many different types of devices.  However, being a standalone application that manages its own music in its own way means you can't interact with the songs as you could when they were in MP3 format or on vinyl.  For developers, the Spotify APIs can change the game.

A Brief History of My Music Listening


I'm a bit of an odd bird in my musical tastes, which were influenced by my mom as we listened to a lot of oldies stations driving around to various events and activities.  Where most oldies stations typically play music that is 30-40 years old, one station in Dallas (KAAM 770) plays music that is around 50 years old.  Thus, back in the 1990s, they were playing lots of big band and swing standards.  (Now they have caught up to where traditional oldies stations were back then, but playing music more along the lines of The Carpenters rather than classic rock.)  I developed an affinity for Nat King Cole, and was soon introduced by family to the music of Frank Sinatra.  Then, playing in various school bands, I took a liking to orchestral/instrumental scores with rich musical and technical content, including game show music, as I was becoming fascinated with all aspects of game show production.  Fast forward to 2007, when I saw The Jersey Boys in concert, and then Frankie Valli in person shortly thereafter, and I have been seeking my fix of 60s & 70s pop ever since.

It fits, along with my game show collection and seeking exact airdates for all my episodes, that I would like to know when all the music in my collection was recorded and by who exactly.

Curating a Collection, and Odd Ways To Listen


In the days of file sharing, I would come across MP3 files that often had incomplete or incorrect metadata as to their origins, especially the year but sometimes even the artist, often attributing the work of a much lesser-known artist, but sharing a similar style, to the greater-known artist.  I would do research on these songs and correct the information in programs such as JRiver Media Center that not only offered a powerful suite of music and playlist management capabilities, but also the best in digital signal processing to make your music really shine on your speakers.  Sometimes, to heighten the emotional content for me, I would pitch-bend the song, usually 1/4 to 1/2 step flat, but occasionally 1/2 step sharp.  (Too much beyond that and the timbre of voices and instruments becomes distorted, ruining the believable effect.)  On vinyl, an adjustable turntable could provide a similar effect, except JRiver was smarter in that it could adjust only the pitch, not the tempo if you didn't desire.

Gone are the days where people would freely trade MP3s on the Internet.  It was a temporary fix for a music industry that refused to adapt to the times, but now it is so inexpensive and convenient to maintain a subscription to a service such as Spotify.  The only problem is you are typically constrained to using only their media player which lacks much in the way of audiophile DSP capabilities (notably not even an equalizer for the desktop version), and the geeky curator will miss the ability to see see so much data at once (and edit it) as JRiver shows.

Nevertheless, to account for the lacking feature (and satiate my curating curiosity and desire for data), I thought that, as a developer, there might be a way to gain further insight into what I've been missing.  Lo and behold, they offer an API for developers that can get you all this information, and possibly even a gateway into playing music through your own DSP tools (if Web-enabled).  Since I have had Spotify since the end of 2014 and haven't really paid much attention to my old music files since then, there are a great many new (to me) songs in my list that I don't know much about except for when I search for them on Google.

On a Quest For Information


Spotify usually provides a lot of interesting, insightful analytics as long as you have not blocked their emails.  (They really don't email you much, so if you're blocking them, you're missing out.)  One of my favorite emails from them included the following tidbit:

Spotify must think I'm an African-American at least 60 years of age.  (Or maybe a white person of around 50, since once they offered me a deal on Chicago + Doobie Brothers tickets.  Of course, I took them up on it.)

This is very cool and only the tip of the iceberg of data you could collect about yourself.  However, things like this often lead to more questions than answers, such as "What's another genre I'd like that doesn't relate except for only some esoteric way a computer would understand?" or "What's the song I really liked in my Discover Weekly but forgot to bookmark?" or "How have my listening preferences changed over time?"

Nevertheless, one thing I've understood since about 2007 is that my musical tastes fall in line chronologically with "doo-wop to disco", meaning core 1962-1978 with fringes between 1956-82 (gotta include the classic rock & roll).  Of course, this doesn't include the works of Frank Sinatra and Nat King Cole, who were often still covering songs written in the 1930s and 40s all the way into the 1960s and beyond.  But because their new recordings were often completely different interpretations and instrumentations, I am not so concerned with the song's original release date as I am with the date of those particular recordings.

As such, I was expecting to obtain data from Spotify and present it graphically in such a way that would display a big hump around 1962-78, with a short tail backward, and a long tail onward into the present.  However, this is what I got:

Not much love for the 1980s; just 60s & 70s, and mostly rehashes of the same from later years.

However, Spotify doesn't concern itself with the original release date of a song, but with the release date of an entire album.  This means that looking back on, for instance, The Voice album by Frank Sinatra, which was in 1955 already a compilation of his much earlier works (and released once again in 1999), any song on that album (originally recorded between 1943-52) would show up as 1955 (or possibly 1999, one year after Sinatra passed away) on Spotify.  I know for a fact there are songs in my library recorded in 1958 and 1961, but the graph above doesn't show anything prior to 1962.  I haven't experimented with how it handles local files I have curated because, quite frankly, I don't want them intermingling, so this data pertains to only what I stream from their service.

So, How Old Am I, According to Spotify?


To be totally honest, I don't know, but if I had to guess, it would think I was as old as about 70, with maybe a 50/50 chance of being white, and formative tastes appearing in the late 1950s, and staying decidedly hip until around 30 when everyone got sick of disco.  Well, I'm around 30 now, and my tastes have never been decidedly hip.

Once upon a time, Spotify made playlists for their users, consisting of music from the 1970s, 80s, and etc. that reflected music you would have listened to back then based on your current tastes.  It struggled to do much for me, but I have to give it credit for giving me a "1970s playlist" consisting of many compilation albums from the likes of Glenn Miller and other famous big bands dating back to the 1940s.

How Old Are You, According to Spotify?


If you are a developer, here is how you tell.  It's definitely cutting-edge JavaScript code (if you traveled back to 2012 to read this), but not too difficult to get working.  All you need is:

  1. A Spotify account where you have signed into the developer portal and registered an app.
  2. The example Spotify Web API app, downloadable from https://github.com/spotify/web-api-auth-examples
  3. Your app in Spotify configured with the correct redirect URI.  If running it locally, it is http://localhost:8888/callback/ because this is already established in app.js for you.
  4. Your app.js file configured with the corrseponding client ID and secret provided by the developer portal, and the redirect URI you specified earlier.

Once you have these things, incorporate the following changes into the code you just checked out from Github (because I'm too lazy to fork it and incorporate the changes for you to download conveniently).  Then, kick off the Node server, open your browser, navigate to your server, open your developer tools, log in, and watch the console.log statements reveal your data.  Hopefully running all these calls for your whole library won't DDoS Spotify; with only about 200 songs in my case, I didn't bother putting in any pauses.  The next step would be seeing how the age of the music you are listening to has evolved over time (will I finally like 1980s music within 10 years?), but I guess that would only be interesting for me, since most people probably only listen to current music.

$ git diff
diff --git a/authorization_code/app.js b/authorization_code/app.js
index b37f9c5..1e51945 100644
--- a/authorization_code/app.js
+++ b/authorization_code/app.js
@@ -12,9 +12,9 @@ var request = require('request'); // "Request" library
 var querystring = require('querystring');
 var cookieParser = require('cookie-parser');

-var client_id = 'CLIENT_ID'; // Your client id
-var client_secret = 'CLIENT_SECRET'; // Your secret
-var redirect_uri = 'REDIRECT_URI'; // Your redirect uri
+var client_id = 'something'; // Your client id
+var client_secret = 'something else'; // Your secret
+var redirect_uri = 'http://localhost:8888/callback/'; // Your redirect uri

 /**
  * Generates a random string containing numbers and letters
@@ -44,7 +44,7 @@ app.get('/login', function(req, res) {
   res.cookie(stateKey, state);

   // your application requests authorization
-  var scope = 'user-read-private user-read-email';
+  var scope = 'user-read-private user-read-email user-library-read';
   res.redirect('https://accounts.spotify.com/authorize?' +
     querystring.stringify({
       response_type: 'code',
diff --git a/authorization_code/public/index.html b/authorization_code/public/index.html
index 9c57f1c..9544ba8 100644
--- a/authorization_code/public/index.html
+++ b/authorization_code/public/index.html
@@ -64,6 +64,7 @@
     <script>
       (function() {

+           var years = {};
         /**
          * Obtains parameters from the hash of the URL
          * @return Object
@@ -78,6 +79,50 @@
           return hashParams;
         }

+               function getAlbumInfo(albumIds, next) {
+                       $.ajax({
+                url: 'https://api.spotify.com/v1/albums/?ids=' + albumIds.join(","),
+                headers: {
+                  'Authorization': 'Bearer ' + access_token
+                },
+                success: function(response) {
+                                 var json = eval(response);
+                                 console.log(json);
+                                 for (var a in json.albums) {
+                                       var album = json.albums[a];
+                                       var year = album.release_date.substring(0, 4);
+                                       if (years[year] === undefined) {
+                                         years[year] = 0;
+                                       }
+                                       years[year]++;
+                                 }
+                                 console.log(years);
+                                 if (next !== undefined) {
+                                       getPlaylists(next);
+                                 }
+                }
+            });
+               }
+
+               function getPlaylists(next) {
+                       $.ajax({
+                url: next,
+                headers: {
+                  'Authorization': 'Bearer ' + access_token
+                },
+                success: function(response) {
+                                 var json = eval(response);
+                                 console.log(json);
+                                 albumIds = [];
+                                 for (var song in json.items) {
+                                       albumIds.push(json.items[song].track.album.id);
+                                 }
+                                 console.log(albumIds);
+                                 getAlbumInfo(albumIds, json.next);
+                }
+            });
+               }
+
         var userProfileSource = document.getElementById('user-profile-template').innerHTML,
             userProfileTemplate = Handlebars.compile(userProfileSource),
             userProfilePlaceholder = document.getElementById('user-profile');
@@ -109,9 +154,9 @@
                 },
                 success: function(response) {
                   userProfilePlaceholder.innerHTML = userProfileTemplate(response);
-
                   $('#login').hide();
                   $('#loggedin').show();
+                                 getPlaylists('https://api.spotify.com/v1/me/tracks');
                 }
             });
           } else {
(END)

Epilogue


I didn't take fully after my mother in my musical preferences.  While I tend to enjoy disco hits with funky grooves or complex chords and progressions, her feeling on the genre is summarized by her shirt with iron-on letters reading "Disco Sucks."  I'll post a picture of it if she can find it.