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 _plugins
directory:
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
The following 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.