"use client" import { useState, useMemo } from "react" import { useQuery } from "@tanstack/react-query" import { getTracks, getApiUrl } from "@/lib/api" import { logout, getUser } from "@/lib/auth" import type { FilterParams, Track } from "@/lib/types" import FilterPanel from "@/components/FilterPanel" import AudioPlayer from "@/components/AudioPlayer" // Helper function to format Discogs genre labels function formatGenre(genre: string): { category: string; subgenre: string } { const parts = genre.split('---') return { category: parts[0] || genre, subgenre: parts[1] || '' } } // Extract unique values for filter options function extractFilterOptions(tracks: Track[]) { const genres = new Set() const moods = new Set() const instruments = new Set() const keys = new Set() tracks.forEach(track => { const genreCategory = formatGenre(track.classification.genre.primary).category genres.add(genreCategory) if (track.classification.mood.primary) { moods.add(track.classification.mood.primary) } track.classification.instruments?.forEach(instrument => { instruments.add(instrument) }) if (track.features.key) { keys.add(track.features.key) } }) return { genres: Array.from(genres).sort(), moods: Array.from(moods).sort(), instruments: Array.from(instruments).sort(), keys: Array.from(keys).sort(), } } export default function Home() { const [filters, setFilters] = useState({}) const [page, setPage] = useState(0) const [currentTrack, setCurrentTrack] = useState(null) const [isPlaying, setIsPlaying] = useState(false) const [searchQuery, setSearchQuery] = useState("") const [isScanning, setIsScanning] = useState(false) const [scanStatus, setScanStatus] = useState("") const limit = 25 const { data: tracksData, isLoading: isLoadingTracks } = useQuery({ queryKey: ['tracks', filters, page], queryFn: () => getTracks({ ...filters, skip: page * limit, limit }), }) // Filter tracks by search query on client side const filteredTracks = useMemo(() => { if (!tracksData?.tracks) return [] if (!searchQuery.trim()) return tracksData.tracks const query = searchQuery.toLowerCase() return tracksData.tracks.filter(track => track.filename.toLowerCase().includes(query) || track.metadata?.title?.toLowerCase().includes(query) || track.metadata?.artist?.toLowerCase().includes(query) ) }, [tracksData?.tracks, searchQuery]) const filterOptions = useMemo(() => { if (!tracksData?.tracks || tracksData.tracks.length === 0) { return { genres: [], moods: [], instruments: [], keys: [] } } return extractFilterOptions(tracksData.tracks) }, [tracksData]) const totalPages = tracksData ? Math.ceil(tracksData.total / limit) : 0 const handleRescan = async () => { try { setIsScanning(true) setScanStatus("Démarrage du scan...") const token = localStorage.getItem('access_token') const headers: HeadersInit = { 'Content-Type': 'application/json', } if (token) { headers['Authorization'] = `Bearer ${token}` } const response = await fetch(`${getApiUrl()}/api/library/scan`, { method: 'POST', headers, }) if (!response.ok) { throw new Error('Échec du démarrage du scan') } setScanStatus("Scan en cours...") // Poll scan status const pollInterval = setInterval(async () => { try { const token = localStorage.getItem('access_token') const pollHeaders: HeadersInit = {} if (token) { pollHeaders['Authorization'] = `Bearer ${token}` } const statusResponse = await fetch(`${getApiUrl()}/api/library/scan/status`, { headers: pollHeaders, }) const status = await statusResponse.json() if (!status.is_scanning) { clearInterval(pollInterval) setScanStatus(`Scan terminé ! ${status.processed} fichiers traités`) setIsScanning(false) // Refresh tracks after scan window.location.reload() } else { setScanStatus(`Scan : ${status.processed}/${status.total_files} fichiers (${status.progress}%)`) } } catch (error) { console.error('Erreur lors de la vérification du statut:', error) } }, 2000) } catch (error) { console.error('Erreur lors du rescan:', error) setScanStatus("Erreur lors du scan") setIsScanning(false) } } return (
{/* Header */}

Bibliothèque Musicale

Gestion intelligente de votre collection audio

{/* Search bar */}
setSearchQuery(e.target.value)} className="w-full px-4 py-2 pl-10 bg-slate-50 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-orange-500 focus:border-transparent text-sm" />
{tracksData?.total || 0} piste{(tracksData?.total || 0) > 1 ? 's' : ''}
{/* Logout button */} {/* Rescan button */} {/* Scan status */} {scanStatus && (
{scanStatus}
)}
{/* Main content with sidebar */}
{/* Sidebar */} {/* Tracks list */}
{isLoadingTracks ? (
Chargement...
) : filteredTracks.length === 0 ? (

Aucune piste trouvée

Essayez de modifier vos filtres ou votre recherche

) : ( <>
{filteredTracks.map((track) => (
{/* Play button */} {/* Track info */}

{track.metadata?.title || track.filename}

{track.metadata?.artist && (

{track.metadata.artist}

)}
{/* Genre */} {(() => { const genre = formatGenre(track.classification.genre.primary) return ( <> {genre.category} {genre.subgenre && ( {genre.subgenre} )} ) })()} {/* Mood */} {track.classification.mood.primary} {/* Key & BPM */} {track.features.key} {Math.round(track.features.tempo_bpm)} BPM {/* Duration */} {Math.floor(track.duration_seconds / 60)}:{String(Math.floor(track.duration_seconds % 60)).padStart(2, '0')}
{/* Instruments */} {track.classification.instruments && track.classification.instruments.length > 0 && (
Instruments: {track.classification.instruments.slice(0, 5).map((instrument, i) => ( {instrument} ))} {track.classification.instruments.length > 5 && ( +{track.classification.instruments.length - 5} )}
)}
))}
{/* Pagination */} {totalPages > 1 && (
Page {page + 1} sur {totalPages}
)} )}
{/* Fixed Audio Player at bottom */}
) }