Compare commits
1 Commits
335-shadcn
...
feat-suppo
| Author | SHA1 | Date | |
|---|---|---|---|
| a4ba8cea27 |
@@ -31,7 +31,7 @@ keys:
|
||||
splashText: No active downloads
|
||||
archiveTitle: Archive
|
||||
clipboardAction: Copied URL to clipboard
|
||||
playlistCheckbox: Download playlist (it will take time, after submitting you may close this window)
|
||||
playlistCheckbox: Download playlist
|
||||
restartAppMessage: Needs a page reload to take effect
|
||||
servedFromReverseProxyCheckbox: Is behind a reverse proxy
|
||||
urlBase: URL base, for reverse proxy support (subdir), defaults to empty
|
||||
|
||||
18
server/common/types.go
Normal file
18
server/common/types.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package common
|
||||
|
||||
import "time"
|
||||
|
||||
// Used to deser the yt-dlp -J output
|
||||
type DownloadInfo struct {
|
||||
URL string `json:"url"`
|
||||
Title string `json:"title"`
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
Resolution string `json:"resolution"`
|
||||
Size int32 `json:"filesize_approx"`
|
||||
VCodec string `json:"vcodec"`
|
||||
ACodec string `json:"acodec"`
|
||||
Extension string `json:"ext"`
|
||||
OriginalURL string `json:"original_url"`
|
||||
FileName string `json:"filename"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package internal
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"github.com/marcopiovanello/yt-dlp-web-ui/v3/server/common"
|
||||
)
|
||||
|
||||
// Used to unmarshall yt-dlp progress
|
||||
type ProgressTemplate struct {
|
||||
@@ -29,29 +31,14 @@ type DownloadProgress struct {
|
||||
ETA float64 `json:"eta"`
|
||||
}
|
||||
|
||||
// Used to deser the yt-dlp -J output
|
||||
type DownloadInfo struct {
|
||||
URL string `json:"url"`
|
||||
Title string `json:"title"`
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
Resolution string `json:"resolution"`
|
||||
Size int32 `json:"filesize_approx"`
|
||||
VCodec string `json:"vcodec"`
|
||||
ACodec string `json:"acodec"`
|
||||
Extension string `json:"ext"`
|
||||
OriginalURL string `json:"original_url"`
|
||||
FileName string `json:"filename"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
// struct representing the response sent to the client
|
||||
// as JSON-RPC result field
|
||||
type ProcessResponse struct {
|
||||
Id string `json:"id"`
|
||||
Progress DownloadProgress `json:"progress"`
|
||||
Info DownloadInfo `json:"info"`
|
||||
Output DownloadOutput `json:"output"`
|
||||
Params []string `json:"params"`
|
||||
Id string `json:"id"`
|
||||
Progress DownloadProgress `json:"progress"`
|
||||
Info common.DownloadInfo `json:"info"`
|
||||
Output DownloadOutput `json:"output"`
|
||||
Params []string `json:"params"`
|
||||
}
|
||||
|
||||
// struct representing the current status of the memoryDB
|
||||
|
||||
@@ -9,20 +9,18 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/marcopiovanello/yt-dlp-web-ui/v3/server/common"
|
||||
"github.com/marcopiovanello/yt-dlp-web-ui/v3/server/config"
|
||||
"github.com/marcopiovanello/yt-dlp-web-ui/v3/server/playlist"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
Entries []DownloadInfo `json:"entries"`
|
||||
Count int `json:"playlist_count"`
|
||||
PlaylistTitle string `json:"title"`
|
||||
Type string `json:"_type"`
|
||||
}
|
||||
|
||||
func PlaylistDetect(req DownloadRequest, mq *MessageQueue, db *MemoryDB) error {
|
||||
params := append(req.Params, "--flat-playlist", "-J")
|
||||
urlWithParams := append([]string{req.URL}, params...)
|
||||
|
||||
var (
|
||||
downloader = config.Instance().DownloaderPath
|
||||
cmd = exec.Command(downloader, req.URL, "--flat-playlist", "-J")
|
||||
cmd = exec.Command(downloader, urlWithParams...)
|
||||
)
|
||||
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
@@ -30,7 +28,7 @@ func PlaylistDetect(req DownloadRequest, mq *MessageQueue, db *MemoryDB) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var m metadata
|
||||
var m playlist.Metadata
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
@@ -53,12 +51,20 @@ func PlaylistDetect(req DownloadRequest, mq *MessageQueue, db *MemoryDB) error {
|
||||
}
|
||||
|
||||
if m.Type == "playlist" {
|
||||
entries := slices.CompactFunc(slices.Compact(m.Entries), func(a DownloadInfo, b DownloadInfo) bool {
|
||||
entries := slices.CompactFunc(slices.Compact(m.Entries), func(a common.DownloadInfo, b common.DownloadInfo) bool {
|
||||
return a.URL == b.URL
|
||||
})
|
||||
|
||||
entries = slices.DeleteFunc(entries, func(e common.DownloadInfo) bool {
|
||||
return strings.Contains(e.URL, "list=")
|
||||
})
|
||||
|
||||
slog.Info("playlist detected", slog.String("url", req.URL), slog.Int("count", len(entries)))
|
||||
|
||||
if err := playlist.ApplyModifiers(&entries, req.Params); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, meta := range entries {
|
||||
// detect playlist title from metadata since each playlist entry will be
|
||||
// treated as an individual download
|
||||
@@ -87,6 +93,8 @@ func PlaylistDetect(req DownloadRequest, mq *MessageQueue, db *MemoryDB) error {
|
||||
db.Set(proc)
|
||||
mq.Publish(proc)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
proc := &Process{
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/marcopiovanello/yt-dlp-web-ui/v3/server/archiver"
|
||||
"github.com/marcopiovanello/yt-dlp-web-ui/v3/server/common"
|
||||
"github.com/marcopiovanello/yt-dlp-web-ui/v3/server/config"
|
||||
)
|
||||
|
||||
@@ -50,7 +51,7 @@ type Process struct {
|
||||
Livestream bool
|
||||
AutoRemove bool
|
||||
Params []string
|
||||
Info DownloadInfo
|
||||
Info common.DownloadInfo
|
||||
Progress DownloadProgress
|
||||
Output DownloadOutput
|
||||
proc *os.Process
|
||||
@@ -302,7 +303,7 @@ func (p *Process) GetFileName(o *DownloadOutput) error {
|
||||
|
||||
func (p *Process) SetPending() {
|
||||
// Since video's title isn't available yet, fill in with the URL.
|
||||
p.Info = DownloadInfo{
|
||||
p.Info = common.DownloadInfo{
|
||||
URL: p.Url,
|
||||
Title: p.Url,
|
||||
CreatedAt: time.Now(),
|
||||
@@ -334,7 +335,7 @@ func (p *Process) SetMetadata() error {
|
||||
return err
|
||||
}
|
||||
|
||||
info := DownloadInfo{
|
||||
info := common.DownloadInfo{
|
||||
URL: p.Url,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
86
server/playlist/modifiers.go
Normal file
86
server/playlist/modifiers.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package playlist
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strconv"
|
||||
|
||||
"github.com/marcopiovanello/yt-dlp-web-ui/v3/server/common"
|
||||
)
|
||||
|
||||
/*
|
||||
Applicable modifiers
|
||||
|
||||
full | short | description
|
||||
---------------------------------------------------------------------------------
|
||||
--playlist-start NUMBER | -I NUMBER: | discard first N entries
|
||||
--playlist-end NUMBER | -I :NUMBER | discard last N entries
|
||||
--playlist-reverse | -I ::-1 | self explanatory
|
||||
--max-downloads NUMBER | | stops after N completed downloads
|
||||
*/
|
||||
|
||||
func ApplyModifiers(entries *[]common.DownloadInfo, args []string) error {
|
||||
for i, modifier := range args {
|
||||
switch modifier {
|
||||
case "--playlist-start":
|
||||
return playlistStart(i, modifier, args, entries)
|
||||
|
||||
case "--playlist-end":
|
||||
return playlistEnd(i, modifier, args, entries)
|
||||
|
||||
case "--max-downloads":
|
||||
return maxDownloads(i, modifier, args, entries)
|
||||
|
||||
case "--playlist-reverse":
|
||||
slices.Reverse(*entries)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func playlistStart(i int, modifier string, args []string, entries *[]common.DownloadInfo) error {
|
||||
if !guard(i, len(modifier)) {
|
||||
return nil
|
||||
}
|
||||
|
||||
n, err := strconv.Atoi(args[i+1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*entries = (*entries)[n:]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func playlistEnd(i int, modifier string, args []string, entries *[]common.DownloadInfo) error {
|
||||
if !guard(i, len(modifier)) {
|
||||
return nil
|
||||
}
|
||||
|
||||
n, err := strconv.Atoi(args[i+1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*entries = (*entries)[:n]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func maxDownloads(i int, modifier string, args []string, entries *[]common.DownloadInfo) error {
|
||||
if !guard(i, len(modifier)) {
|
||||
return nil
|
||||
}
|
||||
|
||||
n, err := strconv.Atoi(args[i+1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*entries = (*entries)[0:n]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func guard(i, len int) bool { return i+1 < len-1 }
|
||||
10
server/playlist/types.go
Normal file
10
server/playlist/types.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package playlist
|
||||
|
||||
import "github.com/marcopiovanello/yt-dlp-web-ui/v3/server/common"
|
||||
|
||||
type Metadata struct {
|
||||
Entries []common.DownloadInfo `json:"entries"`
|
||||
Count int `json:"playlist_count"`
|
||||
PlaylistTitle string `json:"title"`
|
||||
Type string `json:"_type"`
|
||||
}
|
||||
Reference in New Issue
Block a user