128 lines
3.9 KiB
TypeScript
128 lines
3.9 KiB
TypeScript
import React from 'react'
|
|
import { ThemeProvider, createTheme, CssBaseline } from '@mui/material'
|
|
import { Box, Grid, Button, CircularProgress, Typography } from '@mui/material'
|
|
import { PlayArrow } from '@mui/icons-material'
|
|
import { Layout } from './components/Layout'
|
|
import { UploadZone } from './components/UploadZone'
|
|
import { TracklistEditor } from './components/TracklistEditor'
|
|
import { OptionsPanel } from './components/OptionsPanel'
|
|
import { ProgressDisplay } from './components/ProgressDisplay'
|
|
import { DownloadSection } from './components/DownloadSection'
|
|
import { useUploadStore } from './stores/uploadStore'
|
|
import { useTracklistStore } from './stores/tracklistStore'
|
|
import { useOptionsStore } from './stores/optionsStore'
|
|
import { useTaskStore } from './stores/taskStore'
|
|
import { useUIStore } from './stores/uiStore'
|
|
import { useValidationStore } from './stores/validationStore'
|
|
import { useWebSocket } from './hooks/useWebSocket'
|
|
import { startSplit } from './api/client'
|
|
|
|
const App: React.FC = () => {
|
|
const { theme } = useUIStore()
|
|
const { taskId } = useUploadStore()
|
|
const { entries, isValid } = useTracklistStore()
|
|
const { options } = useOptionsStore()
|
|
const {
|
|
isProcessing,
|
|
setTaskId,
|
|
setError,
|
|
setIsProcessing,
|
|
addLog,
|
|
reset,
|
|
} = useTaskStore()
|
|
const { formatError } = useValidationStore()
|
|
|
|
useWebSocket(taskId && isProcessing ? taskId : null)
|
|
|
|
const handleSplit = async () => {
|
|
if (!taskId) {
|
|
alert('Please upload a file first')
|
|
return
|
|
}
|
|
|
|
if (!isValid) {
|
|
alert('Tracklist has errors. Please fix them before splitting.')
|
|
return
|
|
}
|
|
|
|
if (entries.length === 0) {
|
|
alert('Tracklist is empty')
|
|
return
|
|
}
|
|
|
|
try {
|
|
setTaskId(taskId)
|
|
setIsProcessing(true)
|
|
addLog('🚀 Starting split...')
|
|
|
|
const response = await startSplit(taskId, entries, options)
|
|
addLog(`✅ Split task started (ID: ${response.task_id})`)
|
|
} catch (error: any) {
|
|
setError(error.response?.data?.detail || error.message || 'Failed to start split')
|
|
addLog(`❌ Error: ${error.response?.data?.detail || error.message || 'Failed to start split'}`)
|
|
setIsProcessing(false)
|
|
}
|
|
}
|
|
|
|
const handleReset = () => {
|
|
reset()
|
|
}
|
|
|
|
const isSplitDisabled = !taskId || !isValid || entries.length === 0 || isProcessing || !!formatError
|
|
|
|
return (
|
|
<ThemeProvider
|
|
theme={createTheme({
|
|
palette: {
|
|
mode: theme,
|
|
},
|
|
})}
|
|
>
|
|
<CssBaseline />
|
|
<Layout>
|
|
<Grid container spacing={3}>
|
|
<Grid item xs={12} md={6}>
|
|
<UploadZone />
|
|
</Grid>
|
|
<Grid item xs={12} md={6}>
|
|
<TracklistEditor />
|
|
</Grid>
|
|
<Grid item xs={12} md={4}>
|
|
<OptionsPanel />
|
|
</Grid>
|
|
<Grid item xs={12} md={8}>
|
|
<Box sx={{ display: 'flex', gap: 2, mb: 3 }}>
|
|
<Button
|
|
variant="contained"
|
|
color="success"
|
|
startIcon={isProcessing ? <CircularProgress size={20} color="inherit" /> : <PlayArrow />}
|
|
onClick={handleSplit}
|
|
disabled={isSplitDisabled}
|
|
sx={{ flex: 1 }}
|
|
>
|
|
{isProcessing ? 'Processing...' : 'Split'}
|
|
</Button>
|
|
<Button variant="outlined" color="secondary" onClick={handleReset} disabled={isProcessing}>
|
|
Reset
|
|
</Button>
|
|
</Box>
|
|
|
|
{formatError && (
|
|
<Box sx={{ mb: 2, p: 2, bgcolor: 'warning.light', borderRadius: 1 }}>
|
|
<Typography color="warning.dark" variant="body2">
|
|
⚠️ {formatError}
|
|
</Typography>
|
|
</Box>
|
|
)}
|
|
|
|
<ProgressDisplay />
|
|
<DownloadSection />
|
|
</Grid>
|
|
</Grid>
|
|
</Layout>
|
|
</ThemeProvider>
|
|
)
|
|
}
|
|
|
|
export default App
|