Downloading Album Art with Jekyll
August 02, 2018
When I added the album of the month side of my website, I wanted to make sure the archive had access to all the cover art of the previously chosen albums without loading a bunch of unnaturally heavy Spotify iframes. After a solid hour of messing around with the Spotify API followed by several hours of grokking of Ruby, here’s what I came up with.
Spotify recently updated their API to allow authorized users to download a whole bunch of stuff from their servers, including everything from playlists to user data to actual tracks to album cover art. However, since there’s no simple authentication client side that does not require the user to access to their own Spotify account, I had to resort to downloading the covers in the Jekyll build phase instead. Naturally, the next step was to figure out how to actually write a Jekyll plugin.
Creating a Plugin
Fortunately, the documentation for Jekyll’s plugin system is really comprehensive, and I was able to determine a generator would best suit my need to download images into the assets folder.
I first saved the boilerplate plugin to the
module Reading class Generator < Jekyll::Generator def generate(site) end end end
Getting Spotify Authorization
The next step was to figure out how to properly authenticate my Spotify authorization ID and secret in order to receive a token I could use to request album metadata.
I got the credentials here, and used the following instance method in my
Generator to make the request:
def authorize() if @token return @token end # Create authorization id = '***' secret = '***' authorization = 'Basic ' + Base64.strict_encode64(id + ':' + secret) # Make the token request uri = URI.parse('https://accounts.spotify.com/api/token') https = Net::HTTP.new(uri.host, uri.port) https.use_ssl = true request = Net::HTTP::Post.new(uri.path) request['Authorization'] = authorization request.body = 'grant_type=client_credentials' response = https.request(request) # Parse and return token begin @token = JSON.parse(response.body)['access_token'] rescue JSON::ParserError return nil end end
The first thing to note here is that I used the instance variable
@token because I wanted to be able to call
self.authorize any number of times and only have it make the request to the Spotify API once.
The second is that I have never written a single line of Ruby, so please be understanding if I am writing extremely verbose code :’^).
Downloading Cover Art
The next step was to use the token I had downloaded to make the request for the Spotify cover art images for a given album. In the Spotify ecosystem, albums all have a unique ID, and I made sure to bundle this in each document in my albums collection. For example, here is the front-matter for Fugue State:
title: Fugue State artist: Vulfpeck spotify: 3DiHrdZ5z0AqW6JTpLAKxG for: "Most Influential Albums #1" date: 2018-05-23 layout: album
Generator instance method makes the request for the second album cover image in the list provided by Spotify.
The second is usually 300px by 300px as far as I can tell, so it seemed the most appropriate.
def download(id) # Get the URL url = album(id) if url == nil return nil end # Setup to download the file uri = URI.parse(url) https = Net::HTTP.new(uri.host, uri.port) https.use_ssl = uri.scheme == 'https' request = Net::HTTP::Get.new(uri.path) # Download and save File.open('assets/albums/' + id, 'wb') do |output| output.write https.request(request).body end end
Tying it Together
The last part of the project was the actual
generate method, which ties everything together.
This function is called by Jekyll once the generator object has been instantiated:
def generate(site) # Make sure the directory exists FileUtils.mkdir_p 'assets/albums' # Download missing albums site.collections['albums'].docs.each do |album| id = album['spotify'] if !File.file?('assets/albums/' + id) self.download(id) end end end
And that’s it! Ruby is quite a strange language, but it is flexible, easy to use, and definitely well-suited to the Jekyll framework. I’m a big fan of how straightforward this was (well, granted I made about 150 Google queries), and it gives me cautious optimism about implementing more complex plugins in the future.