'use client'; import React, { useEffect, useState } from 'react'; import Image from 'next/image'; import { useNavidrome } from '@/app/components/NavidromeContext'; import { useAudioPlayer } from '@/app/components/AudioPlayerContext'; import { Song } from '@/lib/navidrome'; import { Button } from '@/components/ui/button'; import { Separator } from '@/components/ui/separator'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Input } from '@/components/ui/input'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Search, Play, Plus, User, Disc, ChevronLeft, ChevronRight } from 'lucide-react'; import Loading from '@/app/components/loading'; import { getNavidromeAPI } from '@/lib/navidrome'; type SortOption = 'title' | 'artist' | 'album' | 'year' | 'duration' | 'track'; type SortDirection = 'asc' | 'desc'; const ITEMS_PER_PAGE = 50; export default function SongsPage() { const { getAllSongs } = useNavidrome(); const { playTrack, addToQueue, currentTrack } = useAudioPlayer(); const [songs, setSongs] = useState([]); const [filteredSongs, setFilteredSongs] = useState([]); const [loading, setLoading] = useState(true); const [searchQuery, setSearchQuery] = useState(''); const [sortBy, setSortBy] = useState('title'); const [sortDirection, setSortDirection] = useState('asc'); const [currentPage, setCurrentPage] = useState(1); const api = getNavidromeAPI(); useEffect(() => { const fetchSongs = async () => { setLoading(true); try { const allSongs = await getAllSongs(); setSongs(allSongs); setFilteredSongs(allSongs); } catch (error) { console.error('Failed to fetch songs:', error); } setLoading(false); }; fetchSongs(); }, [getAllSongs]); useEffect(() => { let filtered = songs; // Apply search filter if (searchQuery.trim()) { const query = searchQuery.toLowerCase(); filtered = songs.filter(song => song.title.toLowerCase().includes(query) || song.artist.toLowerCase().includes(query) || song.album.toLowerCase().includes(query) ); } // Apply sorting filtered = [...filtered].sort((a, b) => { let aValue: string | number; let bValue: string | number; switch (sortBy) { case 'title': aValue = a.title.toLowerCase(); bValue = b.title.toLowerCase(); break; case 'artist': aValue = a.artist.toLowerCase(); bValue = b.artist.toLowerCase(); break; case 'album': aValue = a.album.toLowerCase(); bValue = b.album.toLowerCase(); break; case 'year': aValue = a.year || 0; bValue = b.year || 0; break; case 'duration': aValue = a.duration; bValue = b.duration; break; case 'track': aValue = a.track || 0; bValue = b.track || 0; break; default: aValue = a.title.toLowerCase(); bValue = b.title.toLowerCase(); } if (sortDirection === 'asc') { return aValue < bValue ? -1 : aValue > bValue ? 1 : 0; } else { return aValue > bValue ? -1 : aValue < bValue ? 1 : 0; } }); setFilteredSongs(filtered); setCurrentPage(1); // Reset to first page when filters change }, [songs, searchQuery, sortBy, sortDirection]); const handlePlayClick = (song: Song) => { if (!api) { console.error('Navidrome API not available'); return; } const track = { id: song.id, name: song.title, url: api.getStreamUrl(song.id), artist: song.artist, album: song.album, duration: song.duration, coverArt: song.coverArt ? api.getCoverArtUrl(song.coverArt, 300) : undefined, albumId: song.albumId, artistId: song.artistId, starred: !!song.starred }; playTrack(track); }; const handleAddToQueue = (song: Song) => { if (!api) { console.error('Navidrome API not available'); return; } const track = { id: song.id, name: song.title, url: api.getStreamUrl(song.id), artist: song.artist, album: song.album, duration: song.duration, coverArt: song.coverArt ? api.getCoverArtUrl(song.coverArt, 300) : undefined, albumId: song.albumId, artistId: song.artistId, starred: !!song.starred }; addToQueue(track); }; const formatDuration = (seconds: number): string => { const minutes = Math.floor(seconds / 60); const remainingSeconds = seconds % 60; return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`; }; const isCurrentlyPlaying = (song: Song): boolean => { return currentTrack?.id === song.id; }; // Pagination calculations const totalPages = Math.ceil(filteredSongs.length / ITEMS_PER_PAGE); const startIndex = (currentPage - 1) * ITEMS_PER_PAGE; const endIndex = startIndex + ITEMS_PER_PAGE; const paginatedSongs = filteredSongs.slice(startIndex, endIndex); const goToNextPage = () => { if (currentPage < totalPages) { setCurrentPage(currentPage + 1); } }; const goToPreviousPage = () => { if (currentPage > 1) { setCurrentPage(currentPage - 1); } }; if (loading) { return ; } return (
{/* Header */}

Songs

Showing {startIndex + 1}-{Math.min(endIndex, filteredSongs.length)} of {filteredSongs.length} songs {searchQuery && ` (filtered from ${songs.length} total)`}

{/* Search and Filters */}
setSearchQuery(e.target.value)} className="pl-10" />
{/* Songs List */} {filteredSongs.length === 0 ? (

{searchQuery ? 'No songs found matching your search.' : 'No songs available.'}

) : (
{paginatedSongs.map((song, index) => (
handlePlayClick(song)} > {/* Track Number / Play Indicator */}
{isCurrentlyPlaying(song) ? (
) : ( <> {startIndex + index + 1} )}
{/* Album Art */}
{song.album}
{/* Song Info */}

{song.title}

{song.year && ( {song.year} )}
{song.artist}
{song.album}
{/* Duration */}
{formatDuration(song.duration)}
{/* Actions */}
))}
)} {/* Pagination Controls */} {filteredSongs.length > ITEMS_PER_PAGE && (

Page {currentPage} of {totalPages}

)}
); }