Are you tired of manually recreating your Spotify playlists on YouTube Music? In this guide, I'll show you how to build an automated solution using TypeScript that transfers your playlists seamlessly between these two platforms.
Why Automate Playlist Transfers?
Switching between music streaming services shouldn't mean losing your carefully curated playlists. Whether you're migrating permanently or maintaining playlists across both platforms, automation saves hours of tedious manual work.
Prerequisites
- Node.js 18+ installed
- A Spotify Developer account
- Basic TypeScript knowledge
- A YouTube Music account
Step 1: Setting Up the Project
First, create a new TypeScript project:
mkdir spotify-to-ytmusic
cd spotify-to-ytmusic
npm init -y
npm install typescript ts-node @types/node --save-dev
npm install @spotify/web-api-ts-sdk ytmusic-api
npx tsc --init
Step 2: Configure Spotify API Access
Go to the Spotify Developer Dashboard and create an app. You'll need:
- Client ID
- Client Secret
- Redirect URI (set to
http://localhost:3000/callback)
Create a .env file:
SPOTIFY_CLIENT_ID=your_client_id
SPOTIFY_CLIENT_SECRET=your_client_secret
SPOTIFY_REDIRECT_URI=http://localhost:3000/callback
Step 3: Fetch Spotify Playlist Tracks
Using the official Spotify TypeScript SDK:
import { SpotifyApi } from "@spotify/web-api-ts-sdk";
interface Track {
name: string;
artist: string;
album: string;
}
async function getSpotifyPlaylistTracks(
playlistId: string
): Promise<Track[]> {
const sdk = SpotifyApi.withClientCredentials(
process.env.SPOTIFY_CLIENT_ID!,
process.env.SPOTIFY_CLIENT_SECRET!
);
const tracks: Track[] = [];
let offset = 0;
const limit = 50;
while (true) {
const response = await sdk.playlists.getPlaylistItems(
playlistId,
undefined,
undefined,
limit,
offset
);
for (const item of response.items) {
if (item.track && "name" in item.track) {
tracks.push({
name: item.track.name,
artist: item.track.artists[0]?.name || "Unknown",
album: item.track.album?.name || "Unknown",
});
}
}
if (response.items.length < limit) break;
offset += limit;
}
return tracks;
}
Step 4: Search and Match on YouTube Music
Using the ytmusic-api package to find matching songs:
import YTMusic from "ytmusic-api";
interface YTMusicTrack {
videoId: string;
name: string;
artist: string;
}
async function searchYouTubeMusic(
ytmusic: YTMusic,
track: Track
): Promise<YTMusicTrack | null> {
const query = `${track.name} ${track.artist}`;
try {
const results = await ytmusic.searchSongs(query);
if (results.length > 0) {
const match = results[0];
return {
videoId: match.videoId,
name: match.name,
artist: match.artist?.name || "Unknown",
};
}
} catch (error) {
console.error(`Failed to find: ${query}`);
}
return null;
}
Step 5: Create the Transfer Function
Now let's combine everything into a complete transfer function:
import YTMusic from "ytmusic-api";
import { SpotifyApi } from "@spotify/web-api-ts-sdk";
async function transferPlaylist(
spotifyPlaylistId: string,
newPlaylistName: string
): Promise<void> {
console.log("π΅ Starting playlist transfer...");
// Initialize YouTube Music API
const ytmusic = new YTMusic();
await ytmusic.initialize();
// Fetch Spotify tracks
console.log("π₯ Fetching Spotify playlist tracks...");
const spotifyTracks = await getSpotifyPlaylistTracks(spotifyPlaylistId);
console.log(`Found ${spotifyTracks.length} tracks`);
// Search and match on YouTube Music
console.log("π Searching for matches on YouTube Music...");
const matchedTracks: YTMusicTrack[] = [];
const notFound: string[] = [];
for (const track of spotifyTracks) {
const match = await searchYouTubeMusic(ytmusic, track);
if (match) {
matchedTracks.push(match);
console.log(`β Found: ${track.name} - ${track.artist}`);
} else {
notFound.push(`${track.name} - ${track.artist}`);
console.log(`β Not found: ${track.name} - ${track.artist}`);
}
// Rate limiting to avoid API throttling
await new Promise((r) => setTimeout(r, 500));
}
// Summary
console.log("\nπ Transfer Summary:");
console.log(`β Matched: ${matchedTracks.length} tracks`);
console.log(`β Not found: ${notFound.length} tracks`);
if (notFound.length > 0) {
console.log("\nTracks not found:");
notFound.forEach((t) => console.log(` - ${t}`));
}
}
Step 6: Adding Tracks to YouTube Music Playlist
For authenticated playlist creation, you'll need to use youtube-music-ts-api with cookie authentication:
import YouTubeMusic from "youtube-music-ts-api";
async function createYTMusicPlaylist(
cookieString: string,
playlistName: string,
videoIds: string[]
): Promise<void> {
const ytm = new YouTubeMusic();
const ytma = await ytm.authenticate(cookieString);
// Create new playlist
const playlist = await ytma.createPlaylist(
playlistName,
"Imported from Spotify",
"PRIVATE"
);
console.log(`Created playlist: ${playlist.name}`);
// Note: Adding tracks requires track objects
// You'll need to fetch full track details first
// using ytma.getTrack() for each videoId
}
Getting YouTube Music Cookies
To authenticate with YouTube Music:
- Open YouTube Music in your browser
- Open Developer Tools (F12)
- Go to Network tab
- Perform any action (like playing a song)
- Find a request to
music.youtube.com - Copy the
Cookieheader value
Complete Working Example
Here's the full index.ts:
import "dotenv/config";
import YTMusic from "ytmusic-api";
import { SpotifyApi } from "@spotify/web-api-ts-sdk";
interface Track {
name: string;
artist: string;
}
async function main() {
// Spotify playlist ID (from URL)
const playlistId = "37i9dQZF1DXcBWIGoYBM5M"; // Today's Top Hits
const sdk = SpotifyApi.withClientCredentials(
process.env.SPOTIFY_CLIENT_ID!,
process.env.SPOTIFY_CLIENT_SECRET!
);
// Get playlist info
const playlist = await sdk.playlists.getPlaylist(playlistId);
console.log(`Playlist: ${playlist.name}`);
// Get tracks
const response = await sdk.playlists.getPlaylistItems(playlistId);
const tracks: Track[] = response.items
.filter((item) => item.track && "name" in item.track)
.map((item) => ({
name: item.track!.name,
artist: (item.track as any).artists[0]?.name || "Unknown",
}));
console.log(`Found ${tracks.length} tracks`);
// Initialize YT Music
const ytmusic = new YTMusic();
await ytmusic.initialize();
// Search for each track
for (const track of tracks.slice(0, 5)) {
const query = `${track.name} ${track.artist}`;
const results = await ytmusic.searchSongs(query);
if (results.length > 0) {
console.log(`β ${track.name} -> ${results[0].name}`);
} else {
console.log(`β ${track.name} - Not found`);
}
await new Promise((r) => setTimeout(r, 300));
}
}
main().catch(console.error);
Alternative: Using Existing Tools
If you prefer ready-made solutions:
- spotify-to-youtube: NPM package for matching tracks
- SpotTransfer: Full migration tool
- spotify_to_ytmusic: Python CLI tool
Best Practices
- Rate Limiting: Add delays between API calls to avoid throttling
- Error Handling: Some tracks may not have exact matches
- Fuzzy Matching: Consider implementing fuzzy string matching for better results
- Batch Processing: For large playlists, process in chunks
- Logging: Keep track of failed matches for manual addition
Conclusion
With this TypeScript solution, you can automate the tedious process of transferring playlists between Spotify and YouTube Music. The code is extensible - you can add features like:
- Sync scheduling with cron jobs
- Two-way synchronization
- Web UI with Express.js
- Discord bot integration
The full source code is available on GitHub. Happy coding!
Resources:
Top comments (0)