converted dir tree

This commit is contained in:
2023-01-11 23:19:37 +01:00
parent 4d4582b3f7
commit 4c7faa1b46
14 changed files with 229 additions and 107 deletions

View File

@@ -23,18 +23,16 @@ import {
BrowserRouter as Router, Link, Route,
Routes
} from 'react-router-dom';
import { io } from "socket.io-client";
import ArchivedDownloads from "./Archived";
import { AppBar } from "./components/AppBar";
import { Drawer } from "./components/Drawer";
import Home from "./Home";
import Settings from "./Settings";
import { RootState, store } from './stores/store';
import { getWebSocketEndpoint } from "./utils";
import { formatGiB, getWebSocketEndpoint } from "./utils";
function AppContent() {
const [open, setOpen] = useState(false);
const [freeDiskSpace, setFreeDiskSpace] = useState('');
const settings = useSelector((state: RootState) => state.settings)
const status = useSelector((state: RootState) => state.status)
@@ -58,11 +56,6 @@ function AppContent() {
setOpen(!open);
};
/* Get disk free space */
useEffect(() => {
}, [])
return (
<ThemeProvider theme={theme}>
<Router>
@@ -96,14 +89,14 @@ function AppContent() {
yt-dlp WebUI
</Typography>
{
freeDiskSpace ?
status.freeSpace ?
<div style={{
display: 'flex',
alignItems: 'center',
flexWrap: 'wrap',
}}>
<Storage />
<span>&nbsp;{freeDiskSpace}&nbsp;</span>
<span>&nbsp;{formatGiB(status.freeSpace)}&nbsp;</span>
</div>
: null
}
@@ -145,20 +138,6 @@ function AppContent() {
<ListItemText primary="Home" />
</ListItemButton>
</Link>
{/* Next release: list downloaded files */}
{/* <Link to={'/downloaded'} style={
{
textDecoration: 'none',
color: mode === 'dark' ? '#ffffff' : '#000000DE'
}
}>
<ListItemButton disabled={status.downloading}>
<ListItemIcon>
<Download />
</ListItemIcon>
<ListItemText primary="Downloaded" />
</ListItemButton>
</Link> */}
<Link to={'/settings'} style={
{
textDecoration: 'none',

View File

@@ -24,7 +24,7 @@ import { useDispatch, useSelector } from "react-redux";
import { CliArguments } from "./classes";
import { StackableResult } from "./components/StackableResult";
import { serverStates } from "./events";
import { connected } from "./features/status/statusSlice";
import { connected, setFreeSpace } from "./features/status/statusSlice";
import { I18nBuilder } from "./i18n";
import { IDLMetadata, IMessage } from "./interfaces";
import { RPCClient } from "./rpcClient";
@@ -73,10 +73,6 @@ export default function Home({ socket }: Props) {
useEffect(() => {
socket.onopen = () => {
dispatch(connected())
console.log('oke')
socket.send('fetch-jobs')
socket.send('disk-space')
socket.send('retrieve-jobs')
}
}, [])
@@ -85,6 +81,11 @@ export default function Home({ socket }: Props) {
return () => clearInterval(interval)
}, [])
useEffect(() => {
client.freeSpace()
.then(bytes => dispatch(setFreeSpace(bytes.result)))
}, [])
useEffect(() => {
socket.onmessage = (event) => {
const res = client.decode(event.data)
@@ -106,10 +107,9 @@ export default function Home({ socket }: Props) {
}, [])
useEffect(() => {
fetch(`${window.location.protocol}//${settings.serverAddr}:${settings.serverPort}/tree`)
.then(res => res.json())
client.directoryTree()
.then(data => {
setAvailableDownloadPaths(data.flat)
setAvailableDownloadPaths(data.result)
})
}, [])
@@ -150,10 +150,15 @@ export default function Home({ socket }: Props) {
setPickedVideoFormat('')
setPickedBestFormat('')
setTimeout(() => {
resetInput()
setShowBackdrop(true)
}, 250)
setShowBackdrop(true)
client.formats(url)
?.then(formats => {
console.log(formats)
setDownloadFormats(formats.result)
setShowBackdrop(false)
resetInput()
})
}
/**

View File

@@ -4,73 +4,73 @@ import { IMessage } from "../interfaces";
import { ellipsis } from "../utils";
type Props = {
title: string,
thumbnail: string,
resolution: string
percentage: string,
size: number,
speed: number,
stopCallback: VoidFunction,
title: string,
thumbnail: string,
resolution: string
percentage: string,
size: number,
speed: number,
stopCallback: VoidFunction,
}
export function StackableResult({
title,
thumbnail,
resolution,
percentage,
speed,
size,
stopCallback
title,
thumbnail,
resolution,
percentage,
speed,
size,
stopCallback
}: Props) {
const guessResolution = (xByY: string): any => {
if (!xByY) return null;
if (xByY.includes('4320')) return (<EightK color="primary" />);
if (xByY.includes('2160')) return (<FourK color="primary" />);
if (xByY.includes('1080')) return (<Hd color="primary" />);
if (xByY.includes('720')) return (<Sd color="primary" />);
return null;
}
const guessResolution = (xByY: string): any => {
if (!xByY) return null;
if (xByY.includes('4320')) return (<EightK color="primary" />);
if (xByY.includes('2160')) return (<FourK color="primary" />);
if (xByY.includes('1080')) return (<Hd color="primary" />);
if (xByY.includes('720')) return (<Sd color="primary" />);
return null;
}
const percentageToNumber = () => Number(percentage.replace('%', ''))
const percentageToNumber = () => Number(percentage.replace('%', ''))
const roundMB = (bytes: number) => `${(bytes / 1_000_000).toFixed(2)}MiB`
const roundMB = (bytes: number) => `${(bytes / 1_000_000).toFixed(2)}MiB`
return (
<Card>
<CardActionArea>
{thumbnail !== '' ?
<CardMedia
component="img"
height={180}
image={thumbnail}
/> :
<Skeleton variant="rectangular" height={180} />
}
<CardContent>
{title !== '' ?
<Typography gutterBottom variant="h6" component="div">
{ellipsis(title, 54)}
</Typography> :
<Skeleton />
}
<Stack direction="row" spacing={1} py={2}>
<Chip label={'Downloading'} color="primary" />
<Typography>{percentage}</Typography>
<Typography>{speed}</Typography>
<Typography>{roundMB(size ?? 0)}</Typography>
{guessResolution(resolution)}
</Stack>
{percentage ?
<LinearProgress variant="determinate" value={percentageToNumber()} /> :
null
}
</CardContent>
</CardActionArea>
<CardActions>
<Button variant="contained" size="small" color="primary" onClick={stopCallback}>
Stop
</Button>
</CardActions>
</Card>
)
return (
<Card>
<CardActionArea>
{thumbnail !== '' ?
<CardMedia
component="img"
height={180}
image={thumbnail}
/> :
<Skeleton variant="rectangular" height={180} />
}
<CardContent>
{title !== '' ?
<Typography gutterBottom variant="h6" component="div">
{ellipsis(title, 54)}
</Typography> :
<Skeleton />
}
<Stack direction="row" spacing={1} py={2}>
<Chip label={'Downloading'} color="primary" />
<Typography>{percentage}</Typography>
<Typography>{speed}</Typography>
<Typography>{roundMB(size ?? 0)}</Typography>
{guessResolution(resolution)}
</Stack>
{percentage ?
<LinearProgress variant="determinate" value={percentageToNumber()} /> :
null
}
</CardContent>
</CardActionArea>
<CardActions>
<Button variant="contained" size="small" color="primary" onClick={stopCallback}>
Stop
</Button>
</CardActions>
</Card>
)
}

View File

@@ -1,30 +1,47 @@
import { createSlice } from "@reduxjs/toolkit"
import { createSlice, PayloadAction } from "@reduxjs/toolkit"
export interface StatusState {
connected: boolean,
updated: boolean,
downloading: boolean,
freeSpace: number,
}
const initialState: StatusState = {
connected: false,
updated: false,
downloading: false,
freeSpace: 0,
}
export const statusSlice = createSlice({
name: 'status',
initialState,
reducers: {
connected: (state) => { state.connected = true },
disconnected: (state) => { state.connected = false },
updated: (state) => { state.updated = true },
alreadyUpdated: (state) => { state.updated = false },
downloading: (state) => { state.downloading = true },
finished: (state) => { state.downloading = false },
connected: (state) => {
state.connected = true
},
disconnected: (state) => {
state.connected = false
},
updated: (state) => {
state.updated = true
},
alreadyUpdated: (state) => {
state.updated = false
},
downloading: (state) => {
state.downloading = true
},
finished: (state) => {
state.downloading = false
},
setFreeSpace: (state, action: PayloadAction<number>) => {
state.freeSpace = action.payload
}
}
})
export const { connected, disconnected, updated, alreadyUpdated, downloading, finished } = statusSlice.actions
export const { connected, disconnected, updated, alreadyUpdated, downloading, finished, setFreeSpace } = statusSlice.actions
export default statusSlice.reducer

View File

@@ -1,6 +1,8 @@
import type { RPCRequest, RPCResponse } from "./types"
import type { IDLMetadata } from './interfaces'
import { getHttpRPCEndpoint } from './utils'
export class RPCClient {
private socket: WebSocket
private seq: number
@@ -20,7 +22,7 @@ export class RPCClient {
private sendHTTP<T>(req: RPCRequest) {
return new Promise<RPCResponse<T>>((resolve, reject) => {
fetch('/rpc-http', {
fetch(getHttpRPCEndpoint(), {
method: 'POST',
body: JSON.stringify(req)
})
@@ -75,6 +77,20 @@ export class RPCClient {
})
}
public freeSpace() {
return this.sendHTTP<number>({
method: 'Service.FreeSpace',
params: [],
})
}
public directoryTree() {
return this.sendHTTP<string[]>({
method: 'Service.DirectoryTree',
params: [],
})
}
public decode(data: any): RPCResponse<any> {
return JSON.parse(data)
}

View File

@@ -5,6 +5,7 @@ export type RPCMethods =
| "Service.KillAll"
| "Service.FreeSpace"
| "Service.Formats"
| "Service.DirectoryTree"
export type RPCRequest = {
method: RPCMethods,

View File

@@ -111,4 +111,8 @@ export function getWebSocketEndpoint() {
export function getHttpRPCEndpoint() {
return `${window.location.protocol}//${localStorage.getItem('server-addr') || window.location.hostname}:${localStorage.getItem('server-port') || window.location.port}/http-rpc`
}
export function formatGiB(bytes: number) {
return `${(bytes / 1_000_000_000).toFixed(0)}GiB`
}