{"id":16682558,"url":"https://github.com/awernick/plvc","last_synced_at":"2026-04-16T05:34:43.675Z","repository":{"id":37219958,"uuid":"275642523","full_name":"awernick/plvc","owner":"awernick","description":"Spotifty Playlist Version Control","archived":false,"fork":false,"pushed_at":"2023-01-23T22:07:27.000Z","size":43,"stargazers_count":0,"open_issues_count":11,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-12-31T23:55:40.927Z","etag":null,"topics":["git","github","python3","spotify"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/awernick.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-06-28T18:14:56.000Z","updated_at":"2020-09-08T01:26:45.000Z","dependencies_parsed_at":"2023-01-25T05:31:10.440Z","dependency_job_id":null,"html_url":"https://github.com/awernick/plvc","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/awernick/plvc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awernick%2Fplvc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awernick%2Fplvc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awernick%2Fplvc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awernick%2Fplvc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/awernick","download_url":"https://codeload.github.com/awernick/plvc/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awernick%2Fplvc/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31872663,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-15T15:24:51.572Z","status":"online","status_checked_at":"2026-04-16T02:00:06.042Z","response_time":69,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["git","github","python3","spotify"],"created_at":"2024-10-12T14:07:53.974Z","updated_at":"2026-04-16T05:34:43.655Z","avatar_url":"https://github.com/awernick.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# plvc - Spotify Playlist Version Control \n\nI was tired of loosing / forgetting awesome songs due to Spotify's daily modification of their playlists, so I decided to create plvc.\n\nPlvc was created with two goals in mind: \n\n1. Tracking changes to your playlists:\n    - Adding / removing songs\n    - Spotify removing songs due to licensing issues\n\n2. Tracking new / old playlists added to your personal library\n\n## How Does It Work?\n\nThere are three components to plvc:\n\n- Spotify API\n- Git repos\n- Github PR API\n\nTo keep things simple, I decided to use git's version control capabilities to keep track of changes to my Spotify playlists.\n\nFor each playlist in a Spotify account, plvc creates a `{playlist_uid}_{playlist_normalized_name}.txt` file with the following format:\n\n```\n{playlist_uid} - {playlist_name} by {playlist_owner}\n\n{track_uid} - {track_name} by {track_artists}\n{track_uid} - {track_name} by {track_artists}\n{track_uid} - {track_name} by {track_artists}\n...\n```\n\nIn 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.\n\nFor example, let's assume we have these two repos:\n\n```\nplvc.git - repo containing the version control logic\nhistory.git - repo containing playlist text files and history\n```\n\nWhen running plvc, the following steps will occur:\n\n1. Pull the latest master from origin in our `history.git` repo\n2. Create a temporary branch from master. Branch name will be the current timestamp e.g `Aug-28-2020-12-23-05`\n3. Using the Spotify API, generate a playlist text file containing all the track metadata\n4. Create a commit containing the newly generated playlist text files, and push to origin\n5. Using the Github API, create a PR from our temporary branch to our base branch (`master`)\n6. If no conflicts are found, automatically merge and close the PR.\n7. Profit\n\n\n## Usage\n\nTo use plvc, a few ENV variables are needed:\n\n```\nPLAYLIST_REPO_DIR - Absolute path to the repository containg the playlist text files (`history.git` in the example)\nPLAYLIST_REPO_REMOTE_URL - Origin remote url for our playlist history repository\nSPOTIFY_CLIENT_ID - Spotify client ID\nSPOTIFY_CLIENT_SECRET - Spotify client secret\nSPOTIFY_REDIRECT_URI - Spotify redirect URI\nSPOTIFY_USERNAME - Spotify username\nGITHUB_ACCESS_TOKEN - Github access token with repo permissions\nGITHUB_PLAYLIST_REPO_ID - Repository name for the playlist history in Github\nSENTRY_DSN - DSN to report to Sentry. Not needed, but I use it for my own personal reporting in my server\n``` \n\nThese variables can be stored in an `.env` file within the plvc directory, or loaded in your execution environment.\n\n\nTo start plvc:\n\n```\n# Skip if you don't need a virtualenv\npyhton3 -m venv venv \nsource venv/bin/activate\n\npip3 install -r plvc/requirements.txt\npython3 plvc\n```\n\n## Troubleshooting\n\n### 1. My plvc instance is stuck in `[Spotify] Authenticating via OAuth`\n\nIn order to view a user's public and private playlists, plvc needs to use Spotify's [Authorization Code Flow](https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow). This auth flow requires the account owner to accept a prompt giving plvc permission to access their personal information. \n\nThis 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...)\n\nIf 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.\n\nTo obtain a valid `token-info.json` file to be used server side, I recommend:\n\n1. Running plvc in an environment that has a GUI and a browser\n2. Copying the `token-info.json` generated by plvc to your server\n3. Run plvc server-side and ensure that the auth process is working correctly\n\nIf 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.\n\nThe `token-info.json` file should look like this:\n```\n{\n    \"access_token\": \"YOURACCESSTOKEN\",\n    \"token_type\": \"Bearer\",\n    \"expires_in\": 3600,\n    \"scope\": \"playlist-read-collaborative playlist-read-private user-library-read\",\n    \"expires_at\": 1598642585,\n    \"refresh_token\": \"YOURREFRESHTOKEN\"\n}\n```\n\n\n### 2. I can't pull / push from / to my playlist history repository\n\nSince we are pushing / pulling from a remote origin, make sure that the user excuting plvc has read / write permissions to the playlist history repository.\n\n\n### 3. Why are PRs not being created in my Github repository?\n\nMake sure that the Github access token you are using with plvc has the following permissions:\n\n\n\n## Additional Setup\n\nIn my personal setup, I'm running plvc as a systemd service with a 1 hour trigger.\n\nHere are my config files:\n\n**plvc.service:**\n```\n[Unit]\nDescription=plvc\nWants=plvc.timer\n\n[Service]\nType=simple\nWorkingDirectory=/path/to/plvc\nExecStart=/path/to/plvc/venv/bin/python .\n\n[Install]\nWantedBy=multi-user.target\n```\n\n**plvc.timer:**\n```\n[Unit]\nDescription=Run plvc every hour\nRequires=plvc.service\n\n[Timer]\nUnit=plvc.service\nOnUnitInactiveSec=60m\nAccuracySec=1s\n\n[Install]\nWantedBy=timers.target\n```\n\nFor error reporting, I use [Sentry](https://sentry.io/) to send email notifications whenever an exception is raised.\n\n\n\n\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fawernick%2Fplvc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fawernick%2Fplvc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fawernick%2Fplvc/lists"}