Merge pull request 'Permet le téléchargement de fichiers' (#3) from feat-download into master
Reviewed-on: https://codeberg.org/OKI/oki.re/pulls/3
This commit is contained in:
@@ -0,0 +1,52 @@
|
|||||||
|
import {useState} from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import Button from '@mui/material/Button'
|
||||||
|
import Dialog from '@mui/material/Dialog'
|
||||||
|
import DialogActions from '@mui/material/DialogActions'
|
||||||
|
import DialogContent from '@mui/material/DialogContent'
|
||||||
|
import useMediaQuery from '@mui/material/useMediaQuery'
|
||||||
|
import {useTheme} from '@mui/material/styles'
|
||||||
|
|
||||||
|
import DownloadForOfflineIcon from '@mui/icons-material/DownloadForOffline'
|
||||||
|
import FilesList from './files-list'
|
||||||
|
|
||||||
|
export default function FilesDialog({files}) {
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
|
const theme = useTheme()
|
||||||
|
const fullScreen = useMediaQuery(theme.breakpoints.down('md'))
|
||||||
|
|
||||||
|
const handleClickOpen = () => {
|
||||||
|
setOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button endIcon={<DownloadForOfflineIcon />} sx={{marginBottom: 2}} variant='outlined' onClick={handleClickOpen}>
|
||||||
|
Télécharger
|
||||||
|
</Button>
|
||||||
|
<Dialog
|
||||||
|
fullScreen={fullScreen}
|
||||||
|
open={open}
|
||||||
|
aria-labelledby='FilesDialog'
|
||||||
|
onClose={handleClose}
|
||||||
|
>
|
||||||
|
<DialogContent dividers>
|
||||||
|
<FilesList files={files} />
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button autoFocus onClick={handleClose}>
|
||||||
|
Fermer
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
FilesDialog.propTypes = {
|
||||||
|
files: PropTypes.array.isRequired
|
||||||
|
}
|
||||||
@@ -0,0 +1,248 @@
|
|||||||
|
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 {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 musicFiles = files.filter(file => file.attributes.mime.startsWith('audio'))
|
||||||
|
const pdfFiles = files.filter(file => file.attributes.mime === 'application/pdf')
|
||||||
|
|
||||||
|
const sortedMusicFiles = musicFiles.sort((a, b) => {
|
||||||
|
const extensionOrder = {
|
||||||
|
'.flac': 0,
|
||||||
|
'.ogg': 1,
|
||||||
|
'.aac': 2,
|
||||||
|
'.mp3': 3
|
||||||
|
}
|
||||||
|
return extensionOrder[a.attributes.ext.toLowerCase()] - extensionOrder[b.attributes.ext.toLowerCase()]
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleClick = (e, url, fileName) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
FileSaver.saveAs(url, fileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getQuality = (extension, caption) => {
|
||||||
|
switch (extension) {
|
||||||
|
case '.ogg':
|
||||||
|
case '.aac':
|
||||||
|
case '.mp3':
|
||||||
|
return (
|
||||||
|
<Typography sx={{
|
||||||
|
backgroundColor: '#393940',
|
||||||
|
color: '#fff',
|
||||||
|
borderRadius: '0.66rem',
|
||||||
|
border: mode === 'dark' ? '1px solid #fff' : 'none',
|
||||||
|
fontSize: '0.75rem',
|
||||||
|
letterSpacing: '0.1rem',
|
||||||
|
padding: '0.2515rem 0.6707rem',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}}
|
||||||
|
>Faible</Typography>
|
||||||
|
)
|
||||||
|
case '.flac':
|
||||||
|
if (caption === 'MAX') {
|
||||||
|
return (
|
||||||
|
<Typography sx={{
|
||||||
|
backgroundColor: '#332619',
|
||||||
|
color: '#ffbe7d',
|
||||||
|
borderRadius: '0.66rem',
|
||||||
|
border: mode === 'dark' ? '1px solid #ffbe7d' : 'none',
|
||||||
|
fontSize: '0.75rem',
|
||||||
|
letterSpacing: '0.1rem',
|
||||||
|
padding: '0.2515rem 0.6707rem',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
textAlign: 'center'
|
||||||
|
}}
|
||||||
|
>{caption}</Typography>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (caption === 'HAUTE') {
|
||||||
|
return (
|
||||||
|
<Typography sx={{
|
||||||
|
backgroundColor: '#07332f',
|
||||||
|
color: '#21feec',
|
||||||
|
borderRadius: '0.66rem',
|
||||||
|
border: mode === 'dark' ? '1px solid #21feec' : 'none',
|
||||||
|
fontSize: '0.75rem',
|
||||||
|
letterSpacing: '0.1rem',
|
||||||
|
padding: '0.2515rem 0.6707rem',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}}
|
||||||
|
>{caption}</Typography>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Typography sx={{
|
||||||
|
backgroundColor: '#07332f',
|
||||||
|
color: '#21feec',
|
||||||
|
borderRadius: '0.66rem',
|
||||||
|
border: mode === 'dark' ? '1px solid #21feec' : 'none',
|
||||||
|
fontSize: '0.75rem',
|
||||||
|
letterSpacing: '0.1rem',
|
||||||
|
padding: '0.2515rem 0.6707rem',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}}
|
||||||
|
>Haute</Typography>
|
||||||
|
)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return <DescriptionIcon sx={{marginRight: 1}} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{musicFiles.length > 0 && (
|
||||||
|
<List
|
||||||
|
sx={{width: '100%', maxWidth: 800}}
|
||||||
|
component='nav'
|
||||||
|
aria-labelledby='nested-list-subheader'
|
||||||
|
>
|
||||||
|
<ListSubheader disableSticky sx={{backgroundColor: mode === 'light' ? theme.palette.grey[100] : theme.palette.background.default}} color='primary'>
|
||||||
|
<Box paddingBlock={1} display='flex' justifyContent='center'>
|
||||||
|
<LibraryMusicIcon />
|
||||||
|
<Typography gutterBottom marginLeft={1} variant='button'>Musiques</Typography>
|
||||||
|
</Box>
|
||||||
|
</ListSubheader>
|
||||||
|
<TableContainer component={Paper}>
|
||||||
|
<Table size='small' aria-label='Musiques'>
|
||||||
|
<caption><small><strong>MAX</strong> : <i>Jusqu’à 24-bit, 96 kHz, ≃ 3000 kbps (flac)</i><br /> <strong>HAUTE</strong> : <i>16 bits, ≃ 900 kbps (flac)</i></small><br /> <small><strong>FAIBLE</strong> : <i>320 kbps (ogg / aac / mp3)</i></small></caption>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<StyledTableCell align='center'>QUALITÉ</StyledTableCell>
|
||||||
|
<StyledTableCell />
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{sortedMusicFiles.map(file => (
|
||||||
|
<StyledTableRow key={file.id}>
|
||||||
|
<StyledTableCell>
|
||||||
|
{getQuality(file.attributes.ext.toLowerCase(), file?.attributes?.caption?.toUpperCase())}
|
||||||
|
</StyledTableCell>
|
||||||
|
<StyledTableCell align='left'>
|
||||||
|
<Link
|
||||||
|
href='#'
|
||||||
|
underline='hover'
|
||||||
|
sx={{fontWeight: 'bold'}}
|
||||||
|
aria-label='download'
|
||||||
|
onClick={e => handleClick(e, `${apiUrl}${file.attributes.url}`, file.attributes.name)}
|
||||||
|
>
|
||||||
|
{file.attributes.name}
|
||||||
|
</Link>
|
||||||
|
<small style={{marginLeft: 3}}>({formatSize(file.attributes.size)})</small>
|
||||||
|
</StyledTableCell>
|
||||||
|
</StyledTableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
</List>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{pdfFiles.length > 0 && (
|
||||||
|
<List
|
||||||
|
sx={{width: '100%', maxWidth: 800}}
|
||||||
|
component='nav'
|
||||||
|
aria-labelledby='nested-list-subheader'
|
||||||
|
>
|
||||||
|
<ListSubheader disableSticky sx={{marginTop: 2, backgroundColor: mode === 'light' ? theme.palette.grey[100] : theme.palette.background.default}} color='primary'>
|
||||||
|
<Box paddingBlock={1} display='flex' justifyContent='center' alignSelf='center'>
|
||||||
|
<DescriptionIcon />
|
||||||
|
<Typography gutterBottom marginLeft={1} variant='button'>Paroles</Typography>
|
||||||
|
</Box>
|
||||||
|
</ListSubheader>
|
||||||
|
<TableContainer component={Paper}>
|
||||||
|
<Table size='small' aria-label='Paroles'>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<StyledTableCell>LANGUE</StyledTableCell>
|
||||||
|
<StyledTableCell />
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{pdfFiles.map(file => (
|
||||||
|
<StyledTableRow key={file.id}>
|
||||||
|
<StyledTableCell>
|
||||||
|
<strong>
|
||||||
|
{file.attributes.caption}
|
||||||
|
</strong>
|
||||||
|
</StyledTableCell>
|
||||||
|
<StyledTableCell align='left'>
|
||||||
|
<Link
|
||||||
|
href='#'
|
||||||
|
underline='hover'
|
||||||
|
sx={{fontWeight: 'bold'}}
|
||||||
|
aria-label='download'
|
||||||
|
onClick={e => handleClick(e, `${apiUrl}${file.attributes.url}`, file.attributes.name)}
|
||||||
|
>
|
||||||
|
{file.attributes.name}
|
||||||
|
</Link>
|
||||||
|
<small style={{marginLeft: 3}}>({formatSize(file.attributes.size)})</small>
|
||||||
|
</StyledTableCell>
|
||||||
|
</StyledTableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
</List>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
FilesList.propTypes = {
|
||||||
|
files: PropTypes.array.isRequired
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ import ExplicitIcon from '@mui/icons-material/Explicit'
|
|||||||
import {formatJsonString, getAlias} from '../../lib/utils/format'
|
import {formatJsonString, getAlias} from '../../lib/utils/format'
|
||||||
|
|
||||||
import LicenseModal from '../cc/license-modal'
|
import LicenseModal from '../cc/license-modal'
|
||||||
|
import FilesDialog from '../files/files-dialog'
|
||||||
import EntegreMizik from './entegre-mizik'
|
import EntegreMizik from './entegre-mizik'
|
||||||
import OkiMizik from './oki-mizik'
|
import OkiMizik from './oki-mizik'
|
||||||
import DiferansDialog from './diferans-dialog'
|
import DiferansDialog from './diferans-dialog'
|
||||||
@@ -183,6 +184,9 @@ export default function Teks({parole}) {
|
|||||||
<LicenseModal license={parole.creativeCommons.toLowerCase()} />
|
<LicenseModal license={parole.creativeCommons.toLowerCase()} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
{parole?.files?.data && (
|
||||||
|
<FilesDialog files={parole.files.data} />
|
||||||
|
)}
|
||||||
{(parole.okiMizikID || parole.streamAudio.length > 0 || parole.gadeEmbed) && (
|
{(parole.okiMizikID || parole.streamAudio.length > 0 || parole.gadeEmbed) && (
|
||||||
<Box sx={{textAlign: 'center'}}>
|
<Box sx={{textAlign: 'center'}}>
|
||||||
<EntegreMizik parole={parole} isMobile={isMobile} />
|
<EntegreMizik parole={parole} isMobile={isMobile} />
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ export async function jwennTeksEpiSlug(slug) {
|
|||||||
couverture: {
|
couverture: {
|
||||||
populate: '*'
|
populate: '*'
|
||||||
},
|
},
|
||||||
|
files: {
|
||||||
|
populate: '*'
|
||||||
|
},
|
||||||
artistes: {
|
artistes: {
|
||||||
fields: ['alias', 'slug', 'musicBrainzUrl', 'photo'],
|
fields: ['alias', 'slug', 'musicBrainzUrl', 'photo'],
|
||||||
populate: {
|
populate: {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
"date-fns": "^2.16.1",
|
"date-fns": "^2.16.1",
|
||||||
"dotenv": "^16.0.0",
|
"dotenv": "^16.0.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
|
"file-saver": "^2.0.5",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"next": "^14.1.4",
|
"next": "^14.1.4",
|
||||||
"next-auth": "^4.22.1",
|
"next-auth": "^4.22.1",
|
||||||
|
|||||||
@@ -4631,6 +4631,11 @@ file-entry-cache@^6.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
flat-cache "^3.0.4"
|
flat-cache "^3.0.4"
|
||||||
|
|
||||||
|
file-saver@^2.0.5:
|
||||||
|
version "2.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.5.tgz#d61cfe2ce059f414d899e9dd6d4107ee25670c38"
|
||||||
|
integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==
|
||||||
|
|
||||||
filelist@^1.0.1:
|
filelist@^1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.2.tgz#80202f21462d4d1c2e214119b1807c1bc0380e5b"
|
resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.2.tgz#80202f21462d4d1c2e214119b1807c1bc0380e5b"
|
||||||
|
|||||||
Reference in New Issue
Block a user