diff --git a/@types/messageHandler.d.ts b/@types/messageHandler.d.ts index 3929178..7e63da4 100644 --- a/@types/messageHandler.d.ts +++ b/@types/messageHandler.d.ts @@ -40,7 +40,7 @@ export type QueueItem = { q: number, dlVideoOnce: boolean, dubLang: string[], - image: string + image: string, } & 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, ffmpegOptions: string[], mkvmergeOptions: string[], defaultAudio: LanguageItem, defaultSub: LanguageItem, 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; export type FuniSearchReponse = ResponseBase; diff --git a/gui.diff b/gui.diff new file mode 100644 index 0000000..6ce327b Binary files /dev/null and b/gui.diff differ diff --git a/gui/react/public/favicon.webp b/gui/react/public/favicon.webp new file mode 100644 index 0000000..279efcf Binary files /dev/null and b/gui/react/public/favicon.webp differ diff --git a/gui/react/public/index.html b/gui/react/public/index.html index 92cc7d8..e0f99bb 100644 --- a/gui/react/public/index.html +++ b/gui/react/public/index.html @@ -1,6 +1,8 @@ + Multi Downloader + { const messageHandler = React.useContext(messageChannelContext); - return + return - + - - - - + + diff --git a/gui/react/src/Style.tsx b/gui/react/src/Style.tsx index 4f44a07..4f4306f 100644 --- a/gui/react/src/Style.tsx +++ b/gui/react/src/Style.tsx @@ -11,10 +11,8 @@ const makeTheme = (mode: 'dark'|'light') : Partial => { const Style: FCWithChildren = ({children}) => { return - {children} - ; }; diff --git a/gui/react/src/components/AddToQueue/AddToQueue.tsx b/gui/react/src/components/AddToQueue/AddToQueue.tsx index 98333aa..8bbd414 100644 --- a/gui/react/src/components/AddToQueue/AddToQueue.tsx +++ b/gui/react/src/components/AddToQueue/AddToQueue.tsx @@ -1,5 +1,5 @@ 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 DownloadSelector from './DownloadSelector/DownloadSelector'; import EpisodeListing from './DownloadSelector/Listing/EpisodeListing'; @@ -10,14 +10,14 @@ const AddToQueue: React.FC = () => { return - setOpen(false)} maxWidth='md'> - + setOpen(false)} maxWidth='md' PaperProps={{ elevation:4 }}> + - Options + setOpen(false)} /> - diff --git a/gui/react/src/components/AddToQueue/DownloadSelector/DownloadSelector.tsx b/gui/react/src/components/AddToQueue/DownloadSelector/DownloadSelector.tsx index 489b8f1..d1d5ed4 100644 --- a/gui/react/src/components/AddToQueue/DownloadSelector/DownloadSelector.tsx +++ b/gui/react/src/components/AddToQueue/DownloadSelector/DownloadSelector.tsx @@ -1,10 +1,11 @@ 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 MultiSelect from '../../reusable/MultiSelect'; import { messageChannelContext } from '../../../provider/MessageChannel'; import LoadingButton from '@mui/lab/LoadingButton'; import { useSnackbar } from 'notistack'; +import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; type DownloadSelectorProps = { onFinish?: () => unknown @@ -78,14 +79,36 @@ const DownloadSelector: React.FC = ({ onFinish }) => { setLoading(false); }; - return - + return + + + + + General Options + { dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, id: e.target.value } }); - }} label='Item ID' /> + }} label='Show ID'/> { const parsed = parseInt(e.target.value); if (isNaN(parsed) || parsed < 0 || parsed > 10) @@ -94,13 +117,77 @@ const DownloadSelector: React.FC = ({ onFinish }) => { type: 'downloadOptions', payload: { ...store.downloadOptions, q: parsed } }); - }} label='Quality Level (0 for max)' /> - { - dispatch({ - type: 'downloadOptions', - payload: { ...store.downloadOptions, e: e.target.value } - }); - }} label='Episode Select' /> + }} label='Quality Level (0 for max)'/> + + + + + + + Currently only supported on Hidive + + } + arrow + placement='top'> + + + + + + Episode Options + + + + { + dispatch({ + type: 'downloadOptions', + payload: { ...store.downloadOptions, e: e.target.value } + }); + }} placeholder='Episode Select'/> + + List
Episodes
+
+
+ + +
+ + + Language Options + = ({ onFinish }) => { }} allOption /> + = ({ onFinish }) => { }); }} /> - { + + Comming Soon™ + + } + arrow placement='top'> + + + + + + + + + + Burns the selected subtitle PERMANENTLY onto the video
You can choose only 1 subtitle per video + + } arrow placement='top'> + +
+
+
+
+
+ + + { dispatch({ type: 'downloadOptions', payload: { ...store.downloadOptions, fileName: e.target.value } }); - }} sx={{ width: '50%' }} label='Filename' /> - - - - - - - - List episodes - Add to Queue + }} sx={{ width: '87%' }} label='Filename Overwrite' /> + + Click here to see the documentation + + } arrow placement='top'> + + + + + + + + Add to Queue + ; }; diff --git a/gui/react/src/components/MainFrame/MainFrame.css b/gui/react/src/components/MainFrame/MainFrame.css deleted file mode 100644 index 2bbc1ad..0000000 --- a/gui/react/src/components/MainFrame/MainFrame.css +++ /dev/null @@ -1,4 +0,0 @@ -.divider-width::before, .divider-width::after { - border-top: 3px solid white !important; - transform: translateY(40%) !important; -} \ No newline at end of file diff --git a/gui/react/src/components/MainFrame/MainFrame.tsx b/gui/react/src/components/MainFrame/MainFrame.tsx index de4db33..425b321 100644 --- a/gui/react/src/components/MainFrame/MainFrame.tsx +++ b/gui/react/src/components/MainFrame/MainFrame.tsx @@ -1,10 +1,9 @@ import { Box } from '@mui/material'; import React from 'react'; -import './MainFrame.css'; import Queue from './Queue/Queue'; const MainFrame: React.FC = () => { - return + return ; }; diff --git a/gui/react/src/components/MainFrame/Queue/Queue.tsx b/gui/react/src/components/MainFrame/Queue/Queue.tsx index 5a79c9e..319d37e 100644 --- a/gui/react/src/components/MainFrame/Queue/Queue.tsx +++ b/gui/react/src/components/MainFrame/Queue/Queue.tsx @@ -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 { messageChannelContext } from '../../../provider/MessageChannel'; import { queueContext } from '../../../provider/QueueProvider'; +import DeleteIcon from '@mui/icons-material/Delete'; import useDownloadManager from '../DownloadManager/DownloadManager'; @@ -16,102 +17,384 @@ const Queue: React.FC = () => { return data || queue.length > 0 ? <> {data && <> - - Thumbnail - - - - - {data.downloadInfo.title} - - - Language: {data.downloadInfo.language.name} - - - + + + Thumbnail + + + + + {data.downloadInfo.parent.title} + + {data.downloadInfo.title} + - + + + + Downloading: {data.downloadInfo.language.name} + + + + + - + {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) + + } { current && !data && <> - - Thumbnail - - - - - {current.title} - - - Language: - - - - {current.parent.title} - - - - - - 0 / ? parts (0% | X:XX | 0 MB/s | 0MB) - - + + + Thumbnail + + + + + + {current.parent.title} + + + {current.title} + + + + + Downloading: + + + + + + + + + + 0 / ? parts (0% | XX:XX | 0 MB/s | 0MB) + + + + + + } - {queue.length && data && } {queue.map((queueItem, index, { length }) => { - return - - Thumbnail - - - - - {queueItem.title} + return + + Thumbnail + + + + {queueItem.parent.title} - - Languages: {queueItem.dubLang.join(', ')} + + S{queueItem.parent.season}E{queueItem.episode} + + + {queueItem.title} + + + + + Dub(s): {queueItem.dubLang.join(', ')} + + + Sub(s): {queueItem.dlsubs.join(', ')} + + + Quality: {queueItem.q} - - {queueItem.parent.title} - - - - S{queueItem.parent.season}E{queueItem.episode}
- Quality: {queueItem.q} -
- + }} + sx={{ + backgroundColor: '#ff573a25', + height: '40px', + width: '40px', + margin: '4rem', + transition: '250ms', + '&:hover' : { + backgroundColor: '#ff573a', + } + }}> + + + +
+
- {index < length - 1 && } -
; + ; })} - : - + : + Selected episodes will be shown here - - - - - + + + + + + + + + + + + ; diff --git a/gui/react/src/components/MenuBar/MenuBar.tsx b/gui/react/src/components/MenuBar/MenuBar.tsx index 5d74377..b1125a4 100644 --- a/gui/react/src/components/MenuBar/MenuBar.tsx +++ b/gui/react/src/components/MenuBar/MenuBar.tsx @@ -44,7 +44,7 @@ const MenuBar: React.FC = () => { if (!msg) return <>; - return + return @@ -108,7 +108,7 @@ const MenuBar: React.FC = () => { Version: {store.version} - + {transformService(store.service)} ; diff --git a/gui/react/src/provider/ServiceProvider.tsx b/gui/react/src/provider/ServiceProvider.tsx index 9aecfb5..ed862b1 100644 --- a/gui/react/src/provider/ServiceProvider.tsx +++ b/gui/react/src/provider/ServiceProvider.tsx @@ -18,13 +18,11 @@ const ServiceProvider: FCWithChildren = ({ children }) => { }; return service === undefined ? - - Please choose your service + + Please select your service - - diff --git a/gui/react/src/provider/Store.tsx b/gui/react/src/provider/Store.tsx index 5054051..c82ef92 100644 --- a/gui/react/src/provider/Store.tsx +++ b/gui/react/src/provider/Store.tsx @@ -13,6 +13,8 @@ export type DownloadOptions = { all: boolean, but: boolean, novids: boolean, + hslang?: string, + simul: boolean, noaudio: boolean } @@ -48,7 +50,8 @@ const initialState: StoreState = { all: false, but: false, noaudio: false, - novids: false + novids: false, + simul: false }, service: undefined, episodeListing: [], diff --git a/gui/server/services/crunchyroll.ts b/gui/server/services/crunchyroll.ts index 1b9f931..2cb635d 100644 --- a/gui/server/services/crunchyroll.ts +++ b/gui/server/services/crunchyroll.ts @@ -99,7 +99,7 @@ class CrunchyHandler extends Base implements MessageHandler { if (res.isOk) { 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', - novids: data.novids }))) { + novids: data.novids, hslang: data.hslang || '' }))) { const er = new Error(`Unable to download episode ${data.e} from ${data.id}`); er.name = 'Download error'; this.alertError(er); diff --git a/gui/server/services/funimation.ts b/gui/server/services/funimation.ts index bee900e..3bfc4eb 100644 --- a/gui/server/services/funimation.ts +++ b/gui/server/services/funimation.ts @@ -70,7 +70,7 @@ class FunimationHandler extends Base implements MessageHandler { }, image: a.image, e: a.episodeID, - episode: a.epsiodeNumber, + episode: a.epsiodeNumber }; })); return true; diff --git a/gui/server/services/hidive.ts b/gui/server/services/hidive.ts index f3da018..e07eea2 100644 --- a/gui/server/services/hidive.ts +++ b/gui/server/services/hidive.ts @@ -118,7 +118,7 @@ class HidiveHandler extends Base implements MessageHandler { return this.alertError(new Error('Download failed upstream, check for additional logs')); 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.setDownloading(false);