import { useState, useEffect } from 'react' import { Link } from 'react-router-dom' import { ListMusic, Plus, Loader2, Music, ChevronRight, Download, X, Youtube, Link2, ClipboardPaste } from 'lucide-react' import { getPlaylists, getSpotifyPlaylists, importSpotifyPlaylist, importYouTubePlaylist, previewLastfm, importLastfm, importPastedSongs, type PlaylistResponse, type SpotifyPlaylistItem, type LastfmPreviewResponse } from '../lib/api' export default function Playlists() { const [playlists, setPlaylists] = useState([]) const [spotifyPlaylists, setSpotifyPlaylists] = useState([]) const [showImport, setShowImport] = useState(false) const [showYouTubeImport, setShowYouTubeImport] = useState(false) const [youtubeUrl, setYoutubeUrl] = useState('') const [importingYouTube, setImportingYouTube] = useState(false) const [importing, setImporting] = useState(null) const [loadingSpotify, setLoadingSpotify] = useState(false) const [loading, setLoading] = useState(true) const [error, setError] = useState('') const [showLastfmImport, setShowLastfmImport] = useState(false) const [lastfmUsername, setLastfmUsername] = useState('') const [lastfmPeriod, setLastfmPeriod] = useState('overall') const [lastfmPreview, setLastfmPreview] = useState(null) const [loadingLastfmPreview, setLoadingLastfmPreview] = useState(false) const [importingLastfm, setImportingLastfm] = useState(false) const [showPasteImport, setShowPasteImport] = useState(false) const [pasteName, setPasteName] = useState('') const [pasteText, setPasteText] = useState('') const [importingPaste, setImportingPaste] = useState(false) useEffect(() => { loadPlaylists() }, []) const loadPlaylists = async () => { try { const data = await getPlaylists() setPlaylists(data) } catch { setError('Failed to load playlists') } finally { setLoading(false) } } const openImportModal = async () => { setShowImport(true) setLoadingSpotify(true) try { const data = await getSpotifyPlaylists() setSpotifyPlaylists(data) } catch { setError('Failed to load Spotify playlists. Make sure your Spotify account is connected.') } finally { setLoadingSpotify(false) } } const handleYouTubeImport = async () => { if (!youtubeUrl.trim()) return setImportingYouTube(true) setError('') try { const imported = await importYouTubePlaylist(youtubeUrl.trim()) setPlaylists((prev) => [...prev, imported]) setYoutubeUrl('') setShowYouTubeImport(false) } catch (err: any) { setError(err.response?.data?.detail || 'Failed to import YouTube Music playlist') } finally { setImportingYouTube(false) } } const handleLastfmPreview = async () => { if (!lastfmUsername.trim()) return setLoadingLastfmPreview(true) setError('') setLastfmPreview(null) try { const data = await previewLastfm(lastfmUsername.trim()) setLastfmPreview(data) } catch (err: any) { setError(err.response?.data?.detail || 'Last.fm user not found') } finally { setLoadingLastfmPreview(false) } } const handleLastfmImport = async () => { if (!lastfmUsername.trim()) return setImportingLastfm(true) setError('') try { const imported = await importLastfm(lastfmUsername.trim(), lastfmPeriod) setPlaylists((prev) => [...prev, imported]) setLastfmUsername('') setLastfmPreview(null) setShowLastfmImport(false) } catch (err: any) { setError(err.response?.data?.detail || 'Failed to import from Last.fm') } finally { setImportingLastfm(false) } } const handlePasteImport = async () => { if (!pasteName.trim() || !pasteText.trim()) return setImportingPaste(true) setError('') try { const imported = await importPastedSongs(pasteName.trim(), pasteText.trim()) setPlaylists((prev) => [...prev, imported]) setPasteName('') setPasteText('') setShowPasteImport(false) } catch (err: any) { setError(err.response?.data?.detail || 'Failed to import pasted songs') } finally { setImportingPaste(false) } } const parsedLineCount = pasteText .split('\n') .filter((line) => line.trim().length > 0).length const handleImport = async (playlistId: string) => { setImporting(playlistId) try { const imported = await importSpotifyPlaylist(playlistId) setPlaylists((prev) => [...prev, imported]) setSpotifyPlaylists((prev) => prev.filter((p) => p.id !== playlistId)) } catch (err: any) { setError(err.response?.data?.detail || 'Failed to import playlist') } finally { setImporting(null) } } if (loading) { return (
) } return (

Playlists

Manage your imported playlists

{error && (
{error}
)} {/* Playlist Grid */} {playlists.length === 0 ? (

No playlists yet

Import your playlists from Spotify or YouTube Music to start getting personalized music recommendations

) : (
{playlists.map((playlist) => (
{playlist.image_url ? ( {playlist.name} ) : ( )}

{playlist.name}

{playlist.track_count} tracks

{playlist.source}
))}
)} {/* YouTube Music Import Modal */} {showYouTubeImport && (

Import from YouTube Music

Paste a public YouTube Music playlist URL to import its tracks.

setYoutubeUrl(e.target.value)} placeholder="https://music.youtube.com/playlist?list=..." className="w-full pl-10 pr-4 py-3 bg-cream/50 border border-purple-100 rounded-xl text-charcoal placeholder:text-charcoal-muted/40 focus:outline-none focus:ring-2 focus:ring-purple/30 focus:border-purple transition-colors text-sm" />
)} {/* Last.fm Import Modal */} {showLastfmImport && (

Import from Last.fm

Enter your Last.fm username to import your top tracks. No login required.

{ setLastfmUsername(e.target.value); setLastfmPreview(null); }} placeholder="your-lastfm-username" className="w-full px-4 py-3 bg-cream/50 border border-purple-100 rounded-xl text-charcoal placeholder:text-charcoal-muted/40 focus:outline-none focus:ring-2 focus:ring-purple/30 focus:border-purple transition-colors text-sm" />
{lastfmPreview && (
{lastfmPreview.display_name} {lastfmPreview.track_count} top tracks
{lastfmPreview.sample_tracks.map((t, i) => (
{i + 1}.
{t.title} {t.artist} · {t.playcount} plays
))}
)}
)} {/* Paste Songs Import Modal */} {showPasteImport && (

Paste Your Songs

setPasteName(e.target.value)} placeholder="My favorite songs" className="w-full px-4 py-3 bg-cream/50 border border-purple-100 rounded-xl text-charcoal placeholder:text-charcoal-muted/40 focus:outline-none focus:ring-2 focus:ring-purple/30 focus:border-purple transition-colors text-sm" />
{parsedLineCount > 0 && ( {parsedLineCount} {parsedLineCount === 1 ? 'song' : 'songs'} detected )}