Compare commits

...

15 Commits

Author SHA1 Message Date
Marco
d9566c2fcc Update README.md 2023-01-13 20:10:14 +01:00
Marco
dfd29e9e0b Merge pull request #29 from deluxghost/patch-3
Update chinese translation
2023-01-13 20:09:20 +01:00
deluxghost
fc61a00beb Update chinese translation 2023-01-14 01:17:44 +08:00
Marco
a05ae0b9f4 Delete fetch-yt-dlp.sh 2023-01-13 18:00:04 +01:00
185d6efc5a fixed issues addressed by #9 2023-01-13 17:39:02 +01:00
76e8832071 updated Dockerfile 2023-01-13 11:44:04 +01:00
7d2503fe77 code refactoring 2023-01-13 11:43:56 +01:00
Marco
dcd80b7366 Update README.md 2023-01-13 11:16:07 +01:00
Marco
515888b156 Update README.md 2023-01-13 10:53:16 +01:00
Marco
57cc86d328 Update docker-publish.yml 2023-01-13 10:45:23 +01:00
d2f1bfa3bf fixed high cpu due to unproerly closed channel 2023-01-13 10:15:16 +01:00
00645e2370 Updated README.md 2023-01-13 09:36:12 +01:00
6eadda95a9 updated Dockerfile and README 2023-01-13 09:25:29 +01:00
e091b2299e updated README.md 2023-01-13 09:23:21 +01:00
8f28548059 update Dockerfile 2023-01-12 23:39:42 +01:00
10 changed files with 141 additions and 97 deletions

View File

@@ -8,12 +8,12 @@ name: Docker
on: on:
# schedule: # schedule:
# - cron: '39 13 * * *' # - cron: '39 13 * * *'
push: release:
branches: [ master ] branches: [ master ]
# Publish semver tags as releases. # Publish semver tags as releases.
tags: [ 'v*.*.*' ] tags: [ 'v*.*.*' ]
pull_request: # pull_request:
branches: [ master ] # branches: [ master ]
env: env:
# Use docker.io for Docker Hub if empty # Use docker.io for Docker Hub if empty

View File

@@ -1,11 +1,11 @@
FROM alpine:3.17 FROM alpine:3.17
# folder structure # folder structure
WORKDIR /usr/src/yt-dlp-webui/downloads WORKDIR /usr/src/yt-dlp-webui/downloads
VOLUME /usr/src/yt-dlp-webui/downloads VOLUME /downloads
WORKDIR /usr/src/yt-dlp-webui WORKDIR /usr/src/yt-dlp-webui
# install core dependencies # install core dependencies
RUN apk update RUN apk update
RUN apk add curl wget psmisc python3 ffmpeg nodejs npm go yt-dlp RUN apk add curl wget psmisc ffmpeg nodejs npm go yt-dlp
# copy srcs # copy srcs
COPY . . COPY . .
# install node dependencies # install node dependencies
@@ -14,7 +14,7 @@ RUN npm i
RUN npm run build RUN npm run build
# install go dependencies # install go dependencies
WORKDIR /usr/src/yt-dlp-webui WORKDIR /usr/src/yt-dlp-webui
RUN npm go build -o yt-dlp-webui RUN go build -o yt-dlp-webui
# expose and run # expose and run
EXPOSE 3033 EXPOSE 3033
CMD [ "yt-dlp-webui" , "--out", "./downloads" ] CMD [ "./yt-dlp-webui" , "--out", "/downloads" ]

157
README.md
View File

@@ -1,13 +1,9 @@
# yt-dlp Web UI # yt-dlp Web UI
**To anyone who is interested i'm working on a [RPC server for yt-dlp](https://github.com/marcopeocchi/yt-dlp-rpc).**
A not so terrible web ui for yt-dlp. A not so terrible web ui for yt-dlp.
Created for the only purpose of *fetching* videos from my server/nas. Created for the only purpose of *fetching* videos from my server/nas.
Intended to be used with docker but standalone is fine too. 😎👍 Intended to be used with docker and in standalone mode. 😎👍
Developed to be as lightweight as possible (because my server is basically an intel atom sbc). Developed to be as lightweight as possible (because my server is basically an intel atom sbc).
@@ -15,13 +11,14 @@ The bottleneck remains yt-dlp startup time (until yt-dlp will provide a rpc inte
**I strongly recomend the ghcr build instead of docker hub one.** **I strongly recomend the ghcr build instead of docker hub one.**
```shell ```sh
docker pull ghcr.io/marcopeocchi/yt-dlp-web-ui:master docker pull ghcr.io/marcopeocchi/yt-dlp-web-ui:latest
``` ```
--- ![](https://i.ibb.co/RCpfg7q/image.png)
![](https://i.ibb.co/N2749CD/image.png)
Changelog: ## Changelog
``` ```
05/03/22: Korean translation by kimpig 05/03/22: Korean translation by kimpig
@@ -44,40 +41,33 @@ Refactoring and JSDoc.
08/06/22: ARM builds. 08/06/22: ARM builds.
28/02/22: Reworked resume download feature. Now it's pratically instantaneous. It no longer stops and restarts each process, references to each process are saved in memory. 28/06/22: Reworked resume download feature. Now it's pratically instantaneous. It no longer stops and restarts each process, references to each process are saved in memory.
12/01/23: Switched from TypeScript to Golang on the backend. It was a great effort but it was worth it.
``` ```
<img src="https://i.imgur.com/gRNYKjI.png">
## Now with dark mode
<img src="https://i.imgur.com/g52mjdD.png">
## Settings ## Settings
The avaible settings are currently only: The currently avaible settings are:
- Server address - Server address
- Switch theme - Switch theme
- Extract audio - Extract audio
- Switch language - Switch language
- Optional format selection - Optional format selection
- Override the output filename
- Override the output path
- Pass custom yt-dlp arguments safely
<img src="https://i.imgur.com/2zPs8FH.png"> ![](https://i.ibb.co/YdBVcgc/image.png)
<img src="https://i.imgur.com/b4Jhkfk.png"> ![](https://i.ibb.co/Sf102b1/image.png)
<img src="https://i.imgur.com/knjLa8c.png">
## Format selection ## Format selection
![fs1](https://i.ibb.co/fNxDHJd/localhost-1234-2.png) ![fs1](https://i.ibb.co/8dgS6ym/image.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. This feature is disabled by default as this intended to be used to retrieve the best quality automatically.
To enable it go to the settings page: To enable it just go to the settings page and enable the **Enable video/audio formats selection** flag!
![fs2](https://i.ibb.co/YdXRwKc/localhost-1234-3.png)
And set it :D
Future releases will have: Future releases will have:
- ~~Multi download~~ *done* - ~~Multi download~~ *done*
@@ -87,46 +77,109 @@ Future releases will have:
- ~~ARM Build~~ *done available through ghcr.io* - ~~ARM Build~~ *done available through ghcr.io*
## Troubleshooting ## Troubleshooting
- **It says that it isn't connected/ip in the footer is not defined.** - **It says that it isn't connected/ip in the header is not defined.**
- You must set the server ip address in the settings section (gear icon). - You must set the server ip address in the settings section (gear icon).
- **The download doesn't start.** - **The download doesn't start.**
- As before server address is not specified or simply yt-dlp process takes a lot of time to fire up. (Forking yt-dlp isn't fast especially if you have a lower-end/low-power NAS/server/desktop where the server is running) - As before server address is not specified or simply yt-dlp process takes a lot of time to fire up. (Forking yt-dlp isn't fast especially if you have a lower-end/low-power NAS/server/desktop where the server is running)
- **Background jobs are not retrieved.**
- ~~As before forking yt-dlp isn't fast so resuming n background jobs takes _n_*_time to exec yt-dlp_ Just have patience.~~ Fixed.
## Docker installation ## [Docker](https://github.com/marcopeocchi/yt-dlp-web-ui/pkgs/container/yt-dlp-web-ui) installation
```shell ```sh
# recomended for ARM and x86 devices # recomended for ARM and x86 devices
docker pull ghcr.io/marcopeocchi/yt-dlp-web-ui:master docker pull ghcr.io/marcopeocchi/yt-dlp-web-ui:latest
docker run -d -p 3022:3022 -v <your dir>:/usr/src/yt-dlp-webui/downloads ghcr.io/marcopeocchi/yt-dlp-web-ui:master docker run -d -p 3033:3033 -v <your dir>:/usr/src/yt-dlp-webui/downloads ghcr.io/marcopeocchi/yt-dlp-web-ui:latest
# or even # or even
docker pull ghcr.io/marcopeocchi/yt-dlp-web-ui:master docker pull ghcr.io/marcopeocchi/yt-dlp-web-ui:latest
docker create --name yt-dlp-webui -p 8082:3022 -v <your dir>:/usr/src/yt-dlp-webui/downloads ghcr.io/marcopeocchi/yt-dlp-web-ui:master docker create --name yt-dlp-webui -p 8082:3033 -v <your dir>:/usr/src/yt-dlp-webui/downloads ghcr.io/marcopeocchi/yt-dlp-web-ui:latest
``` ```
or Or with docker but building the container manually.
```sh
docker build -t yt-dlp-webui .
docker run -d -p 3033:3033 -v <your dir>:/usr/src/yt-dlp-webui/downloads yt-dlp-webui
```
## [Prebuilt binaries](https://github.com/marcopeocchi/yt-dlp-web-ui/releases) installation
```sh
# download the latest release from the releases page
mv yt-dlp-webui_linux-[your_system_arch] /usr/local/bin/yt-dlp-webui
# /home/user/downloads as an example and yt-dlp in $PATH
yt-dlp-webui --out /home/user/downloads
# specifying yt-dlp path
yt-dlp-webui --out /home/user/downloads --driver /opt/soemdir/yt-dlp
# specifying using a config file
yt-dlp-webui --conf /home/user/.config/yt-dlp-webui.conf
```
### Config file
By running `yt-dlp-webui` in standalone mode you have the ability to also specify a config file.
The config file **will overwrite what have been passed as cli argument**.
```yaml
# Simple configuration file for yt-dlp webui
---
port: 8989
downloadPath: /home/ren/archive
downloaderPath: /usr/local/bin/yt-dlp
```
### Systemd integration
By defining a service file in `/etc/systemd/system/yt-dlp-webui.service` yt-dlp webui can be launched as in background.
```
[Unit]
Description=yt-dlp-webui service file
After=network.target
[Service]
User=some_user
ExecStart=/usr/local/bin/yt-dlp-webui --out /mnt/share/downloads --port 8100
[Install]
WantedBy=multi-user.target
```
```shell ```shell
docker build -t yt-dlp-webui . systemctl enable yt-dlp-webui
docker run -d -p 3022:3022 -v <your dir>:/usr/src/yt-dlp-webui/downloads yt-dlp-webui systemctl start yt-dlp-webui
``` ```
## Manual installation ## Manual installation
```shell ```sh
# the dependencies are: python3, ffmpeg, nodejs, psmisc. # the dependencies are: python3, ffmpeg, nodejs, psmisc, go.
cd frontend
npm i npm i
npm run build-all npm run build
# edit the settings.json specifying port and download path or go build -o yt-dlp-webui main.go
# it will default to the following created folder
mkdir downloads
node dist/main.js
``` ```
## Extendable
You dont'like the Material feel?
Want to build your own frontend? We got you covered 🤠
`yt-dlp-webui` now exposes a nice **JSON-RPC 1.0** interface through Websockets and HTTP-POST
It is **planned** to also expose a **gRPC** server.
Just as an overview, these are the available methods:
- Service.Exec
- Service.Progress
- Service.Formats
- Service.Pending
- Service.Running
- Service.Kill
- Service.KillAll
- Service.Clear
For more information open an issue on GitHub and I will provide more info ASAP.
## FAQ ## FAQ
- **Will it availabe for Raspberry Pi/ generic ARM devices?** - **Will it availabe for Raspberry Pi/ generic ARM devices?**
- Yes, it's currently available through ghcr.io - Yes, it's currently available through ghcr.io
@@ -136,7 +189,9 @@ node dist/main.js
If you plan to use it on a Raspberry Pi ensure to have fast and durable storage. If you plan to use it on a Raspberry Pi ensure to have fast and durable storage.
- **Why the docker image is so heavy?** - **Why the docker image is so heavy?**
- Originally it was 1.8GB circa, now it has been slimmed to ~340MB compressed. This is due to the fact that it encapsule a basic Alpine linux image + FFmpeg + Node.js + Python3 + yt-dlp. - Originally it was 1.8GB circa, now it has been slimmed to ~340MB compressed. This is due to the fact that it encapsule a basic Alpine linux image + FFmpeg + Node.js + Python3 + yt-dlp.
- **Am I forced to run it on port 3022?**
- Well, yes (until now).
- **Why is it so slow to start a download?** - **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.... - I genuinely don't know. I know that standalone yt-dlp is slow to start up even on my M1 Mac, so....
## What yt-dlp-webui is not
`yt-dlp-webui` isn't your ordinary website where to download stuff from the internet, so don't try asking for links of where this is hosted. It's a self hosted platform for a Linux NAS.

View File

@@ -1,13 +0,0 @@
#!/bin/sh
echo "Downloading latest yt-dlp build..."
rm -f yt-dlp
RELEASE=$(curl --silent "https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
wget "https://github.com/yt-dlp/yt-dlp/releases/download/$RELEASE/yt-dlp"
chmod +x yt-dlp
echo "Done!"

View File

@@ -15,7 +15,6 @@
"@mui/icons-material": "^5.6.2", "@mui/icons-material": "^5.6.2",
"@mui/material": "^5.6.4", "@mui/material": "^5.6.4",
"@reduxjs/toolkit": "^1.8.1", "@reduxjs/toolkit": "^1.8.1",
"radash": "^10.6.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-redux": "^8.0.1", "react-redux": "^8.0.1",
@@ -32,8 +31,6 @@
"@types/uuid": "^8.3.4", "@types/uuid": "^8.3.4",
"@vitejs/plugin-react": "^1.3.2", "@vitejs/plugin-react": "^1.3.2",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"path-browserify": "^1.0.1",
"process": "^0.11.10",
"typescript": "^4.6.4", "typescript": "^4.6.4",
"vite": "^2.9.10" "vite": "^2.9.10"
} }

View File

@@ -70,6 +70,7 @@ export default function Home({ socket }: Props) {
socket.onopen = () => { socket.onopen = () => {
dispatch(connected()) dispatch(connected())
setCustomArgs(localStorage.getItem('last-input-args') ?? '') setCustomArgs(localStorage.getItem('last-input-args') ?? '')
setFilenameOverride(localStorage.getItem('last-filename-override') ?? '')
} }
}, []) }, [])
@@ -136,7 +137,6 @@ export default function Home({ socket }: Props) {
setUrl('') setUrl('')
setWorkingUrl('') setWorkingUrl('')
setFilenameOverride('')
setTimeout(() => { setTimeout(() => {
resetInput() resetInput()
@@ -179,6 +179,7 @@ export default function Home({ socket }: Props) {
*/ */
const handleFilenameOverrideChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleFilenameOverrideChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setFilenameOverride(e.target.value) setFilenameOverride(e.target.value)
localStorage.setItem('last-filename-override', e.target.value)
} }
/** /**
@@ -297,6 +298,7 @@ export default function Home({ socket }: Props) {
fullWidth fullWidth
label={i18n.t('customFilename')} label={i18n.t('customFilename')}
variant="outlined" variant="outlined"
value={fileNameOverride}
onChange={handleFilenameOverrideChange} onChange={handleFilenameOverrideChange}
disabled={!status.connected || (settings.formatSelection && downloadFormats != null)} disabled={!status.connected || (settings.formatSelection && downloadFormats != null)}
/> />

View File

@@ -72,15 +72,15 @@ languages:
toastConnected: '已连接到 ' toastConnected: '已连接到 '
toastUpdated: 已更新 yt-dlp 可执行文件! toastUpdated: 已更新 yt-dlp 可执行文件!
formatSelectionEnabler: 启用视频/音频格式选择 formatSelectionEnabler: 启用视频/音频格式选择
themeSelect: 'Theme' themeSelect: '主题'
languageSelect: 'Language' languageSelect: '语言'
overridesAnchor: Overrides overridesAnchor: 覆盖
pathOverrideOption: Enable output path overriding pathOverrideOption: 启用输出路径覆盖
filenameOverrideOption: Enable output file name overriding filenameOverrideOption: 启用输出文件名覆盖
customFilename: Custom filemame (leave blank to use default) customFilename: 自定义文件名(留空使用默认值)
customPath: Custom path customPath: 自定义路径
customArgs: Enable custom yt-dlp args (great power = great responsabilities) customArgs: 启用自定义 yt-dlp 参数(能力越大 = 责任越大)
customArgsInput: Custom yt-dlp arguments customArgsInput: 自定义 yt-dlp 参数
spanish: spanish:
urlInput: YouTube or other supported service video url urlInput: YouTube or other supported service video url
statusTitle: Status statusTitle: Status
@@ -188,4 +188,4 @@ languages:
customFilename: Custom filemame (leave blank to use default) customFilename: Custom filemame (leave blank to use default)
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

View File

@@ -1,28 +1,28 @@
// @ts-nocheck // @ts-nocheck
import i18n from "../../assets/i18n.yaml"; import i18n from "../../assets/i18n.yaml"
export default class I18nBuilder { export default class I18nBuilder {
private language: string; private language: string
private textMap = i18n.languages; private textMap = i18n.languages
constructor(language: string) { constructor(language: string) {
this.language = language; this.language = language
} }
getLanguage(): string { getLanguage(): string {
return this.language; return this.language
} }
setLanguage(language: string): void { setLanguage(language: string): void {
this.language = language; this.language = language
} }
t(key: string): string { t(key: string): string {
const map = this.textMap[this.language] const map = this.textMap[this.language]
if (map) { if (map) {
const translation = map[key]; const translation = map[key]
return translation ?? 'caption not defined'; return translation ?? 'caption not defined'
} }
return 'caption not defined'; return 'caption not defined'
} }
} }

View File

@@ -20,10 +20,13 @@ export class RPCClient {
} }
private sendHTTP<T>(req: RPCRequest) { private sendHTTP<T>(req: RPCRequest) {
return new Promise<RPCResponse<T>>((resolve, reject) => { return new Promise<RPCResponse<T>>((resolve) => {
fetch(getHttpRPCEndpoint(), { fetch(getHttpRPCEndpoint(), {
method: 'POST', method: 'POST',
body: JSON.stringify(req) body: JSON.stringify({
id: this.incrementSeq(),
...req
})
}) })
.then(res => res.json()) .then(res => res.json())
.then(data => resolve(data)) .then(data => resolve(data))
@@ -59,6 +62,7 @@ export class RPCClient {
public running() { public running() {
this.send({ this.send({
id: this.incrementSeq(),
method: 'Service.Running', method: 'Service.Running',
params: [], params: [],
}) })

View File

@@ -130,7 +130,6 @@ func (p *Process) Start(path, filename string) {
go func() { go func() {
defer r.Close() defer r.Close()
defer p.Complete() defer p.Complete()
defer close(eventChan)
for scan.Scan() { for scan.Scan() {
eventChan <- scan.Text() eventChan <- scan.Text()
} }