2016-03-30 06:05:57 +02:00
package conf
import (
2020-01-26 22:42:56 +01:00
"fmt"
2016-03-30 06:05:57 +02:00
"os"
2020-01-26 22:42:56 +01:00
"path/filepath"
2023-01-14 00:10:32 +01:00
"runtime"
2020-09-06 02:28:27 +02:00
"strings"
2020-07-02 22:41:54 +02:00
"time"
2016-03-30 06:05:57 +02:00
2020-10-25 03:55:19 +01:00
"github.com/kr/pretty"
2020-01-24 01:44:08 +01:00
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/log"
2023-01-14 00:10:32 +01:00
"github.com/navidrome/navidrome/utils/number"
2021-05-06 23:56:10 +02:00
"github.com/robfig/cron/v3"
2020-07-02 22:34:21 +02:00
"github.com/spf13/viper"
2016-03-30 06:05:57 +02:00
)
2020-07-02 23:21:25 +02:00
type configOptions struct {
2023-01-18 20:20:06 +01:00
ConfigFile string
Address string
Port int
MusicFolder string
DataFolder string
DbPath string
LogLevel string
ScanInterval time . Duration
ScanSchedule string
SessionTimeout time . Duration
BaseURL string
UILoginBackgroundURL string
EnableTranscodingConfig bool
EnableDownloads bool
EnableExternalServices bool
EnableMediaFileCoverArt bool
TranscodingCacheSize string
ImageCacheSize string
AutoImportPlaylists bool
PlaylistsPath string
AutoTranscodeDownload bool
DefaultDownsamplingFormat string
SearchFullString bool
RecentlyAddedByModTime bool
IgnoredArticles string
IndexGroups string
SubsonicArtistParticipations bool
ProbeCommand string
CoverArtPriority string
CoverJpegQuality int
UIWelcomeMessage string
EnableGravatar bool
EnableFavourites bool
EnableStarRating bool
EnableUserEditing bool
DefaultTheme string
DefaultLanguage string
DefaultUIVolume int
EnableReplayGain bool
EnableCoverAnimation bool
GATrackingID string
EnableLogRedacting bool
AuthRequestLimit int
AuthWindowLength time . Duration
PasswordEncryptionKey string
ReverseProxyUserHeader string
ReverseProxyWhitelist string
Prometheus prometheusOptions
Scanner scannerOptions
2021-02-07 22:46:15 +01:00
2021-11-18 03:11:53 +01:00
Agents string
LastFM lastfmOptions
Spotify spotifyOptions
ListenBrainz listenBrainzOptions
2020-09-06 02:28:27 +02:00
2020-01-22 14:32:31 +01:00
// DevFlags. These are used to enable/disable debugging and incomplete features
2023-01-14 00:10:32 +01:00
DevLogSourceLine bool
DevLogLevels map [ string ] string
DevAutoCreateAdminPassword string
DevAutoLoginUsername string
DevActivityPanel bool
DevEnableShare bool
DevSidebarPlaylists bool
DevEnableBufferedScrobble bool
DevShowArtistPage bool
DevArtworkMaxRequests int
DevArtworkThrottleBacklogLimit int
DevArtworkThrottleBacklogTimeout time . Duration
2016-03-30 06:05:57 +02:00
}
2020-09-06 02:28:27 +02:00
type scannerOptions struct {
2021-07-16 01:53:40 +02:00
Extractor string
GenreSeparators string
2020-09-06 02:28:27 +02:00
}
2020-10-18 05:59:09 +02:00
type lastfmOptions struct {
2021-05-27 22:14:24 +02:00
Enabled bool
2020-10-18 05:59:09 +02:00
ApiKey string
Secret string
Language string
}
2020-10-18 19:23:02 +02:00
type spotifyOptions struct {
ID string
Secret string
}
2021-11-18 03:11:53 +01:00
type listenBrainzOptions struct {
Enabled bool
2022-09-28 03:06:28 +02:00
BaseURL string
2021-11-18 03:11:53 +01:00
}
2022-10-03 01:59:53 +02:00
type prometheusOptions struct {
Enabled bool
MetricsPath string
}
2021-02-07 22:46:15 +01:00
var (
Server = & configOptions { }
hooks [ ] func ( )
)
2020-01-08 16:25:23 +01:00
2020-07-02 22:34:21 +02:00
func LoadFromFile ( confFile string ) {
viper . SetConfigFile ( confFile )
Load ( )
2020-01-26 23:09:08 +01:00
}
2020-07-02 22:34:21 +02:00
func Load ( ) {
err := viper . Unmarshal ( & Server )
2020-01-26 23:09:08 +01:00
if err != nil {
2022-12-21 20:37:08 +01:00
_ , _ = fmt . Fprintln ( os . Stderr , "FATAL: Error parsing config:" , err )
2020-07-02 22:34:21 +02:00
os . Exit ( 1 )
2020-01-26 23:09:08 +01:00
}
2020-07-11 03:43:09 +02:00
err = os . MkdirAll ( Server . DataFolder , os . ModePerm )
if err != nil {
2022-12-21 20:37:08 +01:00
_ , _ = fmt . Fprintln ( os . Stderr , "FATAL: Error creating data path:" , "path" , Server . DataFolder , err )
2020-07-11 03:43:09 +02:00
os . Exit ( 1 )
}
2020-07-02 22:34:21 +02:00
Server . ConfigFile = viper . GetViper ( ) . ConfigFileUsed ( )
2020-01-26 22:42:56 +01:00
if Server . DbPath == "" {
2020-02-28 20:35:32 +01:00
Server . DbPath = filepath . Join ( Server . DataFolder , consts . DefaultDbPath )
2020-01-26 22:42:56 +01:00
}
2020-07-02 22:34:21 +02:00
2020-01-31 14:35:33 +01:00
log . SetLevelString ( Server . LogLevel )
2021-07-15 00:44:14 +02:00
log . SetLogLevels ( Server . DevLogLevels )
2020-01-31 14:35:33 +01:00
log . SetLogSourceLine ( Server . DevLogSourceLine )
2021-05-02 22:39:25 +02:00
log . SetRedacting ( Server . EnableLogRedacting )
2021-05-06 23:56:10 +02:00
if err := validateScanSchedule ( ) ; err != nil {
os . Exit ( 1 )
}
2021-05-30 22:01:36 +02:00
// Print current configuration if log level is Debug
2021-05-12 20:43:09 +02:00
if log . CurrentLevel ( ) >= log . LevelDebug {
2021-05-30 22:01:36 +02:00
prettyConf := pretty . Sprintf ( "Loaded configuration from '%s': %# v" , Server . ConfigFile , Server )
if Server . EnableLogRedacting {
prettyConf = log . Redact ( prettyConf )
}
2022-12-21 20:37:08 +01:00
_ , _ = fmt . Fprintln ( os . Stderr , prettyConf )
2021-05-12 20:43:09 +02:00
}
2021-02-07 22:46:15 +01:00
2021-10-31 00:00:32 +02:00
if ! Server . EnableExternalServices {
disableExternalServices ( )
}
2021-02-07 22:46:15 +01:00
// Call init hooks
for _ , hook := range hooks {
hook ( )
}
}
2021-10-31 00:00:32 +02:00
func disableExternalServices ( ) {
log . Info ( "All external integrations are DISABLED!" )
Server . LastFM . Enabled = false
Server . Spotify . ID = ""
2021-11-18 03:11:53 +01:00
Server . ListenBrainz . Enabled = false
2021-11-25 21:48:32 +01:00
Server . Agents = ""
2021-11-01 14:11:32 +01:00
if Server . UILoginBackgroundURL == consts . DefaultUILoginBackgroundURL {
Server . UILoginBackgroundURL = consts . DefaultUILoginBackgroundURLOffline
}
2021-10-31 00:00:32 +02:00
}
2021-05-06 23:56:10 +02:00
func validateScanSchedule ( ) error {
2021-05-12 20:08:38 +02:00
if Server . ScanInterval != - 1 {
2021-05-06 23:56:10 +02:00
log . Warn ( "ScanInterval is DEPRECATED. Please use ScanSchedule. See docs at https://navidrome.org/docs/usage/configuration-options/" )
if Server . ScanSchedule != "@every 1m" {
log . Error ( "You cannot specify both ScanInterval and ScanSchedule, ignoring ScanInterval" )
} else {
2021-05-12 20:08:38 +02:00
if Server . ScanInterval == 0 {
Server . ScanSchedule = ""
} else {
Server . ScanSchedule = fmt . Sprintf ( "@every %s" , Server . ScanInterval )
}
2021-05-06 23:56:10 +02:00
log . Warn ( "Setting ScanSchedule" , "schedule" , Server . ScanSchedule )
}
}
2021-08-04 01:37:36 +02:00
if Server . ScanSchedule == "0" || Server . ScanSchedule == "" {
2021-09-26 21:57:27 +02:00
Server . ScanSchedule = ""
2021-05-07 02:49:26 +02:00
return nil
}
if _ , err := time . ParseDuration ( Server . ScanSchedule ) ; err == nil {
Server . ScanSchedule = "@every " + Server . ScanSchedule
}
c := cron . New ( )
_ , err := c . AddFunc ( Server . ScanSchedule , func ( ) { } )
if err != nil {
log . Error ( "Invalid ScanSchedule. Please read format spec at https://pkg.go.dev/github.com/robfig/cron#hdr-CRON_Expression_Format" , "schedule" , Server . ScanSchedule , err )
2021-05-06 23:56:10 +02:00
}
2021-05-07 02:49:26 +02:00
return err
2021-05-06 23:56:10 +02:00
}
2021-02-07 22:46:15 +01:00
// AddHook is used to register initialization code that should run as soon as the config is loaded
func AddHook ( hook func ( ) ) {
hooks = append ( hooks , hook )
2016-03-30 06:05:57 +02:00
}
2020-07-02 23:21:25 +02:00
func init ( ) {
2020-07-28 14:49:07 +02:00
viper . SetDefault ( "musicfolder" , filepath . Join ( "." , "music" ) )
viper . SetDefault ( "datafolder" , "." )
2020-07-02 22:34:21 +02:00
viper . SetDefault ( "loglevel" , "info" )
2020-07-09 02:54:56 +02:00
viper . SetDefault ( "address" , "0.0.0.0" )
2020-07-02 22:34:21 +02:00
viper . SetDefault ( "port" , 4533 )
2020-07-02 22:41:54 +02:00
viper . SetDefault ( "sessiontimeout" , consts . DefaultSessionTimeout )
2021-05-12 20:08:38 +02:00
viper . SetDefault ( "scaninterval" , - 1 )
2021-05-06 23:56:10 +02:00
viper . SetDefault ( "scanschedule" , "@every 1m" )
2020-07-02 22:34:21 +02:00
viper . SetDefault ( "baseurl" , "" )
2021-03-24 15:21:31 +01:00
viper . SetDefault ( "uiloginbackgroundurl" , consts . DefaultUILoginBackgroundURL )
2020-07-02 22:34:21 +02:00
viper . SetDefault ( "enabletranscodingconfig" , false )
viper . SetDefault ( "transcodingcachesize" , "100MB" )
2021-02-01 06:31:02 +01:00
viper . SetDefault ( "imagecachesize" , "100MB" )
2020-08-03 05:17:13 +02:00
viper . SetDefault ( "autoimportplaylists" , true )
2021-09-13 03:06:03 +02:00
viper . SetDefault ( "playlistspath" , consts . DefaultPlaylistsPath )
2020-11-10 22:14:43 +01:00
viper . SetDefault ( "enabledownloads" , true )
2021-10-31 00:00:32 +02:00
viper . SetDefault ( "enableexternalservices" , true )
2022-12-27 20:30:00 +01:00
viper . SetDefault ( "enableMediaFileCoverArt" , true )
2022-12-18 18:12:37 +01:00
viper . SetDefault ( "autotranscodedownload" , false )
2023-01-18 20:20:06 +01:00
viper . SetDefault ( "defaultdownsamplingformat" , consts . DefaultDownsamplingFormat )
2020-08-30 19:08:10 +02:00
viper . SetDefault ( "searchfullstring" , false )
2021-03-12 23:49:47 +01:00
viper . SetDefault ( "recentlyaddedbymodtime" , false )
2020-07-02 22:34:21 +02:00
viper . SetDefault ( "ignoredarticles" , "The El La Los Las Le Les Os As O A" )
viper . SetDefault ( "indexgroups" , "A B C D E F G H I J K L M N O P Q R S T U V W X-Z(XYZ) [Unknown]([)" )
2023-01-18 20:20:06 +01:00
viper . SetDefault ( "subsonicartistparticipations" , false )
2020-07-02 22:34:21 +02:00
viper . SetDefault ( "probecommand" , "ffmpeg %s -f ffmetadata" )
2023-01-18 02:22:54 +01:00
viper . SetDefault ( "coverartpriority" , "cover.*, folder.*, front.*, embedded, external" )
2020-07-02 22:34:21 +02:00
viper . SetDefault ( "coverjpegquality" , 75 )
2020-07-03 17:51:15 +02:00
viper . SetDefault ( "uiwelcomemessage" , "" )
2020-11-13 06:40:01 +01:00
viper . SetDefault ( "enablegravatar" , false )
2021-03-29 02:35:49 +02:00
viper . SetDefault ( "enablefavourites" , true )
2021-04-07 22:02:52 +02:00
viper . SetDefault ( "enablestarrating" , true )
2021-05-02 23:10:56 +02:00
viper . SetDefault ( "enableuserediting" , true )
2021-04-18 19:51:00 +02:00
viper . SetDefault ( "defaulttheme" , "Dark" )
2022-09-28 01:16:10 +02:00
viper . SetDefault ( "defaultlanguage" , "" )
2022-11-11 22:31:28 +01:00
viper . SetDefault ( "defaultuivolume" , consts . DefaultUIVolume )
2023-01-18 20:20:06 +01:00
viper . SetDefault ( "enablereplaygain" , false )
2021-06-28 23:10:58 +02:00
viper . SetDefault ( "enablecoveranimation" , true )
2020-07-03 18:53:35 +02:00
viper . SetDefault ( "gatrackingid" , "" )
2021-05-02 22:49:20 +02:00
viper . SetDefault ( "enablelogredacting" , true )
2020-07-19 20:45:05 +02:00
viper . SetDefault ( "authrequestlimit" , 5 )
viper . SetDefault ( "authwindowlength" , 20 * time . Second )
2021-06-19 00:38:38 +02:00
viper . SetDefault ( "passwordencryptionkey" , "" )
2020-07-02 22:34:21 +02:00
2021-06-12 05:17:21 +02:00
viper . SetDefault ( "reverseproxyuserheader" , "Remote-User" )
2021-06-13 18:46:36 +02:00
viper . SetDefault ( "reverseproxywhitelist" , "" )
2021-06-12 05:17:21 +02:00
2022-10-03 01:59:53 +02:00
viper . SetDefault ( "prometheus.enabled" , false )
viper . SetDefault ( "prometheus.metricspath" , "/metrics" )
2021-08-29 01:35:54 +02:00
viper . SetDefault ( "scanner.extractor" , consts . DefaultScannerExtractor )
2021-07-16 17:03:28 +02:00
viper . SetDefault ( "scanner.genreseparators" , ";/," )
2021-07-16 01:53:40 +02:00
2021-02-07 22:46:15 +01:00
viper . SetDefault ( "agents" , "lastfm,spotify" )
2021-05-27 22:14:24 +02:00
viper . SetDefault ( "lastfm.enabled" , true )
2020-10-18 05:59:09 +02:00
viper . SetDefault ( "lastfm.language" , "en" )
2021-06-21 23:09:34 +02:00
viper . SetDefault ( "lastfm.apikey" , consts . LastFMAPIKey )
viper . SetDefault ( "lastfm.secret" , consts . LastFMAPISecret )
2020-10-22 14:31:47 +02:00
viper . SetDefault ( "spotify.id" , "" )
viper . SetDefault ( "spotify.secret" , "" )
2021-11-18 03:11:53 +01:00
viper . SetDefault ( "listenbrainz.enabled" , true )
2022-09-28 03:06:28 +02:00
viper . SetDefault ( "listenbrainz.baseurl" , "https://api.listenbrainz.org/1/" )
2020-09-06 02:28:27 +02:00
2020-07-02 22:34:21 +02:00
// DevFlags. These are used to enable/disable debugging and incomplete features
viper . SetDefault ( "devlogsourceline" , false )
viper . SetDefault ( "devautocreateadminpassword" , "" )
2021-06-19 16:56:39 +02:00
viper . SetDefault ( "devautologinusername" , "" )
2021-02-01 06:31:02 +01:00
viper . SetDefault ( "devactivitypanel" , true )
2021-05-30 21:36:10 +02:00
viper . SetDefault ( "devenableshare" , false )
2021-07-19 22:10:37 +02:00
viper . SetDefault ( "devenablebufferedscrobble" , true )
2021-10-02 19:56:42 +02:00
viper . SetDefault ( "devsidebarplaylists" , true )
2021-10-17 17:07:59 +02:00
viper . SetDefault ( "devshowartistpage" , true )
2023-01-14 00:10:32 +01:00
viper . SetDefault ( "devartworkmaxrequests" , number . Max ( 2 , runtime . NumCPU ( ) ) )
viper . SetDefault ( "devartworkthrottlebackloglimit" , consts . RequestThrottleBacklogLimit )
viper . SetDefault ( "devartworkthrottlebacklogtimeout" , consts . RequestThrottleBacklogTimeout )
2016-03-30 06:05:57 +02:00
}
2020-07-02 23:21:25 +02:00
2020-07-03 15:39:28 +02:00
func InitConfig ( cfgFile string ) {
2020-07-21 00:36:12 +02:00
cfgFile = getConfigFile ( cfgFile )
2020-07-02 23:21:25 +02:00
if cfgFile != "" {
// Use config file from the flag.
viper . SetConfigFile ( cfgFile )
} else {
// Search config in local directory with name "navidrome" (without extension).
viper . AddConfigPath ( "." )
viper . SetConfigName ( "navidrome" )
}
2020-07-03 00:17:31 +02:00
_ = viper . BindEnv ( "port" )
2020-07-02 23:21:25 +02:00
viper . SetEnvPrefix ( "ND" )
2020-09-06 02:28:27 +02:00
replacer := strings . NewReplacer ( "." , "_" )
viper . SetEnvKeyReplacer ( replacer )
2020-07-02 23:21:25 +02:00
viper . AutomaticEnv ( )
2020-07-03 16:19:44 +02:00
err := viper . ReadInConfig ( )
2021-07-21 04:32:36 +02:00
if viper . ConfigFileUsed ( ) != "" && err != nil {
2022-12-21 20:37:08 +01:00
_ , _ = fmt . Fprintln ( os . Stderr , "FATAL: Navidrome could not open config file: " , err )
2020-07-03 16:19:44 +02:00
}
2020-07-02 23:21:25 +02:00
}
2020-07-21 00:36:12 +02:00
func getConfigFile ( cfgFile string ) string {
if cfgFile != "" {
return cfgFile
}
return os . Getenv ( "ND_CONFIGFILE" )
}