253 lines
7.7 KiB
TypeScript
253 lines
7.7 KiB
TypeScript
import React from 'react'
|
|
import {
|
|
Box,
|
|
Paper,
|
|
Typography,
|
|
TextField,
|
|
MenuItem,
|
|
FormControlLabel,
|
|
Switch,
|
|
Collapse,
|
|
IconButton,
|
|
Divider,
|
|
} from '@mui/material'
|
|
// Select is used internally by TextField with select prop, no need to import
|
|
import { ExpandMore, ExpandLess } from '@mui/icons-material'
|
|
import { useOptionsStore } from '../stores/optionsStore'
|
|
|
|
|
|
interface SectionProps {
|
|
title: string
|
|
children: React.ReactNode
|
|
defaultExpanded?: boolean
|
|
}
|
|
|
|
const Section: React.FC<SectionProps> = ({ title, children, defaultExpanded = false }) => {
|
|
const [expanded, setExpanded] = React.useState(defaultExpanded)
|
|
|
|
return (
|
|
<Box sx={{ mb: 2 }}>
|
|
<Box
|
|
sx={{
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
cursor: 'pointer',
|
|
py: 1,
|
|
}}
|
|
onClick={() => setExpanded(!expanded)}
|
|
>
|
|
<Typography variant="subtitle2" sx={{ fontWeight: 600 }}>
|
|
{title}
|
|
</Typography>
|
|
<IconButton size="small">{expanded ? <ExpandLess /> : <ExpandMore />}</IconButton>
|
|
</Box>
|
|
<Divider />
|
|
<Collapse in={expanded}>
|
|
<Box sx={{ pt: 2, pb: 1 }}>{children}</Box>
|
|
</Collapse>
|
|
</Box>
|
|
)
|
|
}
|
|
|
|
export const OptionsPanel: React.FC = () => {
|
|
const { options, setOptions } = useOptionsStore()
|
|
|
|
const handleChange = (field: string, value: any) => {
|
|
setOptions({ [field]: value })
|
|
}
|
|
|
|
return (
|
|
<Paper sx={{ p: 3 }}>
|
|
<Typography variant="h6" sx={{ mb: 2 }}>
|
|
⚙️ Options
|
|
</Typography>
|
|
|
|
{/* Tracklist Section */}
|
|
<Section title="Tracklist Settings" defaultExpanded>
|
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
|
<TextField
|
|
label="Tracklist Format"
|
|
value={options.tracklist_format}
|
|
onChange={(e) => handleChange('tracklist_format', e.target.value)}
|
|
fullWidth
|
|
size="small"
|
|
helperText="Placeholders: %ts (timestamp), %tn (track name), %an (author), %al (album), %date, %ext"
|
|
/>
|
|
</Box>
|
|
</Section>
|
|
|
|
{/* Output Section */}
|
|
<Section title="Output Settings" defaultExpanded>
|
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
|
<TextField
|
|
label="Format"
|
|
select
|
|
value={options.format}
|
|
onChange={(e) => handleChange('format', e.target.value)}
|
|
fullWidth
|
|
size="small"
|
|
>
|
|
<MenuItem value="mp3">MP3</MenuItem>
|
|
<MenuItem value="m4a">M4A</MenuItem>
|
|
<MenuItem value="mkv">MKV</MenuItem>
|
|
<MenuItem value="mp4">MP4</MenuItem>
|
|
<MenuItem value="ogg">OGG</MenuItem>
|
|
<MenuItem value="opus">OPUS</MenuItem>
|
|
<MenuItem value="flac">FLAC</MenuItem>
|
|
<MenuItem value="wav">WAV</MenuItem>
|
|
<MenuItem value="aac">AAC</MenuItem>
|
|
</TextField>
|
|
|
|
<TextField
|
|
label="Transcode to"
|
|
select
|
|
value={options.transcode_to || ''}
|
|
onChange={(e) => handleChange('transcode_to', e.target.value || undefined)}
|
|
fullWidth
|
|
size="small"
|
|
>
|
|
<MenuItem value="">Copy (no transcoding)</MenuItem>
|
|
<MenuItem value="libmp3lame">MP3 (LAME)</MenuItem>
|
|
<MenuItem value="aac">AAC</MenuItem>
|
|
<MenuItem value="libopus">OPUS</MenuItem>
|
|
</TextField>
|
|
|
|
<FormControlLabel
|
|
control={
|
|
<Switch
|
|
checked={options.drop_video}
|
|
onChange={(e) => handleChange('drop_video', e.target.checked)}
|
|
/>
|
|
}
|
|
label="Drop video streams"
|
|
/>
|
|
<FormControlLabel
|
|
control={
|
|
<Switch
|
|
checked={options.drop_subs}
|
|
onChange={(e) => handleChange('drop_subs', e.target.checked)}
|
|
/>
|
|
}
|
|
label="Drop subtitle streams"
|
|
/>
|
|
</Box>
|
|
</Section>
|
|
|
|
{/* Filename Section */}
|
|
<Section title="Filename Settings">
|
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
|
<TextField
|
|
label="Output template"
|
|
value={options.output_template}
|
|
onChange={(e) => handleChange('output_template', e.target.value)}
|
|
fullWidth
|
|
size="small"
|
|
helperText="Placeholders: %tn (track name), %an (author), %al (album), %date, %ext, %num"
|
|
/>
|
|
<FormControlLabel
|
|
control={
|
|
<Switch
|
|
checked={options.number_tracks}
|
|
onChange={(e) => handleChange('number_tracks', e.target.checked)}
|
|
/>
|
|
}
|
|
label="Number tracks (01 - )"
|
|
/>
|
|
<FormControlLabel
|
|
control={
|
|
<Switch
|
|
checked={options.replace_bad_chars}
|
|
onChange={(e) => handleChange('replace_bad_chars', e.target.checked)}
|
|
/>
|
|
}
|
|
label="Replace bad characters"
|
|
/>
|
|
<TextField
|
|
label="Replacement character"
|
|
value={options.replacement_char}
|
|
onChange={(e) => handleChange('replacement_char', e.target.value)}
|
|
size="small"
|
|
disabled={!options.replace_bad_chars}
|
|
/>
|
|
<TextField
|
|
label="Bad characters list"
|
|
value={options.bad_chars}
|
|
onChange={(e) => handleChange('bad_chars', e.target.value)}
|
|
fullWidth
|
|
size="small"
|
|
disabled={!options.replace_bad_chars}
|
|
/>
|
|
<FormControlLabel
|
|
control={
|
|
<Switch
|
|
checked={options.skip_existing}
|
|
onChange={(e) => handleChange('skip_existing', e.target.checked)}
|
|
/>
|
|
}
|
|
label="Skip existing files"
|
|
/>
|
|
</Box>
|
|
</Section>
|
|
|
|
{/* Metadata Section */}
|
|
<Section title="Metadata Settings">
|
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
|
<TextField
|
|
label="Album"
|
|
value={options.album}
|
|
onChange={(e) => handleChange('album', e.target.value)}
|
|
fullWidth
|
|
size="small"
|
|
/>
|
|
<TextField
|
|
label="Comment"
|
|
value={options.comment}
|
|
onChange={(e) => handleChange('comment', e.target.value)}
|
|
fullWidth
|
|
size="small"
|
|
/>
|
|
<FormControlLabel
|
|
control={
|
|
<Switch
|
|
checked={options.no_comment}
|
|
onChange={(e) => handleChange('no_comment', e.target.checked)}
|
|
/>
|
|
}
|
|
label="No comment"
|
|
/>
|
|
<TextField
|
|
label="Comment stream index"
|
|
type="number"
|
|
value={options.comment_stream ?? ''}
|
|
onChange={(e) =>
|
|
handleChange('comment_stream', e.target.value === '' ? null : parseInt(e.target.value))
|
|
}
|
|
size="small"
|
|
disabled={options.no_comment}
|
|
/>
|
|
<FormControlLabel
|
|
control={
|
|
<Switch
|
|
checked={options.merge_comments}
|
|
onChange={(e) => handleChange('merge_comments', e.target.checked)}
|
|
/>
|
|
}
|
|
label="Merge all comments"
|
|
disabled={options.no_comment}
|
|
/>
|
|
<TextField
|
|
label="Comment separator"
|
|
value={options.comment_separator}
|
|
onChange={(e) => handleChange('comment_separator', e.target.value)}
|
|
size="small"
|
|
disabled={!options.merge_comments || options.no_comment}
|
|
/>
|
|
</Box>
|
|
</Section>
|
|
</Paper>
|
|
)
|
|
}
|
|
|
|
|