327 lines
7.8 KiB
Go
327 lines
7.8 KiB
Go
package config
|
|
|
|
import (
|
|
"cmp"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/goccy/go-json"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
)
|
|
|
|
var (
|
|
instance *Config
|
|
once sync.Once
|
|
configPath string
|
|
)
|
|
|
|
type Debrid struct {
|
|
Name string `json:"name"`
|
|
Host string `json:"host"`
|
|
APIKey string `json:"api_key"`
|
|
Folder string `json:"folder"`
|
|
DownloadUncached bool `json:"download_uncached"`
|
|
CheckCached bool `json:"check_cached"`
|
|
RateLimit string `json:"rate_limit"` // 200/minute or 10/second
|
|
|
|
// Webdav
|
|
UseWebdav bool `json:"use_webdav"`
|
|
TorrentRefreshInterval string `json:"torrent_refresh_interval"`
|
|
DownloadLinksRefreshInterval string `json:"downloads_refresh_interval"`
|
|
TorrentRefreshWorkers int `json:"torrent_refresh_workers"`
|
|
WebDavFolderNaming string `json:"webdav_folder_naming"`
|
|
}
|
|
|
|
type Proxy struct {
|
|
Port string `json:"port"`
|
|
Enabled bool `json:"enabled"`
|
|
LogLevel string `json:"log_level"`
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
CachedOnly bool `json:"cached_only"`
|
|
}
|
|
|
|
type QBitTorrent struct {
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
Port string `json:"port"`
|
|
DownloadFolder string `json:"download_folder"`
|
|
Categories []string `json:"categories"`
|
|
RefreshInterval int `json:"refresh_interval"`
|
|
SkipPreCache bool `json:"skip_pre_cache"`
|
|
}
|
|
|
|
type Arr struct {
|
|
Name string `json:"name"`
|
|
Host string `json:"host"`
|
|
Token string `json:"token"`
|
|
Cleanup bool `json:"cleanup"`
|
|
SkipRepair bool `json:"skip_repair"`
|
|
DownloadUncached *bool `json:"download_uncached"`
|
|
}
|
|
|
|
type Repair struct {
|
|
Enabled bool `json:"enabled"`
|
|
Interval string `json:"interval"`
|
|
RunOnStart bool `json:"run_on_start"`
|
|
ZurgURL string `json:"zurg_url"`
|
|
AutoProcess bool `json:"auto_process"`
|
|
}
|
|
|
|
type Auth struct {
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
}
|
|
|
|
type WebDav struct {
|
|
TorrentsRefreshInterval string `json:"torrents_refresh_interval"`
|
|
DownloadLinksRefreshInterval string `json:"download_links_refresh_interval"`
|
|
Workers int `json:"workers"`
|
|
|
|
// Folder
|
|
FolderNaming string `json:"folder_naming"`
|
|
|
|
// Rclone
|
|
RcUrl string `json:"rc_url"`
|
|
RcUser string `json:"rc_user"`
|
|
RcPass string `json:"rc_pass"`
|
|
}
|
|
|
|
type Config struct {
|
|
LogLevel string `json:"log_level"`
|
|
Debrid Debrid `json:"debrid"`
|
|
Debrids []Debrid `json:"debrids"`
|
|
Proxy Proxy `json:"proxy"`
|
|
MaxCacheSize int `json:"max_cache_size"`
|
|
QBitTorrent QBitTorrent `json:"qbittorrent"`
|
|
Arrs []Arr `json:"arrs"`
|
|
Repair Repair `json:"repair"`
|
|
WebDav WebDav `json:"webdav"`
|
|
AllowedExt []string `json:"allowed_file_types"`
|
|
MinFileSize string `json:"min_file_size"` // Minimum file size to download, 10MB, 1GB, etc
|
|
MaxFileSize string `json:"max_file_size"` // Maximum file size to download (0 means no limit)
|
|
Path string `json:"-"` // Path to save the config file
|
|
UseAuth bool `json:"use_auth"`
|
|
Auth *Auth `json:"-"`
|
|
DiscordWebhook string `json:"discord_webhook_url"`
|
|
}
|
|
|
|
func (c *Config) JsonFile() string {
|
|
return filepath.Join(c.Path, "config.json")
|
|
}
|
|
func (c *Config) AuthFile() string {
|
|
return filepath.Join(c.Path, "auth.json")
|
|
}
|
|
|
|
func (c *Config) loadConfig() error {
|
|
// Load the config file
|
|
if configPath == "" {
|
|
return fmt.Errorf("config path not set")
|
|
}
|
|
c.Path = configPath
|
|
file, err := os.ReadFile(c.JsonFile())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := json.Unmarshal(file, &c); err != nil {
|
|
return fmt.Errorf("error unmarshaling config: %w", err)
|
|
}
|
|
|
|
if c.Debrid.Name != "" {
|
|
c.Debrids = append(c.Debrids, c.Debrid)
|
|
}
|
|
|
|
if len(c.AllowedExt) == 0 {
|
|
c.AllowedExt = getDefaultExtensions()
|
|
}
|
|
|
|
// Load the auth file
|
|
c.Auth = c.GetAuth()
|
|
|
|
//Validate the config
|
|
if err := validateConfig(c); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateDebrids(debrids []Debrid) error {
|
|
if len(debrids) == 0 {
|
|
return errors.New("no debrids configured")
|
|
}
|
|
|
|
errChan := make(chan error, len(debrids))
|
|
var wg sync.WaitGroup
|
|
|
|
for _, debrid := range debrids {
|
|
// Basic field validation
|
|
if debrid.Host == "" {
|
|
return errors.New("debrid host is required")
|
|
}
|
|
if debrid.APIKey == "" {
|
|
return errors.New("debrid api key is required")
|
|
}
|
|
if debrid.Folder == "" {
|
|
return errors.New("debrid folder is required")
|
|
}
|
|
|
|
// Check folder existence concurrently
|
|
//wg.Add(1)
|
|
//go func(folder string) {
|
|
// defer wg.Done()
|
|
// if _, err := os.Stat(folder); os.IsNotExist(err) {
|
|
// errChan <- fmt.Errorf("debrid folder does not exist: %s", folder)
|
|
// }
|
|
//}(debrid.Folder)
|
|
}
|
|
|
|
// Wait for all checks to complete
|
|
go func() {
|
|
wg.Wait()
|
|
close(errChan)
|
|
}()
|
|
|
|
// Return first error if any
|
|
if err := <-errChan; err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateQbitTorrent(config *QBitTorrent) error {
|
|
if config.DownloadFolder == "" {
|
|
return errors.New("qbittorent download folder is required")
|
|
}
|
|
if _, err := os.Stat(config.DownloadFolder); os.IsNotExist(err) {
|
|
return fmt.Errorf("qbittorent download folder(%s) does not exist", config.DownloadFolder)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateConfig(config *Config) error {
|
|
// Run validations concurrently
|
|
errChan := make(chan error, 2)
|
|
|
|
go func() {
|
|
errChan <- validateDebrids(config.Debrids)
|
|
}()
|
|
|
|
go func() {
|
|
errChan <- validateQbitTorrent(&config.QBitTorrent)
|
|
}()
|
|
|
|
// Check for errors
|
|
for i := 0; i < 2; i++ {
|
|
if err := <-errChan; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func SetConfigPath(path string) error {
|
|
configPath = path
|
|
return nil
|
|
}
|
|
|
|
func GetConfig() *Config {
|
|
once.Do(func() {
|
|
instance = &Config{} // Initialize instance first
|
|
if err := instance.loadConfig(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "configuration Error: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
})
|
|
return instance
|
|
}
|
|
|
|
func (c *Config) GetMinFileSize() int64 {
|
|
// 0 means no limit
|
|
if c.MinFileSize == "" {
|
|
return 0
|
|
}
|
|
s, err := parseSize(c.MinFileSize)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
return s
|
|
}
|
|
|
|
func (c *Config) GetMaxFileSize() int64 {
|
|
// 0 means no limit
|
|
if c.MaxFileSize == "" {
|
|
return 0
|
|
}
|
|
s, err := parseSize(c.MaxFileSize)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
return s
|
|
}
|
|
|
|
func (c *Config) IsSizeAllowed(size int64) bool {
|
|
if size == 0 {
|
|
return true // Maybe the debrid hasn't reported the size yet
|
|
}
|
|
if c.GetMinFileSize() > 0 && size < c.GetMinFileSize() {
|
|
return false
|
|
}
|
|
if c.GetMaxFileSize() > 0 && size > c.GetMaxFileSize() {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (c *Config) GetAuth() *Auth {
|
|
if !c.UseAuth {
|
|
return nil
|
|
}
|
|
if c.Auth == nil {
|
|
c.Auth = &Auth{}
|
|
if _, err := os.Stat(c.AuthFile()); err == nil {
|
|
file, err := os.ReadFile(c.AuthFile())
|
|
if err == nil {
|
|
_ = json.Unmarshal(file, c.Auth)
|
|
}
|
|
}
|
|
}
|
|
return c.Auth
|
|
}
|
|
|
|
func (c *Config) SaveAuth(auth *Auth) error {
|
|
c.Auth = auth
|
|
data, err := json.Marshal(auth)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return os.WriteFile(c.AuthFile(), data, 0644)
|
|
}
|
|
|
|
func (c *Config) NeedsSetup() bool {
|
|
if c.UseAuth {
|
|
return c.GetAuth().Username == ""
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (c *Config) GetDebridWebDav(d Debrid) Debrid {
|
|
if d.TorrentRefreshInterval == "" {
|
|
d.TorrentRefreshInterval = cmp.Or(c.WebDav.TorrentsRefreshInterval, "15s") // 15 seconds
|
|
}
|
|
if d.DownloadLinksRefreshInterval == "" {
|
|
d.DownloadLinksRefreshInterval = cmp.Or(c.WebDav.DownloadLinksRefreshInterval, "40m") // 40 minutes
|
|
}
|
|
if d.TorrentRefreshWorkers == 0 {
|
|
d.TorrentRefreshWorkers = cmp.Or(c.WebDav.Workers, 30) // 30 workers
|
|
}
|
|
if d.WebDavFolderNaming == "" {
|
|
d.WebDavFolderNaming = cmp.Or(c.WebDav.FolderNaming, "original_no_ext")
|
|
}
|
|
return d
|
|
}
|