Creating Spotify Playlists with R

I didn’t have a car from 2007 until 2019. One of the things I missed most when I started biking everywhere was the radio. NPR news, XRT DJs, playing music too loudly; I loved it all. It didn’t take me too long to figure out that portable speakers could get some of the job done. In 2007 this was much harder than it is now. Instead of Spotify streaming on your iPhone to a decent Bluetooth speaker, I had a Skullcandy backpack hooked up to my MiniDisc player. Over the years streaming services like Spotify and Pandora became functional with dropping data prices and more powerful phones. Eventually I noticed that the endless stream of music was resulting in ear fatigue; I was still missing the news breaks/updates and the DJ banter.

Daily Drive

I became a father 9 months ago. Midway through the pregnancy we bought a new car. When I plugged my iPhone in, the Apple CarPlay activated. This somehow added a new playlist to my Spotify; Daily Drive. It was everything I wanted:

a personalized audio feed that blends music and news in one place for the perfect commute experience. Included at launch:

  • Short-form podcast news updates from Wall Street Journal, NPR, and PRI
  • A mix of your favorite songs and artists interspersed with tracks you’ve yet to discover
  • Updates throughout the day to keep both the music and news fresh
  • An escape from toggling between multiple stations to avoid music that isn’t quite your speed

Initially this was great, and was the next step in the evolution of the audio experience I wanted, although the execution left something to be desired.

Eventually I started crafting my own Daily Drive playlists. When my Discover Weekly playlist refreshes on Mondays I would add WBEZ and NPR news to the queue towards the beginning. I throw in a medium length podcast in there like Planet Money and a long one like Useful Idiots towards the end. I did the same with the Release Radar playlist on Fridays. This was great too for a time. Eventually I realized how much manual effort this required. I thought, “Why not automate this?”

spotifyr

I’ve been using Spotify for the last 5 years or so. I’ve been using R for the last 15 years or so. So I was very excited to learn that there was a spotifyr package to interact with Spotify using R! Unfortunately, every time I went to use the package it was broken due, apparently, to dependency issues. It’s been off of CRAN since early 2020. While you can still install it from Github, on one occasion this led to a chain of devtools::install_github()-ing. When these extra steps were added to the requisite Spotify developer authorization process, it was frustrating. When I realized that Spotify’s API had also changed and I would need to fix spotifyr myself, I forked the project.

tinyverse

These are common issues with R users who build on top of the tidyverse. The breaking changes introduced by this fast-moving project can result in more maintenance work than some authors anticipate. I will spare you some of the horror stories, but it reminds me of a lesson from my urban planning classes in grad school; we forget or are uninterested in the costs of maintenance when we decide to build infrastructure. When you combine unexpected maintenance with life (i.e. relationships, kids, home, work) it’s understandable that some folks can’t or won’t be able to maintain their package. It is after all largely volunteer work.

That’s why many R users are becoming advocates of the tinyverse:

Dependencies matter. Every dependency you add to your project is an invitation to break your project.

You send that invitation to the direct dependency author, and every downstream author. Add dependencies with care. Evaluate the trade-offs between depending on another package/library and using existing functionality.

tinyspotifyr

With my holiday break from being a part-time IBMer and full-time father, I decided to rewrite the spotifyr package in the tinyverse style utilizing minimal dependencies. The repo is here.

The Spotify API allows you to interact with Spotify programmatically. The spotifyr package wraps this functionality, but then extends it with the genius package to do musicological analyses. While this 15-year jam band veteran with a music minor finds this interesting, it feels like bolted-on functionality. My preference would be to have this functionality be it’s own musicology package. In an effort to minimize maintenance costs, tinyspotifyr drops this functionality.

The majority of the additional work/changes are:

  • dropping in base R’s grepl for tidyverse’s str_detect
  • dropping in base R’s paste0 for tidyverse’s str_glue
  • adding in support for podcast shows and episodes
  • updating the playlist commands to work with podcasts; i.e. unlike music, the market variable is required when dealing with podcasts in playlists

I’m somewhat torn on providing a pure wrapper for the Spotify API vs adding what seems like reasonable additional functionality. For example, I’ve included an add_latest_to_playlist() that finds the latest episode of a podcast and puts it into a given playlist even though this isn’t an official Spotify API call. Perhaps this should be spun out into a SpotifyMixtape package.? Time will tell I’m sure.

I’ll be publishing on CRAN shortly. In the meantime you’ll have to use:

devtools::install_github("troyhernandez/tinyspotifyr")

