playlist-migrator / index.html
nock2's picture
undefined - Initial Deployment
a92ea16 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Playlist Migrator | Move Spotify to YouTube</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.gradient-bg {
background: linear-gradient(135deg, #1DB954 0%, #FF0000 100%);
}
.spotify-btn {
background-color: #1DB954;
transition: all 0.3s ease;
}
.spotify-btn:hover {
background-color: #1ed760;
transform: translateY(-2px);
}
.youtube-btn {
background-color: #FF0000;
transition: all 0.3s ease;
}
.youtube-btn:hover {
background-color: #ff3333;
transform: translateY(-2px);
}
.migrate-btn {
background: linear-gradient(135deg, #1DB954 0%, #FF0000 100%);
transition: all 0.3s ease;
}
.migrate-btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(0,0,0,0.2);
}
.playlist-card {
transition: all 0.3s ease;
}
.playlist-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 25px rgba(0,0,0,0.1);
}
.progress-bar {
height: 6px;
background-color: #e0e0e0;
border-radius: 3px;
}
.progress-fill {
height: 100%;
border-radius: 3px;
background: linear-gradient(90deg, #1DB954 0%, #FF0000 100%);
transition: width 0.4s ease;
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<!-- Header -->
<header class="gradient-bg text-white shadow-lg">
<div class="container mx-auto px-4 py-6">
<div class="flex justify-between items-center">
<div class="flex items-center space-x-2">
<i class="fab fa-spotify text-3xl"></i>
<i class="fas fa-exchange-alt text-xl"></i>
<i class="fab fa-youtube text-3xl"></i>
<h1 class="text-2xl font-bold">Playlist Migrator</h1>
</div>
<button class="bg-white text-gray-800 px-4 py-2 rounded-full font-semibold hover:bg-gray-100 transition">
<i class="fas fa-question-circle mr-2"></i>Help
</button>
</div>
</div>
</header>
<!-- Main Content -->
<main class="container mx-auto px-4 py-8">
<div class="max-w-4xl mx-auto">
<!-- Intro Section -->
<section class="bg-white rounded-xl shadow-md p-6 mb-8">
<h2 class="text-2xl font-bold text-gray-800 mb-4">Migrate Your Spotify Playlists to YouTube</h2>
<p class="text-gray-600 mb-6">
Easily transfer your favorite Spotify playlists to YouTube Music. Our migrator preserves your playlist
names, descriptions, and tracks with the highest possible match accuracy.
</p>
<div class="flex flex-wrap gap-4">
<div class="flex-1 min-w-[200px] bg-green-50 p-4 rounded-lg">
<div class="flex items-center mb-2">
<i class="fab fa-spotify text-green-500 text-2xl mr-2"></i>
<h3 class="font-semibold">Spotify Features</h3>
</div>
<ul class="text-sm text-gray-700 space-y-1">
<li><i class="fas fa-check text-green-500 mr-2"></i>Connect your Spotify account</li>
<li><i class="fas fa-check text-green-500 mr-2"></i>View all your playlists</li>
<li><i class="fas fa-check text-green-500 mr-2"></i>Select tracks to migrate</li>
</ul>
</div>
<div class="flex-1 min-w-[200px] bg-red-50 p-4 rounded-lg">
<div class="flex items-center mb-2">
<i class="fab fa-youtube text-red-500 text-2xl mr-2"></i>
<h3 class="font-semibold">YouTube Features</h3>
</div>
<ul class="text-sm text-gray-700 space-y-1">
<li><i class="fas fa-check text-red-500 mr-2"></i>Connect your YouTube account</li>
<li><i class="fas fa-check text-red-500 mr-2"></i>Create new playlists</li>
<li><i class="fas fa-check text-red-500 mr-2"></i>Add matched tracks</li>
</ul>
</div>
</div>
</section>
<!-- Connection Section -->
<section class="bg-white rounded-xl shadow-md p-6 mb-8">
<h2 class="text-xl font-bold text-gray-800 mb-6">Connect Your Accounts</h2>
<div class="grid md:grid-cols-2 gap-6">
<!-- Spotify Connection -->
<div class="border border-gray-200 rounded-lg p-4">
<div class="flex items-center mb-4">
<i class="fab fa-spotify text-green-500 text-3xl mr-3"></i>
<div>
<h3 class="font-semibold">Spotify</h3>
<p class="text-sm text-gray-500">Connect to view your playlists</p>
</div>
</div>
<div id="spotify-status" class="mb-4">
<div class="flex items-center text-sm text-gray-600">
<div class="w-3 h-3 rounded-full bg-gray-300 mr-2"></div>
<span>Not connected</span>
</div>
</div>
<button id="connect-spotify" class="spotify-btn text-white w-full py-3 rounded-lg font-semibold flex items-center justify-center">
<i class="fab fa-spotify mr-2"></i> Connect Spotify
</button>
</div>
<!-- YouTube Connection -->
<div class="border border-gray-200 rounded-lg p-4">
<div class="flex items-center mb-4">
<i class="fab fa-youtube text-red-500 text-3xl mr-3"></i>
<div>
<h3 class="font-semibold">YouTube</h3>
<p class="text-sm text-gray-500">Connect to create playlists</p>
</div>
</div>
<div id="youtube-status" class="mb-4">
<div class="flex items-center text-sm text-gray-600">
<div class="w-3 h-3 rounded-full bg-gray-300 mr-2"></div>
<span>Not connected</span>
</div>
</div>
<button id="connect-youtube" class="youtube-btn text-white w-full py-3 rounded-lg font-semibold flex items-center justify-center">
<i class="fab fa-youtube mr-2"></i> Connect YouTube
</button>
</div>
</div>
</section>
<!-- Playlist Selection (Hidden Initially) -->
<section id="playlist-section" class="hidden bg-white rounded-xl shadow-md p-6 mb-8">
<div class="flex justify-between items-center mb-6">
<h2 class="text-xl font-bold text-gray-800">Your Spotify Playlists</h2>
<div class="relative">
<input type="text" placeholder="Search playlists..." class="pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500">
<i class="fas fa-search absolute left-3 top-3 text-gray-400"></i>
</div>
</div>
<div id="playlist-container" class="grid md:grid-cols-2 gap-4 mb-6">
<!-- Playlist cards will be inserted here by JavaScript -->
</div>
<div class="flex justify-between items-center">
<div class="text-sm text-gray-500">
<span id="selected-count">0</span> of <span id="total-count">0</span> playlists selected
</div>
<button id="select-all" class="text-green-600 font-medium hover:text-green-700">
<i class="fas fa-check-circle mr-1"></i> Select All
</button>
</div>
</section>
<!-- Migration Section (Hidden Initially) -->
<section id="migration-section" class="hidden bg-white rounded-xl shadow-md p-6">
<h2 class="text-xl font-bold text-gray-800 mb-6">Ready to Migrate</h2>
<div class="mb-6">
<div class="flex justify-between mb-1">
<span class="font-medium">Migration Progress</span>
<span id="progress-percent" class="font-medium">0%</span>
</div>
<div class="progress-bar">
<div id="progress-fill" class="progress-fill" style="width: 0%"></div>
</div>
</div>
<div id="migration-details" class="mb-6">
<div class="grid md:grid-cols-3 gap-4">
<div class="bg-gray-50 p-4 rounded-lg">
<div class="text-gray-500 text-sm mb-1">Playlists</div>
<div id="playlist-count" class="text-2xl font-bold">0</div>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<div class="text-gray-500 text-sm mb-1">Tracks</div>
<div id="track-count" class="text-2xl font-bold">0</div>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<div class="text-gray-500 text-sm mb-1">Estimated Time</div>
<div id="time-estimate" class="text-2xl font-bold">~2 min</div>
</div>
</div>
</div>
<div class="flex flex-col sm:flex-row gap-4">
<button id="start-migration" class="migrate-btn text-white flex-1 py-3 rounded-lg font-bold flex items-center justify-center">
<i class="fas fa-exchange-alt mr-2"></i> Start Migration
</button>
<button id="save-for-later" class="border border-gray-300 flex-1 py-3 rounded-lg font-medium flex items-center justify-center hover:bg-gray-50">
<i class="far fa-save mr-2"></i> Save for Later
</button>
</div>
</section>
<!-- Migration Results (Hidden Initially) -->
<section id="results-section" class="hidden bg-white rounded-xl shadow-md p-6 mt-8">
<div class="text-center">
<div class="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
<i class="fas fa-check text-green-500 text-2xl"></i>
</div>
<h2 class="text-xl font-bold text-gray-800 mb-2">Migration Complete!</h2>
<p class="text-gray-600 mb-6">Your playlists have been successfully transferred to YouTube Music.</p>
<div class="grid md:grid-cols-3 gap-4 mb-8">
<div class="bg-green-50 p-4 rounded-lg">
<div class="text-green-500 text-sm mb-1">Successful</div>
<div id="success-count" class="text-2xl font-bold">12</div>
</div>
<div class="bg-yellow-50 p-4 rounded-lg">
<div class="text-yellow-500 text-sm mb-1">Partial Matches</div>
<div id="partial-count" class="text-2xl font-bold">3</div>
</div>
<div class="bg-red-50 p-4 rounded-lg">
<div class="text-red-500 text-sm mb-1">Not Found</div>
<div id="failed-count" class="text-2xl font-bold">1</div>
</div>
</div>
<button id="view-playlists" class="bg-green-600 text-white px-6 py-3 rounded-lg font-semibold hover:bg-green-700 transition mb-4">
<i class="fab fa-youtube mr-2"></i> View on YouTube
</button>
<div class="text-sm text-gray-500">
<button class="text-blue-600 hover:underline mr-4">
<i class="fas fa-download mr-1"></i> Download Report
</button>
<button class="text-blue-600 hover:underline">
<i class="fas fa-redo mr-1"></i> Migrate Another
</button>
</div>
</div>
</section>
</div>
</main>
<!-- Footer -->
<footer class="bg-gray-800 text-white py-8">
<div class="container mx-auto px-4">
<div class="flex flex-col md:flex-row justify-between items-center">
<div class="mb-4 md:mb-0">
<div class="flex items-center space-x-2">
<i class="fab fa-spotify text-2xl"></i>
<i class="fas fa-exchange-alt text-lg"></i>
<i class="fab fa-youtube text-2xl"></i>
<span class="font-bold">Playlist Migrator</span>
</div>
<p class="text-gray-400 text-sm mt-2">The easiest way to move your music between platforms</p>
</div>
<div class="flex space-x-6">
<a href="#" class="hover:text-green-400 transition"><i class="fab fa-github text-xl"></i></a>
<a href="#" class="hover:text-green-400 transition"><i class="fab fa-twitter text-xl"></i></a>
<a href="#" class="hover:text-green-400 transition"><i class="fab fa-discord text-xl"></i></a>
</div>
</div>
<div class="border-t border-gray-700 mt-6 pt-6 text-sm text-gray-400">
<div class="flex flex-col md:flex-row justify-between items-center">
<div class="mb-4 md:mb-0">
&copy; 2023 Playlist Migrator. Not affiliated with Spotify or YouTube.
</div>
<div class="flex space-x-4">
<a href="#" class="hover:text-white transition">Privacy Policy</a>
<a href="#" class="hover:text-white transition">Terms of Service</a>
<a href="#" class="hover:text-white transition">Contact</a>
</div>
</div>
</div>
</div>
</footer>
<script>
// Sample data for demonstration
const samplePlaylists = [
{
id: '1',
name: 'Workout Mix',
description: 'High energy tracks for my workouts',
image: 'https://misc.scdn.co/liked-songs/liked-songs-64.png',
tracks: 32,
owner: 'You'
},
{
id: '2',
name: 'Chill Vibes',
description: 'Relaxing music for evenings',
image: 'https://i.scdn.co/image/ab67706c0000bebbc0d4f8172c9d486d5c8769d8',
tracks: 45,
owner: 'You'
},
{
id: '3',
name: 'Road Trip',
description: 'Perfect for long drives',
image: 'https://i.scdn.co/image/ab67706c0000bebbc0d4f8172c9d486d5c8769d8',
tracks: 28,
owner: 'You'
},
{
id: '4',
name: 'Party Hits',
description: 'All the latest party tracks',
image: 'https://misc.scdn.co/liked-songs/liked-songs-64.png',
tracks: 50,
owner: 'You'
}
];
// DOM Elements
const connectSpotifyBtn = document.getElementById('connect-spotify');
const connectYoutubeBtn = document.getElementById('connect-youtube');
const spotifyStatus = document.getElementById('spotify-status');
const youtubeStatus = document.getElementById('youtube-status');
const playlistSection = document.getElementById('playlist-section');
const playlistContainer = document.getElementById('playlist-container');
const migrationSection = document.getElementById('migration-section');
const resultsSection = document.getElementById('results-section');
const progressFill = document.getElementById('progress-fill');
const progressPercent = document.getElementById('progress-percent');
const startMigrationBtn = document.getElementById('start-migration');
const selectAllBtn = document.getElementById('select-all');
const selectedCount = document.getElementById('selected-count');
const totalCount = document.getElementById('total-count');
const playlistCount = document.getElementById('playlist-count');
const trackCount = document.getElementById('track-count');
// State
let spotifyConnected = false;
let youtubeConnected = false;
let selectedPlaylists = [];
// Event Listeners
connectSpotifyBtn.addEventListener('click', connectSpotify);
connectYoutubeBtn.addEventListener('click', connectYouTube);
selectAllBtn.addEventListener('click', toggleSelectAll);
startMigrationBtn.addEventListener('click', startMigration);
// Functions
function connectSpotify() {
// Simulate Spotify connection
spotifyConnected = true;
connectSpotifyBtn.innerHTML = '<i class="fas fa-check mr-2"></i> Connected';
connectSpotifyBtn.classList.remove('spotify-btn');
connectSpotifyBtn.classList.add('bg-gray-200', 'text-gray-800');
connectSpotifyBtn.disabled = true;
spotifyStatus.innerHTML = `
<div class="flex items-center text-sm text-green-600">
<div class="w-3 h-3 rounded-full bg-green-500 mr-2"></div>
<span>Connected as <span class="font-medium">user123</span></span>
</div>
`;
// Load playlists after connection
loadPlaylists();
checkConnections();
}
function connectYouTube() {
// Simulate YouTube connection
youtubeConnected = true;
connectYoutubeBtn.innerHTML = '<i class="fas fa-check mr-2"></i> Connected';
connectYoutubeBtn.classList.remove('youtube-btn');
connectYoutubeBtn.classList.add('bg-gray-200', 'text-gray-800');
connectYoutubeBtn.disabled = true;
youtubeStatus.innerHTML = `
<div class="flex items-center text-sm text-green-600">
<div class="w-3 h-3 rounded-full bg-green-500 mr-2"></div>
<span>Connected as <span class="font-medium">user123@gmail.com</span></span>
</div>
`;
checkConnections();
}
function checkConnections() {
if (spotifyConnected && youtubeConnected) {
// Both connected
playlistSection.classList.remove('hidden');
}
}
function loadPlaylists() {
playlistContainer.innerHTML = '';
totalCount.textContent = samplePlaylists.length;
samplePlaylists.forEach(playlist => {
const playlistCard = document.createElement('div');
playlistCard.className = 'playlist-card bg-white border border-gray-200 rounded-lg overflow-hidden hover:shadow-md cursor-pointer';
playlistCard.innerHTML = `
<div class="p-4 flex items-start">
<img src="${playlist.image}" alt="${playlist.name}" class="w-16 h-16 rounded mr-4">
<div class="flex-1">
<h3 class="font-semibold text-gray-800">${playlist.name}</h3>
<p class="text-sm text-gray-500 mb-1">${playlist.description}</p>
<div class="flex items-center text-xs text-gray-400">
<span>${playlist.tracks} tracks</span>
<span class="mx-2">•</span>
<span>${playlist.owner}</span>
</div>
</div>
<div class="checkbox-container">
<input type="checkbox" id="playlist-${playlist.id}" class="hidden playlist-checkbox">
<label for="playlist-${playlist.id}" class="w-6 h-6 border-2 border-gray-300 rounded-full flex items-center justify-center cursor-pointer">
<i class="fas fa-check text-white text-xs"></i>
</label>
</div>
</div>
`;
playlistContainer.appendChild(playlistCard);
// Add event listener to checkbox
const checkbox = playlistCard.querySelector('.playlist-checkbox');
checkbox.addEventListener('change', function() {
updateSelectedPlaylists(this, playlist);
});
// Add click event to the whole card
playlistCard.addEventListener('click', function(e) {
// Don't toggle if clicking on the checkbox
if (!e.target.closest('.checkbox-container')) {
const checkbox = this.querySelector('.playlist-checkbox');
checkbox.checked = !checkbox.checked;
checkbox.dispatchEvent(new Event('change'));
}
});
});
}
function updateSelectedPlaylists(checkbox, playlist) {
const label = checkbox.nextElementSibling;
if (checkbox.checked) {
label.classList.add('bg-green-500', 'border-green-500');
selectedPlaylists.push(playlist);
} else {
label.classList.remove('bg-green-500', 'border-green-500');
selectedPlaylists = selectedPlaylists.filter(p => p.id !== playlist.id);
}
selectedCount.textContent = selectedPlaylists.length;
// Show/hide migration section
if (selectedPlaylists.length > 0) {
migrationSection.classList.remove('hidden');
updateMigrationDetails();
} else {
migrationSection.classList.add('hidden');
}
}
function toggleSelectAll() {
const checkboxes = document.querySelectorAll('.playlist-checkbox');
const isAllSelected = selectedPlaylists.length === samplePlaylists.length;
checkboxes.forEach(checkbox => {
if (!isAllSelected) {
checkbox.checked = true;
} else {
checkbox.checked = false;
}
checkbox.dispatchEvent(new Event('change'));
});
selectAllBtn.innerHTML = isAllSelected ?
'<i class="fas fa-check-circle mr-1"></i> Select All' :
'<i class="fas fa-times-circle mr-1"></i> Deselect All';
}
function updateMigrationDetails() {
const totalTracks = selectedPlaylists.reduce((sum, playlist) => sum + playlist.tracks, 0);
playlistCount.textContent = selectedPlaylists.length;
trackCount.textContent = totalTracks;
}
function startMigration() {
// Disable button and show loading state
startMigrationBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Migrating...';
startMigrationBtn.disabled = true;
// Simulate migration progress
let progress = 0;
const interval = setInterval(() => {
progress += Math.random() * 10;
if (progress >= 100) {
progress = 100;
clearInterval(interval);
migrationComplete();
}
progressFill.style.width = `${progress}%`;
progressPercent.textContent = `${Math.round(progress)}%`;
}, 500);
}
function migrationComplete() {
// Hide migration section and show results
migrationSection.classList.add('hidden');
resultsSection.classList.remove('hidden');
// Update results
document.getElementById('success-count').textContent = selectedPlaylists.length;
document.getElementById('partial-count').textContent = Math.floor(selectedPlaylists.length * 0.3);
document.getElementById('failed-count').textContent = Math.floor(selectedPlaylists.length * 0.1);
}
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=nock2/playlist-migrator" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>