ytdl-web/pkg/ytdl/ytdl.go

169 lines
3.4 KiB
Go
Raw Permalink Normal View History

2023-05-23 18:44:05 -04:00
package ytdl
import (
2023-05-23 22:29:56 -04:00
"bytes"
"encoding/json"
"fmt"
2023-05-23 18:44:05 -04:00
"io"
2023-05-23 22:29:56 -04:00
"os/exec"
"strings"
2023-05-23 18:44:05 -04:00
"go.fifitido.net/ytdl-web/config"
"go.fifitido.net/ytdl-web/pkg/ytdl/cache"
"go.fifitido.net/ytdl-web/pkg/ytdl/metadata"
2023-05-23 18:44:05 -04:00
"golang.org/x/exp/slog"
)
type Ytdl interface {
GetMetadata(url string) (*metadata.Metadata, error)
2023-05-23 19:35:52 -04:00
Download(w io.Writer, url, format string, index int) error
2023-05-23 22:29:56 -04:00
Version() string
2023-05-23 18:44:05 -04:00
}
type ytdlImpl struct {
2023-05-23 22:29:56 -04:00
cfg *config.Config
logger *slog.Logger
cache cache.MetadataCache
version string
2023-05-23 18:44:05 -04:00
}
func NewYtdl(cfg *config.Config, logger *slog.Logger, cache cache.MetadataCache) Ytdl {
2023-05-23 22:29:56 -04:00
cmd := exec.Command(
cfg.Ytdlp.BinaryPath,
2023-05-23 22:29:56 -04:00
"--version",
)
var out bytes.Buffer
cmd.Stdout = &out
_ = cmd.Run()
2023-05-23 18:44:05 -04:00
return &ytdlImpl{
2023-05-23 22:29:56 -04:00
cfg: cfg,
logger: logger.With(slog.String("module", "ytdl")),
cache: cache,
version: strings.TrimSpace(out.String()),
2023-05-23 18:44:05 -04:00
}
}
func buildBrowserCookieString(browser, keyring, profile, container string) string {
var sb strings.Builder
sb.WriteString(browser)
2023-05-23 18:44:05 -04:00
if keyring != "" {
sb.WriteByte('+')
sb.WriteString(keyring)
}
if profile != "" {
sb.WriteByte(':')
sb.WriteString(profile)
2023-05-23 18:44:05 -04:00
}
if container != "" {
sb.WriteByte(':')
sb.WriteByte(':')
sb.WriteString(container)
}
return sb.String()
}
func (y *ytdlImpl) appendCookieArgs(args []string) []string {
2023-05-23 18:44:05 -04:00
if y.cfg.Cookies.Enabled {
2023-05-23 21:52:00 -04:00
if y.cfg.Cookies.FromBrowser.Browser != "" {
args = append(args, "--cookies-from-browser", buildBrowserCookieString(
2023-05-23 18:44:05 -04:00
y.cfg.Cookies.FromBrowser.Browser,
y.cfg.Cookies.FromBrowser.Keyring,
y.cfg.Cookies.FromBrowser.Profile,
y.cfg.Cookies.FromBrowser.Container,
))
} else {
args = append(args, "--cookies", y.cfg.Cookies.FilePath)
2023-05-23 18:44:05 -04:00
}
}
return args
2023-05-23 18:44:05 -04:00
}
2023-05-23 22:29:56 -04:00
func (y *ytdlImpl) Version() string {
return y.version
}
2023-05-23 18:44:05 -04:00
// GetMetadata implements Ytdl
func (y *ytdlImpl) GetMetadata(url string) (*metadata.Metadata, error) {
meta, err := y.cache.Get(url)
if err == nil {
return meta, nil
}
args := []string{
url,
"--dump-single-json",
}
args = y.appendCookieArgs(args)
2023-05-23 18:44:05 -04:00
cmd := exec.Command(y.cfg.Ytdlp.BinaryPath, args...)
out, err := cmd.Output()
if err != nil {
2024-09-18 20:46:18 -04:00
attrs := []any{
slog.String("url", url),
slog.String("error", err.Error()),
}
y.logger.Error("failed to get metadata", attrs...)
2023-05-23 18:44:05 -04:00
return nil, err
}
meta = &metadata.Metadata{}
if err := json.Unmarshal(out, meta); err != nil {
y.logger.Error("failed to unmarshal metadata", slog.String("url", url), slog.String("error", err.Error()))
return nil, err
}
if err := y.cache.Set(url, meta, y.cfg.Cache.TTL); err != nil {
2023-05-23 18:44:05 -04:00
y.logger.Warn("failed to cache metadata", slog.String("url", url), slog.String("error", err.Error()))
}
return meta, nil
}
// Download implements Ytdl
2023-05-23 19:35:52 -04:00
func (y *ytdlImpl) Download(w io.Writer, url, format string, index int) error {
args := []string{
url,
"--format", format,
"--output", "-",
}
2023-05-23 18:44:05 -04:00
2023-05-23 19:35:52 -04:00
if index >= 0 {
args = append(args, "--playlist-ites", fmt.Sprint(index+1))
}
args = y.appendCookieArgs(args)
metadata, err := y.cache.Get(url)
if err == nil {
args = append(args, "--load-info-json", "-")
}
cmd := exec.Command(y.cfg.Ytdlp.BinaryPath, args...)
cmd.Stdout = w
if err == nil {
json, err := json.Marshal(metadata)
if err != nil {
return err
}
cmd.Stdin = bytes.NewReader(json)
2023-05-23 19:35:52 -04:00
}
if err := cmd.Run(); err != nil {
y.logger.Error("failed to download", slog.String("url", url), slog.String("error", err.Error()))
2023-05-23 18:44:05 -04:00
return err
}
return nil
}