From d99899eb163eedf8281139ae1e0c87366926f30f Mon Sep 17 00:00:00 2001 From: marcobaobao Date: Mon, 29 Aug 2022 22:44:06 +0200 Subject: [PATCH] Code refactoring --- .../{docker-image.yml => .docker-image.old} | 0 README.md | 20 ++++++++++++++---- frontend/src/Home.tsx | 14 ++++++------- frontend/src/interfaces.ts | 20 ++++++------------ server/src/core/Process.ts | 9 ++++---- server/src/core/downloader.ts | 21 ++++++++----------- ...DownloadInfo.ts => IDownloadMetadata.d.ts} | 8 +++---- .../interfaces/{IPayload.ts => IPayload.d.ts} | 0 .../interfaces/{IRecord.ts => IRecord.d.ts} | 0 .../{ISettings.ts => ISettings.d.ts} | 1 + server/src/main.ts | 18 +++++++++++----- settings.json | 1 + 12 files changed, 62 insertions(+), 50 deletions(-) rename .github/workflows/{docker-image.yml => .docker-image.old} (100%) rename server/src/interfaces/{IDownloadInfo.ts => IDownloadMetadata.d.ts} (56%) rename server/src/interfaces/{IPayload.ts => IPayload.d.ts} (100%) rename server/src/interfaces/{IRecord.ts => IRecord.d.ts} (100%) rename server/src/interfaces/{ISettings.ts => ISettings.d.ts} (81%) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/.docker-image.old similarity index 100% rename from .github/workflows/docker-image.yml rename to .github/workflows/.docker-image.old diff --git a/README.md b/README.md index 2ea75a8..b0dafa1 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,12 @@ Developed to be as lightweight as possible (because my server is basically an in The bottleneck remains yt-dlp startup time (until yt-dlp will provide a rpc interface). +**I strongly recomend the ghrc build instead of docker hub one.** + +```shell +docker pull ghcr.io/marcopeocchi/yt-dlp-web-ui:master +``` + --- Changelog: @@ -86,11 +92,17 @@ Future releases will have: ## Docker installation ```shell -docker pull marcobaobao/yt-dlp-webui:latest #x86 only -# or alternatively for ARM and x86 devices docker pull ghcr.io/marcopeocchi/yt-dlp-web-ui:master -docker run -d -p 3022:3022 -v :/usr/src/yt-dlp-webui/downloads marcobaobao/yt-dlp-webui +# recomended for ARM and x86 devices +docker pull ghcr.io/marcopeocchi/yt-dlp-web-ui:master +docker run -d -p 3022:3022 -v :/usr/src/yt-dlp-webui/downloads ghcr.io/marcopeocchi/yt-dlp-web-ui:master + +# or even +docker pull ghcr.io/marcopeocchi/yt-dlp-web-ui:master +docker create --name yt-dlp-webui -p 8082:3022 -v :/usr/src/yt-dlp-webui/ ghcr.io/marcopeocchi/yt-dlp-web-ui:master ``` + or + ```shell docker build -t yt-dlp-webui . docker run -d -p 3022:3022 -v :/usr/src/yt-dlp-webui/downloads yt-dlp-webui @@ -103,7 +115,7 @@ docker run -d -p 3022:3022 -v :/usr/src/yt-dlp-webui/downloads yt-dlp- npm i npm run build-all -# edit the settings.json specifying the download path or +# edit the settings.json specifying port and download path or # it will default to the following created folder mkdir downloads diff --git a/frontend/src/Home.tsx b/frontend/src/Home.tsx index cb6ac73..ec9e7ca 100644 --- a/frontend/src/Home.tsx +++ b/frontend/src/Home.tsx @@ -18,7 +18,7 @@ import { useDispatch, useSelector } from "react-redux"; import { Socket } from "socket.io-client"; import { StackableResult } from "./components/StackableResult"; import { connected, downloading, finished } from "./features/status/statusSlice"; -import { IDLInfo, IDLInfoBase, IDownloadInfo, IMessage } from "./interfaces"; +import { IDLMetadata, IDLMetadataAndPID, IMessage } from "./interfaces"; import { RootState } from "./stores/store"; import { isValidURL, toFormatArgs, updateInStateMap, } from "./utils"; import { FileUpload } from "@mui/icons-material"; @@ -37,8 +37,8 @@ export default function Home({ socket }: Props) { // ephemeral state const [progressMap, setProgressMap] = useState(new Map()); const [messageMap, setMessageMap] = useState(new Map()); - const [downloadInfoMap, setDownloadInfoMap] = useState(new Map()); - const [downloadFormats, setDownloadFormats] = useState(); + const [downloadInfoMap, setDownloadInfoMap] = useState(new Map()); + const [downloadFormats, setDownloadFormats] = useState(); const [pickedVideoFormat, setPickedVideoFormat] = useState(''); const [pickedAudioFormat, setPickedAudioFormat] = useState(''); const [pickedBestFormat, setPickedBestFormat] = useState(''); @@ -68,7 +68,7 @@ export default function Home({ socket }: Props) { /* Handle download information sent by server */ useEffect(() => { - socket.on('available-formats', (data: IDownloadInfo) => { + socket.on('available-formats', (data: IDLMetadata) => { setShowBackdrop(false) setDownloadFormats(data); }) @@ -76,10 +76,10 @@ export default function Home({ socket }: Props) { /* Handle download information sent by server */ useEffect(() => { - socket.on('info', (data: IDLInfo) => { + socket.on('metadata', (data: IDLMetadataAndPID) => { setShowBackdrop(false) dispatch(downloading()) - updateInStateMap(data.pid, data.info, downloadInfoMap, setDownloadInfoMap); + updateInStateMap(data.pid, data.metadata, downloadInfoMap, setDownloadInfoMap); }) }, []) @@ -374,9 +374,9 @@ export default function Home({ socket }: Props) { formattedLog={message[1]} title={downloadInfoMap.get(message[0])?.title ?? ''} thumbnail={downloadInfoMap.get(message[0])?.thumbnail ?? ''} - resolution={downloadInfoMap.get(message[0])?.resolution ?? '...'} progress={progressMap.get(message[0]) ?? 0} stopCallback={() => abort(message[0])} + resolution={''} /> diff --git a/frontend/src/interfaces.ts b/frontend/src/interfaces.ts index b5d6b4d..248b998 100644 --- a/frontend/src/interfaces.ts +++ b/frontend/src/interfaces.ts @@ -6,22 +6,14 @@ export interface IMessage { pid: number } -export interface IDLInfoBase { - title: string, - thumbnail: string, - upload_date?: string | Date, - duration?: number - resolution?: string -} - -export interface IDownloadInfo { - formats: Array, - best: IDownloadInfoSection, +export interface IDLMetadata { + formats: Array, + best: IDLFormat, thumbnail: string, title: string, } -export interface IDownloadInfoSection { +export interface IDLFormat { format_id: string, format_note: string, fps: number, @@ -30,9 +22,9 @@ export interface IDownloadInfoSection { acodec: string, } -export interface IDLInfo { +export interface IDLMetadataAndPID { pid: number, - info: IDLInfoBase + metadata: IDLMetadata } export interface IDLSpeed { diff --git a/server/src/core/Process.ts b/server/src/core/Process.ts index 8cf261b..a0ee2ba 100644 --- a/server/src/core/Process.ts +++ b/server/src/core/Process.ts @@ -4,6 +4,7 @@ import { Readable } from 'stream'; import { ISettings } from '../interfaces/ISettings'; import { availableParams } from '../utils/params'; import Logger from '../utils/BetterLogger'; +import { IDownloadFormat, IDownloadMetadata } from '../interfaces/IDownloadMetadata'; const log = Logger.instance; @@ -20,7 +21,7 @@ class Process { private settings: ISettings; private stdout: Readable; private pid: number; - private metadata?: IDownloadInfo; + private metadata?: IDownloadMetadata; private exePath = join(__dirname, 'yt-dlp'); constructor(url: string, params: Array, settings: any) { @@ -59,7 +60,7 @@ class Process { * function used internally by the download process to fetch information, usually thumbnail and title * @returns Promise to the lock */ - public getInfo(): Promise { + public getMetadata(): Promise { if (!this.metadata) { let stdoutChunks = []; const ytdlpInfo = spawn(this.exePath, ['-j', this.url]); @@ -74,7 +75,7 @@ class Process { const buffer = Buffer.concat(stdoutChunks); const json = JSON.parse(buffer.toString()); const info = { - formats: json.formats.map((format: IDownloadInfoSection) => { + formats: json.formats.map((format: IDownloadFormat) => { return { format_id: format.format_id ?? '', format_note: format.format_note ?? '', @@ -83,7 +84,7 @@ class Process { vcodec: format.vcodec ?? '', acodec: format.acodec ?? '', } - }).filter((format: IDownloadInfoSection) => format.format_note !== 'storyboard'), + }).filter((format: IDownloadFormat) => format.format_note !== 'storyboard'), best: { format_id: json.format_id ?? '', format_note: json.format_note ?? '', diff --git a/server/src/core/downloader.ts b/server/src/core/downloader.ts index 3d08b8d..f0d4e94 100644 --- a/server/src/core/downloader.ts +++ b/server/src/core/downloader.ts @@ -27,9 +27,9 @@ catch (e) { * @param socket * @param url */ -export async function getFormatsAndInfo(socket: Socket, url: string) { +export async function getFormatsAndMetadata(socket: Socket, url: string) { let p = new Process(url, [], settings); - const formats = await p.getInfo(); + const formats = await p.getMetadata(); socket.emit('available-formats', formats) p = null; } @@ -56,7 +56,7 @@ export async function download(socket: Socket, payload: IPayload) { p.start().then(downloader => { mem_db.add(downloader) - displayDownloadInfo(downloader, socket); + displayDownloadMetadata(downloader, socket); streamProcess(downloader, socket); }); } @@ -66,11 +66,11 @@ export async function download(socket: Socket, payload: IPayload) { * @param process * @param socket */ -function displayDownloadInfo(process: Process, socket: Socket) { - process.getInfo().then(info => { - socket.emit('info', { +function displayDownloadMetadata(process: Process, socket: Socket) { + process.getMetadata().then(metadata => { + socket.emit('metadata', { pid: process.getPid(), - info: info + metadata: metadata, }); }); } @@ -87,11 +87,8 @@ function streamProcess(process: Process, socket: Socket) { pid: process.getPid(), }); } - const stdout = process.getStdout() - stdout.removeAllListeners() - - from(stdout) // stdout as observable + from(process.getStdout().removeAllListeners()) // stdout as observable .pipe( throttle(() => interval(500)), // discard events closer than 500ms map(stdout => formatter(String(stdout), process.getPid())) @@ -148,7 +145,7 @@ export async function retrieveDownload(socket: Socket) { // resume the jobs for (const entry of it) { const [, process] = entry - displayDownloadInfo(process, socket); + displayDownloadMetadata(process, socket); streamProcess(process, socket); } } diff --git a/server/src/interfaces/IDownloadInfo.ts b/server/src/interfaces/IDownloadMetadata.d.ts similarity index 56% rename from server/src/interfaces/IDownloadInfo.ts rename to server/src/interfaces/IDownloadMetadata.d.ts index 9c8d6b1..0702307 100644 --- a/server/src/interfaces/IDownloadInfo.ts +++ b/server/src/interfaces/IDownloadMetadata.d.ts @@ -1,11 +1,11 @@ -interface IDownloadInfo { - formats: Array, - best: IDownloadInfoSection, +export interface IDownloadMetadata { + formats: Array, + best: IDownloadFormat, thumbnail: string, title: string, } -interface IDownloadInfoSection { +export interface IDownloadFormat { format_id: string, format_note: string, fps: number, diff --git a/server/src/interfaces/IPayload.ts b/server/src/interfaces/IPayload.d.ts similarity index 100% rename from server/src/interfaces/IPayload.ts rename to server/src/interfaces/IPayload.d.ts diff --git a/server/src/interfaces/IRecord.ts b/server/src/interfaces/IRecord.d.ts similarity index 100% rename from server/src/interfaces/IRecord.ts rename to server/src/interfaces/IRecord.d.ts diff --git a/server/src/interfaces/ISettings.ts b/server/src/interfaces/ISettings.d.ts similarity index 81% rename from server/src/interfaces/ISettings.ts rename to server/src/interfaces/ISettings.d.ts index 5ed9900..4e9a385 100644 --- a/server/src/interfaces/ISettings.ts +++ b/server/src/interfaces/ISettings.d.ts @@ -1,4 +1,5 @@ export interface ISettings { download_path: string, cliArgs?: string[], + port?: number, } \ No newline at end of file diff --git a/server/src/main.ts b/server/src/main.ts index 840284f..f9cfbf7 100644 --- a/server/src/main.ts +++ b/server/src/main.ts @@ -2,7 +2,7 @@ import { splash } from './utils/logger'; import { join } from 'path'; import { Server } from 'socket.io'; import { ytdlpUpdater } from './utils/updater'; -import { download, abortDownload, retrieveDownload, abortAllDownloads, getFormatsAndInfo } from './core/downloader'; +import { download, abortDownload, retrieveDownload, abortAllDownloads, getFormatsAndMetadata } from './core/downloader'; import { getFreeDiskSpace } from './utils/procUtils'; import { listDownloaded } from './core/downloadArchive'; import { createServer } from 'http'; @@ -12,6 +12,7 @@ import * as Router from 'koa-router'; import * as serve from 'koa-static'; import * as cors from '@koa/cors'; import Logger from './utils/BetterLogger'; +import { ISettings } from './interfaces/ISettings'; const app = new Koa(); const server = createServer(app.callback()); @@ -24,6 +25,14 @@ const io = new Server(server, { } }); +let settings: ISettings; + +try { + settings = require('../settings.json'); +} catch (e) { + log.warn('settings', 'file not found, ignore if using Docker'); +} + // Koa routing router.get('/settings', (ctx, next) => { ctx.redirect('/') @@ -49,7 +58,6 @@ router.get('/stream/:filepath', (ctx, next) => { }) // WebSocket listeners - io.on('connection', socket => { log.info('ws', `${socket.handshake.address} connected!`) @@ -59,7 +67,7 @@ io.on('connection', socket => { }) socket.on('send-url-format-selection', (args) => { log.info('ws', `Formats ${args?.url}`) - if (args.url) getFormatsAndInfo(socket, args?.url) + if (args.url) getFormatsAndMetadata(socket, args?.url) }) socket.on('abort', (args) => { abortDownload(socket, args) @@ -86,10 +94,10 @@ app.use(serve(join(__dirname, 'frontend'))) app.use(cors()) app.use(router.routes()) -server.listen(process.env.PORT || 3022) +server.listen(process.env.PORT || settings.port || 3022) splash() -log.info('http', `Server started on port ${process.env.PORT || 3022}`) +log.info('http', `Server started on port ${process.env.PORT || settings.port || 3022}`) /** * Cleanup handler diff --git a/settings.json b/settings.json index 3ae1a8b..667e474 100644 --- a/settings.json +++ b/settings.json @@ -1,4 +1,5 @@ { + "port": 0, "download_path": "", "cliArgs": [] } \ No newline at end of file