import {useState, useEffect} from 'react' import PropTypes from 'prop-types' import List from '@mui/material/List' import ListSubheader from '@mui/material/ListSubheader' import Typography from '@mui/material/Typography' import Box from '@mui/material/Box' import Skeleton from '@mui/material/Skeleton' import {useTheme, useColorScheme, styled} from '@mui/material/styles' import Table from '@mui/material/Table' import TableHead from '@mui/material/TableHead' import TableBody from '@mui/material/TableBody' import TableCell, {tableCellClasses} from '@mui/material/TableCell' import TableRow from '@mui/material/TableRow' import TableContainer from '@mui/material/TableContainer' import Paper from '@mui/material/Paper' import FileSaver from 'file-saver' import DescriptionIcon from '@mui/icons-material/Description' import LibraryMusicIcon from '@mui/icons-material/LibraryMusic' import {Link} from '@mui/material' const apiUrl = process.env.NEXT_PUBLIC_API_URL_ROOT || 'http://localhost:1337' const StyledTableCell = styled(TableCell)(({theme}) => ({ [`&.${tableCellClasses.head}`]: { backgroundColor: theme.palette.common.black, color: theme.palette.common.white, }, [`&.${tableCellClasses.body}`]: { fontSize: 14, }, })) const StyledTableRow = styled(TableRow)(({theme}) => ({ '&:nth-of-type(odd)': { backgroundColor: theme.palette.action.hover, }, '&:last-child td, &:last-child th': { border: 0, }, })) function formatSize(size) { if (size < 1000) { return Math.round(size) + ' Kb' } const mbSize = size / 1000 return Math.round(mbSize) + ' Mb' } export default function FilesList({files}) { const theme = useTheme() const {mode} = useColorScheme() const [audioMeta, setAudioMeta] = useState({}) const musicFiles = files.filter(file => file.mime.startsWith('audio')) const pdfFiles = files.filter(file => file.mime === 'application/pdf') const sortedMusicFiles = musicFiles.sort((a, b) => { const extensionOrder = { '.flac': 0, '.ogg': 1, '.aac': 2, '.mp3': 3 } return extensionOrder[a.ext.toLowerCase()] - extensionOrder[b.ext.toLowerCase()] }) useEffect(() => { const audioFiles = files.filter(f => f.mime.startsWith('audio')) if (audioFiles.length === 0) return let cancelled = false async function fetchAllMeta() { const mm = await import('music-metadata-browser') const results = {} await Promise.all( audioFiles.map(async file => { try { const meta = await mm.fetchFromUrl(`${apiUrl}${file.url}`, {skipCovers: true}) results[file.id] = meta.format } catch { results[file.id] = null } }) ) if (!cancelled) setAudioMeta(results) } fetchAllMeta() return () => { cancelled = true } }, [files]) const losslessFormats = new Set(['.flac', '.wav', '.aiff', '.alac']) const qualityBadge = ext => { const isHaute = losslessFormats.has(ext.toLowerCase()) return ( {isHaute ? 'Haute' : 'Faible'} ) } const renderMeta = file => { const format = file.ext.replace('.', '').toUpperCase() if (!(file.id in audioMeta)) { return } const meta = audioMeta[file.id] const sampleRate = meta?.sampleRate ? `${meta.sampleRate / 1000} kHz` : null const bitDepth = meta?.bitsPerSample ? `${meta.bitsPerSample} bits` : null const bitrate = meta?.bitrate ? `${Math.round(meta.bitrate / 1000)} kbps` : null const details = [sampleRate, bitDepth ?? bitrate].filter(Boolean).join(' ยท ') return ( {qualityBadge(file.ext)} {format} {details && {details}} ) } const handleClick = (e, url, fileName) => { e.stopPropagation() FileSaver.saveAs(url, fileName) } return ( <> {musicFiles.length > 0 && ( Musiques FORMAT {sortedMusicFiles.map(file => ( {renderMeta(file)} handleClick(e, `${apiUrl}${file.url}`, file.name)} > {file.name} ({formatSize(file.size)}) ))}
)} {pdfFiles.length > 0 && ( Paroles LANGUE {pdfFiles.map(file => ( {file.caption} handleClick(e, `${apiUrl}${file.url}`, file.name)} > {file.name} ({formatSize(file.size)}) ))}
)} ) } FilesList.propTypes = { files: PropTypes.array.isRequired }