mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 04:44:37 +03:00
Replace viper with koanf (#4845)
* Migrate to koanf * Use temp logger for crashes before config is initialised * Remove snake case hacks * Add migration for config file keys * Add migration note for new migration * Renamed viper functions * Remove front-end viper workaround * Correctly default scan options
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
@@ -14,7 +15,9 @@ import (
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/knadh/koanf"
|
||||
"github.com/knadh/koanf/parsers/yaml"
|
||||
"github.com/knadh/koanf/providers/file"
|
||||
|
||||
"github.com/stashapp/stash/internal/identify"
|
||||
"github.com/stashapp/stash/pkg/fsutil"
|
||||
@@ -184,9 +187,9 @@ const (
|
||||
autostartVideoOnPlaySelectedDefault = true
|
||||
ContinuePlaylistDefault = "continue_playlist_default"
|
||||
ShowStudioAsText = "show_studio_as_text"
|
||||
CSSEnabled = "cssEnabled"
|
||||
JavascriptEnabled = "javascriptEnabled"
|
||||
CustomLocalesEnabled = "customLocalesEnabled"
|
||||
CSSEnabled = "cssenabled"
|
||||
JavascriptEnabled = "javascriptenabled"
|
||||
CustomLocalesEnabled = "customlocalesenabled"
|
||||
|
||||
ShowScrubber = "show_scrubber"
|
||||
showScrubberDefault = true
|
||||
@@ -242,12 +245,12 @@ const (
|
||||
DLNAPortDefault = 1338
|
||||
|
||||
// Logging options
|
||||
LogFile = "logFile"
|
||||
LogOut = "logOut"
|
||||
LogFile = "logfile"
|
||||
LogOut = "logout"
|
||||
defaultLogOut = true
|
||||
LogLevel = "logLevel"
|
||||
LogLevel = "loglevel"
|
||||
defaultLogLevel = "Info"
|
||||
LogAccess = "logAccess"
|
||||
LogAccess = "logaccess"
|
||||
defaultLogAccess = true
|
||||
|
||||
// Default settings
|
||||
@@ -261,7 +264,7 @@ const (
|
||||
deleteGeneratedDefaultDefault = true
|
||||
|
||||
// Desktop Integration Options
|
||||
NoBrowser = "noBrowser"
|
||||
NoBrowser = "nobrowser"
|
||||
NoBrowserDefault = false
|
||||
NotificationsEnabled = "notifications_enabled"
|
||||
NotificationsEnabledDefault = true
|
||||
@@ -283,6 +286,10 @@ var (
|
||||
defaultMenuItems = []string{"scenes", "images", "movies", "markers", "galleries", "performers", "studios", "tags"}
|
||||
)
|
||||
|
||||
var jsonUnmarshalConf = koanf.UnmarshalConf{
|
||||
Tag: "json",
|
||||
}
|
||||
|
||||
type MissingConfigError struct {
|
||||
missingFields []string
|
||||
}
|
||||
@@ -303,12 +310,13 @@ func (s *StashBoxError) Error() string {
|
||||
|
||||
type Config struct {
|
||||
// main instance - backed by config file
|
||||
main *viper.Viper
|
||||
main *koanf.Koanf
|
||||
|
||||
// override instance - populated from flags/environment
|
||||
// not written to config file
|
||||
overrides *viper.Viper
|
||||
overrides *koanf.Koanf
|
||||
|
||||
filePath string
|
||||
isNewSystem bool
|
||||
// configUpdates chan int
|
||||
certFile string
|
||||
@@ -326,6 +334,15 @@ func GetInstance() *Config {
|
||||
return instance
|
||||
}
|
||||
|
||||
func (i *Config) load(f string) error {
|
||||
if err := i.main.Load(file.Provider(f), yaml.Parser()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i.filePath = f
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Config) IsNewSystem() bool {
|
||||
return i.isNewSystem
|
||||
}
|
||||
@@ -333,7 +350,7 @@ func (i *Config) IsNewSystem() bool {
|
||||
func (i *Config) SetConfigFile(fn string) {
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
i.main.SetConfigFile(fn)
|
||||
i.filePath = fn
|
||||
}
|
||||
|
||||
func (i *Config) InitTLS() {
|
||||
@@ -381,13 +398,41 @@ func (i *Config) Set(key string, value interface{}) {
|
||||
// }
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
i.main.Set(key, value)
|
||||
|
||||
i.set(key, value)
|
||||
}
|
||||
|
||||
func (i *Config) set(key string, value interface{}) {
|
||||
// assumes lock held
|
||||
|
||||
// default behaviour for Set is to merge the value
|
||||
// we want to replace it
|
||||
i.main.Delete(key)
|
||||
|
||||
if value == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// test for nil interface as well
|
||||
refVal := reflect.ValueOf(value)
|
||||
if refVal.Kind() == reflect.Ptr && refVal.IsNil() {
|
||||
return
|
||||
}
|
||||
|
||||
_ = i.main.Set(key, value)
|
||||
}
|
||||
|
||||
func (i *Config) SetDefault(key string, value interface{}) {
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
i.main.SetDefault(key, value)
|
||||
|
||||
i.setDefault(key, value)
|
||||
}
|
||||
|
||||
func (i *Config) setDefault(key string, value interface{}) {
|
||||
if !i.main.Exists(key) {
|
||||
i.set(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Config) SetPassword(value string) {
|
||||
@@ -402,7 +447,24 @@ func (i *Config) SetPassword(value string) {
|
||||
func (i *Config) Write() error {
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
return i.main.WriteConfig()
|
||||
|
||||
data, err := i.marshal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.WriteFile(i.filePath, data, 0644)
|
||||
}
|
||||
|
||||
func (i *Config) Marshal() ([]byte, error) {
|
||||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
|
||||
return i.marshal()
|
||||
}
|
||||
|
||||
func (i *Config) marshal() ([]byte, error) {
|
||||
return i.main.Marshal(yaml.Parser())
|
||||
}
|
||||
|
||||
// FileEnvSet returns true if the configuration file environment parameter
|
||||
@@ -415,7 +477,7 @@ func FileEnvSet() bool {
|
||||
func (i *Config) GetConfigFile() string {
|
||||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
return i.main.ConfigFileUsed()
|
||||
return i.filePath
|
||||
}
|
||||
|
||||
// GetConfigPath returns the path of the directory containing the used
|
||||
@@ -430,12 +492,12 @@ func (i *Config) GetDefaultDatabaseFilePath() string {
|
||||
return filepath.Join(i.GetConfigPath(), "stash-go.sqlite")
|
||||
}
|
||||
|
||||
// viper returns the viper instance that should be used to get the provided
|
||||
// forKey returns the Koanf instance that should be used to get the provided
|
||||
// key. Returns the overrides instance if the key exists there, otherwise it
|
||||
// returns the main instance. Assumes read lock held.
|
||||
func (i *Config) viper(key string) *viper.Viper {
|
||||
func (i *Config) forKey(key string) *koanf.Koanf {
|
||||
v := i.main
|
||||
if i.overrides.IsSet(key) {
|
||||
if i.overrides.Exists(key) {
|
||||
v = i.overrides
|
||||
}
|
||||
|
||||
@@ -444,10 +506,10 @@ func (i *Config) viper(key string) *viper.Viper {
|
||||
|
||||
// viper returns the viper instance that has the key set. Returns nil
|
||||
// if no instance has the key. Assumes read lock held.
|
||||
func (i *Config) viperWith(key string) *viper.Viper {
|
||||
v := i.viper(key)
|
||||
func (i *Config) with(key string) *koanf.Koanf {
|
||||
v := i.forKey(key)
|
||||
|
||||
if v.IsSet(key) {
|
||||
if v.Exists(key) {
|
||||
return v
|
||||
}
|
||||
|
||||
@@ -458,7 +520,7 @@ func (i *Config) HasOverride(key string) bool {
|
||||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
|
||||
return i.overrides.IsSet(key)
|
||||
return i.overrides.Exists(key)
|
||||
}
|
||||
|
||||
// These functions wrap the equivalent viper functions, checking the override
|
||||
@@ -468,28 +530,28 @@ func (i *Config) unmarshalKey(key string, rawVal interface{}) error {
|
||||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
|
||||
return i.viper(key).UnmarshalKey(key, rawVal)
|
||||
return i.forKey(key).Unmarshal(key, rawVal)
|
||||
}
|
||||
|
||||
func (i *Config) getStringSlice(key string) []string {
|
||||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
|
||||
return i.viper(key).GetStringSlice(key)
|
||||
return i.forKey(key).Strings(key)
|
||||
}
|
||||
|
||||
func (i *Config) getString(key string) string {
|
||||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
|
||||
return i.viper(key).GetString(key)
|
||||
return i.forKey(key).String(key)
|
||||
}
|
||||
|
||||
func (i *Config) getBool(key string) bool {
|
||||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
|
||||
return i.viper(key).GetBool(key)
|
||||
return i.forKey(key).Bool(key)
|
||||
}
|
||||
|
||||
func (i *Config) getBoolDefault(key string, def bool) bool {
|
||||
@@ -497,9 +559,9 @@ func (i *Config) getBoolDefault(key string, def bool) bool {
|
||||
defer i.RUnlock()
|
||||
|
||||
ret := def
|
||||
v := i.viper(key)
|
||||
if v.IsSet(key) {
|
||||
ret = v.GetBool(key)
|
||||
v := i.forKey(key)
|
||||
if v.Exists(key) {
|
||||
ret = v.Bool(key)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
@@ -508,21 +570,21 @@ func (i *Config) getInt(key string) int {
|
||||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
|
||||
return i.viper(key).GetInt(key)
|
||||
return i.forKey(key).Int(key)
|
||||
}
|
||||
|
||||
func (i *Config) getFloat64(key string) float64 {
|
||||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
|
||||
return i.viper(key).GetFloat64(key)
|
||||
return i.forKey(key).Float64(key)
|
||||
}
|
||||
|
||||
func (i *Config) getStringMapString(key string) map[string]string {
|
||||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
|
||||
ret := i.viper(key).GetStringMapString(key)
|
||||
ret := i.forKey(key).StringMap(key)
|
||||
|
||||
// GetStringMapString returns an empty map regardless of whether the
|
||||
// key exists or not.
|
||||
@@ -543,13 +605,13 @@ func (i *Config) GetStashPaths() StashConfigs {
|
||||
var ret StashConfigs
|
||||
|
||||
v := i.main
|
||||
if !v.IsSet(Stash) {
|
||||
if !v.Exists(Stash) {
|
||||
v = i.overrides
|
||||
}
|
||||
|
||||
if err := v.UnmarshalKey(Stash, &ret); err != nil || len(ret) == 0 {
|
||||
if err := v.Unmarshal(Stash, &ret); err != nil || len(ret) == 0 {
|
||||
// fallback to legacy format
|
||||
ss := v.GetStringSlice(Stash)
|
||||
ss := v.Strings(Stash)
|
||||
ret = nil
|
||||
for _, path := range ss {
|
||||
toAdd := &StashConfig{
|
||||
@@ -650,7 +712,7 @@ func (i *Config) GetImageExcludes() []string {
|
||||
|
||||
func (i *Config) GetVideoExtensions() []string {
|
||||
ret := i.getStringSlice(VideoExtensions)
|
||||
if ret == nil {
|
||||
if len(ret) == 0 {
|
||||
ret = defaultVideoExtensions
|
||||
}
|
||||
return ret
|
||||
@@ -658,7 +720,7 @@ func (i *Config) GetVideoExtensions() []string {
|
||||
|
||||
func (i *Config) GetImageExtensions() []string {
|
||||
ret := i.getStringSlice(ImageExtensions)
|
||||
if ret == nil {
|
||||
if len(ret) == 0 {
|
||||
ret = defaultImageExtensions
|
||||
}
|
||||
return ret
|
||||
@@ -666,7 +728,7 @@ func (i *Config) GetImageExtensions() []string {
|
||||
|
||||
func (i *Config) GetGalleryExtensions() []string {
|
||||
ret := i.getStringSlice(GalleryExtensions)
|
||||
if ret == nil {
|
||||
if len(ret) == 0 {
|
||||
ret = defaultGalleryExtensions
|
||||
}
|
||||
return ret
|
||||
@@ -772,16 +834,15 @@ func (i *Config) GetAllPluginConfiguration() map[string]map[string]interface{} {
|
||||
|
||||
ret := make(map[string]map[string]interface{})
|
||||
|
||||
sub := i.viper(PluginsSetting).GetStringMap(PluginsSetting)
|
||||
v := i.forKey(PluginsSetting)
|
||||
|
||||
sub := v.Cut(PluginsSetting)
|
||||
if sub == nil {
|
||||
return ret
|
||||
}
|
||||
|
||||
for plugin := range sub {
|
||||
// HACK: viper changes map keys to case insensitive values, so the workaround is to
|
||||
// convert map keys to snake case for storage
|
||||
name := fromSnakeCase(plugin)
|
||||
ret[name] = fromSnakeCaseMap(i.viper(PluginsSetting).GetStringMap(PluginsSettingPrefix + plugin))
|
||||
for plugin := range sub.Raw() {
|
||||
ret[plugin] = sub.Cut(plugin).Raw()
|
||||
}
|
||||
|
||||
return ret
|
||||
@@ -791,26 +852,20 @@ func (i *Config) GetPluginConfiguration(pluginID string) map[string]interface{}
|
||||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
|
||||
key := PluginsSettingPrefix + toSnakeCase(pluginID)
|
||||
key := PluginsSettingPrefix + pluginID
|
||||
|
||||
// HACK: viper changes map keys to case insensitive values, so the workaround is to
|
||||
// convert map keys to snake case for storage
|
||||
v := i.viper(key).GetStringMap(key)
|
||||
|
||||
return fromSnakeCaseMap(v)
|
||||
return i.forKey(key).Cut(key).Raw()
|
||||
}
|
||||
|
||||
// SetPluginConfiguration sets the configuration for a plugin.
|
||||
// It will overwrite any existing configuration.
|
||||
func (i *Config) SetPluginConfiguration(pluginID string, v map[string]interface{}) {
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
|
||||
pluginID = toSnakeCase(pluginID)
|
||||
|
||||
key := PluginsSettingPrefix + pluginID
|
||||
|
||||
// HACK: viper changes map keys to case insensitive values, so the workaround is to
|
||||
// convert map keys to snake case for storage
|
||||
i.viper(key).Set(key, toSnakeCaseMap(v))
|
||||
i.set(key, v)
|
||||
}
|
||||
|
||||
func (i *Config) GetDisabledPlugins() []string {
|
||||
@@ -1050,9 +1105,9 @@ func (i *Config) GetMaxSessionAge() int {
|
||||
defer i.RUnlock()
|
||||
|
||||
ret := DefaultMaxSessionAge
|
||||
v := i.viper(MaxSessionAge)
|
||||
if v.IsSet(MaxSessionAge) {
|
||||
ret = v.GetInt(MaxSessionAge)
|
||||
v := i.forKey(MaxSessionAge)
|
||||
if v.Exists(MaxSessionAge) {
|
||||
ret = v.Int(MaxSessionAge)
|
||||
}
|
||||
|
||||
return ret
|
||||
@@ -1076,9 +1131,9 @@ func (i *Config) GetUILocation() string {
|
||||
func (i *Config) GetMenuItems() []string {
|
||||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
v := i.viper(MenuItems)
|
||||
if v.IsSet(MenuItems) {
|
||||
return v.GetStringSlice(MenuItems)
|
||||
v := i.forKey(MenuItems)
|
||||
if v.Exists(MenuItems) {
|
||||
return v.Strings(MenuItems)
|
||||
}
|
||||
return defaultMenuItems
|
||||
}
|
||||
@@ -1092,9 +1147,9 @@ func (i *Config) GetWallShowTitle() bool {
|
||||
defer i.RUnlock()
|
||||
|
||||
ret := defaultWallShowTitle
|
||||
v := i.viper(WallShowTitle)
|
||||
if v.IsSet(WallShowTitle) {
|
||||
ret = v.GetBool(WallShowTitle)
|
||||
v := i.forKey(WallShowTitle)
|
||||
if v.Exists(WallShowTitle) {
|
||||
ret = v.Bool(WallShowTitle)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
@@ -1108,9 +1163,9 @@ func (i *Config) GetWallPlayback() string {
|
||||
defer i.RUnlock()
|
||||
|
||||
ret := defaultWallPlayback
|
||||
v := i.viper(WallPlayback)
|
||||
if v.IsSet(WallPlayback) {
|
||||
ret = v.GetString(WallPlayback)
|
||||
v := i.forKey(WallPlayback)
|
||||
if v.Exists(WallPlayback) {
|
||||
ret = v.String(WallPlayback)
|
||||
}
|
||||
|
||||
return ret
|
||||
@@ -1144,14 +1199,14 @@ func (i *Config) getSlideshowDelay() int {
|
||||
// assume have lock
|
||||
|
||||
ret := defaultImageLightboxSlideshowDelay
|
||||
v := i.viper(ImageLightboxSlideshowDelay)
|
||||
if v.IsSet(ImageLightboxSlideshowDelay) {
|
||||
ret = v.GetInt(ImageLightboxSlideshowDelay)
|
||||
v := i.forKey(ImageLightboxSlideshowDelay)
|
||||
if v.Exists(ImageLightboxSlideshowDelay) {
|
||||
ret = v.Int(ImageLightboxSlideshowDelay)
|
||||
} else {
|
||||
// fallback to old location
|
||||
v := i.viper(legacyImageLightboxSlideshowDelay)
|
||||
if v.IsSet(legacyImageLightboxSlideshowDelay) {
|
||||
ret = v.GetInt(legacyImageLightboxSlideshowDelay)
|
||||
v := i.forKey(legacyImageLightboxSlideshowDelay)
|
||||
if v.Exists(legacyImageLightboxSlideshowDelay) {
|
||||
ret = v.Int(legacyImageLightboxSlideshowDelay)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1168,24 +1223,24 @@ func (i *Config) GetImageLightboxOptions() ConfigImageLightboxResult {
|
||||
SlideshowDelay: &delay,
|
||||
}
|
||||
|
||||
if v := i.viperWith(ImageLightboxDisplayModeKey); v != nil {
|
||||
mode := ImageLightboxDisplayMode(v.GetString(ImageLightboxDisplayModeKey))
|
||||
if v := i.with(ImageLightboxDisplayModeKey); v != nil {
|
||||
mode := ImageLightboxDisplayMode(v.String(ImageLightboxDisplayModeKey))
|
||||
ret.DisplayMode = &mode
|
||||
}
|
||||
if v := i.viperWith(ImageLightboxScaleUp); v != nil {
|
||||
value := v.GetBool(ImageLightboxScaleUp)
|
||||
if v := i.with(ImageLightboxScaleUp); v != nil {
|
||||
value := v.Bool(ImageLightboxScaleUp)
|
||||
ret.ScaleUp = &value
|
||||
}
|
||||
if v := i.viperWith(ImageLightboxResetZoomOnNav); v != nil {
|
||||
value := v.GetBool(ImageLightboxResetZoomOnNav)
|
||||
if v := i.with(ImageLightboxResetZoomOnNav); v != nil {
|
||||
value := v.Bool(ImageLightboxResetZoomOnNav)
|
||||
ret.ResetZoomOnNav = &value
|
||||
}
|
||||
if v := i.viperWith(ImageLightboxScrollModeKey); v != nil {
|
||||
mode := ImageLightboxScrollMode(v.GetString(ImageLightboxScrollModeKey))
|
||||
if v := i.with(ImageLightboxScrollModeKey); v != nil {
|
||||
mode := ImageLightboxScrollMode(v.String(ImageLightboxScrollModeKey))
|
||||
ret.ScrollMode = &mode
|
||||
}
|
||||
if v := i.viperWith(ImageLightboxScrollAttemptsBeforeChange); v != nil {
|
||||
ret.ScrollAttemptsBeforeChange = v.GetInt(ImageLightboxScrollAttemptsBeforeChange)
|
||||
if v := i.with(ImageLightboxScrollAttemptsBeforeChange); v != nil {
|
||||
ret.ScrollAttemptsBeforeChange = v.Int(ImageLightboxScrollAttemptsBeforeChange)
|
||||
}
|
||||
|
||||
return ret
|
||||
@@ -1204,20 +1259,14 @@ func (i *Config) GetUIConfiguration() map[string]interface{} {
|
||||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
|
||||
// HACK: viper changes map keys to case insensitive values, so the workaround is to
|
||||
// convert map keys to snake case for storage
|
||||
v := i.viper(UI).GetStringMap(UI)
|
||||
|
||||
return fromSnakeCaseMap(v)
|
||||
return i.forKey(UI).Cut(UI).Raw()
|
||||
}
|
||||
|
||||
func (i *Config) SetUIConfiguration(v map[string]interface{}) {
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
|
||||
// HACK: viper changes map keys to case insensitive values, so the workaround is to
|
||||
// convert map keys to snake case for storage
|
||||
i.viper(UI).Set(UI, toSnakeCaseMap(v))
|
||||
i.set(UI, v)
|
||||
}
|
||||
|
||||
func (i *Config) GetCSSPath() string {
|
||||
@@ -1375,11 +1424,11 @@ func (i *Config) GetDeleteGeneratedDefault() bool {
|
||||
func (i *Config) GetDefaultIdentifySettings() *identify.Options {
|
||||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
v := i.viper(DefaultIdentifySettings)
|
||||
v := i.forKey(DefaultIdentifySettings)
|
||||
|
||||
if v.IsSet(DefaultIdentifySettings) {
|
||||
if v.Exists(DefaultIdentifySettings) && v.Get(DefaultIdentifySettings) != nil {
|
||||
var ret identify.Options
|
||||
if err := v.UnmarshalKey(DefaultIdentifySettings, &ret); err != nil {
|
||||
if err := v.UnmarshalWithConf(DefaultIdentifySettings, &ret, jsonUnmarshalConf); err != nil {
|
||||
return nil
|
||||
}
|
||||
return &ret
|
||||
@@ -1394,11 +1443,11 @@ func (i *Config) GetDefaultIdentifySettings() *identify.Options {
|
||||
func (i *Config) GetDefaultScanSettings() *ScanMetadataOptions {
|
||||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
v := i.viper(DefaultScanSettings)
|
||||
v := i.forKey(DefaultScanSettings)
|
||||
|
||||
if v.IsSet(DefaultScanSettings) {
|
||||
if v.Exists(DefaultScanSettings) && v.Get(DefaultScanSettings) != nil {
|
||||
var ret ScanMetadataOptions
|
||||
if err := v.UnmarshalKey(DefaultScanSettings, &ret); err != nil {
|
||||
if err := v.UnmarshalWithConf(DefaultScanSettings, &ret, jsonUnmarshalConf); err != nil {
|
||||
return nil
|
||||
}
|
||||
return &ret
|
||||
@@ -1413,11 +1462,11 @@ func (i *Config) GetDefaultScanSettings() *ScanMetadataOptions {
|
||||
func (i *Config) GetDefaultAutoTagSettings() *AutoTagMetadataOptions {
|
||||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
v := i.viper(DefaultAutoTagSettings)
|
||||
v := i.forKey(DefaultAutoTagSettings)
|
||||
|
||||
if v.IsSet(DefaultAutoTagSettings) {
|
||||
if v.Exists(DefaultAutoTagSettings) {
|
||||
var ret AutoTagMetadataOptions
|
||||
if err := v.UnmarshalKey(DefaultAutoTagSettings, &ret); err != nil {
|
||||
if err := v.Unmarshal(DefaultAutoTagSettings, &ret); err != nil {
|
||||
return nil
|
||||
}
|
||||
return &ret
|
||||
@@ -1432,11 +1481,11 @@ func (i *Config) GetDefaultAutoTagSettings() *AutoTagMetadataOptions {
|
||||
func (i *Config) GetDefaultGenerateSettings() *models.GenerateMetadataOptions {
|
||||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
v := i.viper(DefaultGenerateSettings)
|
||||
v := i.forKey(DefaultGenerateSettings)
|
||||
|
||||
if v.IsSet(DefaultGenerateSettings) {
|
||||
if v.Exists(DefaultGenerateSettings) {
|
||||
var ret models.GenerateMetadataOptions
|
||||
if err := v.UnmarshalKey(DefaultGenerateSettings, &ret); err != nil {
|
||||
if err := v.Unmarshal(DefaultGenerateSettings, &ret); err != nil {
|
||||
return nil
|
||||
}
|
||||
return &ret
|
||||
@@ -1543,9 +1592,9 @@ func (i *Config) GetMaxUploadSize() int64 {
|
||||
defer i.RUnlock()
|
||||
ret := int64(1024)
|
||||
|
||||
v := i.viper(MaxUploadSize)
|
||||
if v.IsSet(MaxUploadSize) {
|
||||
ret = v.GetInt64(MaxUploadSize)
|
||||
v := i.forKey(MaxUploadSize)
|
||||
if v.Exists(MaxUploadSize) {
|
||||
ret = v.Int64(MaxUploadSize)
|
||||
}
|
||||
return ret << 20
|
||||
}
|
||||
@@ -1645,7 +1694,7 @@ func (i *Config) Validate() error {
|
||||
var missingFields []string
|
||||
|
||||
for _, p := range mandatoryPaths {
|
||||
if !i.viper(p).IsSet(p) || i.viper(p).GetString(p) == "" {
|
||||
if !i.forKey(p).Exists(p) || i.forKey(p).String(p) == "" {
|
||||
missingFields = append(missingFields, p)
|
||||
}
|
||||
}
|
||||
@@ -1656,7 +1705,7 @@ func (i *Config) Validate() error {
|
||||
}
|
||||
}
|
||||
|
||||
if i.GetBlobsStorage() == BlobStorageTypeFilesystem && i.viper(BlobsPath).GetString(BlobsPath) == "" {
|
||||
if i.GetBlobsStorage() == BlobStorageTypeFilesystem && i.forKey(BlobsPath).String(BlobsPath) == "" {
|
||||
return MissingConfigError{
|
||||
missingFields: []string{BlobsPath},
|
||||
}
|
||||
@@ -1676,52 +1725,52 @@ func (i *Config) setDefaultValues() {
|
||||
|
||||
// set the default host and port so that these are written to the config
|
||||
// file
|
||||
i.main.SetDefault(Host, hostDefault)
|
||||
i.main.SetDefault(Port, portDefault)
|
||||
i.setDefault(Host, hostDefault)
|
||||
i.setDefault(Port, portDefault)
|
||||
|
||||
i.main.SetDefault(ParallelTasks, parallelTasksDefault)
|
||||
i.main.SetDefault(SequentialScanning, SequentialScanningDefault)
|
||||
i.main.SetDefault(PreviewSegmentDuration, previewSegmentDurationDefault)
|
||||
i.main.SetDefault(PreviewSegments, previewSegmentsDefault)
|
||||
i.main.SetDefault(PreviewExcludeStart, previewExcludeStartDefault)
|
||||
i.main.SetDefault(PreviewExcludeEnd, previewExcludeEndDefault)
|
||||
i.main.SetDefault(PreviewAudio, previewAudioDefault)
|
||||
i.main.SetDefault(SoundOnPreview, false)
|
||||
i.setDefault(ParallelTasks, parallelTasksDefault)
|
||||
i.setDefault(SequentialScanning, SequentialScanningDefault)
|
||||
i.setDefault(PreviewSegmentDuration, previewSegmentDurationDefault)
|
||||
i.setDefault(PreviewSegments, previewSegmentsDefault)
|
||||
i.setDefault(PreviewExcludeStart, previewExcludeStartDefault)
|
||||
i.setDefault(PreviewExcludeEnd, previewExcludeEndDefault)
|
||||
i.setDefault(PreviewAudio, previewAudioDefault)
|
||||
i.setDefault(SoundOnPreview, false)
|
||||
|
||||
i.main.SetDefault(ThemeColor, DefaultThemeColor)
|
||||
i.setDefault(ThemeColor, DefaultThemeColor)
|
||||
|
||||
i.main.SetDefault(WriteImageThumbnails, writeImageThumbnailsDefault)
|
||||
i.main.SetDefault(CreateImageClipsFromVideos, createImageClipsFromVideosDefault)
|
||||
i.setDefault(WriteImageThumbnails, writeImageThumbnailsDefault)
|
||||
i.setDefault(CreateImageClipsFromVideos, createImageClipsFromVideosDefault)
|
||||
|
||||
i.main.SetDefault(Database, defaultDatabaseFilePath)
|
||||
i.setDefault(Database, defaultDatabaseFilePath)
|
||||
|
||||
i.main.SetDefault(dangerousAllowPublicWithoutAuth, dangerousAllowPublicWithoutAuthDefault)
|
||||
i.main.SetDefault(SecurityTripwireAccessedFromPublicInternet, securityTripwireAccessedFromPublicInternetDefault)
|
||||
i.setDefault(dangerousAllowPublicWithoutAuth, dangerousAllowPublicWithoutAuthDefault)
|
||||
i.setDefault(SecurityTripwireAccessedFromPublicInternet, securityTripwireAccessedFromPublicInternetDefault)
|
||||
|
||||
// Set generated to the metadata path for backwards compat
|
||||
i.main.SetDefault(Generated, i.main.GetString(Metadata))
|
||||
i.setDefault(Generated, i.main.String(Metadata))
|
||||
|
||||
i.main.SetDefault(NoBrowser, NoBrowserDefault)
|
||||
i.main.SetDefault(NotificationsEnabled, NotificationsEnabledDefault)
|
||||
i.main.SetDefault(ShowOneTimeMovedNotification, ShowOneTimeMovedNotificationDefault)
|
||||
i.setDefault(NoBrowser, NoBrowserDefault)
|
||||
i.setDefault(NotificationsEnabled, NotificationsEnabledDefault)
|
||||
i.setDefault(ShowOneTimeMovedNotification, ShowOneTimeMovedNotificationDefault)
|
||||
|
||||
// Set default scrapers and plugins paths
|
||||
i.main.SetDefault(ScrapersPath, defaultScrapersPath)
|
||||
i.main.SetDefault(PluginsPath, defaultPluginsPath)
|
||||
i.setDefault(ScrapersPath, defaultScrapersPath)
|
||||
i.setDefault(PluginsPath, defaultPluginsPath)
|
||||
|
||||
// Set default gallery cover regex
|
||||
i.main.SetDefault(GalleryCoverRegex, galleryCoverRegexDefault)
|
||||
i.setDefault(GalleryCoverRegex, galleryCoverRegexDefault)
|
||||
|
||||
// Set NoProxy default
|
||||
i.main.SetDefault(NoProxy, noProxyDefault)
|
||||
i.setDefault(NoProxy, noProxyDefault)
|
||||
|
||||
// set default package sources
|
||||
i.main.SetDefault(PluginPackageSources, []map[string]string{{
|
||||
i.setDefault(PluginPackageSources, []map[string]string{{
|
||||
"name": sourceDefaultName,
|
||||
"url": pluginPackageSourcesDefault,
|
||||
"localpath": sourceDefaultPath,
|
||||
}})
|
||||
i.main.SetDefault(ScraperPackageSources, []map[string]string{{
|
||||
i.setDefault(ScraperPackageSources, []map[string]string{{
|
||||
"name": sourceDefaultName,
|
||||
"url": scraperPackageSourcesDefault,
|
||||
"localpath": sourceDefaultPath,
|
||||
@@ -1737,13 +1786,13 @@ func (i *Config) setExistingSystemDefaults() {
|
||||
if !i.isNewSystem {
|
||||
// Existing systems as of the introduction of auto-browser open should retain existing
|
||||
// behavior and not start the browser automatically.
|
||||
if !i.main.InConfig(NoBrowser) {
|
||||
i.main.Set(NoBrowser, true)
|
||||
if !i.main.Exists(NoBrowser) {
|
||||
i.set(NoBrowser, true)
|
||||
}
|
||||
|
||||
// Existing systems as of the introduction of the taskbar should inform users.
|
||||
if !i.main.InConfig(ShowOneTimeMovedNotification) {
|
||||
i.main.Set(ShowOneTimeMovedNotification, true)
|
||||
if !i.main.Exists(ShowOneTimeMovedNotification) {
|
||||
i.set(ShowOneTimeMovedNotification, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user