Move playlists freely between Spotify, TIDAL, and YouTube Music.
Connect two services, pick a playlist, and it gets recreated on the other service. Matching uses ISRC codes where both services support them (Spotify ↔ TIDAL is near-perfect) and scored fuzzy search (title + artist + duration) otherwise. Tracks that can't be matched are listed at the end so you can add them by hand.
- No database, no server-side storage — OAuth tokens live in encrypted, httpOnly cookies in your own browser.
- Built with Next.js (App Router) + TypeScript + Tailwind. Deploys to Vercel as-is.
npm install
cp .env.example .env.local # then fill it in, see below
npm run dev # http://localhost:3000SESSION_SECRET can be any long random string:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
You need to register a (free) developer app with each service you want to use. A service with missing credentials just shows as "Not configured" in the UI — the others still work.
- Go to the Spotify Developer Dashboard → Create app.
- Redirect URI:
http://localhost:3000/api/auth/spotify/callback(add your Vercel URL variant too when deploying:https://<app>.vercel.app/api/auth/spotify/callback). - Select Web API. Copy the Client ID and Client Secret into
.env.local. - Dev-mode apps only allow allowlisted users: add each user's email under User Management (up to 25).
- Go to the TIDAL Developer Portal and create an app.
- Redirect URI:
http://localhost:3000/api/auth/tidal/callback(+ Vercel variant). - Copy the Client ID and Client Secret into
.env.local. - The app requests the scopes
user.read playlists.read playlists.write— make sure they're enabled for your app if the portal asks.
YouTube Music has no public API of its own; playlists are shared with YouTube, so this app uses the YouTube Data API v3.
- In Google Cloud Console, create a project and enable YouTube Data API v3 (APIs & Services → Library).
- Configure the OAuth consent screen (External). While the app is in Testing mode, add yourself and any friends as Test users (up to 100).
- Create an OAuth client ID (type: Web application) with redirect URI
http://localhost:3000/api/auth/youtube/callback(+ Vercel variant). - Copy the Client ID and Client Secret into
.env.local.
Quota warning: the free YouTube quota is 10,000 units/day and matching one track costs a 100-unit search (plus 50 to insert). Transfers into YouTube Music therefore stop after roughly 60 tracks/day — the app detects this, keeps what was transferred, and tells you to re-run the next day. Transfers out of YouTube are cheap and effectively unlimited. You can request a quota increase in Google Cloud Console if you need more.
Not supported: Apple requires a paid Apple Developer Program membership ($99/year) to issue MusicKit tokens.
- Push this repo to GitHub and import it in Vercel.
- Set all the env vars from
.env.localin the Vercel project settings, withNEXT_PUBLIC_BASE_URL=https://<your-app>.vercel.app. - Add the
https://<your-app>.vercel.app/api/auth/<provider>/callbackredirect URIs to each developer app (Spotify, TIDAL, Google).
Anyone you've allowlisted (Spotify User Management / Google Test users) can then use the site. Opening it to the general public requires each platform's app review process.
app/api/auth/[provider]/… OAuth start + callback + logout (PKCE, state check)
app/api/[provider]/playlists list / create playlists
app/api/[provider]/…/tracks read source tracks / add matched tracks
app/api/[provider]/match match a batch of tracks against the destination
lib/providers/{spotify,tidal,youtube}.ts one adapter per service
lib/match.ts normalization + scoring (ISRC first, fuzzy fallback)
hooks/useTransfer.ts client-side transfer loop with live progress
The transfer runs in your browser as a series of small API calls (batch match → create → batch add), so long playlists aren't limited by serverless timeouts, and progress is shown per track.