Usage

The following is a simplified version of my Daily Radio script that is in the README of the package. I will expand on this as a vignette hopefully soon.

Create a Daily Radio playlist

library(tinyspotifyr)
playlist_name <- "Daily Radio"

Get your playlists

my_playlists <- get_my_playlists(limit = 50)
Create a new playlist

Find yesterday’s Daily Radio playlist or create a new, empty playlist.

playlist_logical <- (my_playlists$name == playlist_name)
if(sum(playlist_logical) > 0){
  ind <- which(playlist_logical)
  dr <- my_playlists[ind, ]
} else {
  dr <- create_playlist("TroyHernandez", playlist_name, public = FALSE)
}

Add songs to playlist

I use my Discover Weekly playlist as a base and overwrite my existing “Daily Radio” tracks. Using reorder_replace_playlist_items is more robust for playlists than other options.

discover_weekly <- my_playlists[which(my_playlists$name == "Discover Weekly"),]
dw_tracks <- get_playlist_tracks(discover_weekly$id)
dw_uri <- dw_tracks$track.uri
reorder_replace_playlist_items(playlist_id = dr$id, uris = dw_uri)

Add podcasts to your playlist

I listen to 4 songs between each podcast. NPR updates every hour, but sometimes it’s empty and returns an error. Notice the zero indexing.

# Add NPR
try(add_latest_to_playlist(playlist_id = dr$id, uri = "spotify:show:6BRSvIBNQnB68GuoXJRCnQ", position = 0), silent = TRUE)
# Add WBEZ
add_latest_to_playlist(playlist_id = dr$id, uri = "spotify:show:1x1n9iWJLYNXYdDgLk5yQu", position = 1)
# CBS
add_latest_to_playlist(playlist_id = dr$id, uri = "spotify:show:2pLChHUBuwElfAplwVGTdF", position = 6)
# JRE Clips
add_latest_to_playlist(playlist_id = dr$id, uri = "spotify:show:1LMmQF9PH8LjYrktU0Oq5Y", position = 7)
# Chicago Tribune
add_latest_to_playlist(playlist_id = dr$id, uri = "spotify:show:3K1ffPI9ynW3mO24A5rfbF", position = 12)
# Marketplace
add_latest_to_playlist(playlist_id = dr$id, uri = "spotify:show:6zYlX5UGEPmNCWacYUJQGD", position = 13)
# Crains
add_latest_to_playlist(playlist_id = dr$id, uri = "spotify:show:20Ut1ENH9nTy4LqWF9p8vq", position = 18)
# Planet Money
add_latest_to_playlist(playlist_id = dr$id, uri = "spotify:show:4FYpq3lSeQMAhqNI81O0Cn", position = 23)
# WSJ Tech
add_latest_to_playlist(playlist_id = dr$id, uri = "spotify:show:51MrXc7hJQBE2WJf2g4aWN", position = 28)
# Useful idiots
add_latest_to_playlist(playlist_id = dr$id, uri = "spotify:show:5BpYXlVorOw5FZ9pfpu7ff", position = 33)
# Chicago Tonight to the end of the podcast
add_latest_to_playlist(playlist_id = dr$id, uri = "spotify:show:2WuB3zkmXGo7sJUZ6GQIx3")

Good Morning Good Morning

If you’d like to live the luxurious life of personalized radio then I suggest making this a cron job. On my home Ubuntu machine that means editing the crontab file with:

crontab -e

If your R project using tinyspotifyr is ~/DailyRadio and the script is ~/DailyRadio/R/DailyRadio.R, then add this to the end of the crontab file:

1 6-17 * * * (cd ~/DailyRadio; Rscript ~/DailyRadio/R/DailyRadio.R) >> ~/DailyRadio/logs/DailyRadio.log 2>&1

This will change the cron daemon to the ~/DailyRadio directory (where your .httr-oauth for the Spotify API presumably resides), run the DailyRadio.R script every hour from 6:01 AM until 5:01 PM, and write the logs to a log file in case something goes wrong.

I still need to add a cron job to update the NPR news episode every hour. NPR deletes their episode every hour and replaces it. So if I don’t start listening to my Daily Radio before 8am, it disappears and I don’t get my 5 minutes of national news to start my day. Life could be worse, lol. I discovered that using the reorder_replace_playlist_items() function updates/overwrites playlists elegantly. This allows me to just rerun the DailyRadio.R script every hour to ensure that I start my day off with NPR/WBEZ news.

Thanks for reading.

Keep on Rockin…