Skip to content

Repository files navigation

Spotify Playlist Migration Toolkit

A local-only, bring-your-own-app toolkit for applying a human-authored Spotify playlist migration with protected-playlist, stale-state, approval, and verification guards.

The repository contains no shared Spotify account, client ID, hosted service, personal library data, or reusable authorization. Every user creates and controls their own Spotify developer app, OAuth grant, local files, and API quota.

Important AI and content-policy boundary

Do not give a Spotify inventory, track/artist metadata, playlist contents, or a migration plan derived from that content to an AI model. Spotify's current Developer Policy prohibits analyzing Spotify Content and prohibits ingesting Spotify Content into an AI model. Its Building with AI guidance permits AI assistance with code and documentation, not content ingestion.

An agent may help install this repository, run its guarded commands, and report content-free counts and pass/fail receipts. It must not open, print, summarize, classify, or upload .spotify-organizer/*.json. The human account owner authors and reviews the plan. The original idea—having an agent deeply classify a Spotify library by genre, language, mood, or similarity—requires written Spotify approval before it can be offered or distributed.

This project is a technical implementation, not a statement of legal approval. Spotify's rules can change; re-check them before distribution. Each person who operates a fork is responsible for their own app registration, disclosures, and compliance; see the local-data statement in PRIVACY.md.

Why everyone needs their own client ID

A shared client ID is not a public distribution model. Spotify Development Mode currently ties access and quota to the developer account, requires the app owner to meet its Premium requirement, and limits which users can authenticate. See Spotify's current quota-mode documentation and July 2026 quota update.

The client ID is not a password, but reusing one would still couple everyone to one owner's allowlist, subscription eligibility, quota, and policy status. This tool therefore uses bring your own Spotify app and PKCE; it never asks for a client secret.

What the guarded executor does

  • Reads Liked Song and accessible playlist IDs into a private local snapshot.
  • Creates playlists and populates them with the exact ordered track URIs in a human-authored manifest.
  • Keeps Liked Songs independent from playlist membership; there is no unlike action.
  • Excludes configured immutable playlist IDs from item reads and all writes.
  • Verifies exact destinations, unchanged Liked Song IDs, and protected boundaries before approved source playlists can be removed.

It does not stream or download music, enrich artist genres, classify content, make recommendations, expose a public server, or share credentials with an agent.

1. Create your Spotify developer app

  1. Sign in to the Spotify Developer Dashboard.

  2. Create an app using values like these:

    • App name: any unique personal name, such as My Local Playlist Migration.
    • Description: Local, personal playlist migration using the Spotify Web API.
    • Website: your fork's repository URL, if the dashboard asks for one.
    • API/SDK selection: Web API only.
  3. Register this redirect URI exactly (use neither localhost nor a trailing slash):

    http://127.0.0.1:8787/auth/callback
    
  4. In Settings → Users Management, add the name and Spotify email of every account that will authorize this local tool. Development Mode currently permits up to five authenticated users, and the app owner must have Premium.

  5. Copy the client ID. Do not create, paste, or publish a client secret; this project uses Authorization Code with PKCE.

There is no production or "live" callback URL. This toolkit intentionally supports only the loopback callback above and must not be deployed as a hosted service.

2. Install and configure locally

Requirements: Node.js 22+, Bun, npm, and an account that satisfies Spotify's current Development Mode requirements.

git clone YOUR_FORK_OR_REPOSITORY_URL
cd spotify-playlist-migration-toolkit
npm ci
npm run setup -- --client-id YOUR_SPOTIFY_CLIENT_ID --protected NONE
npm run preflight

Replace NONE with comma-separated immutable playlist IDs when any playlist's items must never be fetched and the playlist must never be changed:

npm run setup -- --client-id YOUR_SPOTIFY_CLIENT_ID --protected ID1,ID2

Setup creates .dev.vars with mode 0600 and random local encryption/admin keys. It refuses to overwrite an existing file. .dev.vars, local KV state, inventories, plans, and receipts are gitignored.

3. Start and authorize

Keep the local service running in one terminal:

npm run dev -- --ip 127.0.0.1 --port 8787

In a second terminal:

npm run auth:url

Open the printed accounts.spotify.com URL yourself and approve the requested scopes. Password, passkey, SSO, consent, and 2FA are human-only. Confirm the connection afterward:

npm run auth:status

The refresh token is encrypted before Wrangler's local KV emulator stores it. The encryption key is on the same computer, so this protects against accidental file disclosure—not a malicious process running as the same OS user.

4. Create a private, human-authored plan

Export a fresh local snapshot:

npm run inventory

The command writes .spotify-organizer/inventory.json and prints only its path and counts. Do not give that file to an AI system or commit it. The account owner may use their own Spotify interface and private local snapshot to author .spotify-organizer/plan.json following the manifest contract. Use examples/ only to understand the schema.

Validate without printing account content:

npm run plan:validate -- \
  .spotify-organizer/inventory.json \
  .spotify-organizer/plan.json

The validator requires every unique track present in Liked Songs or an owned playlist to appear exactly once, rejects followed-only tracks, and allows only explicitly listed owned, non-protected source playlists to be removed.

5. Apply without removing sources

After the human reviews and approves the exact manifest:

npm run plan:apply -- create \
  .spotify-organizer/inventory.json \
  .spotify-organizer/plan.json \
  .spotify-organizer/state.json

npm run plan:apply -- populate \
  .spotify-organizer/inventory.json \
  .spotify-organizer/plan.json \
  .spotify-organizer/state.json

Export a new snapshot and verify before any removal:

npm run inventory -- --output .spotify-organizer/post-populate.json

npm run plan:apply -- verify \
  .spotify-organizer/inventory.json \
  .spotify-organizer/plan.json \
  .spotify-organizer/state.json \
  .spotify-organizer/post-populate.json

6. Optional source-playlist removal

Source removal is a separate destructive phase. The human must review the manifest's exact sourcePlaylistIdsToRemove list and approve it separately. Then run:

npm run plan:apply -- delete \
  .spotify-organizer/inventory.json \
  .spotify-organizer/plan.json \
  .spotify-organizer/state.json \
  .spotify-organizer/post-populate.json \
  REMOVE_APPROVED_SOURCE_PLAYLISTS

npm run inventory -- --output .spotify-organizer/final.json

npm run plan:apply -- complete \
  .spotify-organizer/inventory.json \
  .spotify-organizer/plan.json \
  .spotify-organizer/state.json \
  .spotify-organizer/final.json

All live inputs, outputs, state, and receipts are confined to .spotify-organizer/; path traversal and symlink escapes are rejected.

Using the repository skill safely

The included $organize-spotify-playlists skill is an operations guide, not a content-analysis skill. Give an agent this prompt:

Use skills/organize-spotify-playlists/SKILL.md to set up and operate the guarded
local migration workflow. Never open, print, summarize, or upload any file under
.spotify-organizer/. I will author and approve the plan myself.

The agent can run setup checks, start the local server, generate the authorization URL, execute validation/migration commands by path, and report count-only receipts. It must stop for human OAuth, plan approval, and the separate source removal approval.

Privacy, cleanup, and publication

  • Inventory and mutation routes require a bearer token; query-string tokens are rejected.
  • The service binds to 127.0.0.1; never expose it through a tunnel or LAN bind.
  • Protected playlist contents remain items: null and are never fetched.
  • Spotify's playlist-list endpoint may return summaries for protected IDs during create reconciliation; the code checks only the ID and discards every other field before parsing.
  • user-library-modify is required by Spotify's current DELETE /me/library endpoint to remove an approved playlist URI. The executor never sends track URIs to that endpoint and has no unlike action.
  • Writes use input hashes, idempotency markers, ownership checks, and Spotify snapshot preconditions.
  • Never commit .dev.vars, .wrangler/, .spotify-organizer/, or real account exports. npm run publication:check rejects known private artifacts and any unexpected JSON file.
  • Never zip or upload the raw working directory, because ignored local files are still physically present. Follow PUBLICATION.md to create a clean-history public copy.
  • To disconnect, revoke the app in Spotify account settings, stop the exact local server process, and remove only your own ignored local state.

See SECURITY.md for the complete local trust boundary.

Development

npm run check

This runs strict TypeScript checks and the test suite. A clean source tree can add the publication gate with npm run release:check; it intentionally fails if .dev.vars, .wrangler/, or .spotify-organizer/ physically exists. No deploy command is included; the supported distribution is local-only.

License

MIT. See LICENSE.

About

Local-only, bring-your-own-app Spotify playlist migration toolkit with guarded execution and agent-safe operations guidance.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages