Use custom configuration, reducing dependencies
This commit is contained in:
parent
668b36ad04
commit
f3f852fefd
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"ansible.python.interpreterPath": "/bin/python"
|
||||
}
|
||||
114
cmd/root.go
114
cmd/root.go
|
|
@ -1,114 +0,0 @@
|
|||
/*
|
||||
Copyright © 2024 Evan Fiordeliso <evan.fiordeliso@gmail.com>
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
"github.com/dgraph-io/badger/v2"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"go.fifitido.net/ytdl-web/config"
|
||||
"go.fifitido.net/ytdl-web/pkg/server"
|
||||
"go.fifitido.net/ytdl-web/pkg/utils"
|
||||
"go.fifitido.net/ytdl-web/pkg/ytdl"
|
||||
"go.fifitido.net/ytdl-web/pkg/ytdl/cache"
|
||||
)
|
||||
|
||||
var (
|
||||
cfgFile string
|
||||
cfg *config.Config
|
||||
|
||||
rootCmd = &cobra.Command{
|
||||
Use: "ytdl-web",
|
||||
Short: "A web frontend for yt-dlp",
|
||||
Long: `YTDL Web is a web application that grabs the links to videos
|
||||
from over a thousand websites using the yt-dlp project under the hood.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
initLogging()
|
||||
logger := slog.Default()
|
||||
|
||||
db, err := badger.Open(
|
||||
badger.
|
||||
DefaultOptions(cfg.Cache.DirPath).
|
||||
WithLogger(utils.NewBadgerLogger(logger.With("module", "badger"))),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
cache := cache.NewDefaultMetadataCache(db)
|
||||
yt := ytdl.NewYtdl(cfg, slog.Default(), cache)
|
||||
ytdl.SetDefault(yt)
|
||||
|
||||
return server.ListenAndServe(
|
||||
server.WithListenAddr(viper.GetString("http.listen")),
|
||||
server.WithListenPort(viper.GetInt("http.port")),
|
||||
server.WithLogger(logger.With("module", "server")),
|
||||
)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func Execute() error {
|
||||
return rootCmd.Execute()
|
||||
}
|
||||
|
||||
func init() {
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $XDG_CONFIG_HOME/ytdl-web/config.yml)")
|
||||
|
||||
rootCmd.PersistentFlags().IntP("port", "p", 8080, "port to listen on")
|
||||
rootCmd.PersistentFlags().StringP("listen", "l", "", "address to listen on")
|
||||
rootCmd.PersistentFlags().StringP("base-path", "b", "", "the base path, used when behind reverse proxy")
|
||||
rootCmd.PersistentFlags().StringP("ytdlp-path", "y", "", "the path to the yt-dlp binary, used when it is not in $PATH")
|
||||
rootCmd.PersistentFlags().BoolP("cookies-enabled", "C", false, "whether cookies are enabled")
|
||||
rootCmd.PersistentFlags().StringP("cookies", "c", "", "the path to the cookies file")
|
||||
|
||||
viper.BindPFlag("http.port", rootCmd.PersistentFlags().Lookup("port"))
|
||||
viper.BindPFlag("http.listen", rootCmd.PersistentFlags().Lookup("listen"))
|
||||
viper.BindPFlag("http.basePath", rootCmd.PersistentFlags().Lookup("base-path"))
|
||||
viper.BindPFlag("ytdlp.binaryPath", rootCmd.PersistentFlags().Lookup("ytdlp-path"))
|
||||
viper.BindPFlag("cookies.enabled", rootCmd.PersistentFlags().Lookup("cookies-enabled"))
|
||||
viper.BindPFlag("cookies.filePath", rootCmd.PersistentFlags().Lookup("cookies"))
|
||||
}
|
||||
|
||||
func initConfig() {
|
||||
var err error
|
||||
if cfgFile != "" {
|
||||
cfg, err = config.LoadConfig(cfgFile)
|
||||
} else {
|
||||
cfg, err = config.LoadConfig()
|
||||
}
|
||||
|
||||
notFound := &viper.ConfigFileNotFoundError{}
|
||||
switch {
|
||||
case err != nil && !errors.As(err, notFound):
|
||||
cobra.CheckErr(err)
|
||||
case err != nil && errors.As(err, notFound):
|
||||
// The config file is optional, we shouldn't exit when the config is not found
|
||||
break
|
||||
default:
|
||||
fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
|
||||
}
|
||||
}
|
||||
|
||||
func initLogging() {
|
||||
if cfg.IsProduction() {
|
||||
slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
|
||||
AddSource: true,
|
||||
Level: slog.LevelInfo,
|
||||
})))
|
||||
} else {
|
||||
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
|
||||
AddSource: true,
|
||||
Level: slog.LevelDebug,
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
Copyright © 2024 Evan Fiordeliso <evan.fiordeliso@gmail.com>
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/dgraph-io/badger/v2"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
slogchi "github.com/samber/slog-chi"
|
||||
"go.fifitido.net/ytdl-web/pkg/config"
|
||||
"go.fifitido.net/ytdl-web/pkg/routes"
|
||||
"go.fifitido.net/ytdl-web/pkg/serverctx"
|
||||
"go.fifitido.net/ytdl-web/pkg/utils"
|
||||
"go.fifitido.net/ytdl-web/pkg/ytdl"
|
||||
"go.fifitido.net/ytdl-web/pkg/ytdl/cache"
|
||||
)
|
||||
|
||||
var (
|
||||
env = config.String("env", "production", "the environment to run")
|
||||
listenAddr = config.String("listen", ":8080", "address to listen on")
|
||||
basePath = config.String("base_path", "/", "the base path, used when behind reverse proxy")
|
||||
ytdlpPath = config.String("ytdlp.binary", "", "path to yt-dlp executable")
|
||||
cacheTTL = config.Duration("cache.ttl", time.Hour, "the TTL for the cache")
|
||||
cacheDir = config.String("cache.dir", "", "the directory to store the cache")
|
||||
cookies = config.Bool("cookies", false, "whether cookies are enabled")
|
||||
cookiesPath = config.String("cookies.path", "", "the path to the cookies file")
|
||||
cookiesBrowser = config.String("cookies.browser", "", "the browser to use for cookies")
|
||||
cookiesBrowserKeyring = config.String("cookies.browser.keyring", "", "the keyring to use for cookies")
|
||||
cookiesBrowserProfile = config.String("cookies.browser.profile", "", "the profile to use for cookies")
|
||||
cookiesBrowserContainer = config.String("cookies.browser.container", "", "the container to use for cookies")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
|
||||
AddSource: true,
|
||||
Level: slog.LevelInfo,
|
||||
}))
|
||||
|
||||
if env() == "development" {
|
||||
logger = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
|
||||
AddSource: true,
|
||||
Level: slog.LevelDebug,
|
||||
}))
|
||||
}
|
||||
|
||||
slog.SetDefault(logger)
|
||||
|
||||
db, err := badger.Open(
|
||||
badger.
|
||||
DefaultOptions(cacheDir()).
|
||||
WithLogger(utils.NewBadgerLogger(logger.With("module", "badger"))),
|
||||
)
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
cache := cache.NewDefaultMetadataCache(db, cacheTTL())
|
||||
ytdl.SetDefault(
|
||||
ytdl.NewYtdl(&ytdl.Config{
|
||||
BinaryPath: ytdlpPath(),
|
||||
CookiesEnabled: cookies(),
|
||||
CookiesFilePath: cookiesPath(),
|
||||
CookiesBrowser: cookiesBrowser(),
|
||||
CookiesBrowserKeyring: cookiesBrowserKeyring(),
|
||||
CookiesBrowserProfile: cookiesBrowserProfile(),
|
||||
CookiesBrowserContainer: cookiesBrowserContainer(),
|
||||
}, logger, cache),
|
||||
)
|
||||
|
||||
r := chi.NewRouter()
|
||||
|
||||
r.Use(
|
||||
serverctx.Middleware(basePath()),
|
||||
middleware.RequestID,
|
||||
middleware.RealIP,
|
||||
slogchi.New(logger),
|
||||
middleware.Recoverer,
|
||||
)
|
||||
|
||||
r.Mount(basePath(), routes.Router())
|
||||
|
||||
logger.Info("Starting HTTP server", slog.String("listen", listenAddr()))
|
||||
|
||||
if err := http.ListenAndServe(listenAddr(), r); err != nil {
|
||||
logger.Error("failed to serve website", slog.Any("error", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
---
|
||||
# The server environment
|
||||
# For dev environments use Development
|
||||
# For prod environments use Production
|
||||
# For staging envronments use Staging
|
||||
env: Production
|
||||
ytdlp:
|
||||
# The path to the yt-dlp binary
|
||||
# If it is already in your $PATH just yt-dlp will work.
|
||||
binaryPath: yt-dlp
|
||||
http:
|
||||
# The port to listen on
|
||||
port: 8080
|
||||
# The address to listen on
|
||||
# For local only access use 127.0.0.1
|
||||
# For public access use 0.0.0.0
|
||||
listen: 0.0.0.0
|
||||
# The base path of the application, useful for reverse proxies
|
||||
basePath: ""
|
||||
|
||||
# A list of proxy servers to trust for security purposes
|
||||
# Only needed when accessing app behind a proxy
|
||||
trustedProxies: []
|
||||
cookies:
|
||||
# Whether to use cookies when fetching the video metadata
|
||||
enabled: false
|
||||
|
||||
# The path to the netscape formatted cookies file
|
||||
# See: https://www.reddit.com/r/youtubedl/wiki/cookies/ for details.
|
||||
filePath: ~/.cookies
|
||||
|
||||
# Settings for using cookies from a browser's cookies store
|
||||
fromBrowser:
|
||||
# The name of the browser to load cookies from.
|
||||
# Currently supported browsers are: brave, chrome, chromium, edge,
|
||||
# firefox, opera, safari, vivaldi.
|
||||
browser: firefox
|
||||
|
||||
# The keyring used for decrypting Chromium cookies on Linux
|
||||
# Currently supported keyrings are: basictext, gnomekeyring, kwallet
|
||||
keyring: basictext
|
||||
|
||||
# The profile to load cookies from (Firefox)
|
||||
profile: default
|
||||
|
||||
# The container to load cookies from (Firefox)
|
||||
container: none
|
||||
129
config/config.go
129
config/config.go
|
|
@ -1,129 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/adrg/xdg"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Env string `mapstructure:"env"`
|
||||
Ytdlp ConfigYtdlp `mapstructure:"ytdlp"`
|
||||
HTTP ConfigHTTP `mapstructure:"http"`
|
||||
Cache ConfigCache `mapstructure:"cache"`
|
||||
Cookies ConfigCookies `mapstructure:"cookies"`
|
||||
}
|
||||
|
||||
func (c *Config) IsProduction() bool {
|
||||
return c.Env == "Production"
|
||||
}
|
||||
|
||||
func (c *Config) IsDevelopment() bool {
|
||||
return c.Env == "Development"
|
||||
}
|
||||
|
||||
func (c *Config) IsStaging() bool {
|
||||
return c.Env == "Staging"
|
||||
}
|
||||
|
||||
type ConfigYtdlp struct {
|
||||
BinaryPath string `mapstructure:"binaryPath"`
|
||||
}
|
||||
|
||||
type ConfigHTTP struct {
|
||||
Port int `mapstructure:"port"`
|
||||
Listen string `mapstructure:"listen"`
|
||||
BasePath string `mapstructure:"basePath"`
|
||||
TrustedProxies []string `mapstructure:"trustedProxies"`
|
||||
}
|
||||
|
||||
type ConfigCache struct {
|
||||
TTL time.Duration `mapstructure:"ttl"`
|
||||
DirPath string `mapstructure:"dirPath"`
|
||||
}
|
||||
|
||||
type ConfigCookies struct {
|
||||
Enabled bool `mapstructure:"enabled"`
|
||||
FilePath string `mapstructure:"filePath"`
|
||||
FromBrowser ConfigCookiesFromBrowser `mapstructure:"fromBrowser"`
|
||||
}
|
||||
|
||||
type ConfigCookiesFromBrowser struct {
|
||||
Browser string `mapstructure:"browser"`
|
||||
Keyring string `mapstructure:"keyring"`
|
||||
Profile string `mapstructure:"profile"`
|
||||
Container string `mapstructure:"container"`
|
||||
}
|
||||
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
Env: "Production",
|
||||
Ytdlp: ConfigYtdlp{BinaryPath: "yt-dlp"},
|
||||
HTTP: ConfigHTTP{
|
||||
Port: 8080,
|
||||
Listen: "127.0.0.1",
|
||||
BasePath: "/",
|
||||
},
|
||||
Cache: ConfigCache{
|
||||
TTL: time.Hour,
|
||||
DirPath: "/tmp/ytdl-web",
|
||||
},
|
||||
Cookies: ConfigCookies{
|
||||
Enabled: false,
|
||||
FilePath: "./cookies.txt",
|
||||
FromBrowser: ConfigCookiesFromBrowser{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func LoadConfig(paths ...string) (*Config, error) {
|
||||
viper.SetEnvPrefix("YTDL")
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
viper.AutomaticEnv()
|
||||
|
||||
viper.SetConfigName("config")
|
||||
viper.SetConfigType("yaml")
|
||||
|
||||
if len(paths) > 0 {
|
||||
for _, p := range paths {
|
||||
viper.AddConfigPath(path.Dir(p))
|
||||
}
|
||||
} else {
|
||||
envDir := os.Getenv("YTDL_CONFIGDIR")
|
||||
if envDir != "" {
|
||||
viper.AddConfigPath(envDir)
|
||||
}
|
||||
|
||||
viper.AddConfigPath(".")
|
||||
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err == nil {
|
||||
viper.AddConfigPath(homeDir + "/.config/ytdl-web")
|
||||
}
|
||||
|
||||
viper.AddConfigPath(xdg.ConfigHome + "/ytdl-web")
|
||||
for _, dir := range xdg.ConfigDirs {
|
||||
viper.AddConfigPath(dir + "/ytdl-web")
|
||||
}
|
||||
}
|
||||
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
config := DefaultConfig()
|
||||
if err := viper.Unmarshal(config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf("%#v\n", config)
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
|
@ -11,7 +11,12 @@
|
|||
DOCKER_ORG = "apps";
|
||||
DOCKER_PLATFORMS = "linux/amd64,linux/arm64";
|
||||
|
||||
YTDL_ENV = "Development";
|
||||
YTDL_WEB_ENV = "development";
|
||||
YTDL_WEB_YTDLP_BINARY = "/usr/bin/yt-dlp";
|
||||
YTDL_WEB_LISTEN = ":8989";
|
||||
YTDL_WEB_CACHE_DIR = config.devenv.runtime + "/cache";
|
||||
YTDL_WEB_COOKIES = "true";
|
||||
YTDL_WEB_COOKIES_BROWSER = "firefox";
|
||||
};
|
||||
|
||||
# https://devenv.sh/packages/
|
||||
|
|
@ -42,7 +47,7 @@
|
|||
-X $VERSION_PKG.Build=$(build-id)
|
||||
-X $VERSION_PKG.BuildDate=$(build-date)
|
||||
-X $VERSION_PKG.BuiltBy=manual
|
||||
" -o $BINARY_OUT .
|
||||
" -o $BINARY_OUT ./cmd/ytdl-web
|
||||
'';
|
||||
clean.exec = ''
|
||||
rm -rf ./dist/ ./out/ ./tmp/
|
||||
|
|
|
|||
25
go.mod
25
go.mod
|
|
@ -4,12 +4,9 @@ go 1.25.2
|
|||
|
||||
require (
|
||||
github.com/a-h/templ v0.3.960
|
||||
github.com/adrg/xdg v0.5.3
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4
|
||||
github.com/go-chi/chi/v5 v5.2.3
|
||||
github.com/samber/slog-chi v1.17.0
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/spf13/viper v1.21.0
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
@ -18,35 +15,13 @@ require (
|
|||
github.com/dgraph-io/ristretto v0.2.0 // indirect
|
||||
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v1.0.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.18.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/magiconair/properties v1.8.10 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/sagikazarmark/locafero v0.12.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/net v0.46.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/text v0.30.0 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
|
|
|||
120
go.sum
120
go.sum
|
|
@ -3,10 +3,6 @@ github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE
|
|||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/a-h/templ v0.3.960 h1:trshEpGa8clF5cdI39iY4ZrZG8Z/QixyzEyUnA7feTM=
|
||||
github.com/a-h/templ v0.3.960/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo=
|
||||
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
|
||||
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
|
||||
github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
|
||||
github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
|
|
@ -16,198 +12,86 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc
|
|||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk=
|
||||
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA=
|
||||
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgraph-io/ristretto v0.2.0 h1:XAfl+7cmoUDWW/2Lx8TGZQjjxIQ2Ley9DSf52dru4WE=
|
||||
github.com/dgraph-io/ristretto v0.2.0/go.mod h1:8uBHCU/PBV4Ag0CJrP47b9Ofby5dqWNh4FicAdoqFNU=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38=
|
||||
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE=
|
||||
github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
|
||||
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||
github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY=
|
||||
github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co=
|
||||
github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
|
||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
|
||||
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
|
||||
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
|
||||
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
|
||||
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
|
||||
github.com/samber/slog-chi v1.17.0 h1:zP66fV4LGF1y1Dg/+uHNY9Uxkmw1YNWaZGws6M6mPCk=
|
||||
github.com/samber/slog-chi v1.17.0/go.mod h1:a1iIuofF2gS1ii8aXIQhC6TEguLOhOvSM958fY5hToU=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
|
||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
|
||||
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
||||
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/spf13/viper v1.10.0 h1:mXH0UwHS4D2HwWZa75im4xIQynLfblmWV7qcWpfv0yk=
|
||||
github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
|
||||
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
||||
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
|
||||
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
|
||||
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
|
||||
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=
|
||||
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
|||
16
main.go
16
main.go
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
Copyright © 2024 Evan Fiordeliso <evan.fiordeliso@gmail.com>
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"go.fifitido.net/ytdl-web/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := cmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
107
nix/devenv.nix
107
nix/devenv.nix
|
|
@ -1,107 +0,0 @@
|
|||
{ pkgs, ... }:
|
||||
|
||||
{
|
||||
# https://devenv.sh/basics/
|
||||
env.NAME = "ytdl-web";
|
||||
env.BINARY_OUT = "./out/ytdl-web";
|
||||
env.VERSION = "v1.2.3";
|
||||
env.VERSION_PKG = "go.fifitido.net/ytdl-web/version";
|
||||
env.DOCKER_REGISTRY = "git.fifitido.net";
|
||||
env.DOCKER_ORG = "apps";
|
||||
env.DOCKER_PLATFORMS = "linux/amd64,linux/arm64";
|
||||
|
||||
env.YTDL_ENV = "Development";
|
||||
|
||||
# https://devenv.sh/packages/
|
||||
packages = with pkgs; [
|
||||
git
|
||||
air
|
||||
goreleaser
|
||||
docker
|
||||
docker-compose
|
||||
buildkit
|
||||
docker-buildx
|
||||
yt-dlp
|
||||
cobra-cli
|
||||
];
|
||||
|
||||
# https://devenv.sh/scripts/
|
||||
scripts.deps.exec = "go mod download";
|
||||
scripts.tidy.exec = "go mod tidy";
|
||||
scripts.check.exec = "goreleaser check";
|
||||
|
||||
scripts.build-id.exec = "git rev-parse --short HEAD";
|
||||
scripts.build-date.exec = "date -Iseconds";
|
||||
|
||||
scripts.build.exec = ''
|
||||
go build -ldflags="
|
||||
-X $VERSION_PKG.Version=$VERSION
|
||||
-X $VERSION_PKG.Build=$(build-id)
|
||||
-X $VERSION_PKG.BuildDate=$(build-date)
|
||||
-X $VERSION_PKG.BuiltBy=manual
|
||||
" -o $BINARY_OUT .
|
||||
'';
|
||||
scripts.clean.exec = ''
|
||||
rm -rf ./dist/ ./out/ ./tmp/
|
||||
go clean
|
||||
'';
|
||||
scripts.release.exec = ''
|
||||
clean
|
||||
goreleaser release
|
||||
docker-build-release
|
||||
'';
|
||||
scripts.lint.exec = "trunk check";
|
||||
scripts.fmt.exec = "trunk fmt";
|
||||
|
||||
scripts.docker-image.exec = "echo $DOCKER_REGISTRY/$DOCKER_ORG/$NAME";
|
||||
|
||||
scripts.docker-init.exec = ''
|
||||
docker buildx create \
|
||||
--name $NAME \
|
||||
--platform $DOCKER_PLATFORMS
|
||||
'';
|
||||
|
||||
scripts.docker-login.exec = "docker login $DOCKER_REGISTRY";
|
||||
|
||||
scripts.docker-build.exec = ''
|
||||
platform=''${1:-linux}
|
||||
output=''${2:-type=docker}
|
||||
|
||||
docker-init
|
||||
PROGRESS_NO_TRUNC=1 docker buildx build . \
|
||||
--tag $(docker-image):$VERSION \
|
||||
--tag $(docker-image):latest \
|
||||
--platform $platform \
|
||||
--builder $NAME \
|
||||
--build-arg VERSION=$VERSION \
|
||||
--build-arg BUILD=$(build-id) \
|
||||
--build-arg BUILD_DATE=$(build-date) \
|
||||
--build-arg BUILT_BY="Devenv Script" \
|
||||
--progress plain \
|
||||
--output $output
|
||||
'';
|
||||
|
||||
scripts.docker-build-release.exec = "docker-build $DOCKER_PLATFORMS type=image,push=true";
|
||||
|
||||
enterShell = ''
|
||||
echo "Welcome to the $NAME development environment."
|
||||
echo -n "Golang version: $(go version | cut -d ' ' -f 3), "
|
||||
echo -n "Goreleaser version: $(goreleaser --version | tail -n 9 | head -n 1 | cut -c 16-), "
|
||||
echo -n "Docker CLI version: $(docker version -f {{.Client.Version}}), "
|
||||
echo -n "Buildx version: $(docker buildx version | cut -d ' ' -f 2), "
|
||||
echo "yt-dlp version: $(yt-dlp --version)"
|
||||
'';
|
||||
|
||||
# https://devenv.sh/languages/
|
||||
languages.go.enable = true;
|
||||
|
||||
# https://devenv.sh/pre-commit-hooks/
|
||||
pre-commit.hooks.staticcheck.enable = true;
|
||||
pre-commit.hooks.hadolint.enable = true;
|
||||
pre-commit.hooks.yamllint.enable = true;
|
||||
|
||||
# https://devenv.sh/processes/
|
||||
processes.web.exec = "air";
|
||||
|
||||
# See full reference at https://devenv.sh/reference/options/
|
||||
}
|
||||
|
|
@ -22,15 +22,6 @@ buildGoModule rec {
|
|||
"-X $VERSION_PKG.BuiltBy=nix"
|
||||
];
|
||||
|
||||
nativeBuildInputs = [ installShellFiles ];
|
||||
|
||||
postInstall = ''
|
||||
installShellCompletion --cmd ${pname} \
|
||||
--zsh <($out/bin/${pname} completion zsh) \
|
||||
--bash <($out/bin/${pname} completion bash) \
|
||||
--fish <($out/bin/${pname} completion fish)
|
||||
'';
|
||||
|
||||
meta = with lib; {
|
||||
description = "Yet another yt-dlp web frontend written in Go.";
|
||||
homepage = "https://git.fifitido.net/apps/ytdl-web";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,104 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func getEnvKey(key string) string {
|
||||
return fmt.Sprintf("YTDL_WEB_%s", strings.ReplaceAll(strings.ToUpper(key), ".", "_"))
|
||||
}
|
||||
|
||||
func String(key string, defaultValue string, usage string) func() string {
|
||||
flagValue := flag.String(key, defaultValue, usage)
|
||||
return func() string {
|
||||
if flagValue != nil && *flagValue != defaultValue {
|
||||
return *flagValue
|
||||
}
|
||||
|
||||
envValue, ok := os.LookupEnv(getEnvKey(key))
|
||||
if ok {
|
||||
return envValue
|
||||
}
|
||||
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
func Int(key string, defaultValue int, usage string) func() int {
|
||||
flagValue := flag.Int(key, defaultValue, usage)
|
||||
return func() int {
|
||||
if flagValue != nil && *flagValue != defaultValue {
|
||||
return *flagValue
|
||||
}
|
||||
|
||||
envValue, ok := os.LookupEnv(getEnvKey(key))
|
||||
if ok {
|
||||
value, err := strconv.Atoi(envValue)
|
||||
if err == nil {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
func Bool(key string, defaultValue bool, usage string) func() bool {
|
||||
flagValue := flag.Bool(key, defaultValue, usage)
|
||||
return func() bool {
|
||||
if flagValue != nil && *flagValue != defaultValue {
|
||||
return *flagValue
|
||||
}
|
||||
|
||||
envValue, ok := os.LookupEnv(getEnvKey(key))
|
||||
if ok {
|
||||
value, err := strconv.ParseBool(envValue)
|
||||
if err == nil {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
func StringSlice(key string, defaultValue []string, usage string) func() []string {
|
||||
defaultString := strings.Join(defaultValue, ",")
|
||||
flagValue := flag.String(key, defaultString, usage)
|
||||
return func() []string {
|
||||
if flagValue != nil && *flagValue != defaultString {
|
||||
return strings.Split(*flagValue, ",")
|
||||
}
|
||||
|
||||
envValue, ok := os.LookupEnv(getEnvKey(key))
|
||||
if ok {
|
||||
return strings.Split(envValue, ",")
|
||||
}
|
||||
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
func Duration(key string, defaultValue time.Duration, usage string) func() time.Duration {
|
||||
flagValue := flag.Duration(key, defaultValue, usage)
|
||||
return func() time.Duration {
|
||||
if flagValue != nil && *flagValue != defaultValue {
|
||||
return *flagValue
|
||||
}
|
||||
|
||||
envValue, ok := os.LookupEnv(getEnvKey(key))
|
||||
if ok {
|
||||
value, err := time.ParseDuration(envValue)
|
||||
if err == nil {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package server
|
||||
package routes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
@ -16,13 +16,15 @@ import (
|
|||
"go.fifitido.net/ytdl-web/pkg/ytdl/metadata"
|
||||
)
|
||||
|
||||
func Routes(r chi.Router) {
|
||||
func Router() chi.Router {
|
||||
r := chi.NewRouter()
|
||||
r.Get("/", home)
|
||||
r.Route("/download", func(r chi.Router) {
|
||||
r.Get("/", download)
|
||||
r.Head("/proxy", proxyDownload)
|
||||
r.Get("/proxy", proxyDownload)
|
||||
})
|
||||
return r
|
||||
}
|
||||
|
||||
func renderPage(w http.ResponseWriter, r *http.Request, component templ.Component) {
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
package server
|
||||
|
||||
import "log/slog"
|
||||
|
||||
type Options struct {
|
||||
ListenAddr string
|
||||
ListenPort int
|
||||
Logger *slog.Logger
|
||||
}
|
||||
|
||||
func DefaultOptions() *Options {
|
||||
return &Options{
|
||||
ListenAddr: "127.0.0.1",
|
||||
ListenPort: 8080,
|
||||
Logger: slog.Default(),
|
||||
}
|
||||
}
|
||||
|
||||
type Option func(*Options)
|
||||
|
||||
func WithListenAddr(addr string) Option {
|
||||
return func(o *Options) {
|
||||
o.ListenAddr = addr
|
||||
}
|
||||
}
|
||||
|
||||
func WithListenPort(port int) Option {
|
||||
return func(o *Options) {
|
||||
o.ListenPort = port
|
||||
}
|
||||
}
|
||||
|
||||
func WithLogger(logger *slog.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = logger
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
slogchi "github.com/samber/slog-chi"
|
||||
"go.fifitido.net/ytdl-web/pkg/serverctx"
|
||||
)
|
||||
|
||||
func ListenAndServe(options ...Option) error {
|
||||
opts := DefaultOptions()
|
||||
for _, opt := range options {
|
||||
opt(opts)
|
||||
}
|
||||
|
||||
r := chi.NewRouter()
|
||||
|
||||
r.Use(
|
||||
serverctx.Middleware,
|
||||
middleware.RequestID,
|
||||
middleware.RealIP,
|
||||
slogchi.New(opts.Logger),
|
||||
middleware.Recoverer,
|
||||
)
|
||||
|
||||
r.Group(Routes)
|
||||
|
||||
listenAddr := fmt.Sprintf("%s:%d", opts.ListenAddr, opts.ListenPort)
|
||||
opts.Logger.Info("Starting HTTP server", slog.String("addr", opts.ListenAddr), slog.Int("port", opts.ListenPort))
|
||||
return http.ListenAndServe(listenAddr, r)
|
||||
}
|
||||
|
|
@ -8,18 +8,30 @@ import (
|
|||
type contextKey string
|
||||
|
||||
const (
|
||||
isHTTPS contextKey = "isHTTPS"
|
||||
isHTTPSKey contextKey = "isHTTPS"
|
||||
basePathKey contextKey = "basePath"
|
||||
)
|
||||
|
||||
func Middleware(next http.Handler) http.Handler {
|
||||
func Middleware(basePath string) func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
isHttps := r.URL.Scheme == "https" || r.Header.Get("X-Forwarded-Proto") == "https"
|
||||
ctx := context.WithValue(r.Context(), isHTTPS, isHttps)
|
||||
ctx := context.WithValue(r.Context(), isHTTPSKey, isHttps)
|
||||
ctx = context.WithValue(ctx, basePathKey, basePath)
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func IsHTTPS(ctx context.Context) bool {
|
||||
isHttps, ok := ctx.Value(isHTTPS).(bool)
|
||||
isHttps, ok := ctx.Value(isHTTPSKey).(bool)
|
||||
return ok && isHttps
|
||||
}
|
||||
|
||||
func BasePath(ctx context.Context) string {
|
||||
basePath, ok := ctx.Value(basePathKey).(string)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return basePath
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ templ videoFormatView(vm *DownloadsViewModel, vidIndex int, format metadata.Form
|
|||
<a
|
||||
class="btn btn-primary flex-grow-1"
|
||||
download={ filename }
|
||||
href={ pathTo(fmt.Sprintf("download/proxy?url=%s&format=%s&index=%d", vm.Url, format.FormatID, vidIndex)) }
|
||||
href={ pathTo(ctx, fmt.Sprintf("download/proxy?url=%s&format=%s&index=%d", vm.Url, format.FormatID, vidIndex)) }
|
||||
aria-label="Proxied Download"
|
||||
>
|
||||
Download (proxied)
|
||||
|
|
@ -106,8 +106,8 @@ templ Downloads(vm *DownloadsViewModel) {
|
|||
<h2 class="fs-4 text-muted text-center">{ *vm.Meta.Title }</h2>
|
||||
<p style="font-size: 0.85rem">{ vm.Url }</p>
|
||||
<a
|
||||
href={ pathTo() }
|
||||
hx-get={ pathTo() }
|
||||
href={ pathTo(ctx) }
|
||||
hx-get={ pathTo(ctx) }
|
||||
hx-trigger="click"
|
||||
class="btn btn-secondary btn-sm mt-3"
|
||||
style="width: 30rem; max-width: 100%"
|
||||
|
|
|
|||
|
|
@ -110,9 +110,9 @@ func videoFormatView(vm *DownloadsViewModel, vidIndex int, format metadata.Forma
|
|||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 templ.SafeURL
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinURLErrs(pathTo(fmt.Sprintf("download/proxy?url=%s&format=%s&index=%d", vm.Url, format.FormatID, vidIndex)))
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinURLErrs(pathTo(ctx, fmt.Sprintf("download/proxy?url=%s&format=%s&index=%d", vm.Url, format.FormatID, vidIndex)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/views/downloads.templ`, Line: 34, Col: 108}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/views/downloads.templ`, Line: 34, Col: 113}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
|
@ -419,9 +419,9 @@ func Downloads(vm *DownloadsViewModel) templ.Component {
|
|||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var24 templ.SafeURL
|
||||
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinURLErrs(pathTo())
|
||||
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinURLErrs(pathTo(ctx))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/views/downloads.templ`, Line: 109, Col: 19}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/views/downloads.templ`, Line: 109, Col: 22}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
|
@ -432,9 +432,9 @@ func Downloads(vm *DownloadsViewModel) templ.Component {
|
|||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var25 string
|
||||
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(pathTo())
|
||||
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(pathTo(ctx))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/views/downloads.templ`, Line: 110, Col: 21}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/views/downloads.templ`, Line: 110, Col: 24}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
package views
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/a-h/templ"
|
||||
"github.com/spf13/viper"
|
||||
"go.fifitido.net/ytdl-web/pkg/serverctx"
|
||||
)
|
||||
|
||||
func pathTo(path ...string) templ.SafeURL {
|
||||
return templ.SafeURL(viper.GetString("base_path") + "/" + strings.TrimPrefix(strings.Join(path, "/"), "/"))
|
||||
func pathTo(ctx context.Context, path ...string) templ.SafeURL {
|
||||
return templ.SafeURL(serverctx.BasePath(ctx) + "/" + strings.TrimPrefix(strings.Join(path, "/"), "/"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ templ Home(err *Error) {
|
|||
View a complete list of supported websites
|
||||
<a href="https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md">here</a>.
|
||||
</p>
|
||||
<form hx-get={ pathTo("/download") } hx-trigger="submit">
|
||||
<form hx-get={ pathTo(ctx, "/download") } hx-trigger="submit">
|
||||
<div class="mb-3">
|
||||
<label for="url" class="form-label visually-hidden">Url</label>
|
||||
<div class="input-group">
|
||||
|
|
@ -48,7 +48,7 @@ templ Home(err *Error) {
|
|||
if err.RetryUrl != nil {
|
||||
<button
|
||||
class="btn btn-link btn-sm pt-0 lh-base text-decoration-none"
|
||||
hx-get={ pathTo("/download") }
|
||||
hx-get={ pathTo(ctx, "/download") }
|
||||
hx-trigger="click"
|
||||
hx-target="#main-content"
|
||||
hx-swap="innerHTML"
|
||||
|
|
|
|||
|
|
@ -53,9 +53,9 @@ func Home(err *Error) templ.Component {
|
|||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(pathTo("/download"))
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(pathTo(ctx, "/download"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/views/home.templ`, Line: 21, Col: 36}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/views/home.templ`, Line: 21, Col: 41}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
|
@ -97,9 +97,9 @@ func Home(err *Error) templ.Component {
|
|||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(pathTo("/download"))
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(pathTo(ctx, "/download"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/views/home.templ`, Line: 51, Col: 34}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/views/home.templ`, Line: 51, Col: 39}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
|
|
|||
|
|
@ -10,18 +10,21 @@ import (
|
|||
|
||||
type MetadataCache interface {
|
||||
Get(key string) (*metadata.Metadata, error)
|
||||
Set(key string, value *metadata.Metadata, ttl time.Duration) error
|
||||
Set(key string, value *metadata.Metadata) error
|
||||
}
|
||||
|
||||
type DefaultMetadataCache struct {
|
||||
db *badger.DB
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
func NewDefaultMetadataCache(db *badger.DB) *DefaultMetadataCache {
|
||||
func NewDefaultMetadataCache(db *badger.DB, ttl time.Duration) *DefaultMetadataCache {
|
||||
return &DefaultMetadataCache{
|
||||
db: db,
|
||||
ttl: ttl,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DefaultMetadataCache) Get(key string) (*metadata.Metadata, error) {
|
||||
value := &metadata.Metadata{}
|
||||
err := c.db.View(func(txn *badger.Txn) error {
|
||||
|
|
@ -37,13 +40,13 @@ func (c *DefaultMetadataCache) Get(key string) (*metadata.Metadata, error) {
|
|||
return value, err
|
||||
}
|
||||
|
||||
func (c *DefaultMetadataCache) Set(key string, value *metadata.Metadata, ttl time.Duration) error {
|
||||
func (c *DefaultMetadataCache) Set(key string, value *metadata.Metadata) error {
|
||||
return c.db.Update(func(txn *badger.Txn) error {
|
||||
data, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e := badger.NewEntry([]byte(key), data).WithTTL(ttl)
|
||||
e := badger.NewEntry([]byte(key), data).WithTTL(c.ttl)
|
||||
return txn.SetEntry(e)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
package ytdl
|
||||
|
||||
type CookiesFromBrowserConfig struct {
|
||||
Browser string
|
||||
Keyring string
|
||||
Profile string
|
||||
Container string
|
||||
}
|
||||
|
||||
type CookiesConfig struct {
|
||||
Enabled bool
|
||||
FilePath string
|
||||
FromBrowser CookiesFromBrowserConfig
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
BinaryPath string
|
||||
CookiesEnabled bool
|
||||
CookiesFilePath string
|
||||
CookiesBrowser string
|
||||
CookiesBrowserKeyring string
|
||||
CookiesBrowserProfile string
|
||||
CookiesBrowserContainer string
|
||||
}
|
||||
|
|
@ -11,7 +11,6 @@ import (
|
|||
|
||||
"log/slog"
|
||||
|
||||
"go.fifitido.net/ytdl-web/config"
|
||||
"go.fifitido.net/ytdl-web/pkg/ytdl/cache"
|
||||
"go.fifitido.net/ytdl-web/pkg/ytdl/metadata"
|
||||
)
|
||||
|
|
@ -23,7 +22,7 @@ type Ytdl interface {
|
|||
}
|
||||
|
||||
type ytdlImpl struct {
|
||||
cfg *config.Config
|
||||
cfg *Config
|
||||
logger *slog.Logger
|
||||
cache cache.MetadataCache
|
||||
version string
|
||||
|
|
@ -39,9 +38,9 @@ func Default() Ytdl {
|
|||
return defaultYtdl
|
||||
}
|
||||
|
||||
func NewYtdl(cfg *config.Config, logger *slog.Logger, cache cache.MetadataCache) Ytdl {
|
||||
func NewYtdl(cfg *Config, logger *slog.Logger, cache cache.MetadataCache) Ytdl {
|
||||
cmd := exec.Command(
|
||||
cfg.Ytdlp.BinaryPath,
|
||||
cfg.BinaryPath,
|
||||
"--version",
|
||||
)
|
||||
var out bytes.Buffer
|
||||
|
|
@ -80,16 +79,16 @@ func buildBrowserCookieString(browser, keyring, profile, container string) strin
|
|||
}
|
||||
|
||||
func (y *ytdlImpl) appendCookieArgs(args []string) []string {
|
||||
if y.cfg.Cookies.Enabled {
|
||||
if y.cfg.Cookies.FromBrowser.Browser != "" {
|
||||
if y.cfg.CookiesEnabled {
|
||||
if y.cfg.CookiesBrowser != "" {
|
||||
args = append(args, "--cookies-from-browser", buildBrowserCookieString(
|
||||
y.cfg.Cookies.FromBrowser.Browser,
|
||||
y.cfg.Cookies.FromBrowser.Keyring,
|
||||
y.cfg.Cookies.FromBrowser.Profile,
|
||||
y.cfg.Cookies.FromBrowser.Container,
|
||||
y.cfg.CookiesBrowser,
|
||||
y.cfg.CookiesBrowserKeyring,
|
||||
y.cfg.CookiesBrowserProfile,
|
||||
y.cfg.CookiesBrowserContainer,
|
||||
))
|
||||
} else {
|
||||
args = append(args, "--cookies", y.cfg.Cookies.FilePath)
|
||||
args = append(args, "--cookies", y.cfg.CookiesFilePath)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -116,7 +115,7 @@ func (y *ytdlImpl) GetMetadata(url string) (*metadata.Metadata, error) {
|
|||
|
||||
fmt.Printf("ytdlp args: %#v\n", args)
|
||||
|
||||
cmd := exec.Command(y.cfg.Ytdlp.BinaryPath, args...)
|
||||
cmd := exec.Command(y.cfg.BinaryPath, args...)
|
||||
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
|
|
@ -143,7 +142,7 @@ func (y *ytdlImpl) GetMetadata(url string) (*metadata.Metadata, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if err := y.cache.Set(url, meta, y.cfg.Cache.TTL); err != nil {
|
||||
if err := y.cache.Set(url, meta); err != nil {
|
||||
y.logger.Warn("failed to cache metadata", slog.String("url", url), slog.String("error", err.Error()))
|
||||
}
|
||||
|
||||
|
|
@ -169,7 +168,7 @@ func (y *ytdlImpl) Download(w io.Writer, url, format string, index int) error {
|
|||
args = append(args, "--load-info-json", "-")
|
||||
}
|
||||
|
||||
cmd := exec.Command(y.cfg.Ytdlp.BinaryPath, args...)
|
||||
cmd := exec.Command(y.cfg.BinaryPath, args...)
|
||||
cmd.Stdout = w
|
||||
|
||||
if err == nil {
|
||||
|
|
|
|||
Loading…
Reference in New Issue