ytdl-web/ytdl/ytdl.go

120 lines
2.5 KiB
Go

package ytdl
import (
"bytes"
"io"
"os/exec"
"strings"
"go.fifitido.net/ytdl-web/config"
"go.fifitido.net/ytdl-web/ytdl/cache"
"go.fifitido.net/ytdl-web/ytdl/metadata"
"golang.org/x/exp/slog"
)
type Ytdl interface {
GetMetadata(url string) (*metadata.Metadata, error)
Download(w io.Writer, url, format string, index int) error
Version() string
}
type ytdlImpl struct {
cfg *config.Config
logger *slog.Logger
cache cache.MetadataCache
version string
}
func NewYtdl(cfg *config.Config, logger *slog.Logger, cache cache.MetadataCache) Ytdl {
cmd := exec.Command(
cfg.BinaryPath,
"--version",
)
var out bytes.Buffer
cmd.Stdout = &out
_ = cmd.Run()
return &ytdlImpl{
cfg: cfg,
logger: logger.With(slog.String("module", "ytdl")),
cache: cache,
version: strings.TrimSpace(out.String()),
}
}
func (y *ytdlImpl) baseOptions(url string) []Option {
options := []Option{}
metadata, err := y.cache.Get(url)
if err == nil {
options = append(options, WithLoadJson(metadata))
}
if y.cfg.Cookies.Enabled {
if y.cfg.Cookies.FromBrowser.Browser != "" {
options = append(options, WithBrowserCookies(
y.cfg.Cookies.FromBrowser.Browser,
y.cfg.Cookies.FromBrowser.Keyring,
y.cfg.Cookies.FromBrowser.Profile,
y.cfg.Cookies.FromBrowser.Container,
))
} else {
options = append(options, WithCookieFile(y.cfg.Cookies.FilePath))
}
}
if y.cfg.IsDevelopment() {
options = append(options, WithDebug())
}
return options
}
func (y *ytdlImpl) Version() string {
return y.version
}
// GetMetadata implements Ytdl
func (y *ytdlImpl) GetMetadata(url string) (*metadata.Metadata, error) {
meta, err := y.cache.Get(url)
if err == nil {
return meta, nil
}
meta = &metadata.Metadata{}
options := append(
y.baseOptions(url),
WithDumpJson(meta),
)
if err := Exec(y.cfg.BinaryPath, url, options...); err != nil {
y.logger.Error("failed to get 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 {
y.logger.Warn("failed to cache metadata", slog.String("url", url), slog.String("error", err.Error()))
}
return meta, nil
}
// Download implements Ytdl
func (y *ytdlImpl) Download(w io.Writer, url, format string, index int) error {
options := append(
y.baseOptions(url),
WithFormat(format),
WithStreamOutput(w),
)
if index >= 0 {
options = append(options, WithPlaylistIndex(index))
}
if err := Exec(y.cfg.BinaryPath, url, options...); err != nil {
return err
}
return nil
}