code and layout refactoring
This commit is contained in:
@@ -207,7 +207,7 @@ const DownloadDialog: FC<Props> = ({ open, onClose, onDownloadStart }) => {
|
|||||||
backgroundColor: (theme) => theme.palette.background.default,
|
backgroundColor: (theme) => theme.palette.background.default,
|
||||||
minHeight: (theme) => `calc(99vh - ${theme.mixins.toolbar.minHeight}px)`
|
minHeight: (theme) => `calc(99vh - ${theme.mixins.toolbar.minHeight}px)`
|
||||||
}}>
|
}}>
|
||||||
<Container sx={{ my: 4 }} >
|
<Container sx={{ my: 4 }}>
|
||||||
<Grid container spacing={2}>
|
<Grid container spacing={2}>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Paper
|
<Paper
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import { loadingDownloadsState } from '../atoms/downloads'
|
|||||||
import { listViewState } from '../atoms/settings'
|
import { listViewState } from '../atoms/settings'
|
||||||
import { loadingAtom } from '../atoms/ui'
|
import { loadingAtom } from '../atoms/ui'
|
||||||
import DownloadsCardView from './DownloadsCardView'
|
import DownloadsCardView from './DownloadsCardView'
|
||||||
import DownloadsListView from './DownloadsTableView'
|
import DownloadsTableView from './DownloadsTableView'
|
||||||
|
|
||||||
const Downloads: React.FC = () => {
|
const Downloads: React.FC = () => {
|
||||||
const listView = useRecoilValue(listViewState)
|
const tableView = useRecoilValue(listViewState)
|
||||||
const loadingDownloads = useRecoilValue(loadingDownloadsState)
|
const loadingDownloads = useRecoilValue(loadingDownloadsState)
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useRecoilState(loadingAtom)
|
const [isLoading, setIsLoading] = useRecoilState(loadingAtom)
|
||||||
@@ -20,15 +20,9 @@ const Downloads: React.FC = () => {
|
|||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}, [loadingDownloads, isLoading])
|
}, [loadingDownloads, isLoading])
|
||||||
|
|
||||||
if (listView) {
|
if (tableView) return <DownloadsTableView />
|
||||||
return (
|
|
||||||
<DownloadsListView />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return <DownloadsCardView />
|
||||||
<DownloadsCardView />
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Downloads
|
export default Downloads
|
||||||
@@ -16,10 +16,10 @@ const DownloadsCardView: React.FC = () => {
|
|||||||
const abort = (id: string) => client.kill(id)
|
const abort = (id: string) => client.kill(id)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={{ xs: 2, md: 2 }} columns={{ xs: 4, sm: 8, md: 12 }} pt={2}>
|
<Grid container spacing={{ xs: 2, md: 2 }} columns={{ xs: 4, sm: 8, md: 12, xl: 12 }} pt={2}>
|
||||||
{
|
{
|
||||||
downloads.map(download => (
|
downloads.map(download => (
|
||||||
<Grid item xs={4} sm={8} md={6} key={download.id}>
|
<Grid item xs={4} sm={8} md={6} xl={4} key={download.id}>
|
||||||
<DownloadCard
|
<DownloadCard
|
||||||
download={download}
|
download={download}
|
||||||
onStop={() => abort(download.id)}
|
onStop={() => abort(download.id)}
|
||||||
|
|||||||
@@ -45,12 +45,8 @@ const DownloadsTableView: React.FC = () => {
|
|||||||
const abort = (id: string) => client.kill(id)
|
const abort = (id: string) => client.kill(id)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={{ xs: 2, md: 2 }} columns={{ xs: 4, sm: 8, md: 12 }} pt={2}>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<TableContainer
|
<TableContainer
|
||||||
component={Paper}
|
sx={{ minHeight: '80vh', mt: 4 }}
|
||||||
sx={{ minHeight: '80vh' }}
|
|
||||||
elevation={2}
|
|
||||||
hidden={downloads.length === 0}
|
hidden={downloads.length === 0}
|
||||||
>
|
>
|
||||||
<Table size="small">
|
<Table size="small">
|
||||||
@@ -126,8 +122,6 @@ const DownloadsTableView: React.FC = () => {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</TableContainer>
|
</TableContainer>
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { Box, Container, Paper, Typography } from '@mui/material'
|
|
||||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { useRecoilValue } from 'recoil'
|
import { useRecoilValue } from 'recoil'
|
||||||
import { serverURL } from '../atoms/settings'
|
import { serverURL } from '../atoms/settings'
|
||||||
@@ -7,14 +6,15 @@ import { useI18n } from '../hooks/useI18n'
|
|||||||
const token = localStorage.getItem('token')
|
const token = localStorage.getItem('token')
|
||||||
|
|
||||||
const LogTerminal: React.FC = () => {
|
const LogTerminal: React.FC = () => {
|
||||||
|
const [logBuffer, setLogBuffer] = useState<string[]>([])
|
||||||
|
const [isConnecting, setIsConnecting] = useState(true)
|
||||||
|
|
||||||
|
const boxRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
const serverAddr = useRecoilValue(serverURL)
|
const serverAddr = useRecoilValue(serverURL)
|
||||||
|
|
||||||
const { i18n } = useI18n()
|
const { i18n } = useI18n()
|
||||||
|
|
||||||
const [logBuffer, setLogBuffer] = useState<string[]>([])
|
|
||||||
|
|
||||||
const boxRef = useRef<HTMLDivElement>(null)
|
|
||||||
|
|
||||||
const eventSource = useMemo(
|
const eventSource = useMemo(
|
||||||
() => new EventSource(`${serverAddr}/log/sse?token=${token}`),
|
() => new EventSource(`${serverAddr}/log/sse?token=${token}`),
|
||||||
[serverAddr]
|
[serverAddr]
|
||||||
@@ -23,7 +23,7 @@ const LogTerminal: React.FC = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
eventSource.addEventListener('log', event => {
|
eventSource.addEventListener('log', event => {
|
||||||
const msg: string[] = JSON.parse(event.data)
|
const msg: string[] = JSON.parse(event.data)
|
||||||
setLogBuffer(buff => [...buff, ...msg].slice(-100))
|
setLogBuffer(buff => [...buff, ...msg].slice(-500))
|
||||||
|
|
||||||
boxRef.current?.scrollTo(0, boxRef.current.scrollHeight)
|
boxRef.current?.scrollTo(0, boxRef.current.scrollHeight)
|
||||||
})
|
})
|
||||||
@@ -32,6 +32,10 @@ const LogTerminal: React.FC = () => {
|
|||||||
return () => eventSource.close()
|
return () => eventSource.close()
|
||||||
}, [eventSource])
|
}, [eventSource])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
eventSource.onopen = () => setIsConnecting(false)
|
||||||
|
}, [eventSource])
|
||||||
|
|
||||||
const logEntryStyle = (data: string) => {
|
const logEntryStyle = (data: string) => {
|
||||||
const sx = {}
|
const sx = {}
|
||||||
|
|
||||||
@@ -46,20 +50,10 @@ const LogTerminal: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container maxWidth="lg" sx={{ mt: 4, mb: 4 }}>
|
|
||||||
<Paper
|
<div
|
||||||
sx={{
|
|
||||||
p: 2.5,
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography py={1} variant="h5" color="primary">
|
|
||||||
{i18n.t('logsTitle')}
|
|
||||||
</Typography>
|
|
||||||
<Box
|
|
||||||
ref={boxRef}
|
ref={boxRef}
|
||||||
sx={{
|
style={{
|
||||||
fontFamily: 'Roboto Mono',
|
fontFamily: 'Roboto Mono',
|
||||||
height: '70.5vh',
|
height: '70.5vh',
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
@@ -72,15 +66,17 @@ const LogTerminal: React.FC = () => {
|
|||||||
borderRadius: '0.25rem'
|
borderRadius: '0.25rem'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{logBuffer.length === 0 && <Box >{i18n.t('awaitingLogs')}</Box>}
|
{isConnecting ? <div>{'Connecting...'}</div> : <div>{'Connected!'}</div>}
|
||||||
|
|
||||||
|
{logBuffer.length === 0 && <div>{i18n.t('awaitingLogs')}</div>}
|
||||||
|
|
||||||
{logBuffer.map((log, idx) => (
|
{logBuffer.map((log, idx) => (
|
||||||
<Box key={idx} sx={logEntryStyle(log)}>
|
<div key={idx} style={logEntryStyle(log)}>
|
||||||
{log}
|
{log}
|
||||||
</Box>
|
</div>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</div>
|
||||||
</Paper>
|
|
||||||
</Container >
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ export default function Downloaded() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Container
|
<Container
|
||||||
maxWidth="lg"
|
maxWidth="xl"
|
||||||
sx={{ mt: 4, mb: 4, height: '100%' }}
|
sx={{ mt: 4, mb: 4, height: '100%' }}
|
||||||
onClick={() => setShowMenu(false)}
|
onClick={() => setShowMenu(false)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import Splash from '../components/Splash'
|
|||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<Container maxWidth="lg" sx={{ mt: 2, mb: 8 }}>
|
<Container maxWidth="xl" sx={{ mt: 2, mb: 8 }}>
|
||||||
<LoadingBackdrop />
|
<LoadingBackdrop />
|
||||||
<Splash />
|
<Splash />
|
||||||
<Downloads />
|
<Downloads />
|
||||||
|
|||||||
@@ -134,12 +134,12 @@ export default function Settings() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container maxWidth="lg" sx={{ mt: 4, mb: 8 }}>
|
<Container maxWidth="xl" sx={{ mt: 4, mb: 8 }}>
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
<Grid item xs={12} md={12} lg={12}>
|
<Grid item xs={12} md={12} lg={12}>
|
||||||
<Paper
|
<Paper
|
||||||
sx={{
|
sx={{
|
||||||
p: 2,
|
p: 2.5,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
minHeight: 240,
|
minHeight: 240,
|
||||||
|
|||||||
@@ -1,8 +1,25 @@
|
|||||||
|
import { Container, Paper, Typography } from '@mui/material'
|
||||||
import LogTerminal from '../components/LogTerminal'
|
import LogTerminal from '../components/LogTerminal'
|
||||||
|
import { useI18n } from '../hooks/useI18n'
|
||||||
|
|
||||||
const Terminal: React.FC = () => {
|
const Terminal: React.FC = () => {
|
||||||
|
const { i18n } = useI18n()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Container maxWidth="xl" sx={{ mt: 4, mb: 4 }}>
|
||||||
|
<Paper
|
||||||
|
sx={{
|
||||||
|
p: 2.5,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography pb={2} variant="h5" color="primary">
|
||||||
|
{i18n.t('logsTitle')}
|
||||||
|
</Typography>
|
||||||
<LogTerminal />
|
<LogTerminal />
|
||||||
|
</Paper>
|
||||||
|
</Container >
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -105,8 +105,10 @@ func RunBlocking(cfg *RunConfig) {
|
|||||||
go gracefulShutdown(srv, &mdb)
|
go gracefulShutdown(srv, &mdb)
|
||||||
go autoPersist(time.Minute*5, &mdb, logger)
|
go autoPersist(time.Minute*5, &mdb, logger)
|
||||||
|
|
||||||
network := "tcp"
|
var (
|
||||||
address := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)
|
network = "tcp"
|
||||||
|
address = fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)
|
||||||
|
)
|
||||||
|
|
||||||
if strings.HasPrefix(cfg.Host, "/") {
|
if strings.HasPrefix(cfg.Host, "/") {
|
||||||
network = "unix"
|
network = "unix"
|
||||||
@@ -120,6 +122,7 @@ func RunBlocking(cfg *RunConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Info("yt-dlp-webui started", slog.String("address", address))
|
logger.Info("yt-dlp-webui started", slog.String("address", address))
|
||||||
|
|
||||||
if err := srv.Serve(listener); err != nil {
|
if err := srv.Serve(listener); err != nil {
|
||||||
logger.Warn("http server stopped", slog.String("err", err.Error()))
|
logger.Warn("http server stopped", slog.String("err", err.Error()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -21,9 +19,3 @@ func IsValidEntry(d fs.DirEntry) bool {
|
|||||||
!strings.HasSuffix(d.Name(), ".part") &&
|
!strings.HasSuffix(d.Name(), ".part") &&
|
||||||
!strings.HasSuffix(d.Name(), ".ytdl")
|
!strings.HasSuffix(d.Name(), ".ytdl")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ShaSumString(path string) string {
|
|
||||||
h := sha256.New()
|
|
||||||
h.Write([]byte(path))
|
|
||||||
return hex.EncodeToString(h.Sum(nil))
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user