From 368595c08f98a5e1b24ac8e0f5e0a110fcd5d33d Mon Sep 17 00:00:00 2001 From: marcobaobao Date: Fri, 3 Jun 2022 15:52:39 +0200 Subject: [PATCH] format selection enabled --- .gitignore | 2 +- README.md | 24 ++- frontend/src/App.tsx | 4 +- frontend/src/Home.tsx | 163 ++++++++++++++++-- frontend/src/Settings.tsx | 19 +- frontend/src/assets/i18n.yaml | 1 + frontend/src/components/StackableResult.tsx | 2 +- .../src/features/settings/settingsSlice.ts | 10 +- frontend/src/interfaces.ts | 1 + frontend/src/utils.ts | 13 ++ package.json | 2 + server/src/core/Process.ts | 4 +- server/src/core/downloader.ts | 3 +- server/src/core/streamer.ts | 2 +- server/src/interfaces/IDownloadInfo.ts | 1 + server/src/main.ts | 5 +- 16 files changed, 223 insertions(+), 33 deletions(-) diff --git a/.gitignore b/.gitignore index 28d2c1c..696a2f7 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,4 @@ src/server/core/yt-dlp *.ytdl *.part *.db -*.DS_Store \ No newline at end of file +.DS_Store \ No newline at end of file diff --git a/README.md b/README.md index c084805..12ae432 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,8 @@ Refactoring and JSDoc. 04/01/22: Background jobs now are retrieved!! It's still rudimentary but it leverages on yt-dlp resume feature 05/05/22: Material UI update + +03/06/22: The most requested feature finally implemented: Format Selection!! ``` @@ -49,15 +51,30 @@ The avaible settings are currently only: - Switch theme - Extract audio - Switch language +- Optional format selection +## Format selection + +![](https://i.ibb.co/fNxDHJd/localhost-1234-2.png) + +This feature is disabled by default as this WebUI/Wrapper/Software/Bunch of Code is intended to be used to retrieve the best quality automatically. + +To enable it go to the settings page: + +![](https://i.ibb.co/YdXRwKc/localhost-1234-3.png) + +And set it :D + Future releases will have: - ~~Multi download~~ *done* - ~~Exctract audio~~ *done* -- Format selection *in-progess* +- ~~Format selection~~ *done* +- Download archive +- ARM Build ## Troubleshooting - **It says that it isn't connected/ip in the footer is not defined.** @@ -105,8 +122,3 @@ node dist/main.js - Well, yes (until now). - **Why is it so slow to start a download?** - I genuinely don't know. I know that standalone yt-dlp is slow to start up even on my M1 Mac, so.... - -## Todo list -- ~~retrieve background tasks~~ -- format selection -- better ui/ux diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index f9d05b4..1ebd00e 100755 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -237,8 +237,8 @@ function AppContent() { > - }> - }> + }> + }> }> diff --git a/frontend/src/Home.tsx b/frontend/src/Home.tsx index cdeeca1..e67949a 100644 --- a/frontend/src/Home.tsx +++ b/frontend/src/Home.tsx @@ -1,18 +1,16 @@ -import { Backdrop, Button, CircularProgress, Container, Grid, Paper, Snackbar, TextField, } from "@mui/material"; +import { Backdrop, Button, ButtonGroup, CircularProgress, Container, Grid, Paper, Skeleton, Snackbar, TextField, Typography, } from "@mui/material"; import React, { Fragment, useEffect, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; -import { Socket } from "socket.io-client"; +import { io, Socket } from "socket.io-client"; import { StackableResult } from "./components/StackableResult"; import { connected, disconnected, downloading, finished } from "./features/status/statusSlice"; import { IDLInfo, IDLInfoBase, IDownloadInfo, IMessage } from "./interfaces"; import { RootState } from "./stores/store"; -import { updateInStateMap, } from "./utils"; +import { toFormatArgs, updateInStateMap, } from "./utils"; -type Props = { - socket: Socket -} +let socket: Socket; -export default function Home({ socket }: Props) { +export default function Home() { // redux state const settings = useSelector((state: RootState) => state.settings) const status = useSelector((state: RootState) => state.status) @@ -22,11 +20,23 @@ export default function Home({ socket }: Props) { const [progressMap, setProgressMap] = useState(new Map()); const [messageMap, setMessageMap] = useState(new Map()); const [downloadInfoMap, setDownloadInfoMap] = useState(new Map()); + const [downloadFormats, setDownloadFormats] = useState(); + const [pickedVideoFormat, setPickedVideoFormat] = useState(''); + const [pickedAudioFormat, setPickedAudioFormat] = useState(''); + const [pickedBestFormat, setPickedBestFormat] = useState(''); const [url, setUrl] = useState(''); + const [workingUrl, setWorkingUrl] = useState(''); const [showBackdrop, setShowBackdrop] = useState(false); /* -------------------- Effects -------------------- */ + useEffect(() => { + socket = io(`http://${localStorage.getItem('server-addr') || 'localhost'}:3022`); + return () => { + socket.disconnect() + }; + }, []) + /* WebSocket connect event handler*/ useEffect(() => { socket.on('connect', () => { @@ -53,6 +63,7 @@ export default function Home({ socket }: Props) { socket.on('available-formats', (data: IDownloadInfo) => { setShowBackdrop(false) console.log(data) + setDownloadFormats(data); }) }, []) @@ -89,11 +100,37 @@ export default function Home({ socket }: Props) { * Retrive url from input, cli-arguments from checkboxes and emits via WebSocket */ const sendUrl = () => { + const codes = new Array(); + if (pickedVideoFormat !== '') codes.push(pickedVideoFormat); + if (pickedAudioFormat !== '') codes.push(pickedAudioFormat); + if (pickedBestFormat !== '') codes.push(pickedBestFormat); + socket.emit('send-url', { - url: url, - params: settings.cliArgs.toString(), + url: url || workingUrl, + params: settings.cliArgs.toString() + toFormatArgs(codes), }) setUrl('') + setWorkingUrl('') + setTimeout(() => { + const input = document.getElementById('urlInput') as HTMLInputElement; + input.value = ''; + setShowBackdrop(true); + setDownloadFormats(null); + }, 250); + } + + /** + * Retrive url from input and display the formats selection view + */ + const sendUrlFormatSelection = () => { + socket.emit('send-url-format-selection', { + url: url, + }) + setWorkingUrl(url) + setUrl('') + setPickedAudioFormat(''); + setPickedVideoFormat(''); + setPickedBestFormat(''); setTimeout(() => { const input = document.getElementById('urlInput') as HTMLInputElement; input.value = ''; @@ -120,6 +157,7 @@ export default function Home({ socket }: Props) { socket.emit('abort', { pid: id }) return } + setDownloadFormats(null) socket.emit('abort-all') } @@ -145,13 +183,14 @@ export default function Home({ socket }: Props) { label={settings.i18n.t('urlInput')} variant="outlined" onChange={handleUrlChange} + disabled={settings.formatSelection && downloadFormats != null} /> @@ -168,6 +207,110 @@ export default function Home({ socket }: Props) { + {/* Format Selection grid */} + {downloadFormats ? + + + + + + {downloadFormats.title} + + {/* */} + + + + + {/* video only */} + + + Best quality + + + + + + {/* video only */} + + + Video data + + + {downloadFormats.formats + .filter(format => format.acodec === 'none' && format.vcodec !== 'none') + .map((format, idx) => ( + + + + )) + } + + + Audio data + + + {downloadFormats.formats + .filter(format => format.acodec !== 'none' && format.vcodec === 'none') + .map((format, idx) => ( + + + + )) + } + + + + + + + + + + : null} { /*Super big brain flatMap moment*/ Array diff --git a/frontend/src/Settings.tsx b/frontend/src/Settings.tsx index df0f612..2012767 100644 --- a/frontend/src/Settings.tsx +++ b/frontend/src/Settings.tsx @@ -19,17 +19,15 @@ import { } from "@mui/material"; import React, { useState } from "react"; import { useDispatch, useSelector } from "react-redux"; -import { Socket } from "socket.io-client"; -import { LanguageUnion, setCliArgs, setLanguage, setServerAddr, setTheme, ThemeUnion } from "./features/settings/settingsSlice"; +import { io } from "socket.io-client"; +import { LanguageUnion, setCliArgs, setFormatSelection, setLanguage, setServerAddr, setTheme, ThemeUnion } from "./features/settings/settingsSlice"; import { alreadyUpdated, updated } from "./features/status/statusSlice"; import { RootState } from "./stores/store"; import { validateDomain, validateIP } from "./utils"; -type Props = { - socket: Socket -} +const socket = io(`http://${localStorage.getItem('server-addr') || 'localhost'}:3022`) -export default function Settings({ socket }: Props) { +export default function Settings() { const settings = useSelector((state: RootState) => state.settings) const status = useSelector((state: RootState) => state.status) const dispatch = useDispatch() @@ -155,6 +153,15 @@ export default function Settings({ socket }: Props) { } label={settings.i18n.t('extractAudioCheckbox')} /> + dispatch(setFormatSelection(!settings.formatSelection))} + /> + } + label={settings.i18n.t('formatSelectionEnabler')} + />