GUI update baseline

This commit is contained in:
DAREKON 2024-02-16 17:57:24 +01:00
parent 5417db41fd
commit f0cf404c1d
18 changed files with 580 additions and 123 deletions

View file

@ -40,7 +40,7 @@ export type QueueItem = {
q: number, q: number,
dlVideoOnce: boolean, dlVideoOnce: boolean,
dubLang: string[], dubLang: string[],
image: string image: string,
} & ResolveItemsData } & ResolveItemsData
export type ResolveItemsData = { export type ResolveItemsData = {
@ -106,7 +106,9 @@ export type FuniStreamData = { force?: 'Y'|'y'|'N'|'n'|'C'|'c', callbackMaker?:
forceMuxer: AvailableMuxer | undefined, simul: boolean, skipSubMux: boolean, nocleanup: boolean, override: string[], videoTitle: string, forceMuxer: AvailableMuxer | undefined, simul: boolean, skipSubMux: boolean, nocleanup: boolean, override: string[], videoTitle: string,
ffmpegOptions: string[], mkvmergeOptions: string[], defaultAudio: LanguageItem, defaultSub: LanguageItem, ccTag: string } ffmpegOptions: string[], mkvmergeOptions: string[], defaultAudio: LanguageItem, defaultSub: LanguageItem, ccTag: string }
export type FuniSubsData = { nosubs?: boolean, sub: boolean, dlsubs: string[], ccTag: string } export type FuniSubsData = { nosubs?: boolean, sub: boolean, dlsubs: string[], ccTag: string }
export type DownloadData = { id: string, e: string, dubLang: string[], dlsubs: string[], fileName: string, q: number, novids: boolean, noaudio: boolean, dlVideoOnce: boolean } export type DownloadData = {
hslang?: string; id: string, e: string, dubLang: string[], dlsubs: string[], fileName: string, q: number, novids: boolean, noaudio: boolean, dlVideoOnce: boolean
}
export type AuthResponse = ResponseBase<undefined>; export type AuthResponse = ResponseBase<undefined>;
export type FuniSearchReponse = ResponseBase<FunimationSearch>; export type FuniSearchReponse = ResponseBase<FunimationSearch>;

BIN
gui.diff Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -1,6 +1,8 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>Multi Downloader</title>
<link rel="icon" type="image/webp" href="favicon.webp">
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta <meta
http-equiv="Content-Security-Policy" http-equiv="Content-Security-Policy"

BIN
gui/react/react.7z Normal file

Binary file not shown.

View file

@ -13,15 +13,20 @@ const Layout: React.FC = () => {
const messageHandler = React.useContext(messageChannelContext); const messageHandler = React.useContext(messageChannelContext);
return <Box sx={{ display: 'flex', flexDirection: 'column' }}> return <Box sx={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center'}}>
<MenuBar /> <MenuBar />
<Box sx={{ height: 50, mb: 4, display: 'flex', gap: 1, mt: 3 }}> <Box sx={{
height: 50,
width: '95rem',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
//backgroundColor: '#ffffff',
}}>
<LogoutButton /> <LogoutButton />
<AuthButton /> <AuthButton />
<Box sx={{ display: 'flex', gap: 1, height: 36 }}> <Button variant="contained" startIcon={<Folder />} onClick={() => messageHandler?.openFolder('content')} sx={{ height: '37px' }}>Open Output Directory</Button>
<Button variant="contained" startIcon={<Folder />} onClick={() => messageHandler?.openFolder('content')}>Open Output Directory</Button> <Button variant="contained" startIcon={<ClearAll />} onClick={() => messageHandler?.clearQueue() } sx={{ height: '37px' }}>Clear Queue</Button>
<Button variant="contained" startIcon={<ClearAll />} onClick={() => messageHandler?.clearQueue() }>Clear Queue</Button>
</Box>
<AddToQueue /> <AddToQueue />
<StartQueueButton /> <StartQueueButton />
</Box> </Box>

View file

@ -11,10 +11,8 @@ const makeTheme = (mode: 'dark'|'light') : Partial<Theme> => {
const Style: FCWithChildren = ({children}) => { const Style: FCWithChildren = ({children}) => {
return <ThemeProvider theme={makeTheme('dark')}> return <ThemeProvider theme={makeTheme('dark')}>
<Container maxWidth='xl'>
<Box sx={{ position: 'fixed', height: '100%', width: '100%', zIndex: -500, backgroundColor: 'rgb(0, 30, 60)', top: 0, left: 0 }}/> <Box sx={{ position: 'fixed', height: '100%', width: '100%', zIndex: -500, backgroundColor: 'rgb(0, 30, 60)', top: 0, left: 0 }}/>
{children} {children}
</Container>
</ThemeProvider>; </ThemeProvider>;
}; };

View file

@ -1,5 +1,5 @@
import { Add } from '@mui/icons-material'; import { Add } from '@mui/icons-material';
import { Box, Button, Dialog, Divider } from '@mui/material'; import { Box, Button, Dialog, Divider, Typography } from '@mui/material';
import React from 'react'; import React from 'react';
import DownloadSelector from './DownloadSelector/DownloadSelector'; import DownloadSelector from './DownloadSelector/DownloadSelector';
import EpisodeListing from './DownloadSelector/Listing/EpisodeListing'; import EpisodeListing from './DownloadSelector/Listing/EpisodeListing';
@ -10,14 +10,14 @@ const AddToQueue: React.FC = () => {
return <Box> return <Box>
<EpisodeListing /> <EpisodeListing />
<Dialog open={isOpen} onClose={() => setOpen(false)} maxWidth='md'> <Dialog open={isOpen} onClose={() => setOpen(false)} maxWidth='md' PaperProps={{ elevation:4 }}>
<Box sx={{ border: '2px solid white', p: 2 }}> <Box>
<SearchBox /> <SearchBox />
<Divider variant='middle' className="divider-width" light sx={{ color: 'text.primary', fontSize: '1.2rem' }}>Options</Divider> <Divider variant='middle'/>
<DownloadSelector onFinish={() => setOpen(false)} /> <DownloadSelector onFinish={() => setOpen(false)} />
</Box> </Box>
</Dialog> </Dialog>
<Button variant='contained' onClick={() => setOpen(true)}> <Button variant='contained' onClick={() => setOpen(true)}>
<Add /> <Add />
Add to Queue Add to Queue
</Button> </Button>

View file

@ -1,10 +1,11 @@
import React from 'react'; import React from 'react';
import { Box, Button, TextField } from '@mui/material'; import { Box, Button, Divider, InputBase, Link, MenuItem, Select, TextField, Tooltip, Typography } from '@mui/material';
import useStore from '../../../hooks/useStore'; import useStore from '../../../hooks/useStore';
import MultiSelect from '../../reusable/MultiSelect'; import MultiSelect from '../../reusable/MultiSelect';
import { messageChannelContext } from '../../../provider/MessageChannel'; import { messageChannelContext } from '../../../provider/MessageChannel';
import LoadingButton from '@mui/lab/LoadingButton'; import LoadingButton from '@mui/lab/LoadingButton';
import { useSnackbar } from 'notistack'; import { useSnackbar } from 'notistack';
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
type DownloadSelectorProps = { type DownloadSelectorProps = {
onFinish?: () => unknown onFinish?: () => unknown
@ -78,14 +79,36 @@ const DownloadSelector: React.FC<DownloadSelectorProps> = ({ onFinish }) => {
setLoading(false); setLoading(false);
}; };
return <Box sx={{ display: 'flex', flexDirection: 'column' }}> return <Box sx={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center' }}>
<Box sx={{ m: 2, gap: 1, display: 'flex', justifyContent: 'center', alignItems: 'center', flexWrap: 'wrap' }}> <Box sx={{display: 'flex',
flexDirection: 'column',
alignItems: 'center',
margin: '5px',
}}>
<Box sx={{
width: '50rem',
height: '21rem',
margin: '10px',
display: 'flex',
justifyContent: 'space-between',
//backgroundColor: '#ffffff30',
}}>
<Box sx={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: '0.7rem',
//backgroundColor: '#ff000030'
}}>
<Typography sx={{fontSize: '1.4rem'}}>
General Options
</Typography>
<TextField value={store.downloadOptions.id} required onChange={e => { <TextField value={store.downloadOptions.id} required onChange={e => {
dispatch({ dispatch({
type: 'downloadOptions', type: 'downloadOptions',
payload: { ...store.downloadOptions, id: e.target.value } payload: { ...store.downloadOptions, id: e.target.value }
}); });
}} label='Item ID' /> }} label='Show ID'/>
<TextField type='number' value={store.downloadOptions.q} required onChange={e => { <TextField type='number' value={store.downloadOptions.q} required onChange={e => {
const parsed = parseInt(e.target.value); const parsed = parseInt(e.target.value);
if (isNaN(parsed) || parsed < 0 || parsed > 10) if (isNaN(parsed) || parsed < 0 || parsed > 10)
@ -94,13 +117,77 @@ const DownloadSelector: React.FC<DownloadSelectorProps> = ({ onFinish }) => {
type: 'downloadOptions', type: 'downloadOptions',
payload: { ...store.downloadOptions, q: parsed } payload: { ...store.downloadOptions, q: parsed }
}); });
}} label='Quality Level (0 for max)' /> }} label='Quality Level (0 for max)'/>
<TextField disabled={store.downloadOptions.all} value={store.downloadOptions.e} required onChange={e => { <Box sx={{ display: 'flex', gap: '5px' }}>
dispatch({ <Button sx={{ textTransform: 'none'}} onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, noaudio: !store.downloadOptions.noaudio } })} variant={store.downloadOptions.noaudio ? 'contained' : 'outlined'}>Skip Audio</Button>
type: 'downloadOptions', <Button sx={{ textTransform: 'none'}} onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, novids: !store.downloadOptions.novids } })} variant={store.downloadOptions.novids ? 'contained' : 'outlined'}>Skip Video</Button>
payload: { ...store.downloadOptions, e: e.target.value } </Box>
}); <Button sx={{ textTransform: 'none'}} onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, dlVideoOnce: !store.downloadOptions.dlVideoOnce } })} variant={store.downloadOptions.dlVideoOnce ? 'contained' : 'outlined'}>Skip Unnecessary</Button>
}} label='Episode Select' /> <Tooltip title={
<Typography>
Currently only supported on Hidive
</Typography>
}
arrow
placement='top'>
<Button sx={{ textTransform: 'none'}} onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, simul: !store.downloadOptions.simul } })} variant={store.downloadOptions.simul ? 'contained' : 'outlined'}>Download Simulcast ver.</Button>
</Tooltip>
</Box>
<Box sx={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: '0.7rem',
//backgroundColor: '#00000020'
}}>
<Typography sx={{fontSize: '1.4rem'}}>
Episode Options
</Typography>
<Box sx={{
display: 'flex',
flexDirection: 'column',
gap: '1px'
}}>
<Box sx={{
borderColor: '#595959',
borderStyle: 'solid',
borderWidth: '1px',
borderRadius: '5px',
//backgroundColor: '#ff4567',
width: '15rem',
height: '3.5rem',
display: 'flex',
'&:hover' : {
borderColor: '#ffffff',
},
}}>
<InputBase sx={{
ml: 2,
flex: 1,
}}
disabled={store.downloadOptions.all} value={store.downloadOptions.e} required onChange={e => {
dispatch({
type: 'downloadOptions',
payload: { ...store.downloadOptions, e: e.target.value }
});
}} placeholder='Episode Select'/>
<Divider orientation='vertical'/>
<LoadingButton loading={loading} disableElevation disableFocusRipple disableRipple disableTouchRipple onClick={listEpisodes} variant='text' sx={{ textTransform: 'none'}}><Typography>List<br/>Episodes</Typography></LoadingButton>
</Box>
</Box>
<Button sx={{ textTransform: 'none'}} onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, all: !store.downloadOptions.all } })} variant={store.downloadOptions.all ? 'contained' : 'outlined'}>Download All</Button>
<Button sx={{ textTransform: 'none'}} onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, but: !store.downloadOptions.but } })} variant={store.downloadOptions.but ? 'contained' : 'outlined'}>Download All but</Button>
</Box>
<Box sx={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: '0.7rem',
//backgroundColor: '#00ff0020'
}}>
<Typography sx={{fontSize: '1.4rem'}}>
Language Options
</Typography>
<MultiSelect <MultiSelect
title='Dub Languages' title='Dub Languages'
values={availableDubs} values={availableDubs}
@ -113,6 +200,7 @@ const DownloadSelector: React.FC<DownloadSelectorProps> = ({ onFinish }) => {
}} }}
allOption allOption
/> />
<MultiSelect <MultiSelect
title='Sub Languages' title='Sub Languages'
values={availableSubs} values={availableSubs}
@ -124,22 +212,105 @@ const DownloadSelector: React.FC<DownloadSelectorProps> = ({ onFinish }) => {
}); });
}} }}
/> />
<TextField value={store.downloadOptions.fileName} onChange={e => { <Tooltip title={
<Typography>
Comming Soon
</Typography>
}
arrow placement='top'>
<Box sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '100%',
gap: '1rem'
}}>
<Box sx={{
borderColor: '#595959',
borderStyle: 'solid',
borderWidth: '1px',
borderRadius: '5px',
//backgroundColor: '#ff4567',
width: '15rem',
height: '3.5rem',
display: 'flex',
'&:hover' : {
borderColor: '#ffffff',
},
}}>
<Button sx={{ textTransform: 'none' }} variant='outlined' disabled={true}>Hardsub</Button>
<Divider orientation='vertical'/>
<Select sx={{
flex: 1
}}
title='Hardsub lang.'
placeholder='Hardsub lang.'
disabled={true}
value={store.downloadOptions.hslang}
onChange={(e) => {
dispatch({
type: 'downloadOptions',
payload: { ...store.downloadOptions, hslang: (e.target.value as string) === '' ? undefined : e.target.value as string }
});
}}
>
<MenuItem>Deutsch</MenuItem>
</Select>
</Box>
<Tooltip title={
<Typography>
Burns the selected subtitle <b>PERMANENTLY</b> onto the video<br/>You can choose only <b>1</b> subtitle per video
</Typography>
} arrow placement='top'>
<InfoOutlinedIcon sx={{
transition: '100ms',
ml: '0.35rem',
mr: '0.65rem',
'&:hover' : {
color: '#ffffff30',
}
}} />
</Tooltip>
</Box>
</Tooltip>
</Box>
</Box>
<Box sx={{width: '95%', height: '0.3rem', backgroundColor: '#ffffff50', borderRadius: '10px', marginBottom: '20px'}}/>
<Box sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '100%',
gap: '15px'
}}>
<TextField value={store.downloadOptions.fileName} onChange={e => {
dispatch({ dispatch({
type: 'downloadOptions', type: 'downloadOptions',
payload: { ...store.downloadOptions, fileName: e.target.value } payload: { ...store.downloadOptions, fileName: e.target.value }
}); });
}} sx={{ width: '50%' }} label='Filename' /> }} sx={{ width: '87%' }} label='Filename Overwrite' />
<Button onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, all: !store.downloadOptions.all } })} variant={store.downloadOptions.all ? 'contained' : 'outlined'}>Download all</Button> <Tooltip title={
<Button onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, but: !store.downloadOptions.but } })} variant={store.downloadOptions.but ? 'contained' : 'outlined'}>Download all but</Button> <Typography>
<Button onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, noaudio: !store.downloadOptions.noaudio } })} variant={store.downloadOptions.noaudio ? 'contained' : 'outlined'}>Skip Audio</Button> Click here to see the documentation
<Button onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, novids: !store.downloadOptions.novids } })} variant={store.downloadOptions.novids ? 'contained' : 'outlined'}>Skip Video</Button> </Typography>
<Button onClick={() => dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, dlVideoOnce: !store.downloadOptions.dlVideoOnce } })} variant={store.downloadOptions.dlVideoOnce ? 'contained' : 'outlined'}>Skip unnecessary Downloads</Button> } arrow placement='top'>
</Box> <Link href='https://github.com/anidl/multi-downloader-nx/blob/master/docs/DOCUMENTATION.md#filename-template' rel="noopener noreferrer" target="_blank">
<Box sx={{ gap: 2, flex: 0, m: 1, mb: 3, display: 'flex', justifyContent: 'center' }}> <InfoOutlinedIcon sx={{
<LoadingButton loading={loading} onClick={listEpisodes} variant='contained'>List episodes</LoadingButton> transition: '100ms',
<LoadingButton loading={loading} onClick={addToQueue} variant='contained'>Add to Queue</LoadingButton> '&:hover' : {
color: '#ffffff30',
}
}} />
</Link>
</Tooltip>
</Box>
</Box> </Box>
<Box sx={{width: '95%', height: '0.3rem', backgroundColor: '#ffffff50', borderRadius: '10px', marginTop: '10px'}}/>
<LoadingButton sx={{ margin: '15px', textTransform: 'none' }} loading={loading} onClick={addToQueue} variant='contained'>Add to Queue</LoadingButton>
</Box>; </Box>;
}; };

