first proposal download path selection

This commit is contained in:
2022-09-24 12:53:36 +02:00
parent 8fcb585519
commit b037c041f4
7 changed files with 144 additions and 24 deletions

View File

@@ -5,10 +5,13 @@ import {
ButtonGroup, ButtonGroup,
CircularProgress, CircularProgress,
Container, Container,
FormControl,
Grid, Grid,
IconButton, IconButton,
InputAdornment, InputAdornment,
MenuItem,
Paper, Paper,
Select,
Snackbar, Snackbar,
styled, styled,
TextField, TextField,
@@ -44,6 +47,9 @@ export default function Home({ socket }: Props) {
const [pickedAudioFormat, setPickedAudioFormat] = useState(''); const [pickedAudioFormat, setPickedAudioFormat] = useState('');
const [pickedBestFormat, setPickedBestFormat] = useState(''); const [pickedBestFormat, setPickedBestFormat] = useState('');
const [downloadPath, setDownloadPath] = useState<number>(0);
const [availableDownloadPaths, setAvailableDownloadPaths] = useState<string[]>([]);
const [url, setUrl] = useState(''); const [url, setUrl] = useState('');
const [workingUrl, setWorkingUrl] = useState(''); const [workingUrl, setWorkingUrl] = useState('');
const [showBackdrop, setShowBackdrop] = useState(false); const [showBackdrop, setShowBackdrop] = useState(false);
@@ -106,6 +112,14 @@ export default function Home({ socket }: Props) {
}) })
}, []) }, [])
useEffect(() => {
fetch(`${window.location.protocol}//${settings.serverAddr}:${settings.serverPort}/tree`)
.then(res => res.json())
.then(data => {
setAvailableDownloadPaths(data.flat)
})
}, [])
/* -------------------- component functions -------------------- */ /* -------------------- component functions -------------------- */
/** /**
@@ -119,6 +133,7 @@ export default function Home({ socket }: Props) {
socket.emit('send-url', { socket.emit('send-url', {
url: immediate || url || workingUrl, url: immediate || url || workingUrl,
path: availableDownloadPaths[downloadPath],
params: settings.cliArgs.toString() + toFormatArgs(codes), params: settings.cliArgs.toString() + toFormatArgs(codes),
}) })
setUrl('') setUrl('')
@@ -211,25 +226,44 @@ export default function Home({ socket }: Props) {
flexDirection: 'column', flexDirection: 'column',
}} }}
> >
<TextField <Grid container spacing={1}>
id="urlInput" <Grid item xs={10}>
label={settings.i18n.t('urlInput')} <TextField
variant="outlined" fullWidth
onChange={handleUrlChange} id="urlInput"
disabled={!status.connected || (settings.formatSelection && downloadFormats != null)} label={settings.i18n.t('urlInput')}
InputProps={{ variant="outlined"
endAdornment: ( onChange={handleUrlChange}
<InputAdornment position="end"> disabled={!status.connected || (settings.formatSelection && downloadFormats != null)}
<label htmlFor="icon-button-file"> InputProps={{
<Input id="icon-button-file" type="file" accept=".txt" onChange={parseUrlListFile} /> endAdornment: (
<IconButton color="primary" aria-label="upload file" component="span"> <InputAdornment position="end">
<FileUpload /> <label htmlFor="icon-button-file">
</IconButton> <Input id="icon-button-file" type="file" accept=".txt" onChange={parseUrlListFile} />
</label> <IconButton color="primary" aria-label="upload file" component="span">
</InputAdornment> <FileUpload />
), </IconButton>
}} </label>
/> </InputAdornment>
),
}}
/>
</Grid>
<Grid item xs={2}>
<FormControl fullWidth>
<Select
defaultValue={0}
value={availableDownloadPaths[downloadPath]}
onChange={(e) => setDownloadPath(e.target.value)}
>
{availableDownloadPaths.map((val: string, idx: number) => (
<MenuItem key={idx} value={idx}>{val}</MenuItem>
))}
</Select>
</FormControl>
</Grid>
</Grid>
<Grid container spacing={1} pt={2}> <Grid container spacing={1} pt={2}>
<Grid item> <Grid item>
<Button <Button
@@ -365,7 +399,7 @@ export default function Home({ socket }: Props) {
<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 }} pt={2}>
{ /*Super big brain flatMap moment*/ { /*Super big brain flatMap moment*/
Array Array
.from(messageMap) .from<any>(messageMap)
.filter(flattened => [...flattened][0]) .filter(flattened => [...flattened][0])
.filter(flattened => [...flattened][1].toString() !== serverStates.PROG_DONE) .filter(flattened => [...flattened][1].toString() !== serverStates.PROG_DONE)
.flatMap(message => ( .flatMap(message => (

View File

@@ -41,6 +41,12 @@ class Process {
public async start(callback?: Function): Promise<this> { public async start(callback?: Function): Promise<this> {
const sanitizedParams = this.params.filter((param: string) => availableParams.includes(param)); const sanitizedParams = this.params.filter((param: string) => availableParams.includes(param));
if (this.settings?.download_path) {
if (this.settings.download_path.charAt(this.settings.download_path.length - 1) !== '/') {
this.settings.download_path = `${this.settings.download_path}/`
}
}
const ytldp = spawn(this.exePath, const ytldp = spawn(this.exePath,
['-o', `${this.settings?.download_path || 'downloads/'}%(title)s.%(ext)s`] ['-o', `${this.settings?.download_path || 'downloads/'}%(title)s.%(ext)s`]
.concat(sanitizedParams) .concat(sanitizedParams)

View File

@@ -61,6 +61,9 @@ export async function download(socket: Socket, payload: IPayload) {
payload.params.split(' ') : payload.params.split(' ') :
payload.params; payload.params;
const scopedSettings = { ...settings }
scopedSettings.download_path = payload.path;
let p = new Process(url, params, settings); let p = new Process(url, params, settings);
p.start().then(downloader => { p.start().then(downloader => {
@@ -111,7 +114,10 @@ function streamProcess(process: Process, socket: Socket) {
map(stdout => formatter(String(stdout), process.getPid())) map(stdout => formatter(String(stdout), process.getPid()))
) )
.subscribe({ .subscribe({
next: (stdout) => socket.emit('progress', stdout), next: (stdout) => {
socket.emit('progress', stdout)
log.info(`proc-${stdout.pid}`, `${stdout.progress}\t${stdout.dlSpeed}`)
},
complete: () => { complete: () => {
process.kill().then(() => { process.kill().then(() => {
emitAbort(); emitAbort();

View File

@@ -5,6 +5,7 @@
export interface IPayload { export interface IPayload {
url: string url: string
params: Array<string> | string, params: Array<string> | string,
path: string,
title?: string, title?: string,
thumbnail?: string, thumbnail?: string,
size?: string, size?: string,

View File

@@ -2,7 +2,13 @@ import { splash } from './utils/logger';
import { join } from 'path'; import { join } from 'path';
import { Server } from 'socket.io'; import { Server } from 'socket.io';
import { ytdlpUpdater } from './utils/updater'; import { ytdlpUpdater } from './utils/updater';
import { download, abortDownload, retrieveDownload, abortAllDownloads, getFormatsAndMetadata } from './core/downloader'; import {
download,
abortDownload,
retrieveDownload,
abortAllDownloads,
getFormatsAndMetadata
} from './core/downloader';
import { getFreeDiskSpace } from './utils/procUtils'; import { getFreeDiskSpace } from './utils/procUtils';
import { listDownloaded } from './core/downloadArchive'; import { listDownloaded } from './core/downloadArchive';
import { createServer } from 'http'; import { createServer } from 'http';
@@ -13,6 +19,7 @@ import * as serve from 'koa-static';
import * as cors from '@koa/cors'; import * as cors from '@koa/cors';
import Logger from './utils/BetterLogger'; import Logger from './utils/BetterLogger';
import { ISettings } from './interfaces/ISettings'; import { ISettings } from './interfaces/ISettings';
import { directoryTree } from './utils/directoryUtils';
const app = new Koa(); const app = new Koa();
const server = createServer(app.callback()); const server = createServer(app.callback());
@@ -56,6 +63,10 @@ router.get('/archive', (ctx, next) => {
router.get('/stream/:filepath', (ctx, next) => { router.get('/stream/:filepath', (ctx, next) => {
streamer(ctx, next) streamer(ctx, next)
}) })
router.get('/tree', (ctx, next) => {
ctx.body = directoryTree()
next()
})
// WebSocket listeners // WebSocket listeners
io.on('connection', socket => { io.on('connection', socket => {

View File

@@ -0,0 +1,62 @@
import { readdirSync, statSync } from "fs";
import { ISettings } from "../interfaces/ISettings";
let settings: ISettings;
class Node {
public path: string
public children: Node[]
constructor(path: string) {
this.path = path
this.children = []
}
}
function buildTreeDFS(rootPath: string, directoryOnly: boolean) {
const root = new Node(rootPath)
const stack: Node[] = []
const flattened: string[] = []
stack.push(root)
flattened.push(rootPath)
while (stack.length) {
const current = stack.pop()
if (current) {
const children = readdirSync(current.path)
for (const it of children) {
const childPath = `${current.path}/${it}`
const childNode = new Node(childPath)
if (directoryOnly) {
if (statSync(childPath).isDirectory()) {
current.children.push(childNode)
stack.push(childNode)
flattened.push(childNode.path)
}
} else {
current.children.push(childNode)
if (statSync(childPath).isDirectory()) {
stack.push(childNode)
flattened.push(childNode.path)
}
}
}
}
}
return {
tree: root,
flat: flattened
}
}
try {
settings = require('../../settings.json');
} catch (e) { }
export function directoryTree() {
const tree = buildTreeDFS(settings.download_path || 'downloads', true)
return tree
}

View File

@@ -17,8 +17,8 @@ export const splash = () => {
const reset = "\u001b[0m" const reset = "\u001b[0m"
console.log(`${fg} __ ____ __ __ ______`) console.log(`${fg} __ ____ __ __ ______`)
console.log(" __ __/ /________/ / /__ _ _____ / / / / / / _/") console.log(" __ __/ /________/ / /__ _ _____ / / / / / / _/")
console.log(" / // / __/___/ _ / / _ \ | |/|/ / -_) _ \/ /_/ // / ") console.log(" / // / __/___/ _ / / _ \\ | |/|/ / -_) _ \\/ /_/ // / ")
console.log(" \_, /\__/ \_,_/_/ .__/ |__,__/\__/_.__/\____/___/ ") console.log(" \\_, /\\__/ \\_,_/_/ .__/ |__,__/\\__/_.__/\\____/___/ ")
console.log(`/___/ /_/ \n${reset}`) console.log(`/___/ /_/ \n${reset}`)
console.log(" yt-dlp-webUI - A web-ui for yt-dlp, simply enough") console.log(" yt-dlp-webUI - A web-ui for yt-dlp, simply enough")
console.log("---------------------------------------------------\n") console.log("---------------------------------------------------\n")