Refactoring code and structure
This commit is contained in:
@@ -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
4
.gitignore
vendored
@@ -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
|
||||||
|
|||||||
@@ -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" ]
|
||||||
|
|||||||
@@ -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"> Extract audio</label>
|
<label htmlFor="-x"> Extract audio</label>
|
||||||
|
|
||||||
|
<input type="checkbox" name="-nomtime" defaultChecked={cliArgs.noMTime} ref={mtInput}
|
||||||
|
onClick={setNoMTime} />
|
||||||
|
<label htmlFor="-x"> Don't set file modification time</label>
|
||||||
</div>
|
</div>
|
||||||
</div> :
|
</div> :
|
||||||
null
|
null
|
||||||
|
|||||||
49
frontend/src/classes.ts
Normal file
49
frontend/src/classes.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 || '?'}`;
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -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);
|
||||||
|
|
||||||
Reference in New Issue
Block a user