config.go
⎇
Raw
1package config
2
3import (
4 "fmt"
5 "log/slog"
6 "os"
7 "path/filepath"
8 "strconv"
9 "strings"
10)
11
12type Config struct {
13 Port int
14 DataDir string
15 DBPath string
16 LibraryDir string
17 TempDir string
18 BinDir string
19 YTDLPPath string
20 YTDLPManaged bool
21 DenoPath string
22 DenoManaged bool
23 // UpdateExternalTools lets VidArchive run the updater of a tool it did not
24 // install. It is the escape hatch for a platform with no mapped release
25 // build: install the binary once by hand, then manage it from here.
26 UpdateExternalTools bool
27 FFmpegPath string
28 FFprobePath string
29 BaseURL string
30 Workers int
31 SchedulerInterval int
32 LogLevel string
33 LogFormat string
34 Username string
35 PasswordHash string
36}
37
38func New() *Config {
39 dataDir := getEnv("VIDARCHIVE_DATA_DIR", "./data")
40 binDir := getEnv("VIDARCHIVE_BIN_DIR", filepath.Join(dataDir, "bin"))
41 ytdlpPath, ytdlpManaged := managedPath("VIDARCHIVE_YTDLP_PATH", binDir, "yt-dlp")
42 denoPath, denoManaged := managedPath("VIDARCHIVE_DENO_PATH", binDir, "deno")
43
44 return &Config{
45 Port: getEnvInt("VIDARCHIVE_PORT", 8080, 1),
46 DataDir: dataDir,
47 DBPath: getEnv("VIDARCHIVE_DB_PATH", filepath.Join(dataDir, "vidarchive.db")),
48 LibraryDir: getEnv("VIDARCHIVE_LIBRARY_DIR", filepath.Join(dataDir, "library")),
49 TempDir: getEnv("VIDARCHIVE_TEMP_DIR", filepath.Join(dataDir, "temp")),
50 BinDir: binDir,
51 YTDLPPath: ytdlpPath,
52 YTDLPManaged: ytdlpManaged,
53 DenoPath: denoPath,
54 DenoManaged: denoManaged,
55 UpdateExternalTools: getEnvBool("VIDARCHIVE_UPDATE_EXTERNAL_TOOLS"),
56 FFmpegPath: getEnv("VIDARCHIVE_FFMPEG_PATH", "ffmpeg"),
57 FFprobePath: getEnv("VIDARCHIVE_FFPROBE_PATH", "ffprobe"),
58 BaseURL: getEnv("VIDARCHIVE_BASE_URL", ""),
59 Workers: getEnvInt("VIDARCHIVE_WORKERS", 2, 1),
60 SchedulerInterval: getEnvInt("VIDARCHIVE_SCHEDULER_INTERVAL", 60, 1),
61 LogLevel: getEnv("VIDARCHIVE_LOG_LEVEL", "info"),
62 LogFormat: getEnv("VIDARCHIVE_LOG_FORMAT", "text"),
63 Username: os.Getenv("VIDARCHIVE_USERNAME"),
64 PasswordHash: os.Getenv("VIDARCHIVE_PASSWORD_HASH"),
65 }
66}
67
68// SetupLogging also redirects the stdlib log package, so output from
69// dependencies is formatted and levelled the same way.
70func (c *Config) SetupLogging() {
71 var level slog.Level
72 if err := level.UnmarshalText([]byte(c.LogLevel)); err != nil {
73 fmt.Fprintf(os.Stderr, "invalid VIDARCHIVE_LOG_LEVEL %q: %v, using info\n", c.LogLevel, err)
74 level = slog.LevelInfo
75 }
76
77 opts := &slog.HandlerOptions{Level: level}
78 var h slog.Handler = slog.NewTextHandler(os.Stderr, opts)
79 if strings.EqualFold(c.LogFormat, "json") {
80 h = slog.NewJSONHandler(os.Stderr, opts)
81 }
82 slog.SetDefault(slog.New(h))
83}
84
85func (c *Config) IsHTTPS() bool {
86 return strings.HasPrefix(c.BaseURL, "https://")
87}
88
89// VidArchive installs only what it owns, but it updates anything the operator
90// lets it update.
91func (c *Config) CanUpdateYTDLP() bool { return c.YTDLPManaged || c.UpdateExternalTools }
92func (c *Config) CanUpdateDeno() bool { return c.DenoManaged || c.UpdateExternalTools }
93
94// managedPath decides who owns a tool's binary. An explicit VIDARCHIVE_*_PATH
95// means the operator does, so VidArchive never installs over it.
96func managedPath(key, binDir, name string) (string, bool) {
97 if v := os.Getenv(key); v != "" {
98 return v, false
99 }
100 return filepath.Join(binDir, name), true
101}
102
103func getEnv(key, defaultVal string) string {
104 if v := os.Getenv(key); v != "" {
105 return v
106 }
107 return defaultVal
108}
109
110func getEnvBool(key string) bool {
111 v, _ := strconv.ParseBool(os.Getenv(key))
112 return v
113}
114
115// getEnvInt falls back to the default below min: a zero or negative worker
116// count, tick interval or port stalls downloads, spins the scheduler or fails to
117// bind.
118func getEnvInt(key string, defaultVal, min int) int {
119 v := os.Getenv(key)
120 if v == "" {
121 return defaultVal
122 }
123 i, err := strconv.Atoi(v)
124 if err != nil {
125 fmt.Fprintf(os.Stderr, "invalid %s: %v, using default %d\n", key, err, defaultVal)
126 return defaultVal
127 }
128 if i < min {
129 fmt.Fprintf(os.Stderr, "invalid %s: %d is below the minimum %d, using default %d\n", key, i, min, defaultVal)
130 return defaultVal
131 }
132 return i
133}
134