Two CLI tools built on the same Spotify auth/client foundation:
albumize— takes a CSV ofartist,albumrows and builds a Spotify playlist containing every track it can find from those albums, reporting anything it couldn't match confidently.cover— given an existing playlist, samples its most frequent artists (+ their genres), builds a prompt from them, and generates + uploads an AI cover image for the playlist.
Built with Deno — no node_modules, no build step.
- Install Deno if you haven't:
brew install deno - In your Spotify Developer Dashboard app, add this redirect URI:
http://127.0.0.1:8080/callback - Copy
.env.exampleto.envand fill in yourSPOTIFY_CLIENT_ID(found on your app's dashboard page) and, if you'll usecover, yourOPENAI_API_KEY. No Spotify client secret is needed — auth uses PKCE.
cp .env.example .env
# edit .env with your client ID (and OpenAI key, if using `cover`)deno task albumize path/to/albums.csv --name "My Playlist" --dry-run--name(required): name of the playlist to create.--public true|false(defaultfalse): whether the playlist is public.--dry-run: match and print the report only, skip creating/populating a playlist. Use this first to sanity-check matches before spending API calls creating a real playlist.
CSV input needs artist,album columns (header row required).
After matching, the tool prints a report:
- Matched — albums found with high confidence, included automatically.
- Ambiguous — best guess included, but flagged for a manual spot-check.
- Not found — nothing confident enough; add these to the playlist by hand.
deno task cover <playlist-id-or-url> --dry-runAccepts a bare playlist ID, a spotify:playlist:... URI, or an open.spotify.com/playlist/... URL.
--sample-size(default12): how many of the playlist's most-frequent artists to sample (used to derive genres — see below).--prompt "<extra text>": appended to the end of the generated prompt, e.g. to steer toward a specific visual style (--prompt "in the style of a layered paper collage"). Skips the interactive style menu described below.--no-style: skip the interactive style menu without providing--prompt— goes straight to the plain genre-only prompt. Useful for scripting, since the menu otherwise blocks waiting on stdin.--no-upload: generate and save the image locally but don't attempt to set it as the playlist's cover. Since setting a cover requires owning the playlist (or being a collaborator), this is also how you can generate art for a public playlist you don't own — reading a public playlist's tracks needs no special permission, only the upload step does.--dry-run: sample artists, fetch genres, and print the constructed prompt — skips the OpenAI call and the cover upload. Free to run repeatedly; only the real run costs an OpenAI image generation.
The prompt is built from genre only, not artist names — a real, recognizable musician in the prompt can trip OpenAI's output moderation for depicting a real person's likeness, even with no request for a portrait. Sampled artists still drive which genres get used and show up in the CLI output, just not in the text sent to the image model.
If neither --prompt nor --no-style is given, the tool prompts interactively with a numbered list of visual styles (collage, film photography, geometric abstraction, risograph print, etc.) — pick one to steer the image, or press Enter for a plain genre-only prompt. This exists because a fixed "abstract, atmospheric" prompt alone tends to converge on similar-looking imagery across different playlists.
Prints the sampled artists, their genres, and the exact prompt sent to the image model. On a real run, the generated image is saved locally to cover-art/<playlist-id>-<timestamp>.jpg (each run gets its own file, so repeat runs don't overwrite earlier ones) in addition to being uploaded as the playlist's cover.
This command requests an additional scope (ugc-image-upload) beyond what albumize needs — if you've already authorized albumize on this machine, the first cover run will open your browser once more to pick up the new scope; after that it's cached like normal.
Sample output:
The first run of either command opens your browser for one-time Spotify authorization. Tokens are cached at ~/.config/spotify-playlist-tools/token.json and silently refreshed on later runs.



