diff --git a/.dockerignore b/.dockerignore index a50a970..bfae780 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,8 +3,8 @@ dist package-lock.json .parcel-cache .git -lib/*.exe -lib/yt-dlp +server/*.exe +server/yt-dlp .env *.mp4 *.ytdl diff --git a/.gitignore b/.gitignore index c509d35..56fae11 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,8 @@ dist package-lock.json node_modules -lib/*.exe -lib/yt-dlp +server/*.exe +server/yt-dlp .env *.mp4 *.ytdl diff --git a/Dockerfile b/Dockerfile index 33c7e01..9349bcd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,8 +9,8 @@ RUN apt-get install psmisc RUN npm install COPY . . RUN npm run build -RUN chmod +x ./lib/fetch-yt-dlp.sh -RUN ./lib/fetch-yt-dlp.sh && mv yt-dlp ./lib +RUN chmod +x ./server/fetch-yt-dlp.sh +RUN ./server/fetch-yt-dlp.sh && mv yt-dlp ./server RUN rm -rf .parcel-cache EXPOSE 3022 CMD [ "node" , "./server.js" ] diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index e90dbdb..c13b0fb 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,10 +1,9 @@ import { io } from "socket.io-client"; -import React, { useState, useEffect, Fragment } from "react"; +import React, { useState, useEffect, useRef, Fragment } from "react"; import { Container, Row, Col, - ProgressBar, InputGroup, FormControl, Button, @@ -15,6 +14,7 @@ import { buildMessage, updateInStateMap, validateDomain, validateIP } from "./ut import { IDLInfo, IDLInfoBase, IMessage } from "./interfaces"; import { MessageToast } from "./components/MessageToast"; import { StackableResult } from "./components/StackableResult"; +import { CliArguments } from "./classes"; import './App.css'; const socket = io(`http://${localStorage.getItem('server-addr') || 'localhost'}:3022`) @@ -32,7 +32,19 @@ export function App() { const [updatedBin, setUpdatedBin] = useState(false); const [showSettings, setShowSettings] = useState(false); 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 -------------------- */ @@ -101,9 +113,7 @@ export function App() { setHalt(true) socket.emit('send-url', { url: url, - params: { - xa: extractAudio - }, + params: cliArgs.toString(), }) setUrl('') const input = document.getElementById('urlInput') as HTMLInputElement; @@ -112,7 +122,7 @@ export function App() { /** * Update the url state whenever the input value changes - * @param {React.ChangeEvent} e Input change event + * @param e Input change event */ const handleUrlChange = (e: React.ChangeEvent) => { setUrl(e.target.value) @@ -121,7 +131,7 @@ export function App() { /** * Update the server ip address state and localstorage whenever the input value changes. * Validate the ip-addr then set. - * @param {React.ChangeEvent} e Input change event + * @param e Input change event */ const handleAddrChange = (e: React.ChangeEvent) => { 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. - * @param {number} id The download id / pid + * @param id The download id / pid * @returns void */ const abort = (id?: number) => { @@ -175,13 +185,38 @@ export function App() { /** * Handle extract audio checkbox */ - const toggleExtractAudio = () => { - if (extractAudio) { - localStorage.setItem('-x', 'false') - setExtractAudio(false) + const setExtractAudio = () => { + if (cliArgs.extractAudio) { + xaInput.current.checked = false; + cliArgs.extractAudio = false; + + const lStorageItem = localStorage.getItem('cliArgs'); + localStorage.setItem('cliArgs', lStorageItem.replace('-x ', '')); } else { - localStorage.setItem('-x', 'true') - setExtractAudio(true) + xaInput.current.checked = 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'}
- toggleExtractAudio()} checked={extractAudio} /> + +    + +
: null diff --git a/frontend/src/classes.ts b/frontend/src/classes.ts new file mode 100644 index 0000000..4421172 --- /dev/null +++ b/frontend/src/classes.ts @@ -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; + } + } +} \ No newline at end of file diff --git a/frontend/src/utils.ts b/frontend/src/utils.ts index 6b1adc3..f5d0147 100644 --- a/frontend/src/utils.ts +++ b/frontend/src/utils.ts @@ -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 - * @param {num} k Map key - * @param {*} v Map value - * @param {Map} target The target map saved in-state - * @param {Function} callback calls React's StateAction function with the newly created Map - * @param {boolean} remove -optional- is it an update or a deletion operation? + * @param k Map key + * @param v Map value + * @param target The target map saved in-state + * @param callback calls React's StateAction function with the newly created Map + * @param remove -optional- is it an update or a deletion operation? */ export const updateInStateMap = (k: number, v: any, target: Map, callback: Function, remove: boolean = false) => { if (remove) { @@ -69,7 +69,7 @@ export const updateInStateMap = (k: number, v: any, target: Map, ca /** * Pre like function * @param data - * @returns + * @returns formatted server message */ export function buildMessage(data: IMessage) { return `operation: ${data.status || '...'} \nprogress: ${data.progress || '?'} \nsize: ${data.size || '?'} \nspeed: ${data.dlSpeed || '?'}`; diff --git a/package.json b/package.json index 717e4b2..9b91cdc 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "dev": "nodemon app.js", "build": "parcel build ./frontend/index.html", "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", "license": "ISC", diff --git a/server.js b/server.js index 71345a7..c8cd7ba 100644 --- a/server.js +++ b/server.js @@ -1,18 +1,18 @@ const Koa = require('koa'), serve = require('koa-static'), cors = require('@koa/cors'), - { logger, splash } = require('./lib/logger'), + { logger, splash } = require('./server/logger'), { join } = require('path'), { Server } = require('socket.io'), { createServer } = require('http'), - { ytdlpUpdater } = require('./lib/updater'), + { ytdlpUpdater } = require('./server/updater'), { download, abortDownload, retriveDownload, abortAllDownloads, - } = require('./lib/downloader'), - db = require('./lib/db'); + } = require('./server/downloader'), + db = require('./server/db'); const app = new Koa() const server = createServer(app.callback()) diff --git a/lib/Process.js b/server/Process.js similarity index 88% rename from lib/Process.js rename to server/Process.js index aa60f7c..c949052 100644 --- a/lib/Process.js +++ b/server/Process.js @@ -6,7 +6,7 @@ const { logger } = require('./logger'); * Represents a download process that spawns yt-dlp. * @constructor * @param {string} url - The downlaod url. - * @param {string} params - The cli arguments passed by the frontend. + * @param {Array} params - The cli arguments passed by the frontend. * @param {*} settings - The download settings passed by the frontend. */ @@ -28,12 +28,10 @@ class Process { async start(callback) { await this.#__internalGetInfo(); - const ytldp = spawn('./lib/yt-dlp', - [ - '-o', `${this.settings?.download_path || 'downloads/'}%(title)s.%(ext)s`, - this.params, - this.url - ] + const ytldp = spawn('./server/yt-dlp', + ['-o', `${this.settings?.download_path || 'downloads/'}%(title)s.%(ext)s`] + .concat(this.params) + .concat([this.url]) ); this.pid = ytldp.pid; @@ -54,7 +52,7 @@ class Process { async #__internalGetInfo() { let lock = true; 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) => { stdoutChunks.push(data); diff --git a/lib/ProcessPool.js b/server/ProcessPool.js similarity index 100% rename from lib/ProcessPool.js rename to server/ProcessPool.js diff --git a/lib/db.js b/server/db.js similarity index 100% rename from lib/db.js rename to server/db.js diff --git a/lib/downloader.js b/server/downloader.js similarity index 98% rename from lib/downloader.js rename to server/downloader.js index 1e35e0e..dd4505a 100644 --- a/lib/downloader.js +++ b/server/downloader.js @@ -34,8 +34,10 @@ async function download(socket, payload) { return; } - const url = payload.url - const params = payload.params?.xa ? '-x' : ''; + const url = payload.url; + const params = payload.params.split(' '); + + console.log(params) const p = new Process(url, params, settings); diff --git a/lib/fetch-yt-dlp.sh b/server/fetch-yt-dlp.sh similarity index 100% rename from lib/fetch-yt-dlp.sh rename to server/fetch-yt-dlp.sh diff --git a/lib/logger.js b/server/logger.js similarity index 100% rename from lib/logger.js rename to server/logger.js diff --git a/lib/procUtils.js b/server/procUtils.js similarity index 100% rename from lib/procUtils.js rename to server/procUtils.js diff --git a/lib/updater.js b/server/updater.js similarity index 100% rename from lib/updater.js rename to server/updater.js