Ruleplay is a headless Spotify playlist builder that reads one or more source playlists, applies YAML-defined filter rules, and rebuilds derived playlists from scratch.
Warning
This project was generated with AI assistance. Review the code, configuration, Spotify permissions, and generated playlists before relying on it.
- Reads tracks from one or more Spotify source playlists
- Deduplicates tracks by Spotify track ID
- Supports boolean rule composition with
all,any, andnot - Supports filtering on:
track.idtrack.nametrack.explicitartists.idsartists.namesartists.genresalbum.release_datealbum.release_year
- Supports these operators:
eqneqgtgteltltecontainsin
- Caches artist metadata in SQLite, including
artist_name - Provides
auth,dry-run, andrebuildcommands
- Spotify currently blocks audio-feature access for many development-mode apps, so
audio.*filters are not dependable in practice. - The code still has audio-field support, but if Spotify denies that endpoint, audio-based rules will not be usable.
- Generated playlist identity is keyed by filter
namein the database. Renaming a filter infilters.yamlmakes Ruleplay treat it like a new playlist unless you also update the DB row infilter_playlists. - Spotify currently rejects some optional playlist metadata fields for this app, so playlist create/update requests use minimal payloads.
- Python 3.11+
- A Spotify developer app
- Spotify app credentials:
SPOTIFY_CLIENT_IDSPOTIFY_CLIENT_SECRETSPOTIFY_REDIRECT_URI
For local auth, use this redirect URI in your Spotify app settings:
http://127.0.0.1:8888/callback
- Create
.envfrom.env.exampleand fill in your Spotify credentials:
SPOTIFY_CLIENT_ID=your_spotify_client_id
SPOTIFY_CLIENT_SECRET=your_spotify_client_secret
SPOTIFY_REDIRECT_URI=http://127.0.0.1:8888/callback
SPOTIFY_TOKEN_CACHE=.spotify_token_cache- Create the virtual environment and install dependencies:
uv venv --python 3.11 .venv
uv pip install -r requirements.txt- Copy
filters.example.yamltofilters.yamland edit it.
filters.yaml must contain:
source_playlists: a non-empty list of Spotify playlist IDsfilters: a non-empty list of named rule definitions
Example:
source_playlists:
- PLAYLIST_ID_1
- PLAYLIST_ID_2
filters:
- name: Punk
rules:
field: artists.genres
op: contains
value: punk
- name: 90s
rules:
all:
- field: album.release_year
op: gte
value: 1990
- field: album.release_year
op: lte
value: 1999To get a playlist ID from Spotify, copy it from a URL like:
https://open.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5M
The playlist ID is the last path segment:
37i9dQZF1DXcBWIGoYBM5M
Authenticate and cache the refresh token:
.\.venv\Scripts\python.exe main.py authPreview matches without modifying Spotify playlists:
.\.venv\Scripts\python.exe main.py dry-runRebuild all derived playlists:
.\.venv\Scripts\python.exe main.py rebuildEach run prints:
- tracks scanned
- unique tracks
- warnings
- match count per filter
- playlist IDs created or updated
- per-filter failures
Generated playlist names come directly from each filter's name.
If you change a filter name in filters.yaml, Ruleplay will use that new playlist name, but the database will treat it as a different filter unless you also update the row in filter_playlists.
Example:
UPDATE filter_playlists
SET filter_name = 'Punk'
WHERE filter_name = 'Only punk';By default, Ruleplay creates spotify_builder.sqlite3 in the project root.
Tables:
filter_playlistsartist_cacheaudio_feature_cache
Example query for artist inspection:
SELECT artist_id, artist_name, genres_json
FROM artist_cache
ORDER BY artist_name;More implementation details are in docs/implementation.md.