Refactoring code and structure

This commit is contained in:
2022-01-29 14:48:58 +01:00
parent 416ccab7ef
commit 4d54ea04b9
16 changed files with 132 additions and 44 deletions

View File

@@ -3,8 +3,8 @@ dist
package-lock.json package-lock.json
.parcel-cache .parcel-cache
.git .git
lib/*.exe server/*.exe
lib/yt-dlp server/yt-dlp
.env .env
*.mp4 *.mp4
*.ytdl *.ytdl

4
.gitignore vendored
View File

@@ -2,8 +2,8 @@
dist dist
package-lock.json package-lock.json
node_modules node_modules
lib/*.exe server/*.exe
lib/yt-dlp server/yt-dlp
.env .env
*.mp4 *.mp4
*.ytdl *.ytdl

View File

@@ -9,8 +9,8 @@ RUN apt-get install psmisc
RUN npm install RUN npm install
COPY . . COPY . .
RUN npm run build RUN npm run build
RUN chmod +x ./lib/fetch-yt-dlp.sh RUN chmod +x ./server/fetch-yt-dlp.sh
RUN ./lib/fetch-yt-dlp.sh && mv yt-dlp ./lib RUN ./server/fetch-yt-dlp.sh && mv yt-dlp ./server
RUN rm -rf .parcel-cache RUN rm -rf .parcel-cache
EXPOSE 3022 EXPOSE 3022
CMD [ "node" , "./server.js" ] CMD [ "node" , "./server.js" ]

View File

@@ -1,10 +1,9 @@
import { io } from "socket.io-client"; import { io } from "socket.io-client";
import React, { useState, useEffect, Fragment } from "react"; import React, { useState, useEffect, useRef, Fragment } from "react";
import { import {
Container, Container,
Row, Row,
Col, Col,
ProgressBar,
InputGroup, InputGroup,
FormControl, FormControl,
Button, Button,
@@ -15,6 +14,7 @@ import { buildMessage, updateInStateMap, validateDomain, validateIP } from "./ut
import { IDLInfo, IDLInfoBase, IMessage } from "./interfaces"; import { IDLInfo, IDLInfoBase, IMessage } from "./interfaces";
import { MessageToast } from "./components/MessageToast"; import { MessageToast } from "./components/MessageToast";
import { StackableResult } from "./components/StackableResult"; import { StackableResult } from "./components/StackableResult";
import { CliArguments } from "./classes";
import './App.css'; import './App.css';
const socket = io(`http://${localStorage.getItem('server-addr') || 'localhost'}:3022`) const socket = io(`http://${localStorage.getItem('server-addr') || 'localhost'}:3022`)
@@ -32,7 +32,19 @@ export function App() {
const [updatedBin, setUpdatedBin] = useState(false); const [updatedBin, setUpdatedBin] = useState(false);
const [showSettings, setShowSettings] = useState(false); const [showSettings, setShowSettings] = useState(false);
const [darkMode, setDarkMode] = useState(localStorage.getItem('theme') === 'dark'); const [darkMode, setDarkMode] = useState(localStorage.getItem('theme') === 'dark');
const [extractAudio, setExtractAudio] = useState(localStorage.getItem('-x') === 'true');
const xaInput = useRef(null);
const mtInput = useRef(null);
/* -------------------- Init ----------------------- */
const cliArgs = new CliArguments();
if (!localStorage.getItem('cliArgs')) {
localStorage.setItem('cliArgs', '')
}
cliArgs.fromString(localStorage.getItem('cliArgs'))
/* -------------------- Effects -------------------- */ /* -------------------- Effects -------------------- */
@@ -101,9 +113,7 @@ export function App() {
setHalt(true) setHalt(true)
socket.emit('send-url', { socket.emit('send-url', {
url: url, url: url,
params: { params: cliArgs.toString(),
xa: extractAudio
},
}) })
setUrl('') setUrl('')
const input = document.getElementById('urlInput') as HTMLInputElement; const input = document.getElementById('urlInput') as HTMLInputElement;
@@ -112,7 +122,7 @@ export function App() {
/** /**
* Update the url state whenever the input value changes * Update the url state whenever the input value changes
* @param {React.ChangeEvent<HTMLInputElement>} e Input change event * @param e Input change event
*/ */
const handleUrlChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleUrlChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setUrl(e.target.value) setUrl(e.target.value)
@@ -121,7 +131,7 @@ export function App() {
/** /**
* Update the server ip address state and localstorage whenever the input value changes. * Update the server ip address state and localstorage whenever the input value changes.
* Validate the ip-addr then set. * Validate the ip-addr then set.
* @param {React.ChangeEvent<HTMLInputElement>} e Input change event * @param e Input change event
*/ */
const handleAddrChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleAddrChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const input = e.target.value; const input = e.target.value;
@@ -138,7 +148,7 @@ export function App() {
/** /**
* Abort a specific download if id's provided, other wise abort all running ones. * Abort a specific download if id's provided, other wise abort all running ones.
* @param {number} id The download id / pid * @param id The download id / pid
* @returns void * @returns void
*/ */
const abort = (id?: number) => { const abort = (id?: number) => {
@@ -175,13 +185,38 @@ export function App() {
/** /**
* Handle extract audio checkbox * Handle extract audio checkbox
*/ */
const toggleExtractAudio = () => { const setExtractAudio = () => {
if (extractAudio) { if (cliArgs.extractAudio) {
localStorage.setItem('-x', 'false') xaInput.current.checked = false;
setExtractAudio(false) cliArgs.extractAudio = false;
const lStorageItem = localStorage.getItem('cliArgs');
localStorage.setItem('cliArgs', lStorageItem.replace('-x ', ''));
} else { } else {
localStorage.setItem('-x', 'true') xaInput.current.checked = true;
setExtractAudio(true) cliArgs.extractAudio = true;
const lStorageItem = localStorage.getItem('cliArgs');
localStorage.setItem('cliArgs', lStorageItem.concat('-x ', ''));
}
}
/**
* Handle no modified time header checkbox
*/
const setNoMTime = () => {
if (cliArgs.noMTime) {
mtInput.current.checked = false;
cliArgs.noMTime = false;
const lStorageItem = localStorage.getItem('cliArgs');
localStorage.setItem('cliArgs', lStorageItem.replace('--no-mtime ', ''));
} else {
mtInput.current.checked = true;
cliArgs.noMTime = true;
const lStorageItem = localStorage.getItem('cliArgs');
localStorage.setItem('cliArgs', lStorageItem.concat('--no-mtime ', ''));
} }
} }
@@ -276,9 +311,13 @@ export function App() {
{darkMode ? 'Light theme' : 'Dark theme'} {darkMode ? 'Light theme' : 'Dark theme'}
</Button> </Button>
<div className="pt-2"> <div className="pt-2">
<input type="checkbox" name="-x" id="-x" <input type="checkbox" name="-x" defaultChecked={cliArgs.extractAudio} ref={xaInput}
onClick={() => toggleExtractAudio()} checked={extractAudio} /> onClick={setExtractAudio} />
<label htmlFor="-x">&nbsp;Extract audio</label> <label htmlFor="-x">&nbsp;Extract audio</label>
&nbsp;&nbsp;
<input type="checkbox" name="-nomtime" defaultChecked={cliArgs.noMTime} ref={mtInput}
onClick={setNoMTime} />
<label htmlFor="-x">&nbsp;Don't set file modification time</label>
</div> </div>
</div> : </div> :
null null

49
frontend/src/classes.ts Normal file
View File

@@ -0,0 +1,49 @@
export class CliArguments {
private _extractAudio: boolean;
private _noMTime: boolean;
constructor() {
this._extractAudio = false;
this._noMTime = false;
}
public get extractAudio(): boolean {
return this._extractAudio;
}
public set extractAudio(v: boolean) {
this._extractAudio = v;
}
public get noMTime(): boolean {
return this._noMTime;
}
public set noMTime(v: boolean) {
this._noMTime = v;
}
public toString(): string {
let args = '';
if (this._extractAudio) {
args += '-x '
}
if (this._noMTime) {
args += '--no-mtime '
}
return args.trim();
}
public fromString(str: string): void {
if (str.includes('-x')) {
this._extractAudio = true;
}
if (str.includes('--no-mtime')) {
this._noMTime = true;
}
}
}

View File

@@ -51,11 +51,11 @@ export function detectSpeed(str: string): number {
/** /**
* Update a map stored in React State, in this specific impl. all maps have integer keys * Update a map stored in React State, in this specific impl. all maps have integer keys
* @param {num} k Map key * @param k Map key
* @param {*} v Map value * @param v Map value
* @param {Map<number, any>} target The target map saved in-state * @param target The target map saved in-state
* @param {Function} callback calls React's StateAction function with the newly created Map * @param callback calls React's StateAction function with the newly created Map
* @param {boolean} remove -optional- is it an update or a deletion operation? * @param remove -optional- is it an update or a deletion operation?
*/ */
export const updateInStateMap = (k: number, v: any, target: Map<number, any>, callback: Function, remove: boolean = false) => { export const updateInStateMap = (k: number, v: any, target: Map<number, any>, callback: Function, remove: boolean = false) => {
if (remove) { if (remove) {
@@ -69,7 +69,7 @@ export const updateInStateMap = (k: number, v: any, target: Map<number, any>, ca
/** /**
* Pre like function * Pre like function
* @param data * @param data
* @returns * @returns formatted server message
*/ */
export function buildMessage(data: IMessage) { export function buildMessage(data: IMessage) {
return `operation: ${data.status || '...'} \nprogress: ${data.progress || '?'} \nsize: ${data.size || '?'} \nspeed: ${data.dlSpeed || '?'}`; return `operation: ${data.status || '...'} \nprogress: ${data.progress || '?'} \nsize: ${data.size || '?'} \nspeed: ${data.dlSpeed || '?'}`;

View File

@@ -7,7 +7,7 @@
"dev": "nodemon app.js", "dev": "nodemon app.js",
"build": "parcel build ./frontend/index.html", "build": "parcel build ./frontend/index.html",
"fe": "parcel ./frontend/index.html --open", "fe": "parcel ./frontend/index.html --open",
"fetch": "./lib/fetch-yt-dlp.sh && mv yt-dlp ./lib" "fetch": "./server/fetch-yt-dlp.sh && mv yt-dlp ./server"
}, },
"author": "marcobaobao", "author": "marcobaobao",
"license": "ISC", "license": "ISC",

View File

@@ -1,18 +1,18 @@
const Koa = require('koa'), const Koa = require('koa'),
serve = require('koa-static'), serve = require('koa-static'),
cors = require('@koa/cors'), cors = require('@koa/cors'),
{ logger, splash } = require('./lib/logger'), { logger, splash } = require('./server/logger'),
{ join } = require('path'), { join } = require('path'),
{ Server } = require('socket.io'), { Server } = require('socket.io'),
{ createServer } = require('http'), { createServer } = require('http'),
{ ytdlpUpdater } = require('./lib/updater'), { ytdlpUpdater } = require('./server/updater'),
{ {
download, download,
abortDownload, abortDownload,
retriveDownload, retriveDownload,
abortAllDownloads, abortAllDownloads,
} = require('./lib/downloader'), } = require('./server/downloader'),
db = require('./lib/db'); db = require('./server/db');
const app = new Koa() const app = new Koa()
const server = createServer(app.callback()) const server = createServer(app.callback())

View File

@@ -6,7 +6,7 @@ const { logger } = require('./logger');
* Represents a download process that spawns yt-dlp. * Represents a download process that spawns yt-dlp.
* @constructor * @constructor
* @param {string} url - The downlaod url. * @param {string} url - The downlaod url.
* @param {string} params - The cli arguments passed by the frontend. * @param {Array<String>} params - The cli arguments passed by the frontend.
* @param {*} settings - The download settings passed by the frontend. * @param {*} settings - The download settings passed by the frontend.
*/ */
@@ -28,12 +28,10 @@ class Process {
async start(callback) { async start(callback) {
await this.#__internalGetInfo(); await this.#__internalGetInfo();
const ytldp = spawn('./lib/yt-dlp', const ytldp = spawn('./server/yt-dlp',
[ ['-o', `${this.settings?.download_path || 'downloads/'}%(title)s.%(ext)s`]
'-o', `${this.settings?.download_path || 'downloads/'}%(title)s.%(ext)s`, .concat(this.params)
this.params, .concat([this.url])
this.url
]
); );
this.pid = ytldp.pid; this.pid = ytldp.pid;
@@ -54,7 +52,7 @@ class Process {
async #__internalGetInfo() { async #__internalGetInfo() {
let lock = true; let lock = true;
let stdoutChunks = []; let stdoutChunks = [];
const ytdlpInfo = spawn('./lib/yt-dlp', ['-s', '-j', this.url]); const ytdlpInfo = spawn('./server/yt-dlp', ['-s', '-j', this.url]);
ytdlpInfo.stdout.on('data', (data) => { ytdlpInfo.stdout.on('data', (data) => {
stdoutChunks.push(data); stdoutChunks.push(data);

View File

@@ -34,8 +34,10 @@ async function download(socket, payload) {
return; return;
} }
const url = payload.url const url = payload.url;
const params = payload.params?.xa ? '-x' : ''; const params = payload.params.split(' ');
console.log(params)
const p = new Process(url, params, settings); const p = new Process(url, params, settings);