Code refactoring and bump deps
This commit is contained in:
10
Dockerfile
10
Dockerfile
@@ -1,7 +1,4 @@
|
|||||||
# Multi stage build Dockerfile
|
FROM golang:1.20-alpine AS build
|
||||||
|
|
||||||
# There's no point in using the edge (development branch of alpine)
|
|
||||||
FROM alpine:3.17 AS build
|
|
||||||
# folder structure
|
# folder structure
|
||||||
WORKDIR /usr/src/yt-dlp-webui
|
WORKDIR /usr/src/yt-dlp-webui
|
||||||
# install core dependencies
|
# install core dependencies
|
||||||
@@ -15,7 +12,7 @@ RUN npm install
|
|||||||
RUN npm run build
|
RUN npm run build
|
||||||
# build backend + incubator
|
# build backend + incubator
|
||||||
WORKDIR /usr/src/yt-dlp-webui
|
WORKDIR /usr/src/yt-dlp-webui
|
||||||
RUN go build -o yt-dlp-webui
|
RUN CGO_ENABLED=0 GOOS=linux go build -o yt-dlp-webui
|
||||||
|
|
||||||
# but here yes :)
|
# but here yes :)
|
||||||
FROM alpine:edge
|
FROM alpine:edge
|
||||||
@@ -28,8 +25,7 @@ WORKDIR /app
|
|||||||
RUN apk update && \
|
RUN apk update && \
|
||||||
apk add psmisc ffmpeg yt-dlp
|
apk add psmisc ffmpeg yt-dlp
|
||||||
|
|
||||||
COPY --from=build /usr/src/yt-dlp-webui /app
|
COPY --from=build /usr/src/yt-dlp-webui/yt-dlp-webui /app
|
||||||
RUN chmod +x /app/yt-dlp-webui
|
|
||||||
|
|
||||||
EXPOSE 3033
|
EXPOSE 3033
|
||||||
CMD [ "./yt-dlp-webui" , "--out", "/downloads" ]
|
CMD [ "./yt-dlp-webui" , "--out", "/downloads" ]
|
||||||
|
|||||||
10
Makefile
10
Makefile
@@ -1,14 +1,14 @@
|
|||||||
default:
|
default:
|
||||||
go build -o yt-dlp-webui main.go
|
CGO_ENABLED=0 go build -o yt-dlp-webui main.go
|
||||||
|
|
||||||
all:
|
all:
|
||||||
cd frontend && pnpm build && cd ..
|
cd frontend && pnpm build && cd ..
|
||||||
go build -o yt-dlp-webui main.go
|
CGO_ENABLED=0 go build -o yt-dlp-webui main.go
|
||||||
|
|
||||||
multiarch:
|
multiarch:
|
||||||
GOOS=linux GOARCH=arm go build -o yt-dlp-webui_linux-arm *.go
|
CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -o yt-dlp-webui_linux-arm main.go
|
||||||
GOOS=linux GOARCH=arm64 go build -o yt-dlp-webui_linux-arm64 *.go
|
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o yt-dlp-webui_linux-arm64 main.go
|
||||||
GOOS=linux GOARCH=amd64 go build -o yt-dlp-webui_linux-amd64 *.go
|
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o yt-dlp-webui_linux-amd64 main.go
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
mv yt-dlp-webui* build
|
mv yt-dlp-webui* build
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "yt-dlp-webui",
|
"name": "yt-dlp-webui",
|
||||||
"version": "2.0.6",
|
"version": "2.0.7",
|
||||||
"description": "Frontend compontent of yt-dlp-webui",
|
"description": "Frontend compontent of yt-dlp-webui",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
@@ -9,28 +9,28 @@
|
|||||||
"author": "marcopeocchi",
|
"author": "marcopeocchi",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.10.5",
|
"@emotion/react": "^11.10.6",
|
||||||
"@emotion/styled": "^11.10.5",
|
"@emotion/styled": "^11.10.6",
|
||||||
"@mui/icons-material": "^5.11.0",
|
"@mui/icons-material": "^5.11.16",
|
||||||
"@mui/material": "^5.11.5",
|
"@mui/material": "^5.12.0",
|
||||||
"@reduxjs/toolkit": "^1.9.1",
|
"@reduxjs/toolkit": "^1.9.3",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-redux": "^8.0.5",
|
"react-redux": "^8.0.5",
|
||||||
"react-router-dom": "^6.7.0",
|
"react-router-dom": "^6.9.0",
|
||||||
"rxjs": "^7.8.0",
|
"rxjs": "^7.8.0",
|
||||||
"uuid": "^9.0.0"
|
"uuid": "^9.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@modyfi/vite-plugin-yaml": "^1.0.4",
|
"@modyfi/vite-plugin-yaml": "^1.0.4",
|
||||||
"@types/node": "^18.11.18",
|
"@types/node": "^18.15.11",
|
||||||
"@types/react": "^18.0.21",
|
"@types/react": "^18.0.35",
|
||||||
"@types/react-dom": "^18.0.10",
|
"@types/react-dom": "^18.0.10",
|
||||||
"@types/react-router-dom": "^5.3.3",
|
"@types/react-router-dom": "^5.3.3",
|
||||||
"@types/uuid": "^9.0.0",
|
"@types/uuid": "^9.0.1",
|
||||||
"@vitejs/plugin-react": "^3.0.1",
|
"@vitejs/plugin-react": "^3.1.0",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"typescript": "^4.9.4",
|
"typescript": "^5.0.4",
|
||||||
"vite": "^4.0.4"
|
"vite": "^4.2.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,36 +1,38 @@
|
|||||||
import { ThemeProvider } from "@emotion/react";
|
import { ThemeProvider } from '@emotion/react'
|
||||||
import {
|
import {
|
||||||
ChevronLeft,
|
ChevronLeft,
|
||||||
Dashboard,
|
Dashboard,
|
||||||
// Download,
|
|
||||||
Menu, Settings as SettingsIcon,
|
|
||||||
FormatListBulleted,
|
FormatListBulleted,
|
||||||
|
Menu,
|
||||||
SettingsEthernet,
|
SettingsEthernet,
|
||||||
|
Settings as SettingsIcon,
|
||||||
Storage
|
Storage
|
||||||
} from "@mui/icons-material";
|
} from '@mui/icons-material'
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
createTheme, CssBaseline,
|
CssBaseline,
|
||||||
Divider,
|
Divider,
|
||||||
IconButton, List,
|
IconButton, List,
|
||||||
ListItemIcon, ListItemText, Toolbar,
|
ListItemIcon, ListItemText, Toolbar,
|
||||||
Typography
|
Typography,
|
||||||
} from "@mui/material";
|
createTheme
|
||||||
import { grey } from "@mui/material/colors";
|
} from '@mui/material'
|
||||||
import ListItemButton from '@mui/material/ListItemButton';
|
import ListItemButton from '@mui/material/ListItemButton'
|
||||||
import { lazy, Suspense, useMemo, useState } from "react";
|
import { grey } from '@mui/material/colors'
|
||||||
import { Provider, useDispatch, useSelector } from "react-redux";
|
import { Suspense, lazy, useMemo, useState } from 'react'
|
||||||
|
import { Provider, useDispatch, useSelector } from 'react-redux'
|
||||||
import {
|
import {
|
||||||
BrowserRouter as Router, Link, Route,
|
Link, Route,
|
||||||
|
BrowserRouter as Router,
|
||||||
Routes
|
Routes
|
||||||
} from 'react-router-dom';
|
} from 'react-router-dom'
|
||||||
import { AppBar } from "./components/AppBar";
|
import Home from './Home'
|
||||||
import { Drawer } from "./components/Drawer";
|
import { AppBar } from './components/AppBar'
|
||||||
import { toggleListView } from "./features/settings/settingsSlice";
|
import { Drawer } from './components/Drawer'
|
||||||
import Home from "./Home";
|
import { toggleListView } from './features/settings/settingsSlice'
|
||||||
import { RootState, store } from './stores/store';
|
import { RootState, store } from './stores/store'
|
||||||
import { formatGiB, getWebSocketEndpoint } from "./utils";
|
import { formatGiB, getWebSocketEndpoint } from './utils'
|
||||||
|
|
||||||
function AppContent() {
|
function AppContent() {
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
@@ -179,7 +181,7 @@ function AppContent() {
|
|||||||
</Box>
|
</Box>
|
||||||
</Router>
|
</Router>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function App() {
|
export function App() {
|
||||||
@@ -187,5 +189,5 @@ export function App() {
|
|||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<AppContent />
|
<AppContent />
|
||||||
</Provider>
|
</Provider>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { FileUpload } from "@mui/icons-material";
|
import { FileUpload } from '@mui/icons-material'
|
||||||
import {
|
import {
|
||||||
|
Alert,
|
||||||
Backdrop,
|
Backdrop,
|
||||||
Button,
|
Button,
|
||||||
ButtonGroup,
|
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
Container,
|
Container,
|
||||||
FormControl,
|
FormControl,
|
||||||
@@ -15,21 +15,21 @@ import {
|
|||||||
Select,
|
Select,
|
||||||
Snackbar,
|
Snackbar,
|
||||||
styled,
|
styled,
|
||||||
TextField,
|
TextField
|
||||||
Typography
|
} from '@mui/material'
|
||||||
} from "@mui/material";
|
import { Buffer } from 'buffer'
|
||||||
import { Buffer } from 'buffer';
|
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { DownloadsCardView } from './components/DownloadsCardView'
|
||||||
import { DownloadsCardView } from "./components/DownloadsCardView";
|
import { DownloadsListView } from './components/DownloadsListView'
|
||||||
import { DownloadsListView } from "./components/DownloadsListView";
|
import FormatsGrid from './components/FormatsGrid'
|
||||||
import { CliArguments } from "./features/core/argsParser";
|
import { CliArguments } from './features/core/argsParser'
|
||||||
import I18nBuilder from "./features/core/intl";
|
import I18nBuilder from './features/core/intl'
|
||||||
import { RPCClient } from "./features/core/rpcClient";
|
import { RPCClient } from './features/core/rpcClient'
|
||||||
import { connected, setFreeSpace } from "./features/status/statusSlice";
|
import { connected, setFreeSpace } from './features/status/statusSlice'
|
||||||
import { RootState } from "./stores/store";
|
import { RootState } from './stores/store'
|
||||||
import { IDLMetadata, RPCResult } from "./types";
|
import type { DLMetadata, RPCResult } from './types'
|
||||||
import { isValidURL, toFormatArgs } from "./utils";
|
import { isValidURL, toFormatArgs } from './utils'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
socket: WebSocket
|
socket: WebSocket
|
||||||
@@ -42,30 +42,35 @@ export default function Home({ socket }: Props) {
|
|||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
|
|
||||||
// ephemeral state
|
// ephemeral state
|
||||||
const [activeDownloads, setActiveDownloads] = useState<Array<RPCResult>>();
|
const [activeDownloads, setActiveDownloads] = useState<Array<RPCResult>>()
|
||||||
const [downloadFormats, setDownloadFormats] = useState<IDLMetadata>();
|
const [downloadFormats, setDownloadFormats] = useState<DLMetadata>()
|
||||||
const [pickedVideoFormat, setPickedVideoFormat] = useState('');
|
const [pickedVideoFormat, setPickedVideoFormat] = useState('')
|
||||||
const [pickedAudioFormat, setPickedAudioFormat] = useState('');
|
const [pickedAudioFormat, setPickedAudioFormat] = useState('')
|
||||||
const [pickedBestFormat, setPickedBestFormat] = useState('');
|
const [pickedBestFormat, setPickedBestFormat] = useState('')
|
||||||
|
|
||||||
const [customArgs, setCustomArgs] = useState('');
|
const [customArgs, setCustomArgs] = useState('')
|
||||||
const [downloadPath, setDownloadPath] = useState(0);
|
const [downloadPath, setDownloadPath] = useState(0)
|
||||||
const [availableDownloadPaths, setAvailableDownloadPaths] = useState<string[]>([]);
|
const [availableDownloadPaths, setAvailableDownloadPaths] = useState<string[]>([])
|
||||||
|
|
||||||
const [fileNameOverride, setFilenameOverride] = useState('');
|
const [fileNameOverride, setFilenameOverride] = useState('')
|
||||||
|
|
||||||
const [url, setUrl] = useState('');
|
const [url, setUrl] = useState('')
|
||||||
const [workingUrl, setWorkingUrl] = useState('');
|
const [workingUrl, setWorkingUrl] = useState('')
|
||||||
|
|
||||||
const [showBackdrop, setShowBackdrop] = useState(true);
|
const [showBackdrop, setShowBackdrop] = useState(true)
|
||||||
const [showToast, setShowToast] = useState(true);
|
const [showToast, setShowToast] = useState(true)
|
||||||
|
|
||||||
// memos
|
// memos
|
||||||
const i18n = useMemo(() => new I18nBuilder(settings.language), [settings.language])
|
const i18n = useMemo(() => new I18nBuilder(settings.language), [settings.language])
|
||||||
const client = useMemo(() => new RPCClient(socket), [settings.serverAddr, settings.serverPort])
|
const client = useMemo(() => new RPCClient(socket), [settings.serverAddr, settings.serverPort])
|
||||||
const cliArgs = useMemo(() => new CliArguments().fromString(settings.cliArgs), [settings.cliArgs])
|
const cliArgs = useMemo(() => new CliArguments().fromString(settings.cliArgs), [settings.cliArgs])
|
||||||
|
|
||||||
|
// refs
|
||||||
|
const urlInputRef = useRef<HTMLInputElement>(null)
|
||||||
|
const customFilenameInputRef = useRef<HTMLInputElement>(null)
|
||||||
|
|
||||||
/* -------------------- Effects -------------------- */
|
/* -------------------- Effects -------------------- */
|
||||||
|
|
||||||
/* WebSocket connect event handler*/
|
/* WebSocket connect event handler*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
socket.onopen = () => {
|
socket.onopen = () => {
|
||||||
@@ -84,8 +89,7 @@ export default function Home({ socket }: Props) {
|
|||||||
}, [status.connected])
|
}, [status.connected])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
client.freeSpace()
|
client.freeSpace().then(bytes => dispatch(setFreeSpace(bytes.result)))
|
||||||
.then(bytes => dispatch(setFreeSpace(bytes.result)))
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -118,7 +122,19 @@ export default function Home({ socket }: Props) {
|
|||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
/* -------------------- component functions -------------------- */
|
const [socketHasError, setSocketHasError] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
socket.onerror = () => {
|
||||||
|
setSocketHasError(true)
|
||||||
|
setShowBackdrop(false)
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
socket.onerror = null
|
||||||
|
}
|
||||||
|
}, [socket])
|
||||||
|
|
||||||
|
/* -------------------- callbacks-------------------- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrive url from input, cli-arguments from checkboxes and emits via WebSocket
|
* Retrive url from input, cli-arguments from checkboxes and emits via WebSocket
|
||||||
@@ -222,12 +238,9 @@ export default function Home({ socket }: Props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const resetInput = () => {
|
const resetInput = () => {
|
||||||
const input = document.getElementById('urlInput') as HTMLInputElement;
|
urlInputRef.current!.value = '';
|
||||||
input.value = '';
|
if (customFilenameInputRef.current) {
|
||||||
|
customFilenameInputRef.current!.value = '';
|
||||||
const filename = document.getElementById('customFilenameInput') as HTMLInputElement;
|
|
||||||
if (filename) {
|
|
||||||
filename.value = '';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +270,7 @@ export default function Home({ socket }: Props) {
|
|||||||
<Grid container>
|
<Grid container>
|
||||||
<TextField
|
<TextField
|
||||||
fullWidth
|
fullWidth
|
||||||
id="urlInput"
|
ref={urlInputRef}
|
||||||
label={i18n.t('urlInput')}
|
label={i18n.t('urlInput')}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
onChange={handleUrlChange}
|
onChange={handleUrlChange}
|
||||||
@@ -278,10 +291,9 @@ export default function Home({ socket }: Props) {
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Grid container spacing={1} sx={{ mt: 1 }}>
|
<Grid container spacing={1} sx={{ mt: 1 }}>
|
||||||
{
|
{
|
||||||
settings.enableCustomArgs ?
|
settings.enableCustomArgs &&
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<TextField
|
<TextField
|
||||||
id="customArgsInput"
|
|
||||||
fullWidth
|
fullWidth
|
||||||
label={i18n.t('customArgsInput')}
|
label={i18n.t('customArgsInput')}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
@@ -289,14 +301,13 @@ export default function Home({ socket }: Props) {
|
|||||||
value={customArgs}
|
value={customArgs}
|
||||||
disabled={!status.connected || (settings.formatSelection && downloadFormats != null)}
|
disabled={!status.connected || (settings.formatSelection && downloadFormats != null)}
|
||||||
/>
|
/>
|
||||||
</Grid> :
|
</Grid>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
settings.fileRenaming ?
|
settings.fileRenaming &&
|
||||||
<Grid item xs={8}>
|
<Grid item xs={8}>
|
||||||
<TextField
|
<TextField
|
||||||
id="customFilenameInput"
|
ref={customFilenameInputRef}
|
||||||
fullWidth
|
fullWidth
|
||||||
label={i18n.t('customFilename')}
|
label={i18n.t('customFilename')}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
@@ -304,11 +315,10 @@ export default function Home({ socket }: Props) {
|
|||||||
onChange={handleFilenameOverrideChange}
|
onChange={handleFilenameOverrideChange}
|
||||||
disabled={!status.connected || (settings.formatSelection && downloadFormats != null)}
|
disabled={!status.connected || (settings.formatSelection && downloadFormats != null)}
|
||||||
/>
|
/>
|
||||||
</Grid> :
|
</Grid>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
settings.pathOverriding ?
|
settings.pathOverriding &&
|
||||||
<Grid item xs={4}>
|
<Grid item xs={4}>
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
<InputLabel>{i18n.t('customPath')}</InputLabel>
|
<InputLabel>{i18n.t('customPath')}</InputLabel>
|
||||||
@@ -324,8 +334,7 @@ export default function Home({ socket }: Props) {
|
|||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Grid> :
|
</Grid>
|
||||||
null
|
|
||||||
}
|
}
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid container spacing={1} pt={2}>
|
<Grid container spacing={1} pt={2}>
|
||||||
@@ -351,120 +360,31 @@ export default function Home({ socket }: Props) {
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Grid >
|
</Grid >
|
||||||
{/* Format Selection grid */}
|
{/* Format Selection grid */}
|
||||||
{
|
{downloadFormats && <FormatsGrid
|
||||||
downloadFormats ? <Grid container spacing={2} mt={2}>
|
downloadFormats={downloadFormats}
|
||||||
<Grid item xs={12}>
|
onBestQualitySelected={(id) => {
|
||||||
<Paper
|
setPickedBestFormat(id)
|
||||||
sx={{
|
|
||||||
p: 2,
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Grid container>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Typography variant="h6" component="div" pb={1}>
|
|
||||||
{downloadFormats.title}
|
|
||||||
</Typography>
|
|
||||||
{/* <Skeleton variant="rectangular" height={180} /> */}
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} pb={1}>
|
|
||||||
<img src={downloadFormats.thumbnail} height={260} width="100%" style={{ objectFit: 'cover' }} />
|
|
||||||
</Grid>
|
|
||||||
{/* video only */}
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Typography variant="body1" component="div">
|
|
||||||
Best quality
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
<Grid item pr={2} py={1}>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
disabled={pickedBestFormat !== ''}
|
|
||||||
onClick={() => {
|
|
||||||
setPickedBestFormat(downloadFormats.best.format_id)
|
|
||||||
setPickedVideoFormat('')
|
setPickedVideoFormat('')
|
||||||
setPickedAudioFormat('')
|
setPickedAudioFormat('')
|
||||||
}}>
|
}}
|
||||||
{downloadFormats.best.format_note || downloadFormats.best.format_id} - {downloadFormats.best.vcodec}+{downloadFormats.best.acodec}
|
onVideoSelected={(id) => {
|
||||||
({downloadFormats.best.resolution}{(downloadFormats.best.filesize_approx>0)?", ~"+Math.round(downloadFormats.best.filesize_approx/1024/1024)+" MiB":""})
|
setPickedVideoFormat(id)
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
{/* video only */}
|
|
||||||
{downloadFormats.formats.filter(format => format.acodec === 'none' && format.vcodec !== 'none').length ?
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Typography variant="body1" component="div">
|
|
||||||
Video data {downloadFormats.formats[1].acodec}
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
{downloadFormats.formats
|
|
||||||
.filter(format => format.acodec === 'none' && format.vcodec !== 'none')
|
|
||||||
.map((format, idx) => (
|
|
||||||
<Grid item pr={2} py={1} key={idx}>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
onClick={() => {
|
|
||||||
setPickedVideoFormat(format.format_id)
|
|
||||||
setPickedBestFormat('')
|
setPickedBestFormat('')
|
||||||
}}
|
}}
|
||||||
disabled={pickedVideoFormat === format.format_id}
|
onAudioSelected={(id) => {
|
||||||
>
|
setPickedAudioFormat(id)
|
||||||
{format.format_note} - {format.vcodec === 'none' ? format.acodec : format.vcodec}
|
|
||||||
({format.resolution}{(format.filesize_approx>0)?", ~"+Math.round(format.filesize_approx/1024/1024)+" MiB":""})
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
{downloadFormats.formats.filter(format => format.acodec === 'none' && format.vcodec !== 'none').length ?
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Typography variant="body1" component="div">
|
|
||||||
Audio data
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
{downloadFormats.formats
|
|
||||||
.filter(format => format.acodec !== 'none' && format.vcodec === 'none')
|
|
||||||
.map((format, idx) => (
|
|
||||||
<Grid item pr={2} py={1} key={idx}>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
onClick={() => {
|
|
||||||
setPickedAudioFormat(format.format_id)
|
|
||||||
setPickedBestFormat('')
|
setPickedBestFormat('')
|
||||||
}}
|
}}
|
||||||
disabled={pickedAudioFormat === format.format_id}
|
onClear={() => {
|
||||||
>
|
|
||||||
{format.format_note} - {format.vcodec === 'none' ? format.acodec : format.vcodec}
|
|
||||||
{(format.filesize_approx>0)?" (~"+Math.round(format.filesize_approx/1024/1024)+" MiB)":""}
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
<Grid item xs={12} pt={2}>
|
|
||||||
<ButtonGroup disableElevation variant="contained">
|
|
||||||
<Button
|
|
||||||
onClick={() => sendUrl()}
|
|
||||||
disabled={!pickedBestFormat && !(pickedAudioFormat || pickedVideoFormat)}
|
|
||||||
> Download
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
setPickedAudioFormat('');
|
setPickedAudioFormat('');
|
||||||
setPickedVideoFormat('');
|
setPickedVideoFormat('');
|
||||||
setPickedBestFormat('');
|
setPickedBestFormat('');
|
||||||
}}
|
}}
|
||||||
> Clear
|
onSubmit={sendUrl}
|
||||||
</Button>
|
pickedBestFormat={pickedBestFormat}
|
||||||
</ButtonGroup>
|
pickedVideoFormat={pickedVideoFormat}
|
||||||
</Grid>
|
pickedAudioFormat={pickedAudioFormat}
|
||||||
</Grid>
|
/>}
|
||||||
</Paper>
|
|
||||||
</Grid>
|
|
||||||
</Grid> : null
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
settings.listView ?
|
settings.listView ?
|
||||||
<DownloadsListView downloads={activeDownloads ?? []} abortFunction={abort} /> :
|
<DownloadsListView downloads={activeDownloads ?? []} abortFunction={abort} /> :
|
||||||
@@ -473,9 +393,17 @@ export default function Home({ socket }: Props) {
|
|||||||
<Snackbar
|
<Snackbar
|
||||||
open={showToast === status.connected}
|
open={showToast === status.connected}
|
||||||
autoHideDuration={1500}
|
autoHideDuration={1500}
|
||||||
message="Connected"
|
|
||||||
onClose={() => setShowToast(false)}
|
onClose={() => setShowToast(false)}
|
||||||
/>
|
>
|
||||||
|
<Alert variant="filled" severity="success">
|
||||||
|
{`Connected to (${settings.serverAddr}:${settings.serverPort})`}
|
||||||
|
</Alert>
|
||||||
|
</Snackbar>
|
||||||
|
<Snackbar open={socketHasError}>
|
||||||
|
<Alert variant="filled" severity="error">
|
||||||
|
{`${i18n.t('rpcConnErr')} (${settings.serverAddr}:${settings.serverPort})`}
|
||||||
|
</Alert>
|
||||||
|
</Snackbar>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -16,15 +16,16 @@ import {
|
|||||||
Switch,
|
Switch,
|
||||||
TextField,
|
TextField,
|
||||||
Typography
|
Typography
|
||||||
} from "@mui/material";
|
} from '@mui/material'
|
||||||
import { useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
import { debounceTime, distinctUntilChanged, map, of, takeWhile } from "rxjs";
|
import { Subject, debounceTime, distinctUntilChanged, map, takeWhile } from 'rxjs'
|
||||||
import { CliArguments } from "./features/core/argsParser";
|
import { CliArguments } from './features/core/argsParser'
|
||||||
import I18nBuilder from "./features/core/intl";
|
import I18nBuilder from './features/core/intl'
|
||||||
import { RPCClient } from "./features/core/rpcClient";
|
import { RPCClient } from './features/core/rpcClient'
|
||||||
import {
|
import {
|
||||||
LanguageUnion,
|
LanguageUnion,
|
||||||
|
ThemeUnion,
|
||||||
setCliArgs,
|
setCliArgs,
|
||||||
setEnableCustomArgs,
|
setEnableCustomArgs,
|
||||||
setFileRenaming,
|
setFileRenaming,
|
||||||
@@ -33,32 +34,31 @@ import {
|
|||||||
setPathOverriding,
|
setPathOverriding,
|
||||||
setServerAddr,
|
setServerAddr,
|
||||||
setServerPort,
|
setServerPort,
|
||||||
setTheme,
|
setTheme
|
||||||
ThemeUnion
|
} from './features/settings/settingsSlice'
|
||||||
} from "./features/settings/settingsSlice";
|
import { updated } from './features/status/statusSlice'
|
||||||
import { updated } from "./features/status/statusSlice";
|
import { RootState } from './stores/store'
|
||||||
import { RootState } from "./stores/store";
|
import { validateDomain, validateIP } from './utils'
|
||||||
import { validateDomain, validateIP } from "./utils";
|
|
||||||
|
|
||||||
export default function Settings({ socket }: { socket: WebSocket }) {
|
export default function Settings({ socket }: { socket: WebSocket }) {
|
||||||
const settings = useSelector((state: RootState) => state.settings)
|
|
||||||
const status = useSelector((state: RootState) => state.status)
|
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
|
|
||||||
|
const status = useSelector((state: RootState) => state.status)
|
||||||
|
const settings = useSelector((state: RootState) => state.settings)
|
||||||
|
|
||||||
const [invalidIP, setInvalidIP] = useState(false);
|
const [invalidIP, setInvalidIP] = useState(false);
|
||||||
|
|
||||||
const i18n = useMemo(() => new I18nBuilder(settings.language), [settings.language])
|
const i18n = useMemo(() => new I18nBuilder(settings.language), [settings.language])
|
||||||
const client = useMemo(() => new RPCClient(socket), [settings.serverAddr, settings.serverPort])
|
|
||||||
const cliArgs = useMemo(() => new CliArguments().fromString(settings.cliArgs), [settings.cliArgs])
|
const client = useMemo(() => new RPCClient(socket), [])
|
||||||
/**
|
const cliArgs = useMemo(() => new CliArguments().fromString(settings.cliArgs), [])
|
||||||
* Update the server ip address state and localstorage whenever the input value changes.
|
|
||||||
* Validate the ip-addr then set.s
|
const serverAddr$ = useMemo(() => new Subject<string>(), [])
|
||||||
* @param event Input change event
|
const serverPort$ = useMemo(() => new Subject<string>(), [])
|
||||||
*/
|
|
||||||
const handleAddrChange = (event: any) => {
|
useEffect(() => {
|
||||||
const $serverAddr = of(event)
|
const sub = serverAddr$
|
||||||
.pipe(
|
.pipe(
|
||||||
map(event => event.target.value),
|
|
||||||
debounceTime(500),
|
debounceTime(500),
|
||||||
distinctUntilChanged()
|
distinctUntilChanged()
|
||||||
)
|
)
|
||||||
@@ -73,24 +73,20 @@ export default function Settings({ socket }: { socket: WebSocket }) {
|
|||||||
setInvalidIP(true)
|
setInvalidIP(true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return $serverAddr.unsubscribe()
|
return () => sub.unsubscribe()
|
||||||
}
|
}, [serverAddr$])
|
||||||
|
|
||||||
/**
|
useEffect(() => {
|
||||||
* Set server port
|
const sub = serverPort$
|
||||||
*/
|
|
||||||
const handlePortChange = (event: any) => {
|
|
||||||
const $port = of(event)
|
|
||||||
.pipe(
|
.pipe(
|
||||||
map(event => event.target.value),
|
|
||||||
map(val => Number(val)),
|
map(val => Number(val)),
|
||||||
takeWhile(val => isFinite(val) && val <= 65535),
|
takeWhile(val => isFinite(val) && val <= 65535),
|
||||||
)
|
)
|
||||||
.subscribe(port => {
|
.subscribe(port => {
|
||||||
dispatch(setServerPort(port.toString()))
|
dispatch(setServerPort(port.toString()))
|
||||||
})
|
})
|
||||||
return $port.unsubscribe()
|
return () => sub.unsubscribe()
|
||||||
}
|
}, [])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Language toggler handler
|
* Language toggler handler
|
||||||
@@ -107,7 +103,7 @@ export default function Settings({ socket }: { socket: WebSocket }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send via WebSocket a message in order to update the yt-dlp binary from server
|
* Send via WebSocket a message to update yt-dlp binary
|
||||||
*/
|
*/
|
||||||
const updateBinary = () => {
|
const updateBinary = () => {
|
||||||
client.updateExecutable().then(() => dispatch(updated()))
|
client.updateExecutable().then(() => dispatch(updated()))
|
||||||
@@ -136,7 +132,7 @@ export default function Settings({ socket }: { socket: WebSocket }) {
|
|||||||
label={i18n.t('serverAddressTitle')}
|
label={i18n.t('serverAddressTitle')}
|
||||||
defaultValue={settings.serverAddr}
|
defaultValue={settings.serverAddr}
|
||||||
error={invalidIP}
|
error={invalidIP}
|
||||||
onChange={handleAddrChange}
|
onChange={(e) => serverAddr$.next(e.currentTarget.value)}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
startAdornment: <InputAdornment position="start">ws://</InputAdornment>,
|
startAdornment: <InputAdornment position="start">ws://</InputAdornment>,
|
||||||
}}
|
}}
|
||||||
@@ -148,7 +144,7 @@ export default function Settings({ socket }: { socket: WebSocket }) {
|
|||||||
fullWidth
|
fullWidth
|
||||||
label={i18n.t('serverPortTitle')}
|
label={i18n.t('serverPortTitle')}
|
||||||
defaultValue={settings.serverPort}
|
defaultValue={settings.serverPort}
|
||||||
onChange={handlePortChange}
|
onChange={(e) => serverPort$.next(e.currentTarget.value)}
|
||||||
error={isNaN(Number(settings.serverPort)) || Number(settings.serverPort) > 65535}
|
error={isNaN(Number(settings.serverPort)) || Number(settings.serverPort) > 65535}
|
||||||
sx={{ mb: 2 }}
|
sx={{ mb: 2 }}
|
||||||
/>
|
/>
|
||||||
@@ -186,16 +182,6 @@ export default function Settings({ socket }: { socket: WebSocket }) {
|
|||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
{/* <Grid item xs={12} md={6}>
|
|
||||||
<TextField
|
|
||||||
fullWidth
|
|
||||||
label={'Max download speed' || i18n.t('serverPortTitle')}
|
|
||||||
defaultValue={settings.serverPort}
|
|
||||||
onChange={handlePortChange}
|
|
||||||
error={isNaN(Number(settings.serverPort)) || Number(settings.serverPort) > 65535}
|
|
||||||
sx={{ mb: 2 }}
|
|
||||||
/>
|
|
||||||
</Grid> */}
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
control={
|
control={
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ languages:
|
|||||||
customPath: Custom path
|
customPath: Custom path
|
||||||
customArgs: Enable custom yt-dlp args (great power = great responsabilities)
|
customArgs: Enable custom yt-dlp args (great power = great responsabilities)
|
||||||
customArgsInput: Custom yt-dlp arguments
|
customArgsInput: Custom yt-dlp arguments
|
||||||
|
rpcConnErr: Error while conencting to RPC server
|
||||||
italian:
|
italian:
|
||||||
urlInput: URL di YouTube o di qualsiasi altro servizio supportato
|
urlInput: URL di YouTube o di qualsiasi altro servizio supportato
|
||||||
statusTitle: Stato
|
statusTitle: Stato
|
||||||
@@ -55,6 +56,7 @@ languages:
|
|||||||
customPath: Custom path
|
customPath: Custom path
|
||||||
customArgs: Enable custom yt-dlp args (great power = great responsabilities)
|
customArgs: Enable custom yt-dlp args (great power = great responsabilities)
|
||||||
customArgsInput: Custom yt-dlp arguments
|
customArgsInput: Custom yt-dlp arguments
|
||||||
|
rpcConnErr: Error nella connessione al server RPC
|
||||||
chinese:
|
chinese:
|
||||||
urlInput: YouTube 或其他受支持服务的视频网址
|
urlInput: YouTube 或其他受支持服务的视频网址
|
||||||
statusTitle: 状态
|
statusTitle: 状态
|
||||||
@@ -82,6 +84,7 @@ languages:
|
|||||||
customPath: 自定义路径
|
customPath: 自定义路径
|
||||||
customArgs: 启用自定义 yt-dlp 参数(能力越大 = 责任越大)
|
customArgs: 启用自定义 yt-dlp 参数(能力越大 = 责任越大)
|
||||||
customArgsInput: 自定义 yt-dlp 参数
|
customArgsInput: 自定义 yt-dlp 参数
|
||||||
|
rpcConnErr: Error while conencting to RPC server
|
||||||
spanish:
|
spanish:
|
||||||
urlInput: YouTube or other supported service video url
|
urlInput: YouTube or other supported service video url
|
||||||
statusTitle: Status
|
statusTitle: Status
|
||||||
@@ -109,6 +112,7 @@ languages:
|
|||||||
customPath: Custom path
|
customPath: Custom path
|
||||||
customArgs: Enable custom yt-dlp args (great power = great responsabilities)
|
customArgs: Enable custom yt-dlp args (great power = great responsabilities)
|
||||||
customArgsInput: Custom yt-dlp arguments
|
customArgsInput: Custom yt-dlp arguments
|
||||||
|
rpcConnErr: Error while conencting to RPC server
|
||||||
russian:
|
russian:
|
||||||
urlInput: YouTube or other supported service video url
|
urlInput: YouTube or other supported service video url
|
||||||
statusTitle: Status
|
statusTitle: Status
|
||||||
@@ -136,6 +140,7 @@ languages:
|
|||||||
customPath: Custom path
|
customPath: Custom path
|
||||||
customArgs: Enable custom yt-dlp args (great power = great responsabilities)
|
customArgs: Enable custom yt-dlp args (great power = great responsabilities)
|
||||||
customArgsInput: Custom yt-dlp arguments
|
customArgsInput: Custom yt-dlp arguments
|
||||||
|
rpcConnErr: Error while conencting to RPC server
|
||||||
korean:
|
korean:
|
||||||
urlInput: YouTube나 다른 지원되는 사이트의 URL
|
urlInput: YouTube나 다른 지원되는 사이트의 URL
|
||||||
statusTitle: 상태
|
statusTitle: 상태
|
||||||
@@ -163,6 +168,7 @@ languages:
|
|||||||
customPath: Custom path
|
customPath: Custom path
|
||||||
customArgs: Enable custom yt-dlp args (great power = great responsabilities)
|
customArgs: Enable custom yt-dlp args (great power = great responsabilities)
|
||||||
customArgsInput: Custom yt-dlp arguments
|
customArgsInput: Custom yt-dlp arguments
|
||||||
|
rpcConnErr: Error while conencting to RPC server
|
||||||
japanese:
|
japanese:
|
||||||
urlInput: YouTubeまたはサポート済み動画のURL
|
urlInput: YouTubeまたはサポート済み動画のURL
|
||||||
statusTitle: 状態
|
statusTitle: 状態
|
||||||
@@ -191,3 +197,4 @@ languages:
|
|||||||
customPath: 保存先
|
customPath: 保存先
|
||||||
customArgs: yt-dlpのオプションの有効化 (最適設定にする場合)
|
customArgs: yt-dlpのオプションの有効化 (最適設定にする場合)
|
||||||
customArgsInput: yt-dlpのオプション
|
customArgsInput: yt-dlpのオプション
|
||||||
|
rpcConnErr: Error while conencting to RPC server
|
||||||
126
frontend/src/components/FormatsGrid.tsx
Normal file
126
frontend/src/components/FormatsGrid.tsx
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import { Button, ButtonGroup, Grid, Paper, Typography } from "@mui/material"
|
||||||
|
import type { DLMetadata } from "../types"
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
downloadFormats: DLMetadata
|
||||||
|
onAudioSelected: (format: string) => void
|
||||||
|
onVideoSelected: (format: string) => void
|
||||||
|
onBestQualitySelected: (format: string) => void
|
||||||
|
onSubmit: () => void
|
||||||
|
onClear: () => void
|
||||||
|
pickedBestFormat: string
|
||||||
|
pickedAudioFormat: string
|
||||||
|
pickedVideoFormat: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function FormatsGrid({
|
||||||
|
downloadFormats,
|
||||||
|
onAudioSelected,
|
||||||
|
onVideoSelected,
|
||||||
|
onBestQualitySelected,
|
||||||
|
onSubmit,
|
||||||
|
onClear,
|
||||||
|
pickedBestFormat,
|
||||||
|
pickedAudioFormat,
|
||||||
|
pickedVideoFormat,
|
||||||
|
}: Props) {
|
||||||
|
return (
|
||||||
|
<Grid container spacing={2} mt={2}>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Paper
|
||||||
|
sx={{
|
||||||
|
p: 2,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Grid container>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Typography variant="h6" component="div" pb={1}>
|
||||||
|
{downloadFormats.title}
|
||||||
|
</Typography>
|
||||||
|
{/* <Skeleton variant="rectangular" height={180} /> */}
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} pb={1}>
|
||||||
|
<img src={downloadFormats.thumbnail} height={260} width="100%" style={{ objectFit: 'cover' }} />
|
||||||
|
</Grid>
|
||||||
|
{/* video only */}
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Typography variant="body1" component="div">
|
||||||
|
Best quality
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item pr={2} py={1}>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
disabled={pickedBestFormat !== ''}
|
||||||
|
onClick={() => onBestQualitySelected(downloadFormats.best.format_id)}
|
||||||
|
>
|
||||||
|
{downloadFormats.best.format_note || downloadFormats.best.format_id} - {downloadFormats.best.vcodec}+{downloadFormats.best.acodec}
|
||||||
|
({downloadFormats.best.resolution}{(downloadFormats.best.filesize_approx > 0) ? ", ~" + Math.round(downloadFormats.best.filesize_approx / 1024 / 1024) + " MiB" : ""})
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
{/* video only */}
|
||||||
|
{downloadFormats.formats.filter(format => format.acodec === 'none' && format.vcodec !== 'none').length &&
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Typography variant="body1" component="div">
|
||||||
|
Video data {downloadFormats.formats[1].acodec}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
}
|
||||||
|
{downloadFormats.formats
|
||||||
|
.filter(format => format.acodec === 'none' && format.vcodec !== 'none')
|
||||||
|
.map((format, idx) => (
|
||||||
|
<Grid item pr={2} py={1} key={idx}>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={() => onVideoSelected(format.format_id)}
|
||||||
|
disabled={pickedVideoFormat === format.format_id}
|
||||||
|
>
|
||||||
|
{format.format_note} - {format.vcodec === 'none' ? format.acodec : format.vcodec}
|
||||||
|
({format.resolution}{(format.filesize_approx > 0) ? ", ~" + Math.round(format.filesize_approx / 1024 / 1024) + " MiB" : ""})
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
{downloadFormats.formats.filter(format => format.acodec === 'none' && format.vcodec !== 'none').length &&
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Typography variant="body1" component="div">
|
||||||
|
Audio data
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
}
|
||||||
|
{downloadFormats.formats
|
||||||
|
.filter(format => format.acodec !== 'none' && format.vcodec === 'none')
|
||||||
|
.map((format, idx) => (
|
||||||
|
<Grid item pr={2} py={1} key={idx}>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={() => onAudioSelected(format.format_id)}
|
||||||
|
disabled={pickedAudioFormat === format.format_id}
|
||||||
|
>
|
||||||
|
{format.format_note} - {format.vcodec === 'none' ? format.acodec : format.vcodec}
|
||||||
|
{(format.filesize_approx > 0) ? " (~" + Math.round(format.filesize_approx / 1024 / 1024) + " MiB)" : ""}
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
<Grid item xs={12} pt={2}>
|
||||||
|
<ButtonGroup disableElevation variant="contained">
|
||||||
|
<Button
|
||||||
|
onClick={() => onSubmit()}
|
||||||
|
disabled={!pickedBestFormat && !(pickedAudioFormat || pickedVideoFormat)}
|
||||||
|
> Download
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => onClear()}
|
||||||
|
> Clear
|
||||||
|
</Button>
|
||||||
|
</ButtonGroup>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Paper>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { RPCRequest, RPCResponse, IDLMetadata } from "../../types"
|
import type { RPCRequest, RPCResponse, DLMetadata } from "../../types"
|
||||||
|
|
||||||
import { getHttpRPCEndpoint } from '../../utils'
|
import { getHttpRPCEndpoint } from '../../utils'
|
||||||
|
|
||||||
@@ -19,18 +19,17 @@ export class RPCClient {
|
|||||||
this.socket.send(JSON.stringify(req))
|
this.socket.send(JSON.stringify(req))
|
||||||
}
|
}
|
||||||
|
|
||||||
private sendHTTP<T>(req: RPCRequest) {
|
private async sendHTTP<T>(req: RPCRequest) {
|
||||||
return new Promise<RPCResponse<T>>((resolve) => {
|
const res = await fetch(getHttpRPCEndpoint(), {
|
||||||
fetch(getHttpRPCEndpoint(), {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
|
...req,
|
||||||
id: this.incrementSeq(),
|
id: this.incrementSeq(),
|
||||||
...req
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.then(res => res.json())
|
const data: RPCResponse<T> = await res.json()
|
||||||
.then(data => resolve(data))
|
|
||||||
})
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
public download(url: string, args: string, pathOverride = '', renameTo = '') {
|
public download(url: string, args: string, pathOverride = '', renameTo = '') {
|
||||||
@@ -50,7 +49,7 @@ export class RPCClient {
|
|||||||
|
|
||||||
public formats(url: string) {
|
public formats(url: string) {
|
||||||
if (url) {
|
if (url) {
|
||||||
return this.sendHTTP<IDLMetadata>({
|
return this.sendHTTP<DLMetadata>({
|
||||||
id: this.incrementSeq(),
|
id: this.incrementSeq(),
|
||||||
method: 'Service.Formats',
|
method: 'Service.Formats',
|
||||||
params: [{
|
params: [{
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import React from 'react'
|
import { StrictMode } from 'react'
|
||||||
import ReactDOM from 'react-dom/client'
|
import { createRoot } from 'react-dom/client'
|
||||||
import { App } from './App'
|
import { App } from './App'
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById('root')!)
|
const root = createRoot(document.getElementById('root')!)
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<StrictMode>
|
||||||
<App></App>
|
<App />
|
||||||
</React.StrictMode>
|
</StrictMode>
|
||||||
)
|
)
|
||||||
|
|||||||
8
frontend/src/types.d.ts
vendored
8
frontend/src/types.d.ts
vendored
@@ -45,14 +45,14 @@ export type RPCParams = {
|
|||||||
Params?: string
|
Params?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IDLMetadata {
|
export interface DLMetadata {
|
||||||
formats: Array<IDLFormat>,
|
formats: Array<DLFormat>,
|
||||||
best: IDLFormat,
|
best: DLFormat,
|
||||||
thumbnail: string,
|
thumbnail: string,
|
||||||
title: string,
|
title: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IDLFormat {
|
export interface DLFormat {
|
||||||
format_id: string,
|
format_id: string,
|
||||||
format_note: string,
|
format_note: string,
|
||||||
fps: number,
|
fps: number,
|
||||||
|
|||||||
27
go.mod
27
go.mod
@@ -3,25 +3,28 @@ module github.com/marcopeocchi/yt-dlp-web-ui
|
|||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/goccy/go-json v0.10.0
|
github.com/goccy/go-json v0.10.2
|
||||||
github.com/gofiber/fiber/v2 v2.41.0
|
github.com/gofiber/fiber/v2 v2.43.0
|
||||||
github.com/gofiber/websocket/v2 v2.1.2
|
github.com/gofiber/websocket/v2 v2.1.5
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/marcopeocchi/fazzoletti v0.0.0-20221114144444-1e802380a7db
|
github.com/marcopeocchi/fazzoletti v0.0.0-20230308161120-c545580f79fa
|
||||||
golang.org/x/sys v0.4.0
|
golang.org/x/sys v0.7.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||||
github.com/fasthttp/websocket v1.5.0 // indirect
|
github.com/fasthttp/websocket v1.5.2 // indirect
|
||||||
github.com/klauspost/compress v1.15.14 // indirect
|
github.com/klauspost/compress v1.16.4 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
github.com/mattn/go-isatty v0.0.18 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||||
github.com/rivo/uniseg v0.4.3 // indirect
|
github.com/philhofer/fwd v1.1.2 // indirect
|
||||||
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d // indirect
|
github.com/rivo/uniseg v0.4.4 // indirect
|
||||||
|
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect
|
||||||
|
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
|
||||||
|
github.com/tinylib/msgp v1.1.8 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasthttp v1.43.0 // indirect
|
github.com/valyala/fasthttp v1.45.0 // indirect
|
||||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
69
go.sum
69
go.sum
@@ -1,61 +1,130 @@
|
|||||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
|
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||||
|
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
github.com/fasthttp/websocket v1.5.0 h1:B4zbe3xXyvIdnqjOZrafVFklCUq5ZLo/TqCt5JA1wLE=
|
github.com/fasthttp/websocket v1.5.0 h1:B4zbe3xXyvIdnqjOZrafVFklCUq5ZLo/TqCt5JA1wLE=
|
||||||
github.com/fasthttp/websocket v1.5.0/go.mod h1:n0BlOQvJdPbTuBkZT0O5+jk/sp/1/VCzquR1BehI2F4=
|
github.com/fasthttp/websocket v1.5.0/go.mod h1:n0BlOQvJdPbTuBkZT0O5+jk/sp/1/VCzquR1BehI2F4=
|
||||||
|
github.com/fasthttp/websocket v1.5.2 h1:KdCb0EpLpdJpfE3IPA5YLK/aYBO3dhZcvwxz6tXe2LQ=
|
||||||
|
github.com/fasthttp/websocket v1.5.2/go.mod h1:S0KC1VBlx1SaXGXq7yi1wKz4jMub58qEnHQG9oHuqBw=
|
||||||
github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
|
github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
|
||||||
github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/gofiber/fiber/v2 v2.40.1/go.mod h1:Gko04sLksnHbzLSRBFWPFdzM9Ws9pRxvvIaohJK1dsk=
|
github.com/gofiber/fiber/v2 v2.40.1/go.mod h1:Gko04sLksnHbzLSRBFWPFdzM9Ws9pRxvvIaohJK1dsk=
|
||||||
github.com/gofiber/fiber/v2 v2.41.0 h1:YhNoUS/OTjEz+/WLYuQ01xI7RXgKEFnGBKMagAu5f0M=
|
github.com/gofiber/fiber/v2 v2.41.0 h1:YhNoUS/OTjEz+/WLYuQ01xI7RXgKEFnGBKMagAu5f0M=
|
||||||
github.com/gofiber/fiber/v2 v2.41.0/go.mod h1:RdebcCuCRFp4W6hr3968/XxwJVg0K+jr9/Ae0PFzZ0Q=
|
github.com/gofiber/fiber/v2 v2.41.0/go.mod h1:RdebcCuCRFp4W6hr3968/XxwJVg0K+jr9/Ae0PFzZ0Q=
|
||||||
|
github.com/gofiber/fiber/v2 v2.43.0 h1:yit3E4kHf178B60p5CQBa/3v+WVuziWMa/G2ZNyLJB0=
|
||||||
|
github.com/gofiber/fiber/v2 v2.43.0/go.mod h1:mpS1ZNE5jU+u+BA4FbM+KKnUzJ4wzTK+FT2tG3tU+6I=
|
||||||
github.com/gofiber/websocket/v2 v2.1.2 h1:EulKyLB/fJgui5+6c8irwEnYQ9FRsrLZfkrq9OfTDGc=
|
github.com/gofiber/websocket/v2 v2.1.2 h1:EulKyLB/fJgui5+6c8irwEnYQ9FRsrLZfkrq9OfTDGc=
|
||||||
github.com/gofiber/websocket/v2 v2.1.2/go.mod h1:S+sKWo0xeC7Wnz5h4/8f6D/NxsrLFIdWDYB3SyVO9pE=
|
github.com/gofiber/websocket/v2 v2.1.2/go.mod h1:S+sKWo0xeC7Wnz5h4/8f6D/NxsrLFIdWDYB3SyVO9pE=
|
||||||
|
github.com/gofiber/websocket/v2 v2.1.5 h1:2weAMr0Shb2ubhZ3+P4bkeWL+uCZ/NlgjSa1siEcvFM=
|
||||||
|
github.com/gofiber/websocket/v2 v2.1.5/go.mod h1:BZZEk+XsjjF0V6/sAw00iGcB69dFb6Hb85ER9gr/xaU=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/klauspost/compress v1.14.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
github.com/klauspost/compress v1.14.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||||
github.com/klauspost/compress v1.15.14 h1:i7WCKDToww0wA+9qrUZ1xOjp218vfFo3nTU6UHp+gOc=
|
github.com/klauspost/compress v1.15.14 h1:i7WCKDToww0wA+9qrUZ1xOjp218vfFo3nTU6UHp+gOc=
|
||||||
github.com/klauspost/compress v1.15.14/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
github.com/klauspost/compress v1.15.14/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
||||||
|
github.com/klauspost/compress v1.16.4 h1:91KN02FnsOYhuunwU4ssRe8lc2JosWmizWa91B5v1PU=
|
||||||
|
github.com/klauspost/compress v1.16.4/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||||
github.com/marcopeocchi/fazzoletti v0.0.0-20221114144444-1e802380a7db h1:SmKRgCLsImPxBTIzmUpbQyv+7FembiZaq/QTwtDqar4=
|
github.com/marcopeocchi/fazzoletti v0.0.0-20221114144444-1e802380a7db h1:SmKRgCLsImPxBTIzmUpbQyv+7FembiZaq/QTwtDqar4=
|
||||||
github.com/marcopeocchi/fazzoletti v0.0.0-20221114144444-1e802380a7db/go.mod h1:RvfVo/6Sbnfra9kkvIxDW8NYOOaYsHjF0DdtMCs9cdo=
|
github.com/marcopeocchi/fazzoletti v0.0.0-20221114144444-1e802380a7db/go.mod h1:RvfVo/6Sbnfra9kkvIxDW8NYOOaYsHjF0DdtMCs9cdo=
|
||||||
|
github.com/marcopeocchi/fazzoletti v0.0.0-20230308161120-c545580f79fa h1:uaAQLGhN4SesB9inOQ1Q6EH+BwTWHQOvwhR0TIJvnYc=
|
||||||
|
github.com/marcopeocchi/fazzoletti v0.0.0-20230308161120-c545580f79fa/go.mod h1:RvfVo/6Sbnfra9kkvIxDW8NYOOaYsHjF0DdtMCs9cdo=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
||||||
|
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
|
github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||||
|
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
|
||||||
|
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw=
|
github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw=
|
||||||
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
|
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||||
|
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
|
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 h1:rmMl4fXJhKMNWl+K+r/fq4FbbKI+Ia2m9hYBLm2h4G4=
|
||||||
|
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94/go.mod h1:90zrgN3D/WJsDd1iXHT96alCoN2KJo6/4x1DZC3wZs8=
|
||||||
github.com/savsgio/gotils v0.0.0-20211223103454-d0aaa54c5899/go.mod h1:oejLrk1Y/5zOF+c/aHtXqn3TFlzzbAgPWg8zBiAHDas=
|
github.com/savsgio/gotils v0.0.0-20211223103454-d0aaa54c5899/go.mod h1:oejLrk1Y/5zOF+c/aHtXqn3TFlzzbAgPWg8zBiAHDas=
|
||||||
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d h1:Q+gqLBOPkFGHyCJxXMRqtUgUbTjI8/Ze8vu8GGyNFwo=
|
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d h1:Q+gqLBOPkFGHyCJxXMRqtUgUbTjI8/Ze8vu8GGyNFwo=
|
||||||
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4=
|
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4=
|
||||||
|
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk=
|
||||||
|
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g=
|
||||||
|
github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw=
|
||||||
|
github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
|
||||||
|
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasthttp v1.33.0/go.mod h1:KJRK/MXx0J+yd0c5hlR+s1tIHD72sniU8ZJjl97LIw4=
|
github.com/valyala/fasthttp v1.33.0/go.mod h1:KJRK/MXx0J+yd0c5hlR+s1tIHD72sniU8ZJjl97LIw4=
|
||||||
github.com/valyala/fasthttp v1.41.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY=
|
github.com/valyala/fasthttp v1.41.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY=
|
||||||
github.com/valyala/fasthttp v1.43.0 h1:Gy4sb32C98fbzVWZlTM1oTMdLWGyvxR03VhM6cBIU4g=
|
github.com/valyala/fasthttp v1.43.0 h1:Gy4sb32C98fbzVWZlTM1oTMdLWGyvxR03VhM6cBIU4g=
|
||||||
github.com/valyala/fasthttp v1.43.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY=
|
github.com/valyala/fasthttp v1.43.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY=
|
||||||
|
github.com/valyala/fasthttp v1.45.0 h1:zPkkzpIn8tdHZUrVa6PzYd0i5verqiPSkgTd3bSUcpA=
|
||||||
|
github.com/valyala/fasthttp v1.45.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
|
||||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||||
|
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
||||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||||
|
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
|||||||
9
main.go
9
main.go
@@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"embed"
|
"embed"
|
||||||
"flag"
|
"flag"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
@@ -11,8 +10,6 @@ import (
|
|||||||
"github.com/marcopeocchi/yt-dlp-web-ui/server/config"
|
"github.com/marcopeocchi/yt-dlp-web-ui/server/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ContextKey interface{}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
port int
|
port int
|
||||||
downloadPath string
|
downloadPath string
|
||||||
@@ -48,9 +45,5 @@ func main() {
|
|||||||
cfg.DownloadPath(downloadPath)
|
cfg.DownloadPath(downloadPath)
|
||||||
cfg.DownloaderPath(downloaderPath)
|
cfg.DownloaderPath(downloaderPath)
|
||||||
|
|
||||||
ctx := context.Background()
|
server.RunBlocking(port, frontend)
|
||||||
ctx = context.WithValue(ctx, ContextKey("port"), port)
|
|
||||||
ctx = context.WithValue(ctx, ContextKey("frontend"), frontend)
|
|
||||||
|
|
||||||
server.RunBlocking(ctx)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
@@ -17,10 +16,7 @@ import (
|
|||||||
|
|
||||||
var db MemoryDB
|
var db MemoryDB
|
||||||
|
|
||||||
func RunBlocking(ctx context.Context) {
|
func RunBlocking(port int, frontend fs.FS) {
|
||||||
fe := ctx.Value("frontend").(fs.SubFS)
|
|
||||||
port := ctx.Value("port").(int)
|
|
||||||
|
|
||||||
service := new(Service)
|
service := new(Service)
|
||||||
rpc.Register(service)
|
rpc.Register(service)
|
||||||
|
|
||||||
@@ -28,9 +24,13 @@ func RunBlocking(ctx context.Context) {
|
|||||||
|
|
||||||
app.Use(cors.New())
|
app.Use(cors.New())
|
||||||
app.Use("/", filesystem.New(filesystem.Config{
|
app.Use("/", filesystem.New(filesystem.Config{
|
||||||
Root: http.FS(fe),
|
Root: http.FS(frontend),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
app.Get("/settings", func(c *fiber.Ctx) error {
|
||||||
|
return c.Redirect("/")
|
||||||
|
})
|
||||||
|
|
||||||
// RPC handlers
|
// RPC handlers
|
||||||
// websocket
|
// websocket
|
||||||
app.Get("/ws-rpc", websocket.New(func(c *websocket.Conn) {
|
app.Get("/ws-rpc", websocket.New(func(c *websocket.Conn) {
|
||||||
@@ -52,8 +52,10 @@ func RunBlocking(ctx context.Context) {
|
|||||||
app.Post("/http-rpc", func(c *fiber.Ctx) error {
|
app.Post("/http-rpc", func(c *fiber.Ctx) error {
|
||||||
reader := c.Context().RequestBodyStream()
|
reader := c.Context().RequestBodyStream()
|
||||||
writer := c.Response().BodyWriter()
|
writer := c.Response().BodyWriter()
|
||||||
|
|
||||||
res := NewRPCRequest(reader).Call()
|
res := NewRPCRequest(reader).Call()
|
||||||
io.Copy(writer, res)
|
io.Copy(writer, res)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user