View file

@ -1,4 +0,0 @@
.divider-width::before, .divider-width::after {
border-top: 3px solid white !important;
transform: translateY(40%) !important;
}

View file

@ -1,10 +1,9 @@
import { Box } from '@mui/material'; import { Box } from '@mui/material';
import React from 'react'; import React from 'react';
import './MainFrame.css';
import Queue from './Queue/Queue'; import Queue from './Queue/Queue';
const MainFrame: React.FC = () => { const MainFrame: React.FC = () => {
return <Box sx={{ marginLeft: 1 }}> return <Box sx={{ }}>
<Queue /> <Queue />
</Box>; </Box>;
}; };

View file

@ -1,7 +1,8 @@
import { Box, Button, CircularProgress, Divider, LinearProgress, Skeleton, Typography } from '@mui/material'; import { Badge, Box, Button, CircularProgress, Divider, IconButton, LinearProgress, Skeleton, Tooltip, Typography } from '@mui/material';
import React from 'react'; import React from 'react';
import { messageChannelContext } from '../../../provider/MessageChannel'; import { messageChannelContext } from '../../../provider/MessageChannel';
import { queueContext } from '../../../provider/QueueProvider'; import { queueContext } from '../../../provider/QueueProvider';
import DeleteIcon from '@mui/icons-material/Delete';
import useDownloadManager from '../DownloadManager/DownloadManager'; import useDownloadManager from '../DownloadManager/DownloadManager';
@ -16,102 +17,384 @@ const Queue: React.FC = () => {
return data || queue.length > 0 ? <> return data || queue.length > 0 ? <>
{data && <> {data && <>
<Box sx={{ height: 200, display: 'grid', gridTemplateColumns: '20% 1fr', gap: 1, mb: 1, mt: 1 }}> <Box sx={{
<img src={data.downloadInfo.image} height='auto' width='100%' alt="Thumbnail" /> display: 'flex',
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}> width: '100%',
<Box sx={{ display: 'flex', flexDirection: 'column' }}> flexDirection: 'column',
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr max-content' }}> alignItems: 'center',
<Typography variant='h5' color='text.primary'> }}>
{data.downloadInfo.title} <Box sx={{
</Typography> marginTop: '2rem',
<Typography variant='h5' color='text.primary'> marginBottom: '1rem',
Language: {data.downloadInfo.language.name} height: '11rem',
</Typography> width: '95rem',
</Box> backgroundColor: '#282828',
<Typography variant='h6' color='text.primary'> boxShadow: '0px 0px 50px #00000090',
borderRadius: '10px',
display: 'flex',
transition: '250ms'
}}>
<img style={{
borderRadius: '5px',
margin: '5px',
boxShadow: '0px 0px 10px #00000090',
userSelect: 'none',
}}
src={data.downloadInfo.image} height='auto' width='auto' alt="Thumbnail" />
<Box
sx={{
display: 'flex',
flexDirection: 'column',
width: '100%',
justifyContent: 'center'
}}>
<Box sx={{
display: 'flex',
}}>
<Box sx={{
//backgroundColor: '#ff0000',
width: '70%',
marginLeft: '10px'
}}>
<Box sx={{
flexDirection: 'column',
display: 'flex',
justifyContent: 'space-between',
}}>
<Typography color='text.primary' sx={{
fontSize: '1.8rem',
}}>
{data.downloadInfo.parent.title} {data.downloadInfo.parent.title}
</Typography> </Typography>
<Typography color='text.primary' sx={{
fontSize: '1.2rem',
}}>
{data.downloadInfo.title}
</Typography>
</Box> </Box>
<LinearProgress variant='determinate' sx={{ height: '10px' }} value={(typeof data.progress.percent === 'string' ? parseInt(data.progress.percent) : data.progress.percent)} /> </Box>
<Box sx={{
//backgroundColor: '#00ff00',
width: '30%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
}}>
<Typography color='text.primary' sx={{
fontSize: '1.8rem',
}}>
Downloading: {data.downloadInfo.language.name}
</Typography>
</Box>
</Box>
<Box sx={{
height: '50%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
//backgroundColor: '#0000ff',
}}>
<LinearProgress variant='determinate'
sx={{
height: '20px',
width: '97.53%',
margin: '10px',
boxShadow: '0px 0px 10px #00000090',
borderRadius: '10px',
}} value={(typeof data.progress.percent === 'string' ? parseInt(data.progress.percent) : data.progress.percent)}
/>
<Box> <Box>
<Typography variant="body1" color='text.primary'> <Typography color='text.primary'
sx={{
fontSize: '1.3rem',
}}>
{data.progress.cur} / {(data.progress.total)} parts ({data.progress.percent}% | {formatTime(data.progress.time)} | {(data.progress.downloadSpeed / 1024 / 1024).toFixed(2)} MB/s | {(data.progress.bytes / 1024 / 1024).toFixed(2)}MB) {data.progress.cur} / {(data.progress.total)} parts ({data.progress.percent}% | {formatTime(data.progress.time)} | {(data.progress.downloadSpeed / 1024 / 1024).toFixed(2)} MB/s | {(data.progress.bytes / 1024 / 1024).toFixed(2)}MB)
</Typography> </Typography>
</Box> </Box>
</Box> </Box>
</Box>
</Box>
</Box> </Box>
</> </>
} }
{ {
current && !data && <> current && !data && <>
<Box sx={{ height: 200, display: 'grid', gridTemplateColumns: '20% 1fr', gap: 1, mb: 1, mt: 1 }}> <Box sx={{
<img src={current.image} height='auto' width='100%' alt="Thumbnail" /> display: 'flex',
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}> width: '100%',
<Box sx={{ display: 'flex', flexDirection: 'column' }}> flexDirection: 'column',
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr max-content' }}> alignItems: 'center',
<Typography variant='h5' color='text.primary'> }}>
{current.title} <Box sx={{
</Typography> marginTop: '2rem',
<Typography variant='h5' color='text.primary'> marginBottom: '1rem',
Language: <CircularProgress variant="indeterminate" /> height: '13rem',
</Typography> width: '93rem',
</Box> backgroundColor: '#282828',
<Typography variant='h6' color='text.primary'> boxShadow: '0px 0px 50px #00000090',
{current.parent.title} borderRadius: '10px',
</Typography> display: 'flex',
</Box> transition: '250ms'
<LinearProgress variant='indeterminate' sx={{ height: '10px' }} /> }}>
<Box> <img style={{
<Typography variant="body1" color='text.primary'> borderRadius: '5px',
0 / ? parts (0% | X:XX | 0 MB/s | 0MB) margin: '5px',
</Typography> boxShadow: '0px 0px 10px #00000090',
</Box> userSelect: 'none',
}}
src={current.image} height='auto' width='auto' alt="Thumbnail" />
<Box
sx={{
display: 'flex',
flexDirection: 'column',
width: '100%',
justifyContent: 'center'
}}>
<Box sx={{
display: 'flex',
}}>
<Box sx={{
//backgroundColor: '#ff0000',
width: '70%',
marginLeft: '10px'
}}>
<Box sx={{
flexDirection: 'column',
display: 'flex',
justifyContent: 'space-between',
}}>
<Typography color='text.primary' sx={{
fontSize: '1.8rem',
}}>
{current.parent.title}
</Typography>
<Typography color='text.primary' sx={{
fontSize: '1.2rem',
}}>
{current.title}
</Typography>
</Box> </Box>
</Box> </Box>
<Box sx={{
//backgroundColor: '#00ff00',
width: '30%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
}}>
<Box sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between'
}}>
<Typography color='text.primary' sx={{
fontSize: '1.8rem',
}}>
Downloading:
</Typography>
<CircularProgress variant="indeterminate" sx={{
marginLeft: '2rem',
}}/>
</Box>
</Box>
</Box>
<Box sx={{
height: '50%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
//backgroundColor: '#0000ff',
}}>
<LinearProgress variant='indeterminate'
sx={{
height: '20px',
width: '97.53%',
margin: '10px',
boxShadow: '0px 0px 10px #00000090',
borderRadius: '10px',
}}
/>
<Box>
<Typography color='text.primary'
sx={{
fontSize: '1.3rem',
}}>
0 / ? parts (0% | XX:XX | 0 MB/s | 0MB)
</Typography>
</Box>
</Box>
</Box>
</Box>
</Box>
</> </>
} }
{queue.length && data && <Divider variant="fullWidth" />}
{queue.map((queueItem, index, { length }) => { {queue.map((queueItem, index, { length }) => {
return <Box key={`queue_item_${index}`}> return <Box key={`queue_item_${index}`} sx={{
<Box sx={{ height: 200, display: 'grid', gridTemplateColumns: '20% 1fr', gap: 1, mb: 1, mt: 1 }}> display: 'flex',
<img src={queueItem.image} height='auto' width='100%' alt="Thumbnail" /> width: '100%',
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}> height: '12rem',
<Box sx={{ display: 'flex', flexDirection: 'column' }}> flexDirection: 'column',
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 200px' }}> alignItems: 'center',
<Typography variant='h5' color='text.primary'> }}>
{queueItem.title} <Box sx={{
marginTop: '1.5rem',
marginBottom: '1.5rem',
height: '11rem',
width: '90rem',
backgroundColor: '#282828',
boxShadow: '0px 0px 10px #00000090',
borderRadius: '10px',
display: 'flex',
}}>
<img style={{
borderRadius: '5px',
margin: '5px',
boxShadow: '0px 0px 5px #00000090',
userSelect: 'none',
}}
src={queueItem.image} height='auto' width='auto' alt="Thumbnail" />
<Box sx={{
margin: '5px',
display: 'flex',
justifyContent: 'space-between',
}}>
<Box sx={{
width: '30rem',
marginRight: '5px',
marginLeft: '5px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}>
<Typography color='text.primary' sx={{
fontSize: '1.8rem',
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
}}>
{queueItem.parent.title}
</Typography> </Typography>
<Typography variant='h5' color='text.primary'> <Typography color='text.primary' sx={{
Languages: {queueItem.dubLang.join(', ')} fontSize: '1.6rem',
marginTop: '-0.4rem',
marginBottom: '0.4rem',
}}>
S{queueItem.parent.season}E{queueItem.episode}
</Typography>
<Typography color='text.primary' sx={{
fontSize: '1.2rem',
marginTop: '-0.4rem',
marginBottom: '0.4rem',
textOverflow: 'ellipsis',
}}>
{queueItem.title}
</Typography>
</Box>
<Box sx={{
width: '30rem',
marginRight: '5px',
marginLeft: '5px',
display: 'flex',
flexDirection: 'column',
overflow: 'hidden',
whiteSpace: 'nowrap',
justifyContent: 'space-between',
}}>
<Typography color='text.primary' sx={{
fontSize: '1.8rem',
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
}}>
Dub(s): {queueItem.dubLang.join(', ')}
</Typography>
<Typography color='text.primary' sx={{
fontSize: '1.8rem',
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
}}>
Sub(s): {queueItem.dlsubs.join(', ')}
</Typography>
<Typography color='text.primary' sx={{
fontSize: '1.8rem',
}}>
Quality: {queueItem.q}
</Typography> </Typography>
</Box> </Box>
<Typography variant='h6' color='text.primary'> <Box sx={{
{queueItem.parent.title} //backgroundColor: '#ffffff',
</Typography> marginRight: '5px',
</Box> marginLeft: '5px',
<Typography variant='body1' color='text.primary'> }}>
S{queueItem.parent.season}E{queueItem.episode} <br /> <Tooltip title="Delete from queue" arrow placement='top'>
Quality: {queueItem.q} <IconButton
</Typography> onClick={() => {
<Button onClick={() => {
msg.removeFromQueue(index); msg.removeFromQueue(index);
}} sx={{ position: 'relative', left: '50%', transform: 'translateX(-50%)', width: '60%' }} variant="outlined" color="warning"> }}
Remove from Queue sx={{
</Button> backgroundColor: '#ff573a25',
height: '40px',
width: '40px',
margin: '4rem',
transition: '250ms',
'&:hover' : {
backgroundColor: '#ff573a',
}
}}>
<DeleteIcon />
</IconButton>
</Tooltip>
</Box>
</Box>
</Box> </Box>
</Box> </Box>
{index < length - 1 && <Divider variant="fullWidth" />} ;
</Box>;
})} })}
</> : <Box> </> : <Box sx={{
<Typography color='text.primary' variant='h4'> display: 'flex',
width: '100%',
height: '12rem',
flexDirection: 'column',
alignItems: 'center',
}}>
<Typography color='text.primary' sx={{
fontSize: '2rem',
margin: '10px'
}}>
Selected episodes will be shown here Selected episodes will be shown here
</Typography> </Typography>
<Box sx={{ height: 200, display: 'grid', gridTemplateColumns: '20% 1fr', gap: 1 }}> <Box sx={{
<Skeleton variant='rectangular' height={'100%'}/> display: 'flex',
<Box sx={{ display: 'grid', gridTemplateRows: '33% 1fr', gap: 1 }}> margin: '10px'
<Skeleton variant='text' height={'100%'} /> }}>
<Skeleton variant='text' height={'100%'} /> <Skeleton variant='rectangular' height={'10rem'} width={'20rem'} sx={{ margin: '5px', borderRadius: '5px' }}/>
<Box sx={{
display: 'flex',
flexDirection: 'column',
}}>
<Skeleton variant='text' height={'100%'} width={'30rem'} sx={{ margin: '5px', borderRadius: '5px' }}/>
<Skeleton variant='text' height={'100%'} width={'30rem'} sx={{ margin: '5px', borderRadius: '5px' }}/>
</Box>
</Box>
<Box sx={{
display: 'flex',
margin: '10px'
}}>
<Skeleton variant='rectangular' height={'10rem'} width={'20rem'} sx={{ margin: '5px', borderRadius: '5px' }}/>
<Box sx={{
display: 'flex',
flexDirection: 'column',
}}>
<Skeleton variant='text' height={'100%'} width={'30rem'} sx={{ margin: '5px', borderRadius: '5px' }}/>
<Skeleton variant='text' height={'100%'} width={'30rem'} sx={{ margin: '5px', borderRadius: '5px' }}/>
</Box> </Box>
</Box> </Box>
</Box>; </Box>;

View file

@ -44,7 +44,7 @@ const MenuBar: React.FC = () => {
if (!msg) if (!msg)
return <></>; return <></>;
return <Box sx={{ width: '100%', display: 'flex' }}> return <Box sx={{ width: '95rem', display: 'flex', marginBottom: '1rem' }}>
<Button onClick={(e) => handleClick(e, 'settings')}> <Button onClick={(e) => handleClick(e, 'settings')}>
Settings Settings
</Button> </Button>
@ -108,7 +108,7 @@ const MenuBar: React.FC = () => {
Version: {store.version} Version: {store.version}
</MenuItem> </MenuItem>
</Menu> </Menu>
<Typography variant="h5" color="text.primary" component="div" align="center" sx={{flexGrow: 1}}> <Typography variant="h5" color="text.primary" sx={{ alignSelf: 'center', textAlign: 'center', width: '100%'}}>
{transformService(store.service)} {transformService(store.service)}
</Typography> </Typography>
</Box>; </Box>;

View file

@ -18,13 +18,11 @@ const ServiceProvider: FCWithChildren = ({ children }) => {
}; };
return service === undefined ? return service === undefined ?
<Box> <Box sx={{ justifyContent: 'center', alignItems: 'center', display: 'flex', flexDirection: 'column', height: '50rem'}}>
<Typography color="text.primary" variant='h3' sx={{ textAlign: 'center', mb: 5 }}>Please choose your service</Typography> <Typography color="text.primary" variant='h3' sx={{ textAlign: 'center', mb: 5 }}>Please select your service</Typography>
<Box sx={{ display: 'flex', gap: 2, justifyContent: 'center' }}> <Box sx={{ display: 'flex', gap: 2, justifyContent: 'center' }}>
<Button size='large' variant="contained" onClick={() => setService('funi')} startIcon={<Avatar src={'https://static.funimation.com/static/img/favicon.ico'} />}>Funimation</Button> <Button size='large' variant="contained" onClick={() => setService('funi')} startIcon={<Avatar src={'https://static.funimation.com/static/img/favicon.ico'} />}>Funimation</Button>
<Divider orientation='vertical' flexItem />
<Button size='large' variant="contained" onClick={() => setService('crunchy')} startIcon={<Avatar src={'https://static.crunchyroll.com/cxweb/assets/img/favicons/favicon-32x32.png'} />}>Crunchyroll</Button> <Button size='large' variant="contained" onClick={() => setService('crunchy')} startIcon={<Avatar src={'https://static.crunchyroll.com/cxweb/assets/img/favicons/favicon-32x32.png'} />}>Crunchyroll</Button>
<Divider orientation='vertical' flexItem />
<Button size='large' variant="contained" onClick={() => setService('hidive')} startIcon={<Avatar src={'https://www.hidive.com/favicon.ico'} />}>Hidive</Button> <Button size='large' variant="contained" onClick={() => setService('hidive')} startIcon={<Avatar src={'https://www.hidive.com/favicon.ico'} />}>Hidive</Button>
</Box> </Box>
</Box> </Box>

View file

@ -13,6 +13,8 @@ export type DownloadOptions = {
all: boolean, all: boolean,
but: boolean, but: boolean,
novids: boolean, novids: boolean,
hslang?: string,
simul: boolean,
noaudio: boolean noaudio: boolean
} }
@ -48,7 +50,8 @@ const initialState: StoreState = {
all: false, all: false,
but: false, but: false,
noaudio: false, noaudio: false,
novids: false novids: false,
simul: false
}, },
service: undefined, service: undefined,
episodeListing: [], episodeListing: [],

