Deterministic Spotify playlist creation from exact track URIs.
Project description
Perfect Playlist
Build the Spotify playlist you chose, exactly as you chose it.
Perfect Playlist is a local CLI and importable Python package for turning an ordered set of exact Spotify track references into an exact playlist. It preserves order and duplicates, validates before writing, and verifies what Spotify stored afterward.
Vision
AI should be able to build a deterministic playlist without outsourcing the final decision to another generative system. Perfect Playlist is intended to become that dependable final-mile primitive for both people and AI agents.
Mission
Provide the smallest safe interface that can discover Spotify tracks, inspect exact candidates, preserve a chosen TrackSequence, and build or compare playlists without substitution, hidden reordering, silent skips, or destructive overwrites.
Why This Exists
The idea came from trying to create deterministic playlists through ChatGPT's Spotify integration. Asking ChatGPT to ask Spotify to "generate" a playlist passes the intent through multiple interpretive layers. It is like playing a game of Telephone with AI: the request may sound similar at the other end, but the exact tracks and order are no longer guaranteed.
Perfect Playlist removes that last interpretive handoff. An AI agent can search, inspect, and deliberately select exact Spotify track references, then hand the resulting TrackSequence to a deterministic build command. Discovery can be intelligent; the final write should be exact.
This project is not trying to replace recommendation systems. It provides the reliable mechanism that builds a playlist after the choices have already been made.
Product Direction
buildis the primary action and creates a new public playlist by default.- A public or private playlist can be built by supplying an owned, empty target.
addis a secondary, append-only action and never changes visibility.verifycompares any two Sources as exact ordered TrackSequences.exportcreates durable YAML, JSON, text, or link output without overwriting files.searchandinspectexpose facts without choosing tracks or writing playlists.- Repair and natural-language resolve workflows are removed from the interface.
The approved behavior is specified in the CLI contract. The canonical TrackSequence, Source pipeline, authentication behavior, deterministic write workflows, read-only discovery commands, agent guidance, and offline/live QA matrix are complete.
Documentation
- Product vision and ubiquitous language
- Authoritative CLI contract
- Reconciled implementation plan
- Git completion workflow
- Live Spotify QA evidence and handoff
Start with the documentation index when implementing or reviewing the project.
Requirements
- Python 3.11+
- A Spotify developer app
- A registered loopback redirect URI such as
http://127.0.0.1:8888/callback
Installation
Install the released command and Python package from PyPI:
python -m pip install perfect-playlist
perfect-playlist --help
Create a private environment file outside the package installation:
SPOTIPY_CLIENT_ID=your_client_id_here
SPOTIPY_CLIENT_SECRET=your_client_secret_here
SPOTIPY_REDIRECT_URI=http://127.0.0.1:8888/callback
Point Perfect Playlist to that file, then authorize the Spotify account once:
$env:PERFECT_PLAYLIST_SECRETS_FILE='C:\path\to\spotify-secrets.env'
perfect-playlist auth login
Keep PERFECT_PLAYLIST_SECRETS_FILE set whenever you run the CLI, or set the three SPOTIPY_* variables directly. OAuth tokens are stored separately in the operating-system user cache.
Development Setup
py -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -e ".[dev]"
New-Item -ItemType Directory -Force resources | Out-Null
Copy-Item spotify-secrets.env.example resources/spotify-secrets.env
Populate the local, gitignored resources/spotify-secrets.env:
SPOTIPY_CLIENT_ID=your_client_id_here
SPOTIPY_CLIENT_SECRET=your_client_secret_here
SPOTIPY_REDIRECT_URI=http://127.0.0.1:8888/callback
PERFECT_PLAYLIST_RUN_INTEGRATION_TESTS=0
The package loads this file directly with python-dotenv. The safe committed shape is spotify-secrets.env.example; Ralph-specific configuration is documented in the repository setup guide. The real file, its values, OAuth material, and token caches must never be committed.
Register the exact redirect URI in Spotify's developer dashboard, then authorize once:
perfect-playlist auth login
OAuth tokens are stored outside the repository in the operating-system user cache.
Usage
Use Search and Inspect to choose exact tracks, store the chosen canonical URIs in a durable Source, and pass that Source to write commands:
tracks:
- spotify:track:5Qamlcya1Hz5Z4AgzdQ5q8
- spotify:track:5irdISp39LC6kD10muguYh
perfect-playlist search 'track:"Call On You" artist:"Corey Lingo"'
perfect-playlist inspect spotify:track:5Qamlcya1Hz5Z4AgzdQ5q8
perfect-playlist build tracks.yaml
perfect-playlist add tracks.yaml --target spotify:playlist:PLAYLIST_ID
perfect-playlist verify tracks.yaml spotify:playlist:PLAYLIST_ID
perfect-playlist export spotify:playlist:PLAYLIST_ID --out playlist.yaml
With no --name or --target, Build creates a new public My Perfect Playlist, advancing to a numeric suffix if that name is already owned. Use --target only for an owned empty Build Target; Add requires --target and appends without changing earlier tracks or visibility.
The importable API exposes the same workflow boundaries and typed results:
from perfect_playlist import (
add_to_playlist,
build_public_playlist,
build_target_playlist,
compare_track_sequences,
inspect_track,
read_source,
search_tracks,
)
source = read_source("tracks.yaml")
candidates = search_tracks('track:"Call On You" artist:"Corey Lingo"')
selected = inspect_track(candidates[0].uri)
built = build_public_playlist(source)
target_build = build_target_playlist(source, "spotify:playlist:PLAYLIST_ID")
added = add_to_playlist(source, "spotify:playlist:WRITABLE_PLAYLIST_ID")
verification = compare_track_sequences(source, read_source(built.playlist.uri))
Search results and inspected metadata are command responses, not Sources. The caller deliberately chooses canonical URIs before Build or Add.
Current Validation
Offline checks:
$env:PERFECT_PLAYLIST_RUN_INTEGRATION_TESTS='0'
python -m pytest
python -m ruff check --no-cache perfect_playlist tests
python -m mypy --no-incremental perfect_playlist tests
The live Spotify test is controlled by resources/spotify-secrets.env. Set
PERFECT_PLAYLIST_RUN_INTEGRATION_TESTS=1 to include a temporary public
create/verify/unfollow cycle in every full pytest run. Leave it at 0 to keep
live writes opt-in.
$env:PERFECT_PLAYLIST_RUN_INTEGRATION_TESTS='1'
python -m pytest
The latest credentialed run proved public creation and exact ordered writes, but Spotify did not persist a requested private state during API creation. The package now fails closed before adding tracks. The approved private flow therefore fills an empty private playlist that the user already owns instead of claiming that the API can create one reliably. See live QA evidence.
Determinism Policy
Write operations accept only Sources that resolve to exact Spotify track URIs. Human-readable song requests belong in an agent's discovery workflow, where Search and Inspect can be used deliberately. They are never silently resolved inside Build or Add.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file perfect_playlist-1.0.0.tar.gz.
File metadata
- Download URL: perfect_playlist-1.0.0.tar.gz
- Upload date:
- Size: 81.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f638c97ac6460c9ac98b5e08d155b341ff5d25519c38797aefc25c8225b28940
|
|
| MD5 |
d45cd91d1e6f2d91f756380a72ac9401
|
|
| BLAKE2b-256 |
8bc38bc15470d69c7733e0fe61e2d31b6768298534f998a6ffa6246cd98a2bd4
|
Provenance
The following attestation bundles were made for perfect_playlist-1.0.0.tar.gz:
Publisher:
publish.yml on devin-thomas/perfect-playlist
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
perfect_playlist-1.0.0.tar.gz -
Subject digest:
f638c97ac6460c9ac98b5e08d155b341ff5d25519c38797aefc25c8225b28940 - Sigstore transparency entry: 2195029611
- Sigstore integration time:
-
Permalink:
devin-thomas/perfect-playlist@9d4528f961256dd701f5d6f7df3ec4489244b9e4 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/devin-thomas
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9d4528f961256dd701f5d6f7df3ec4489244b9e4 -
Trigger Event:
release
-
Statement type:
File details
Details for the file perfect_playlist-1.0.0-py3-none-any.whl.
File metadata
- Download URL: perfect_playlist-1.0.0-py3-none-any.whl
- Upload date:
- Size: 23.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fe76ff20d408fca5b7276ec779ef0595ad24a37af63dba976f1a58e9cc456530
|
|
| MD5 |
7248b4153d01638dc5c31613380faa4a
|
|
| BLAKE2b-256 |
7cdc7bb7bc64875d65077a07d94e11c758b1838975099df4a5869264e5d384f2
|
Provenance
The following attestation bundles were made for perfect_playlist-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on devin-thomas/perfect-playlist
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
perfect_playlist-1.0.0-py3-none-any.whl -
Subject digest:
fe76ff20d408fca5b7276ec779ef0595ad24a37af63dba976f1a58e9cc456530 - Sigstore transparency entry: 2195029643
- Sigstore integration time:
-
Permalink:
devin-thomas/perfect-playlist@9d4528f961256dd701f5d6f7df3ec4489244b9e4 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/devin-thomas
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9d4528f961256dd701f5d6f7df3ec4489244b9e4 -
Trigger Event:
release
-
Statement type: