Turning subreddits into playlists
I subscribe to the ProgMetal subreddit, because there’s nothing I like more than hearing a virtuoso play an 8-string guitar solo while assorted drummers, bassists, keyboard players, cellists and french horn players alternate bars of 19/16 and 7/4.
The majority of posts on /r/ProgMetal are links to tracks on YouTube that the poster wants to share with the community. As with all of reddit, those posts are then up- or down-voted by other users, resulting in a selection of posts that can be ordered by ‘hot’ness.
I see interesting-looking tracks from this sub come up frequently, but often at times when it’s not practical to open up YouTube; and by the time I am in a position to listen, that interesting-looking track is long forgotten. I realised that what I’d really like is for tracks from this sub to be collected into a weekly playlist that I could listen to at my leisure. So this is what I set out to make.
The plan was to make an app that would call the reddit API to get the top n posts from a given subreddit, filter out any that aren’t just a track recommendation and then use the Spotify API to put those songs in a playlist.
Node — Between the Buried and Me
Red(dit) — King Crimson
I used a reddit API wrapper npm package called snoowrap rather than send http requests myself (although in hindsight this wasn’t a huge benefit).
npm install snoowrap
I made a class to encapsulate my reddit-related code.
This class just wraps snoowrap and exposes a method to get the top posts from a sub. It then attempt to filter them down to only song posts by making sure they’re media posts and then using a regex to attempt to get the band name and song title. This last part is a little fragile because of course a post title could be anything, but a lot of subs, including /r/ProgMetal, use the convention of “Band name - Song Title” so it’s a reasonable bet that will catch most things.
Play(list) With Me — Jeff Beck
Spotify also has a good web API. However in terms of authorisation, it’s evidently primarily intended for making 3rd party streaming clients than for this kind of server-side work, so it was a little more of a challenge to work out exactly how to get the right set of access keys, secrets and tokens to be able to make calls that would affect my personal Spotify account, such as edit a playlist.
However, Postman’s blog has a very good guide that pointed me in the right direction. The answer was to create an application in the Spotify developer dashboard to get a client ID and client secret. Then manually call the authorize endpoint using Postman to generate an access token and a refresh token. For details of exactly how to do that, read the section of this post headed “Setup the environment”.
In this case it turned out to be easier to make http calls directly than to use an API client library. I used axios for the http calls.
npm install axios
Because access tokens are short lived, it would also be necessary to use the refresh token periodically to get a new one. This process is described here. I installed querystring as a helper for making the necessary application/x-www-form-urlencoded calls.
npm install querystring
With a full suite of keys and tokens, I made another class to encapsulate all the Spotify-related code.
I handled the refresh token issue by having a static factory method, SpotifyApi.newApi, which takes care of generating a new access token and then passes that to the class constructor so any time you instantiate the class you do so with a new access token. In a language that allowed it I might have made the constructor private.
The class then has two other methods: one — searchSongUris — that takes the track objects returned by the reddit class and attempts to find corresponding Spotify URIs for each of them, and another — replacePlaylist — that replaces the contents of an existing Spotify playlist with those URIs.
The Bigger Picture — Dream Theater
We need a method to glue all this together.
keys.js, imported at the top, is a (gitignored) file containing all my IDs, secrets, tokens etc. This method simple instantiates the reddit and Spotify API classes and calls their methods in turn to get the tracks from reddit and save them in the Spotify playlist.
Finally we just need an entry point which imports and calls
createPlaylist("progmetal", "<playlist id>", 50)
With this in index.js, we can now run the app from the command line with
To Bid You Farewell — Opeth
The full version of this lives in this github repo. There you can see that I’ve added a few small extras, like the ability to pass in options — the subreddit, the playlist and the limit — as command line arguments. I’ve also added a handler function and some serverless config so it can be deployed as an AWS Lambda and called automatically once a week on a schedule.
The resulting playlist is here, in case you want to experience the music that inspired this mostly unnecessary reason to spend weekend time reading API documentation.