import React, { useEffect, useState, useMemo } from 'react'; import { SubsonicPlaylist, getPlaylists, getPlaylist, deletePlaylist } from '../api/subsonic'; import { usePlayerStore } from '../store/playerStore'; import { Play, Trash2, ChevronUp, ChevronDown } from 'lucide-react'; import { useTranslation } from 'react-i18next'; type SortKey = 'name' | 'songCount' | 'duration'; type SortDir = 'asc' | 'desc'; function formatDuration(seconds: number): string { const h = Math.floor(seconds / 3600); const m = Math.floor((seconds % 3600) / 60); return h > 0 ? `${h}h ${m}m` : `${m}m`; } function SortHeader({ label, sortKey, current, dir, onSort }: { label: string; sortKey: SortKey; current: SortKey; dir: SortDir; onSort: (k: SortKey) => void; }) { const active = current === sortKey; return ( ); } export default function Playlists() { const { t } = useTranslation(); const [playlists, setPlaylists] = useState([]); const [loading, setLoading] = useState(true); const [filter, setFilter] = useState(''); const [sortKey, setSortKey] = useState('name'); const [sortDir, setSortDir] = useState('asc'); const playTrack = usePlayerStore(s => s.playTrack); const clearQueue = usePlayerStore(s => s.clearQueue); const fetchPlaylists = () => { setLoading(true); getPlaylists() .then(data => { setPlaylists(data); setLoading(false); }) .catch(err => { console.error('Failed to load playlists', err); setLoading(false); }); }; useEffect(() => { fetchPlaylists(); }, []); const handleSort = (key: SortKey) => { if (sortKey === key) setSortDir(d => d === 'asc' ? 'desc' : 'asc'); else { setSortKey(key); setSortDir('asc'); } }; const handlePlay = async (id: string) => { try { const data = await getPlaylist(id); const tracks = data.songs.map((s: any) => ({ id: s.id, title: s.title, artist: s.artist, album: s.album, albumId: s.albumId, artistId: s.artistId, duration: s.duration, coverArt: s.coverArt, track: s.track, year: s.year, bitRate: s.bitRate, suffix: s.suffix, userRating: s.userRating, })); if (tracks.length > 0) { clearQueue(); playTrack(tracks[0], tracks); } } catch (e) { console.error('Failed to play playlist', e); } }; const handleDelete = async (id: string, name: string) => { if (confirm(t('playlists.confirmDelete', { name }))) { try { await deletePlaylist(id); fetchPlaylists(); } catch (e) { console.error('Failed to delete playlist', e); } } }; const visible = useMemo(() => { const q = filter.toLowerCase(); const filtered = q ? playlists.filter(p => p.name.toLowerCase().includes(q)) : playlists; return [...filtered].sort((a, b) => { let cmp = 0; if (sortKey === 'name') cmp = a.name.localeCompare(b.name); else if (sortKey === 'songCount') cmp = a.songCount - b.songCount; else cmp = a.duration - b.duration; return sortDir === 'asc' ? cmp : -cmp; }); }, [playlists, filter, sortKey, sortDir]); return (

{t('playlists.title')}

setFilter(e.target.value)} />
{loading ? (
) : playlists.length === 0 ? (
{t('playlists.empty')}
) : (
{visible.length === 0 ? (
{t('playlists.noResults')}
) : visible.map(p => (
{p.name} {t('playlists.track', { count: p.songCount })} {formatDuration(p.duration)}
))}
)}
); }