mirror of
https://github.com/navidrome/navidrome.git
synced 2025-04-03 20:47:35 +03:00
Refactor DownloadMenuDialog to use useTranscodingOptions
hook
This commit is contained in:
parent
c8293fcdd8
commit
65174d3fb2
9 changed files with 107 additions and 211 deletions
|
@ -132,7 +132,7 @@ const AlbumActions = ({
|
|||
{...shareDialog.props}
|
||||
ids={[record.id]}
|
||||
resource={'album'}
|
||||
title={`Share album '${record.name}'`}
|
||||
name={record.name}
|
||||
/>
|
||||
</TopToolbar>
|
||||
)
|
||||
|
|
|
@ -22,3 +22,7 @@ DraggableTypes.ALL.push(
|
|||
export const MAX_SIDEBAR_PLAYLISTS = 100
|
||||
|
||||
export const DEFAULT_SHARE_BITRATE = 128
|
||||
|
||||
export const BITRATE_CHOICES = [
|
||||
32, 48, 64, 80, 96, 112, 128, 160, 192, 256, 320,
|
||||
].map((b) => ({ id: b, name: b.toString() }))
|
||||
|
|
|
@ -1,45 +1,16 @@
|
|||
import React, { useState } from 'react'
|
||||
import { SimpleForm, useTranslate } from 'react-admin'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { ReferenceManyField, useTranslate } from 'react-admin'
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
FormControlLabel,
|
||||
FormGroup,
|
||||
MenuItem,
|
||||
Switch,
|
||||
TextField,
|
||||
} from '@material-ui/core'
|
||||
import subsonic from '../subsonic'
|
||||
import { closeDownloadMenu } from '../actions'
|
||||
import { formatBytes } from '../utils'
|
||||
|
||||
const DownloadTranscodings = (props) => {
|
||||
const translate = useTranslate()
|
||||
|
||||
return (
|
||||
<>
|
||||
<TextField
|
||||
fullWidth
|
||||
id="downloadFormat"
|
||||
select
|
||||
label={translate('resources.transcoding.fields.targetFormat')}
|
||||
onChange={(e) => props.onChange(e.target.value)}
|
||||
value={props.value}
|
||||
>
|
||||
{Object.values(props.data).map((transcoding) => (
|
||||
<MenuItem key={transcoding.id} value={transcoding.targetFormat}>
|
||||
{transcoding.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
</>
|
||||
)
|
||||
}
|
||||
import { useTranscodingOptions } from './useTranscodingOptions'
|
||||
|
||||
const DownloadMenuDialog = () => {
|
||||
const { open, record, recordType } = useSelector(
|
||||
|
@ -48,9 +19,8 @@ const DownloadMenuDialog = () => {
|
|||
const dispatch = useDispatch()
|
||||
const translate = useTranslate()
|
||||
|
||||
const [originalFormat, setUseOriginalFormat] = useState(true)
|
||||
const [targetFormat, setTargetFormat] = useState('')
|
||||
const [targetRate, setTargetRate] = useState(0)
|
||||
const { TranscodingOptionsInput, format, maxBitRate, originalFormat } =
|
||||
useTranscodingOptions()
|
||||
|
||||
const handleClose = (e) => {
|
||||
dispatch(closeDownloadMenu())
|
||||
|
@ -59,114 +29,51 @@ const DownloadMenuDialog = () => {
|
|||
|
||||
const handleDownload = (e) => {
|
||||
if (record) {
|
||||
subsonic.download(
|
||||
record.id,
|
||||
originalFormat ? 'raw' : targetFormat,
|
||||
targetRate
|
||||
)
|
||||
if (originalFormat) {
|
||||
subsonic.download(record.id, 'raw')
|
||||
} else {
|
||||
subsonic.download(record.id, format, maxBitRate?.toString())
|
||||
}
|
||||
dispatch(closeDownloadMenu())
|
||||
}
|
||||
e.stopPropagation()
|
||||
}
|
||||
|
||||
const handleOriginal = (e) => {
|
||||
const original = e.target.checked
|
||||
|
||||
setUseOriginalFormat(original)
|
||||
|
||||
if (original) {
|
||||
setTargetFormat('')
|
||||
setTargetRate(0)
|
||||
}
|
||||
}
|
||||
|
||||
const type = recordType
|
||||
? translate(`resources.${recordType}.name`, {
|
||||
smart_count: 1,
|
||||
}).toLocaleLowerCase()
|
||||
: ''
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
onBackdropClick={handleClose}
|
||||
aria-labelledby="download-dialog"
|
||||
fullWidth={true}
|
||||
maxWidth={'sm'}
|
||||
>
|
||||
<DialogTitle id="download-dialog">
|
||||
{record &&
|
||||
`${translate('resources.album.actions.download')} ${type} ${
|
||||
record.name || record.title
|
||||
} (${formatBytes(record.size)})`}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<Box
|
||||
component="form"
|
||||
sx={{
|
||||
'& .MuiTextField-root': { m: 1, width: '25ch' },
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<FormGroup>
|
||||
<FormControlLabel
|
||||
control={<Switch checked={originalFormat} />}
|
||||
label={translate('message.originalFormat')}
|
||||
onChange={handleOriginal}
|
||||
/>
|
||||
</FormGroup>
|
||||
{!originalFormat && (
|
||||
<>
|
||||
<ReferenceManyField
|
||||
fullWidth
|
||||
source=""
|
||||
target="name"
|
||||
reference="transcoding"
|
||||
sort={{ field: 'name', order: 'ASC' }}
|
||||
>
|
||||
<DownloadTranscodings
|
||||
onChange={setTargetFormat}
|
||||
value={targetFormat}
|
||||
/>
|
||||
</ReferenceManyField>
|
||||
<TextField
|
||||
fullWidth
|
||||
id="downloadRate"
|
||||
select
|
||||
label={translate('resources.player.fields.maxBitRate')}
|
||||
value={targetRate}
|
||||
onChange={(e) => setTargetRate(e.target.value)}
|
||||
>
|
||||
<MenuItem value={0}>-</MenuItem>
|
||||
{[32, 48, 64, 80, 96, 112, 128, 160, 192, 256, 320].map(
|
||||
(bits) => (
|
||||
<MenuItem key={bits} value={bits}>
|
||||
{bits}
|
||||
</MenuItem>
|
||||
)
|
||||
)}
|
||||
</TextField>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
onClick={handleDownload}
|
||||
color="primary"
|
||||
disabled={!originalFormat && !targetFormat}
|
||||
>
|
||||
{translate('resources.album.actions.download')}
|
||||
</Button>
|
||||
<Button onClick={handleClose} color="secondary">
|
||||
{translate('ra.action.close')}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</>
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
onBackdropClick={handleClose}
|
||||
aria-labelledby="download-dialog"
|
||||
fullWidth={true}
|
||||
maxWidth={'sm'}
|
||||
>
|
||||
<DialogTitle id="download-dialog">
|
||||
{translate('message.downloadDialogTitle', {
|
||||
resource: translate(`resources.${recordType}.name`, {
|
||||
smart_count: 1,
|
||||
}).toLocaleLowerCase(),
|
||||
name: record?.name || record?.title,
|
||||
size: formatBytes(record?.size),
|
||||
})}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<SimpleForm toolbar={null} variant={'outlined'}>
|
||||
<TranscodingOptionsInput
|
||||
fullWidth
|
||||
label={translate('message.originalFormat')}
|
||||
/>
|
||||
</SimpleForm>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleDownload} color="primary">
|
||||
{translate('ra.action.download')}
|
||||
</Button>
|
||||
<Button onClick={handleClose} color="secondary">
|
||||
{translate('ra.action.close')}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -5,13 +5,20 @@ import {
|
|||
DialogContent,
|
||||
DialogTitle,
|
||||
} from '@material-ui/core'
|
||||
import { SimpleForm, TextInput, useCreate, useNotify } from 'react-admin'
|
||||
import {
|
||||
SimpleForm,
|
||||
TextInput,
|
||||
useCreate,
|
||||
useNotify,
|
||||
useTranslate,
|
||||
} from 'react-admin'
|
||||
import { useState } from 'react'
|
||||
import { shareUrl } from '../utils'
|
||||
import { useTranscodingOptions } from './useTranscodingOptions'
|
||||
|
||||
export const ShareDialog = ({ open, onClose, ids, resource, title }) => {
|
||||
export const ShareDialog = ({ open, onClose, ids, resource, name }) => {
|
||||
const notify = useNotify()
|
||||
const translate = useTranslate()
|
||||
const [description, setDescription] = useState('')
|
||||
const { TranscodingOptionsInput, format, maxBitRate, originalFormat } =
|
||||
useTranscodingOptions()
|
||||
|
@ -57,9 +64,16 @@ export const ShareDialog = ({ open, onClose, ids, resource, title }) => {
|
|||
onBackdropClick={onClose}
|
||||
aria-labelledby="share-dialog"
|
||||
fullWidth={true}
|
||||
maxWidth={'xs'}
|
||||
maxWidth={'sm'}
|
||||
>
|
||||
<DialogTitle id="share-dialog">{title}</DialogTitle>
|
||||
<DialogTitle id="share-dialog">
|
||||
{translate('message.shareDialogTitle', {
|
||||
resource: translate(`resources.${resource}.name`, {
|
||||
smart_count: ids?.length,
|
||||
}).toLocaleLowerCase(),
|
||||
name,
|
||||
})}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<SimpleForm toolbar={null} variant={'outlined'}>
|
||||
<TextInput
|
||||
|
@ -69,15 +83,18 @@ export const ShareDialog = ({ open, onClose, ids, resource, title }) => {
|
|||
setDescription(event.target.value)
|
||||
}}
|
||||
/>
|
||||
<TranscodingOptionsInput />
|
||||
<TranscodingOptionsInput
|
||||
fullWidth
|
||||
label={translate('message.shareOriginalFormat')}
|
||||
/>
|
||||
</SimpleForm>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={createShare} color="primary">
|
||||
Share
|
||||
{translate('ra.action.share')}
|
||||
</Button>
|
||||
<Button onClick={onClose} color="primary">
|
||||
Cancel
|
||||
{translate('ra.action.close')}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import config from '../config'
|
||||
import { DEFAULT_SHARE_BITRATE } from '../consts'
|
||||
import { BooleanInput, SelectInput, useGetList } from 'react-admin'
|
||||
import { BITRATE_CHOICES, DEFAULT_SHARE_BITRATE } from '../consts'
|
||||
import {
|
||||
BooleanInput,
|
||||
SelectInput,
|
||||
useGetList,
|
||||
useTranslate,
|
||||
} from 'react-admin'
|
||||
|
||||
export const useTranscodingOptions = () => {
|
||||
const translate = useTranslate()
|
||||
const [format, setFormat] = useState(config.defaultDownsamplingFormat)
|
||||
const [maxBitRate, setMaxBitRate] = useState(DEFAULT_SHARE_BITRATE)
|
||||
const [originalFormat, setUseOriginalFormat] = useState(true)
|
||||
|
@ -22,7 +28,7 @@ export const useTranscodingOptions = () => {
|
|||
loadingFormats
|
||||
? []
|
||||
: Object.values(formats).map((f) => {
|
||||
return { id: f.targetFormat, name: f.targetFormat }
|
||||
return { id: f.targetFormat, name: f.name }
|
||||
}),
|
||||
[formats, loadingFormats]
|
||||
)
|
||||
|
@ -39,14 +45,15 @@ export const useTranscodingOptions = () => {
|
|||
)
|
||||
|
||||
const TranscodingOptionsInput = useMemo(() => {
|
||||
return ({ basePath, ...props }) => {
|
||||
return ({ label, basePath, ...props }) => {
|
||||
return (
|
||||
<>
|
||||
<BooleanInput
|
||||
{...props}
|
||||
source="original"
|
||||
defaultValue={originalFormat}
|
||||
label={'Share in original format'}
|
||||
label={label}
|
||||
fullWidth
|
||||
onChange={handleOriginal}
|
||||
/>
|
||||
{!originalFormat && (
|
||||
|
@ -55,6 +62,7 @@ export const useTranscodingOptions = () => {
|
|||
{...props}
|
||||
source="format"
|
||||
defaultValue={format}
|
||||
label={translate('resources.player.fields.transcodingId')}
|
||||
choices={formatOptions}
|
||||
onChange={(event) => {
|
||||
setFormat(event.target.value)
|
||||
|
@ -63,20 +71,9 @@ export const useTranscodingOptions = () => {
|
|||
<SelectInput
|
||||
{...props}
|
||||
source="maxBitRate"
|
||||
label={translate('resources.player.fields.maxBitRate')}
|
||||
defaultValue={maxBitRate}
|
||||
choices={[
|
||||
{ id: 32, name: '32' },
|
||||
{ id: 48, name: '48' },
|
||||
{ id: 64, name: '64' },
|
||||
{ id: 80, name: '80' },
|
||||
{ id: 96, name: '96' },
|
||||
{ id: 112, name: '112' },
|
||||
{ id: 128, name: '128' },
|
||||
{ id: 160, name: '160' },
|
||||
{ id: 192, name: '192' },
|
||||
{ id: 256, name: '256' },
|
||||
{ id: 320, name: '320' },
|
||||
]}
|
||||
choices={BITRATE_CHOICES}
|
||||
onChange={(event) => {
|
||||
setMaxBitRate(event.target.value)
|
||||
}}
|
||||
|
@ -86,7 +83,14 @@ export const useTranscodingOptions = () => {
|
|||
</>
|
||||
)
|
||||
}
|
||||
}, [handleOriginal, formatOptions, format, maxBitRate, originalFormat])
|
||||
}, [
|
||||
handleOriginal,
|
||||
formatOptions,
|
||||
format,
|
||||
maxBitRate,
|
||||
originalFormat,
|
||||
translate,
|
||||
])
|
||||
|
||||
return {
|
||||
TranscodingOptionsInput,
|
||||
|
|
|
@ -257,7 +257,9 @@
|
|||
"open_menu": "Open menu",
|
||||
"close_menu": "Close menu",
|
||||
"unselect": "Unselect",
|
||||
"skip": "Skip"
|
||||
"skip": "Skip",
|
||||
"share": "Share",
|
||||
"download": "Download"
|
||||
},
|
||||
"boolean": {
|
||||
"true": "Yes",
|
||||
|
@ -367,6 +369,9 @@
|
|||
"musicbrainz": "Open in MusicBrainz"
|
||||
},
|
||||
"lastfmLink": "Read More...",
|
||||
"shareOriginalFormat": "Share in original format",
|
||||
"shareDialogTitle": "Share %{resource} '%{name}'",
|
||||
"downloadDialogTitle": "Download %{resource} '%{name}' (%{size})",
|
||||
"originalFormat": "Download in original format"
|
||||
},
|
||||
"menu": {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import React from 'react'
|
||||
import {
|
||||
TextInput,
|
||||
BooleanInput,
|
||||
|
@ -12,6 +11,7 @@ import {
|
|||
} from 'react-admin'
|
||||
import { Title } from '../common'
|
||||
import config from '../config'
|
||||
import { BITRATE_CHOICES } from '../consts'
|
||||
|
||||
const PlayerTitle = ({ record }) => {
|
||||
const translate = useTranslate()
|
||||
|
@ -30,23 +30,7 @@ const PlayerEdit = (props) => (
|
|||
>
|
||||
<SelectInput source="name" resettable />
|
||||
</ReferenceInput>
|
||||
<SelectInput
|
||||
source="maxBitRate"
|
||||
choices={[
|
||||
{ id: 32, name: '32' },
|
||||
{ id: 48, name: '48' },
|
||||
{ id: 64, name: '64' },
|
||||
{ id: 80, name: '80' },
|
||||
{ id: 96, name: '96' },
|
||||
{ id: 112, name: '112' },
|
||||
{ id: 128, name: '128' },
|
||||
{ id: 160, name: '160' },
|
||||
{ id: 192, name: '192' },
|
||||
{ id: 256, name: '256' },
|
||||
{ id: 320, name: '320' },
|
||||
{ id: 0, name: '-' },
|
||||
]}
|
||||
/>
|
||||
<SelectInput source="maxBitRate" resettable choices={BITRATE_CHOICES} />
|
||||
<BooleanInput source="reportRealPath" fullWidth />
|
||||
{(config.lastFMEnabled || config.listenBrainzEnabled) && (
|
||||
<BooleanInput source="scrobbleEnabled" fullWidth />
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
useTranslate,
|
||||
} from 'react-admin'
|
||||
import { Title } from '../common'
|
||||
import { BITRATE_CHOICES } from '../consts'
|
||||
|
||||
const TranscodingTitle = () => {
|
||||
const translate = useTranslate()
|
||||
|
@ -27,19 +28,7 @@ const TranscodingCreate = (props) => (
|
|||
<TextInput source="targetFormat" validate={[required()]} />
|
||||
<SelectInput
|
||||
source="defaultBitRate"
|
||||
choices={[
|
||||
{ id: 32, name: '32' },
|
||||
{ id: 48, name: '48' },
|
||||
{ id: 64, name: '64' },
|
||||
{ id: 80, name: '80' },
|
||||
{ id: 96, name: '96' },
|
||||
{ id: 112, name: '112' },
|
||||
{ id: 128, name: '128' },
|
||||
{ id: 160, name: '160' },
|
||||
{ id: 192, name: '192' },
|
||||
{ id: 256, name: '256' },
|
||||
{ id: 320, name: '320' },
|
||||
]}
|
||||
choices={BITRATE_CHOICES}
|
||||
defaultValue={192}
|
||||
/>
|
||||
<TextInput
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
} from 'react-admin'
|
||||
import { Title } from '../common'
|
||||
import { TranscodingNote } from './TranscodingNote'
|
||||
import { BITRATE_CHOICES } from '../consts'
|
||||
|
||||
const TranscodingTitle = ({ record }) => {
|
||||
const translate = useTranslate()
|
||||
|
@ -27,22 +28,7 @@ const TranscodingEdit = (props) => {
|
|||
<SimpleForm variant={'outlined'}>
|
||||
<TextInput source="name" validate={[required()]} />
|
||||
<TextInput source="targetFormat" validate={[required()]} />
|
||||
<SelectInput
|
||||
source="defaultBitRate"
|
||||
choices={[
|
||||
{ id: 32, name: '32' },
|
||||
{ id: 48, name: '48' },
|
||||
{ id: 64, name: '64' },
|
||||
{ id: 80, name: '80' },
|
||||
{ id: 96, name: '96' },
|
||||
{ id: 112, name: '112' },
|
||||
{ id: 128, name: '128' },
|
||||
{ id: 160, name: '160' },
|
||||
{ id: 192, name: '192' },
|
||||
{ id: 256, name: '256' },
|
||||
{ id: 320, name: '320' },
|
||||
]}
|
||||
/>
|
||||
<SelectInput source="defaultBitRate" choices={BITRATE_CHOICES} />
|
||||
<TextInput source="command" fullWidth validate={[required()]} />
|
||||
</SimpleForm>
|
||||
</Edit>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue