Compare commits

...

23 Commits

Author SHA1 Message Date
Marco
73a557d318 Merge pull request #42 from mimaburao/test
update japanese
2023-03-04 00:03:53 +01:00
mimaburao
ae1da10d6e update japanese 2023-03-03 22:44:12 +09:00
Marco
cd7ce6f55c Merge pull request #41 from marcopeocchi/opt-sync-map
changed map+rwMutext to sync.Map
2023-03-01 15:09:20 +01:00
aaad68a42c changed map+rwMutext to sync.Map 2023-03-01 15:06:11 +01:00
Marco
72857882e4 Merge pull request #37 from cnbeining/fix-websocket-wss
Fix WebSocket protocol detecton under HTTPS
2023-02-19 13:14:12 +01:00
David Zhuang
59abd76966 Fix WebSocket protocol detecton under HTTPS 2023-02-18 17:51:01 -05:00
Marco
8ab7c4db4d Update Dockerfile 2023-02-18 00:14:26 +01:00
Marco
17d48354cb Merge pull request #35 from Skyr/show-selectformat-button
If formats selection enabled: Show "select format" string in button
2023-02-08 17:54:25 +01:00
Marco
ac54a1dd13 Merge pull request #34 from Skyr/show-download-size
In format selection: Show resolution and download size (if available)
2023-02-08 17:54:12 +01:00
Stefan Schlott
75c6c84c5c If formats selection enabled: Show "select format" string in button
(instead of start)
2023-02-04 12:24:42 +01:00
Stefan Schlott
cdad7ca873 In format selection: Show resolution and download size (if available) 2023-02-04 12:13:20 +01:00
Marco
1f6d6d7839 Update Dockerfile 2023-01-22 10:28:42 +01:00
Marco
e59cf383d5 Update Dockerfile 2023-01-22 10:16:24 +01:00
Marco
643c752b6a Update Dockerfile 2023-01-21 21:47:59 +01:00
Marco
5e51bf7ff5 Update Dockerfile 2023-01-21 18:04:01 +01:00
Marco
245b70f654 Update docker-publish.yml 2023-01-21 10:54:42 +01:00
Marco
2d1fc0dda5 Update Dockerfile 2023-01-21 10:52:43 +01:00
ee83bad6e8 Update Dockerfile 2023-01-20 22:00:05 +01:00
Marco
3609f573a2 Update docker-image.yml 2023-01-20 21:55:10 +01:00
e258dea2ca reviewed Dockerfile 2023-01-20 21:50:35 +01:00
Marco
f2622adc7e Update README.md 2023-01-20 21:48:01 +01:00
Marco
4f4348cb91 Update docker-image.yml 2023-01-20 21:42:21 +01:00
Marco
fabe1c7d5e Update docker-image.yml 2023-01-20 19:49:30 +01:00
14 changed files with 109 additions and 97 deletions

View File

@@ -22,6 +22,5 @@ jobs:
with:
version: latest
- name: Build the Docker image
run: docker buildx build . --file Dockerfile --tag ${{secrets.DOCKER_HUB_USERNAME}}/yt-dlp-webui:latest --platform linux/amd64,linux/arm/v7,linux/arm64
- name: Publish the Docker image
run: docker push ${{secrets.DOCKER_HUB_USERNAME}}/yt-dlp-webui:latest
run: docker buildx build . --file Dockerfile --tag ${{secrets.DOCKER_HUB_USERNAME}}/yt-dlp-webui:latest --push --platform linux/amd64,linux/arm/v7,linux/arm64

View File

@@ -1,4 +1,4 @@
name: Docker
name: Docker (ghcr.io)
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
@@ -6,14 +6,13 @@ name: Docker
# documentation.
on:
# schedule:
# - cron: '39 13 * * *'
release:
branches: [ master ]
# Publish semver tags as releases.
tags: [ 'v*.*.*' ]
# pull_request:
# branches: [ master ]
push:
branches: [ master ]
pull_request:
branches: [ master ]
env:
# Use docker.io for Docker Hub if empty

View File

@@ -6,24 +6,30 @@ FROM alpine:3.17 AS build
WORKDIR /usr/src/yt-dlp-webui
# install core dependencies
RUN apk update && \
apk add curl wget psmisc ffmpeg nodejs yarn go yt-dlp
apk add nodejs npm go
# copia la salsa
COPY . .
# build frontend
WORKDIR /usr/src/yt-dlp-webui/frontend
RUN yarn install && \
yarn build
RUN npm install
RUN npm run build
# build backend + incubator
WORKDIR /usr/src/yt-dlp-webui
RUN go build -o yt-dlp-webui
# but here yes :)
FROM alpine:edge
FROM alpine:3.17
WORKDIR /usr/src/yt-dlp-webui/downloads
WORKDIR /downloads
VOLUME /downloads
COPY --from=build /usr/src/yt-dlp-webui /usr/bin/yt-dlp-webui
WORKDIR /app
RUN apk update && \
apk add psmisc ffmpeg yt-dlp
COPY --from=build /usr/src/yt-dlp-webui /app
RUN chmod +x /app/yt-dlp-webui
EXPOSE 3033
CMD [ "/usr/bin/yt-dlp-webui" , "--out", "/downloads" ]
CMD [ "./yt-dlp-webui" , "--out", "/downloads" ]

View File

@@ -9,8 +9,11 @@ Developed to be as lightweight as possible (because my server is basically an in
The bottleneck remains yt-dlp startup time.
**I strongly recomend the ghcr build instead of docker hub one.**
**Docker images are available on [Docker Hub](https://hub.docker.com/r/marcobaobao/yt-dlp-webui) or [ghcr.io](https://github.com/marcopeocchi/yt-dlp-web-ui/pkgs/container/yt-dlp-web-ui)**.
```sh
docker pull marcobaobao/yt-dlp-webui:latest
```
```sh
docker pull ghcr.io/marcopeocchi/yt-dlp-web-ui:latest
```
@@ -86,6 +89,8 @@ Future releases will have:
```sh
# recomended for ARM and x86 devices
docker pull ghcr.io/marcopeocchi/yt-dlp-web-ui:latest
# or
# docker pull marcobaobao/yt-dlp-webui:latest
docker run -d -p 3033:3033 -v <your dir>:/downloads ghcr.io/marcopeocchi/yt-dlp-web-ui:latest
# or even

View File

@@ -335,7 +335,7 @@ export default function Home({ socket }: Props) {
disabled={url === ''}
onClick={() => settings.formatSelection ? sendUrlFormatSelection() : sendUrl()}
>
{i18n.t('startButton')}
{settings.formatSelection ? i18n.t('selectFormatButton') : i18n.t('startButton')}
</Button>
</Grid>
<Grid item>
@@ -387,6 +387,7 @@ export default function Home({ socket }: Props) {
setPickedAudioFormat('')
}}>
{downloadFormats.best.format_note || downloadFormats.best.format_id} - {downloadFormats.best.vcodec}+{downloadFormats.best.acodec}
&nbsp;({downloadFormats.best.resolution}{(downloadFormats.best.filesize_approx>0)?", ~"+Math.round(downloadFormats.best.filesize_approx/1024/1024)+" MiB":""})
</Button>
</Grid>
{/* video only */}
@@ -411,6 +412,7 @@ export default function Home({ socket }: Props) {
disabled={pickedVideoFormat === format.format_id}
>
{format.format_note} - {format.vcodec === 'none' ? format.acodec : format.vcodec}
&nbsp;({format.resolution}{(format.filesize_approx>0)?", ~"+Math.round(format.filesize_approx/1024/1024)+" MiB":""})
</Button>
</Grid>
))
@@ -436,6 +438,7 @@ export default function Home({ socket }: Props) {
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>
))

View File

@@ -4,6 +4,7 @@ languages:
urlInput: YouTube or other supported service video URL
statusTitle: Status
statusReady: Ready
selectFormatButton: Select format
startButton: Start
abortAllButton: Abort All
updateBinButton: Update yt-dlp binary
@@ -166,6 +167,7 @@ languages:
urlInput: YouTubeまたはサポート済み動画のURL
statusTitle: 状態
statusReady: 準備
selectFormatButton: フォーマット選択
startButton: 開始
abortAllButton: すべて中止
updateBinButton: yt-dlp更新
@@ -180,12 +182,12 @@ languages:
toastConnected: '接続中 '
toastUpdated: yt-dlpを更新しました!
formatSelectionEnabler: 選択可能な動画/音源
themeSelect: 'Theme'
languageSelect: 'Language'
overridesAnchor: Overrides
pathOverrideOption: Enable output path overriding
filenameOverrideOption: Enable output file name overriding
customFilename: Custom filemame (leave blank to use default)
customPath: Custom path
customArgs: Enable custom yt-dlp args (great power = great responsabilities)
customArgsInput: Custom yt-dlp arguments
themeSelect: 'テーマ'
languageSelect: '言語'
overridesAnchor: 上書き
pathOverrideOption: 保存するディレクトリ
filenameOverrideOption: ファイル名の上書き
customFilename: (空白の場合は元のファイル名)
customPath: 保存先
customArgs: yt-dlpのオプションの有効化 (最適設定にする場合)
customArgsInput: yt-dlpのオプション

View File

@@ -59,4 +59,5 @@ export interface IDLFormat {
resolution: string,
vcodec: string,
acodec: string,
filesize_approx: number,
}

View File

@@ -74,7 +74,8 @@ export function toFormatArgs(codes: string[]): string {
}
export function getWebSocketEndpoint() {
return `ws://${localStorage.getItem('server-addr') || window.location.hostname}:${localStorage.getItem('server-port') || window.location.port}/ws-rpc`
const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws'
return `${protocol}://${localStorage.getItem('server-addr') || window.location.hostname}:${localStorage.getItem('server-port') || window.location.port}/ws-rpc`
}
export function getHttpRPCEndpoint() {

View File

@@ -1,6 +1,8 @@
package server
import (
"errors"
"fmt"
"log"
"os"
"sync"
@@ -13,86 +15,74 @@ import (
// In-Memory volatile Thread-Safe Key-Value Storage
type MemoryDB struct {
table map[string]*Process
mu sync.Mutex
}
// Inits the db with an empty map of string->Process pointer
func (m *MemoryDB) New() {
m.table = make(map[string]*Process)
table sync.Map
}
// Get a process pointer given its id
func (m *MemoryDB) Get(id string) *Process {
m.mu.Lock()
res := m.table[id]
m.mu.Unlock()
return res
func (m *MemoryDB) Get(id string) (*Process, error) {
entry, ok := db.table.Load(id)
if !ok {
return nil, errors.New("no process found for the given key")
}
return entry.(*Process), nil
}
// Store a pointer of a process and return its id
func (m *MemoryDB) Set(process *Process) string {
id := uuid.Must(uuid.NewRandom()).String()
m.mu.Lock()
m.table[id] = process
m.mu.Unlock()
db.table.Store(id, process)
return id
}
// Update a process info/metadata, given the process id
func (m *MemoryDB) Update(id string, info DownloadInfo) {
m.mu.Lock()
if m.table[id] != nil {
m.table[id].Info = info
func (m *MemoryDB) UpdateInfo(id string, info DownloadInfo) error {
entry, ok := db.table.Load(id)
if ok {
entry.(*Process).Info = info
db.table.Store(id, entry)
return nil
}
m.mu.Unlock()
return fmt.Errorf("can't update row with id %s", id)
}
// Update a process progress data, given the process id
// Used for updating completition percentage or ETA
func (m *MemoryDB) UpdateProgress(id string, progress DownloadProgress) {
m.mu.Lock()
if m.table[id] != nil {
m.table[id].Progress = progress
func (m *MemoryDB) UpdateProgress(id string, progress DownloadProgress) error {
entry, ok := db.table.Load(id)
if ok {
entry.(*Process).Progress = progress
db.table.Store(id, entry)
return nil
}
m.mu.Unlock()
return fmt.Errorf("can't update row with id %s", id)
}
// Removes a process progress, given the process id
func (m *MemoryDB) Delete(id string) {
m.mu.Lock()
delete(m.table, id)
m.mu.Unlock()
db.table.Delete(id)
}
// Returns a slice of all currently stored processes id
func (m *MemoryDB) Keys() []string {
m.mu.Lock()
keys := make([]string, len(m.table))
i := 0
for k := range m.table {
keys[i] = k
i++
}
m.mu.Unlock()
return keys
func (m *MemoryDB) Keys() *[]string {
running := []string{}
db.table.Range(func(key, value any) bool {
running = append(running, key.(string))
return true
})
return &running
}
// Returns a slice of all currently stored processes progess
func (m *MemoryDB) All() []ProcessResponse {
running := make([]ProcessResponse, len(m.table))
i := 0
for k, v := range m.table {
if v != nil {
running[i] = ProcessResponse{
Id: k,
Info: v.Info,
Progress: v.Progress,
}
i++
}
}
return running
func (m *MemoryDB) All() *[]ProcessResponse {
running := []ProcessResponse{}
db.table.Range(func(key, value any) bool {
running = append(running, ProcessResponse{
Id: key.(string),
Info: value.(*Process).Info,
Progress: value.(*Process).Progress,
})
return true
})
return &running
}
// WIP: Persist the database in a single file named "session.dat"
@@ -100,7 +90,7 @@ func (m *MemoryDB) Persist() {
running := m.All()
session, err := json.Marshal(Session{
Processes: running,
Processes: *running,
})
if err != nil {
log.Println(cli.Red, "Failed to persist database", cli.Reset)

View File

@@ -118,7 +118,7 @@ func (p *Process) Start(path, filename string) {
}
info := DownloadInfo{URL: p.url}
json.Unmarshal(stdout, &info)
p.mem.Update(p.id, info)
p.mem.UpdateInfo(p.id, info)
}()
// --------------- progress block --------------- //

View File

@@ -18,7 +18,7 @@ import "time"
// -t-> |>
//
// --A-----C-----G-------|>
func Debounce(interval time.Duration, source chan string, cb func(emit string)) {
func Debounce(interval time.Duration, source chan string, f func(emit string)) {
var item string
timer := time.NewTimer(interval)
for {
@@ -27,7 +27,7 @@ func Debounce(interval time.Duration, source chan string, cb func(emit string))
timer.Reset(interval)
case <-timer.C:
if item != "" {
cb(item)
f(item)
}
}
}

View File

@@ -17,10 +17,6 @@ import (
var db MemoryDB
func init() {
db.New()
}
func RunBlocking(ctx context.Context) {
fe := ctx.Value("frontend").(fs.SubFS)
port := ctx.Value("port").(int)

View File

@@ -40,7 +40,11 @@ func (t *Service) Exec(args DownloadSpecificArgs, result *string) error {
// Progess retrieves the Progress of a specific Process given its Id
func (t *Service) Progess(args Args, progress *DownloadProgress) error {
*progress = db.Get(args.Id).Progress
proc, err := db.Get(args.Id)
if err != nil {
return err
}
*progress = proc.Progress
return nil
}
@@ -54,21 +58,23 @@ func (t *Service) Formats(args Args, progress *DownloadFormats) error {
// Pending retrieves a slice of all Pending/Running processes ids
func (t *Service) Pending(args NoArgs, pending *Pending) error {
*pending = Pending(db.Keys())
*pending = *db.Keys()
return nil
}
// Running retrieves a slice of all Processes progress
func (t *Service) Running(args NoArgs, running *Running) error {
*running = db.All()
*running = *db.All()
return nil
}
// Kill kills a process given its id and remove it from the memoryDB
func (t *Service) Kill(args string, killed *string) error {
log.Println("Trying killing process with id", args)
proc := db.Get(args)
var err error
proc, err := db.Get(args)
if err != nil {
return err
}
if proc != nil {
err = proc.Kill()
}
@@ -81,8 +87,11 @@ func (t *Service) KillAll(args NoArgs, killed *string) error {
log.Println("Killing all spawned processes", args)
keys := db.Keys()
var err error
for _, key := range keys {
proc := db.Get(key)
for _, key := range *keys {
proc, err := db.Get(key)
if err != nil {
return err
}
if proc != nil {
proc.Kill()
}

View File

@@ -36,6 +36,7 @@ type Format struct {
Resolution string `json:"resolution"`
VCodec string `json:"vcodec"`
ACodec string `json:"acodec"`
Size float32 `json:"filesize_approx"`
}
// struct representing the response sent to the client