diff --git a/components/files/files-list.js b/components/files/files-list.js index 18fc0a1..26af1b7 100644 --- a/components/files/files-list.js +++ b/components/files/files-list.js @@ -1,8 +1,10 @@ +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' @@ -49,6 +51,7 @@ function formatSize(size) { 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') @@ -63,86 +66,81 @@ export default function FilesList({files}) { 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) } - const getQuality = (extension, caption) => { - switch (extension) { - case '.ogg': - case '.aac': - case '.mp3': - return ( - Faible - ) - case '.flac': - if (caption === 'MAX') { - return ( - {caption} - ) - } - - if (caption === 'HAUTE') { - return ( - {caption} - ) - } - - return ( - Haute - ) - - default: - return - } - } - return ( <> {musicFiles.length > 0 && ( @@ -159,10 +157,9 @@ export default function FilesList({files}) { - - QUALITÉ + FORMAT @@ -170,7 +167,7 @@ export default function FilesList({files}) { {sortedMusicFiles.map(file => ( - {getQuality(file.ext.toLowerCase(), file?.caption?.toUpperCase())} + {renderMeta(file)}
MAX : Jusqu’à 24-bit, 96 kHz, ≃ 3000 kbps (flac)
HAUTE : 16 bits, ≃ 900 kbps (flac)

FAIBLE : 320 kbps (ogg / aac / mp3)