View file

@ -99,7 +99,7 @@ class CrunchyHandler extends Base implements MessageHandler {
if (res.isOk) { if (res.isOk) {
for (const select of res.value) { for (const select of res.value) {
if (!(await this.crunchy.downloadEpisode(select, {..._default, skipsubs: false, callbackMaker: this.makeProgressHandler.bind(this), q: data.q, fileName: data.fileName, dlsubs: data.dlsubs, dlVideoOnce: data.dlVideoOnce, force: 'y', if (!(await this.crunchy.downloadEpisode(select, {..._default, skipsubs: false, callbackMaker: this.makeProgressHandler.bind(this), q: data.q, fileName: data.fileName, dlsubs: data.dlsubs, dlVideoOnce: data.dlVideoOnce, force: 'y',
novids: data.novids }))) { novids: data.novids, hslang: data.hslang || '' }))) {
const er = new Error(`Unable to download episode ${data.e} from ${data.id}`); const er = new Error(`Unable to download episode ${data.e} from ${data.id}`);
er.name = 'Download error'; er.name = 'Download error';
this.alertError(er); this.alertError(er);

View file

@ -70,7 +70,7 @@ class FunimationHandler extends Base implements MessageHandler {
}, },
image: a.image, image: a.image,
e: a.episodeID, e: a.episodeID,
episode: a.epsiodeNumber, episode: a.epsiodeNumber
}; };
})); }));
return true; return true;

View file

@ -118,7 +118,7 @@ class HidiveHandler extends Base implements MessageHandler {
return this.alertError(new Error('Download failed upstream, check for additional logs')); return this.alertError(new Error('Download failed upstream, check for additional logs'));
for (const ep of res.value) { for (const ep of res.value) {
await this.hidive.getEpisode(ep, {..._default, callbackMaker: this.makeProgressHandler.bind(this), dubLang: data.dubLang, dlsubs: data.dlsubs, fileName: data.fileName, q: data.q, force: 'y', noaudio: data.noaudio, novids: data.novids}); await this.hidive.getEpisode(ep, {..._default, callbackMaker: this.makeProgressHandler.bind(this), dubLang: data.dubLang, dlsubs: data.dlsubs, fileName: data.fileName, q: data.q, force: 'y', noaudio: data.noaudio, novids: data.novids });
} }
this.sendMessage({ name: 'finish', data: undefined }); this.sendMessage({ name: 'finish', data: undefined });
this.setDownloading(false); this.setDownloading(false);