/plvc

Spotifty Playlist Version Control

Primary LanguagePython

plvc - Spotify Playlist Version Control

I was tired of loosing / forgetting awesome songs due to Spotify's daily modification of their playlists, so I decided to create plvc.

Plvc was created with two goals in mind:

  1. Tracking changes to your playlists:

    • Adding / removing songs
    • Spotify removing songs due to licensing issues
  2. Tracking new / old playlists added to your personal library

How Does It Work?

There are three components to plvc:

  • Spotify API
  • Git repos
  • Github PR API

To keep things simple, I decided to use git's version control capabilities to keep track of changes to my Spotify playlists.

For each playlist in a Spotify account, plvc creates a {playlist_uid}_{playlist_normalized_name}.txt file with the following format:

{playlist_uid} - {playlist_name} by {playlist_owner}

{track_uid} - {track_name} by {track_artists}
{track_uid} - {track_name} by {track_artists}
{track_uid} - {track_name} by {track_artists}
...

In addition to creating playlist text files, plvc requires an additional git repository to store / track changes to the generated files. This repository will contain the history of track / playlist changes, making it easy to see how your music changed over time.

For example, let's assume we have these two repos:

plvc.git - repo containing the version control logic
history.git - repo containing playlist text files and history

When running plvc, the following steps will occur:

  1. Pull the latest master from origin in our history.git repo
  2. Create a temporary branch from master. Branch name will be the current timestamp e.g Aug-28-2020-12-23-05
  3. Using the Spotify API, generate a playlist text file containing all the track metadata
  4. Create a commit containing the newly generated playlist text files, and push to origin
  5. Using the Github API, create a PR from our temporary branch to our base branch (master)
  6. If no conflicts are found, automatically merge and close the PR.
  7. Profit

Usage

To use plvc, a few ENV variables are needed:

PLAYLIST_REPO_DIR - Absolute path to the repository containg the playlist text files (`history.git` in the example)
PLAYLIST_REPO_REMOTE_URL - Origin remote url for our playlist history repository
SPOTIFY_CLIENT_ID - Spotify client ID
SPOTIFY_CLIENT_SECRET - Spotify client secret
SPOTIFY_REDIRECT_URI - Spotify redirect URI
SPOTIFY_USERNAME - Spotify username
GITHUB_ACCESS_TOKEN - Github access token with repo permissions
GITHUB_PLAYLIST_REPO_ID - Repository name for the playlist history in Github
SENTRY_DSN - DSN to report to Sentry. Not needed, but I use it for my own personal reporting in my server

These variables can be stored in an .env file within the plvc directory, or loaded in your execution environment.

To start plvc:

# Skip if you don't need a virtualenv
pyhton3 -m venv venv 
source venv/bin/activate

pip3 install -r plvc/requirements.txt
python3 plvc

Troubleshooting

1. My plvc instance is stuck in [Spotify] Authenticating via OAuth

In order to view a user's public and private playlists, plvc needs to use Spotify's Authorization Code Flow. This auth flow requires the account owner to accept a prompt giving plvc permission to access their personal information.

This auth flow should work fine if you are running plvc in an environment with a graphical user interface (e.g. your personal laptop, a server with a windowing system, etc...)

If you are running plvc on a server, you will need to obtain your Spotify access token and refresh token somehow and store it in a token-info.json at the root of your plvc directory.

To obtain a valid token-info.json file to be used server side, I recommend:

  1. Running plvc in an environment that has a GUI and a browser
  2. Copying the token-info.json generated by plvc to your server
  3. Run plvc server-side and ensure that the auth process is working correctly

If you know of other ways of obtaining a valid Spotify access token and refresh token, then simply create a token-info.json file in the root of your plvc directory and plug in the values.

The token-info.json file should look like this:

{
    "access_token": "YOURACCESSTOKEN",
    "token_type": "Bearer",
    "expires_in": 3600,
    "scope": "playlist-read-collaborative playlist-read-private user-library-read",
    "expires_at": 1598642585,
    "refresh_token": "YOURREFRESHTOKEN"
}

2. I can't pull / push from / to my playlist history repository

Since we are pushing / pulling from a remote origin, make sure that the user excuting plvc has read / write permissions to the playlist history repository.

3. Why are PRs not being created in my Github repository?

Make sure that the Github access token you are using with plvc has the following permissions:

Additional Setup

In my personal setup, I'm running plvc as a systemd service with a 1 hour trigger.

Here are my config files:

plvc.service:

[Unit]
Description=plvc
Wants=plvc.timer

[Service]
Type=simple
WorkingDirectory=/path/to/plvc
ExecStart=/path/to/plvc/venv/bin/python .

[Install]
WantedBy=multi-user.target

plvc.timer:

[Unit]
Description=Run plvc every hour
Requires=plvc.service

[Timer]
Unit=plvc.service
OnUnitInactiveSec=60m
AccuracySec=1s

[Install]
WantedBy=timers.target

For error reporting, I use Sentry to send email notifications whenever an exception is raised.