2016-03-30 06:05:57 +02:00
package conf
import (
2020-01-26 22:42:56 +01:00
"fmt"
2023-02-16 03:13:38 +01:00
"net/url"
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
2024-01-20 20:50:30 +01:00
UnixSocketPerm string
2023-01-18 20:20:06 +01:00
MusicFolder string
DataFolder string
2023-06-02 23:14:11 +02:00
CacheFolder string
2023-01-18 20:20:06 +01:00
DbPath string
LogLevel string
ScanInterval time . Duration
ScanSchedule string
SessionTimeout time . Duration
BaseURL string
2023-02-16 03:13:38 +01:00
BasePath string
BaseHost string
BaseScheme string
2023-03-17 21:07:01 +01:00
TLSCert string
TLSKey string
2023-01-18 20:20:06 +01:00
UILoginBackgroundURL string
2023-02-01 16:21:30 +01:00
UIWelcomeMessage string
MaxSidebarPlaylists int
2023-01-18 20:20:06 +01:00
EnableTranscodingConfig bool
EnableDownloads bool
EnableExternalServices bool
EnableMediaFileCoverArt bool
TranscodingCacheSize string
ImageCacheSize string
2023-02-06 15:50:04 +01:00
EnableArtworkPrecache bool
2023-01-18 20:20:06 +01:00
AutoImportPlaylists bool
PlaylistsPath string
AutoTranscodeDownload bool
DefaultDownsamplingFormat string
SearchFullString bool
RecentlyAddedByModTime bool
2023-12-12 02:37:11 +01:00
PreferSortTags bool
2023-01-18 20:20:06 +01:00
IgnoredArticles string
IndexGroups string
SubsonicArtistParticipations bool
2023-02-07 19:08:25 +01:00
FFmpegPath string
Jukebox mode (#2289)
* Adding cache directory to ignore-list
* Adding jukebox-related config options
* Adding DevEnableJukebox config option pls. dummy server
* Adding types and routers
* Now without panic
* First draft on parsing the action
* Some cleanups
* Adding playback server
* Verify audio device configuration
* Adding debug-build target to have full symbol support
* Adding beep sound library pls some example code. Not working yet
* Play a fixed mp3 on any interface access for testing purposes
* Put action code into separate file, adding stringer, more debug output, prepare structs, validation
* Put action parameter parser code where it belongs
* Have a single Action transporting all information
* User fmt.Errorf for error-generation
* Adding wide playback interface
* Use action map for parsing, stringer instead switch stmt.
* Use but only one switch case and direct dispatch, refactoring
* Add error handling and pushing to client
* send decent errormessage, no internal server error
* Adding playback devices slice and load it from config
* Combine config-verification and structure init
* Return user-specific device
* Separate playback server from device
* Use dataStore to retrieve mediafile by id
* WIP: Playlist and start/stop handling. Doing start/stop the hard way as of now
* WIP: set, start and stop work on one single song. More to come
* Dont need to wait for the end
* Merge jukebox_action.go into jukebox.go
* Remove getParameterAsInt64(). Use existing requiredParamInt() instead
* Dont need to call newFailure() explicitly
* Remove int64, use int instead.
* Add and set action now accept multiple ids
* Kickout copy of childFromMediaFile(). It is not needed here.
* Refactoring devices and playbackServer
* Turn (internal) playback.DeviceStatus into subsonic JukeboxStatus when rendering output. Indexes int64 -> int
* Now we have a position and playing status
* Switching gain to float32, xs:float is defined as 32 bit. Fixing nasty copy/pointer bug
* Now with volume control
* Start working the queue
* Remove user from device interface
* Rename function GetDevice -> GetDeviceForUser to make intention clearer
* Have a nice stringer for the queue
* User Prepared boolean for now to allow pause/unpause
* Skipping works, but without offsets
* Make ChildFromMediaFile public to be used in jukebox get() implementation
* Return position in seconds and implement offset-skip in seconds
* Default offset to 0
* Adding a simple setGain implementation
* Prepare for transcoding AAC
* WIP: transcode to WAV to use beeps wav decoder. Not done yet.
* WIP: out of sheer desparation: convert to MP3 (which works) rather than WAV to troubleshoot issue.
* Use FLAC as intermediate format to play Apple AAC
* A bit of cleanup
* Catching the end-of-stream event for further reactions
* Have a trackSwitching goroutine waiting on channel when track ends
* Move decoder code into own file. Restructure code a bit
* Now with going on to play the next song in the playlist
* Adding shuffle feature
* Implementing remove action
* Cleanup code
* Remove templates for ffmpeg mp3 generation. Not needed anymore.
* Adding some documentation
* Check whether offset into track is in range. Fixing potential remove track bug. Documentation
* Make golangci-lint happy: handling return values
* Adding test suite and example dummy for playback package
* Adding some basic queue tests
* Only use Jukebox.Enabled config option
* Adding stream closing handling
* Pass context.Context to all PlaybackDevice methods
* Remove unneeded function
* Correct spelling
* Reduce visibility of ChildFromMediaFile
* Decomplicate action-parsing
* Adding simple tempfile-based AAC->FLAC transcoding. No parallel reading and writing yet.
* Try to optimize pipe-writing, tempfile-handling and reading. Not done yet.
* Do a synchronous copy of the tempfile. Racecondition detected
* More debugging statements and fixing the play/pause bug. More work needed
* Start the trackSwitcher() with each device once. Return JSON position even if its 0. More debug-output
* Moving all track-handling code into own module
* Fix typo. Do not pass ctx around when not applicable
* WIP: More refactoring, debugging output
* Fix nil pointer
* Repairing MP3 playback by pinning indirect dependencies: hajimehoshi/go-mp3 and hajimehoshi/oto
* Do not forget to cleanup after a skip action
* Make resync with master easy
* Adding missing mocks
* Adding missing error-handling found by linter
* Updating github.com/hajimehoshi/oto
* Removing duplicate function
* Move BEEP-related code into own package
* Juggle beep-related code around as preparation for interface access
* More refactoring for interface separation
* Gather CloseDevice() behind Track interface.
* Adding skeleton, draft audio-interface using mpv.io
* Adding majority of interface commands using messages to mpv socket.
* Adding end-of-stream handling
* MPV: start/stop are working
* postition is given in float in mpv
* Unify Close() and CloseDevice(). Using temp filename for controlling socket
* Wait until control-socket shows up. Cleanup socket in Close()
* Use canceable command. Rename to Executor
* Skipping tracks works now
* Now with actually setting the position
* Fix regain
* Add missing error-handling found by linter
* Adding retry mode on time-pos property getter
* Remove unneeded code on queue
* Putting build-tag beep onto beep files
* Remove deprecated call to rand.Seed()
"As of Go 1.20 there is no reason to call Seed with a random value. Programs that call Seed with a known value to get a specific sequence of results should use New(NewSource(seed)) to obtain a local random generator."
* Using int32 to conform to Subsonic API spec
* Fix merge error
* Minor style changes
* Get username from context
---------
Co-authored-by: Deluan <deluan@navidrome.org>
2023-09-10 17:25:22 +02:00
MPVPath string
2023-01-18 20:20:06 +01:00
CoverArtPriority string
CoverJpegQuality int
2023-03-31 00:28:05 +02:00
ArtistArtPriority string
2023-01-18 20:20:06 +01:00
EnableGravatar bool
EnableFavourites bool
EnableStarRating bool
EnableUserEditing bool
2023-01-30 02:33:10 +01:00
EnableSharing bool
2023-03-11 05:01:03 +01:00
DefaultDownloadableShare bool
2023-01-18 20:20:06 +01:00
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
Jukebox mode (#2289)
* Adding cache directory to ignore-list
* Adding jukebox-related config options
* Adding DevEnableJukebox config option pls. dummy server
* Adding types and routers
* Now without panic
* First draft on parsing the action
* Some cleanups
* Adding playback server
* Verify audio device configuration
* Adding debug-build target to have full symbol support
* Adding beep sound library pls some example code. Not working yet
* Play a fixed mp3 on any interface access for testing purposes
* Put action code into separate file, adding stringer, more debug output, prepare structs, validation
* Put action parameter parser code where it belongs
* Have a single Action transporting all information
* User fmt.Errorf for error-generation
* Adding wide playback interface
* Use action map for parsing, stringer instead switch stmt.
* Use but only one switch case and direct dispatch, refactoring
* Add error handling and pushing to client
* send decent errormessage, no internal server error
* Adding playback devices slice and load it from config
* Combine config-verification and structure init
* Return user-specific device
* Separate playback server from device
* Use dataStore to retrieve mediafile by id
* WIP: Playlist and start/stop handling. Doing start/stop the hard way as of now
* WIP: set, start and stop work on one single song. More to come
* Dont need to wait for the end
* Merge jukebox_action.go into jukebox.go
* Remove getParameterAsInt64(). Use existing requiredParamInt() instead
* Dont need to call newFailure() explicitly
* Remove int64, use int instead.
* Add and set action now accept multiple ids
* Kickout copy of childFromMediaFile(). It is not needed here.
* Refactoring devices and playbackServer
* Turn (internal) playback.DeviceStatus into subsonic JukeboxStatus when rendering output. Indexes int64 -> int
* Now we have a position and playing status
* Switching gain to float32, xs:float is defined as 32 bit. Fixing nasty copy/pointer bug
* Now with volume control
* Start working the queue
* Remove user from device interface
* Rename function GetDevice -> GetDeviceForUser to make intention clearer
* Have a nice stringer for the queue
* User Prepared boolean for now to allow pause/unpause
* Skipping works, but without offsets
* Make ChildFromMediaFile public to be used in jukebox get() implementation
* Return position in seconds and implement offset-skip in seconds
* Default offset to 0
* Adding a simple setGain implementation
* Prepare for transcoding AAC
* WIP: transcode to WAV to use beeps wav decoder. Not done yet.
* WIP: out of sheer desparation: convert to MP3 (which works) rather than WAV to troubleshoot issue.
* Use FLAC as intermediate format to play Apple AAC
* A bit of cleanup
* Catching the end-of-stream event for further reactions
* Have a trackSwitching goroutine waiting on channel when track ends
* Move decoder code into own file. Restructure code a bit
* Now with going on to play the next song in the playlist
* Adding shuffle feature
* Implementing remove action
* Cleanup code
* Remove templates for ffmpeg mp3 generation. Not needed anymore.
* Adding some documentation
* Check whether offset into track is in range. Fixing potential remove track bug. Documentation
* Make golangci-lint happy: handling return values
* Adding test suite and example dummy for playback package
* Adding some basic queue tests
* Only use Jukebox.Enabled config option
* Adding stream closing handling
* Pass context.Context to all PlaybackDevice methods
* Remove unneeded function
* Correct spelling
* Reduce visibility of ChildFromMediaFile
* Decomplicate action-parsing
* Adding simple tempfile-based AAC->FLAC transcoding. No parallel reading and writing yet.
* Try to optimize pipe-writing, tempfile-handling and reading. Not done yet.
* Do a synchronous copy of the tempfile. Racecondition detected
* More debugging statements and fixing the play/pause bug. More work needed
* Start the trackSwitcher() with each device once. Return JSON position even if its 0. More debug-output
* Moving all track-handling code into own module
* Fix typo. Do not pass ctx around when not applicable
* WIP: More refactoring, debugging output
* Fix nil pointer
* Repairing MP3 playback by pinning indirect dependencies: hajimehoshi/go-mp3 and hajimehoshi/oto
* Do not forget to cleanup after a skip action
* Make resync with master easy
* Adding missing mocks
* Adding missing error-handling found by linter
* Updating github.com/hajimehoshi/oto
* Removing duplicate function
* Move BEEP-related code into own package
* Juggle beep-related code around as preparation for interface access
* More refactoring for interface separation
* Gather CloseDevice() behind Track interface.
* Adding skeleton, draft audio-interface using mpv.io
* Adding majority of interface commands using messages to mpv socket.
* Adding end-of-stream handling
* MPV: start/stop are working
* postition is given in float in mpv
* Unify Close() and CloseDevice(). Using temp filename for controlling socket
* Wait until control-socket shows up. Cleanup socket in Close()
* Use canceable command. Rename to Executor
* Skipping tracks works now
* Now with actually setting the position
* Fix regain
* Add missing error-handling found by linter
* Adding retry mode on time-pos property getter
* Remove unneeded code on queue
* Putting build-tag beep onto beep files
* Remove deprecated call to rand.Seed()
"As of Go 1.20 there is no reason to call Seed with a random value. Programs that call Seed with a known value to get a specific sequence of results should use New(NewSource(seed)) to obtain a local random generator."
* Using int32 to conform to Subsonic API spec
* Fix merge error
* Minor style changes
* Get username from context
---------
Co-authored-by: Deluan <deluan@navidrome.org>
2023-09-10 17:25:22 +02:00
Jukebox jukeboxOptions
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
2023-03-15 15:56:13 +01:00
DevEnableProfiler bool
2023-01-14 00:10:32 +01:00
DevAutoCreateAdminPassword string
DevAutoLoginUsername string
DevActivityPanel bool
DevSidebarPlaylists bool
DevEnableBufferedScrobble bool
DevShowArtistPage bool
2023-11-27 19:06:23 +01:00
DevOffsetOptimize int
2023-01-14 00:10:32 +01:00
DevArtworkMaxRequests int
DevArtworkThrottleBacklogLimit int
DevArtworkThrottleBacklogTimeout time . Duration
2023-02-02 22:55:12 +01:00
DevArtistInfoTimeToLive time . Duration
DevAlbumInfoTimeToLive time . Duration
2016-03-30 06:05:57 +02:00
}
2020-09-06 02:28:27 +02:00
type scannerOptions struct {
2023-05-19 21:27:47 +02:00
Extractor string
GenreSeparators string
GroupAlbumReleases bool
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
}
Jukebox mode (#2289)
* Adding cache directory to ignore-list
* Adding jukebox-related config options
* Adding DevEnableJukebox config option pls. dummy server
* Adding types and routers
* Now without panic
* First draft on parsing the action
* Some cleanups
* Adding playback server
* Verify audio device configuration
* Adding debug-build target to have full symbol support
* Adding beep sound library pls some example code. Not working yet
* Play a fixed mp3 on any interface access for testing purposes
* Put action code into separate file, adding stringer, more debug output, prepare structs, validation
* Put action parameter parser code where it belongs
* Have a single Action transporting all information
* User fmt.Errorf for error-generation
* Adding wide playback interface
* Use action map for parsing, stringer instead switch stmt.
* Use but only one switch case and direct dispatch, refactoring
* Add error handling and pushing to client
* send decent errormessage, no internal server error
* Adding playback devices slice and load it from config
* Combine config-verification and structure init
* Return user-specific device
* Separate playback server from device
* Use dataStore to retrieve mediafile by id
* WIP: Playlist and start/stop handling. Doing start/stop the hard way as of now
* WIP: set, start and stop work on one single song. More to come
* Dont need to wait for the end
* Merge jukebox_action.go into jukebox.go
* Remove getParameterAsInt64(). Use existing requiredParamInt() instead
* Dont need to call newFailure() explicitly
* Remove int64, use int instead.
* Add and set action now accept multiple ids
* Kickout copy of childFromMediaFile(). It is not needed here.
* Refactoring devices and playbackServer
* Turn (internal) playback.DeviceStatus into subsonic JukeboxStatus when rendering output. Indexes int64 -> int
* Now we have a position and playing status
* Switching gain to float32, xs:float is defined as 32 bit. Fixing nasty copy/pointer bug
* Now with volume control
* Start working the queue
* Remove user from device interface
* Rename function GetDevice -> GetDeviceForUser to make intention clearer
* Have a nice stringer for the queue
* User Prepared boolean for now to allow pause/unpause
* Skipping works, but without offsets
* Make ChildFromMediaFile public to be used in jukebox get() implementation
* Return position in seconds and implement offset-skip in seconds
* Default offset to 0
* Adding a simple setGain implementation
* Prepare for transcoding AAC
* WIP: transcode to WAV to use beeps wav decoder. Not done yet.
* WIP: out of sheer desparation: convert to MP3 (which works) rather than WAV to troubleshoot issue.
* Use FLAC as intermediate format to play Apple AAC
* A bit of cleanup
* Catching the end-of-stream event for further reactions
* Have a trackSwitching goroutine waiting on channel when track ends
* Move decoder code into own file. Restructure code a bit
* Now with going on to play the next song in the playlist
* Adding shuffle feature
* Implementing remove action
* Cleanup code
* Remove templates for ffmpeg mp3 generation. Not needed anymore.
* Adding some documentation
* Check whether offset into track is in range. Fixing potential remove track bug. Documentation
* Make golangci-lint happy: handling return values
* Adding test suite and example dummy for playback package
* Adding some basic queue tests
* Only use Jukebox.Enabled config option
* Adding stream closing handling
* Pass context.Context to all PlaybackDevice methods
* Remove unneeded function
* Correct spelling
* Reduce visibility of ChildFromMediaFile
* Decomplicate action-parsing
* Adding simple tempfile-based AAC->FLAC transcoding. No parallel reading and writing yet.
* Try to optimize pipe-writing, tempfile-handling and reading. Not done yet.
* Do a synchronous copy of the tempfile. Racecondition detected
* More debugging statements and fixing the play/pause bug. More work needed
* Start the trackSwitcher() with each device once. Return JSON position even if its 0. More debug-output
* Moving all track-handling code into own module
* Fix typo. Do not pass ctx around when not applicable
* WIP: More refactoring, debugging output
* Fix nil pointer
* Repairing MP3 playback by pinning indirect dependencies: hajimehoshi/go-mp3 and hajimehoshi/oto
* Do not forget to cleanup after a skip action
* Make resync with master easy
* Adding missing mocks
* Adding missing error-handling found by linter
* Updating github.com/hajimehoshi/oto
* Removing duplicate function
* Move BEEP-related code into own package
* Juggle beep-related code around as preparation for interface access
* More refactoring for interface separation
* Gather CloseDevice() behind Track interface.
* Adding skeleton, draft audio-interface using mpv.io
* Adding majority of interface commands using messages to mpv socket.
* Adding end-of-stream handling
* MPV: start/stop are working
* postition is given in float in mpv
* Unify Close() and CloseDevice(). Using temp filename for controlling socket
* Wait until control-socket shows up. Cleanup socket in Close()
* Use canceable command. Rename to Executor
* Skipping tracks works now
* Now with actually setting the position
* Fix regain
* Add missing error-handling found by linter
* Adding retry mode on time-pos property getter
* Remove unneeded code on queue
* Putting build-tag beep onto beep files
* Remove deprecated call to rand.Seed()
"As of Go 1.20 there is no reason to call Seed with a random value. Programs that call Seed with a known value to get a specific sequence of results should use New(NewSource(seed)) to obtain a local random generator."
* Using int32 to conform to Subsonic API spec
* Fix merge error
* Minor style changes
* Get username from context
---------
Co-authored-by: Deluan <deluan@navidrome.org>
2023-09-10 17:25:22 +02:00
type AudioDeviceDefinition [ ] string
type jukeboxOptions struct {
Enabled bool
Devices [ ] AudioDeviceDefinition
Default 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 )
2023-06-02 23:14:11 +02:00
err := viper . ReadInConfig ( )
if err != nil {
_ , _ = fmt . Fprintln ( os . Stderr , "FATAL: Error reading config file:" , err )
os . Exit ( 1 )
}
2020-07-02 22:34:21 +02:00
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 )
}
2023-06-02 23:14:11 +02:00
if Server . CacheFolder == "" {
Server . CacheFolder = filepath . Join ( Server . DataFolder , "cache" )
}
err = os . MkdirAll ( Server . CacheFolder , os . ModePerm )
if err != nil {
_ , _ = fmt . Fprintln ( os . Stderr , "FATAL: Error creating cache path:" , "path" , Server . CacheFolder , err )
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 )
}
2023-02-16 03:13:38 +01:00
if Server . BaseURL != "" {
u , err := url . Parse ( Server . BaseURL )
if err != nil {
_ , _ = fmt . Fprintf ( os . Stderr , "FATAL: Invalid BaseURL %s: %s\n" , Server . BaseURL , err . Error ( ) )
os . Exit ( 1 )
}
Server . BasePath = u . Path
u . Path = ""
u . RawQuery = ""
Server . BaseHost = u . Host
Server . BaseScheme = u . Scheme
}
2021-05-30 22:01:36 +02:00
// Print current configuration if log level is Debug
2023-12-25 22:29:59 +01:00
if log . IsGreaterOrEqualTo ( 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" ) )
2023-06-02 23:14:11 +02:00
viper . SetDefault ( "cachefolder" , "" )
2020-07-28 14:49:07 +02:00
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 )
2024-01-20 20:50:30 +01:00
viper . SetDefault ( "unixsocketperm" , "0660" )
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" , "" )
2023-03-17 21:07:01 +01:00
viper . SetDefault ( "tlscert" , "" )
viper . SetDefault ( "tlskey" , "" )
2021-03-24 15:21:31 +01:00
viper . SetDefault ( "uiloginbackgroundurl" , consts . DefaultUILoginBackgroundURL )
2023-02-01 16:21:30 +01:00
viper . SetDefault ( "uiwelcomemessage" , "" )
viper . SetDefault ( "maxsidebarplaylists" , consts . DefaultMaxSidebarPlaylists )
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" )
2023-02-06 15:50:04 +01:00
viper . SetDefault ( "enableartworkprecache" , true )
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 )
2023-07-11 00:07:58 +02: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 )
2023-12-12 02:37:11 +01:00
viper . SetDefault ( "prefersorttags" , 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 )
2023-02-07 19:08:25 +01:00
viper . SetDefault ( "ffmpegpath" , "" )
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 )
2023-03-31 00:28:05 +02:00
viper . SetDefault ( "artistartpriority" , "artist.*, album/artist.*, external" )
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-02-09 23:45:38 +01:00
viper . SetDefault ( "enablereplaygain" , true )
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" )
Jukebox mode (#2289)
* Adding cache directory to ignore-list
* Adding jukebox-related config options
* Adding DevEnableJukebox config option pls. dummy server
* Adding types and routers
* Now without panic
* First draft on parsing the action
* Some cleanups
* Adding playback server
* Verify audio device configuration
* Adding debug-build target to have full symbol support
* Adding beep sound library pls some example code. Not working yet
* Play a fixed mp3 on any interface access for testing purposes
* Put action code into separate file, adding stringer, more debug output, prepare structs, validation
* Put action parameter parser code where it belongs
* Have a single Action transporting all information
* User fmt.Errorf for error-generation
* Adding wide playback interface
* Use action map for parsing, stringer instead switch stmt.
* Use but only one switch case and direct dispatch, refactoring
* Add error handling and pushing to client
* send decent errormessage, no internal server error
* Adding playback devices slice and load it from config
* Combine config-verification and structure init
* Return user-specific device
* Separate playback server from device
* Use dataStore to retrieve mediafile by id
* WIP: Playlist and start/stop handling. Doing start/stop the hard way as of now
* WIP: set, start and stop work on one single song. More to come
* Dont need to wait for the end
* Merge jukebox_action.go into jukebox.go
* Remove getParameterAsInt64(). Use existing requiredParamInt() instead
* Dont need to call newFailure() explicitly
* Remove int64, use int instead.
* Add and set action now accept multiple ids
* Kickout copy of childFromMediaFile(). It is not needed here.
* Refactoring devices and playbackServer
* Turn (internal) playback.DeviceStatus into subsonic JukeboxStatus when rendering output. Indexes int64 -> int
* Now we have a position and playing status
* Switching gain to float32, xs:float is defined as 32 bit. Fixing nasty copy/pointer bug
* Now with volume control
* Start working the queue
* Remove user from device interface
* Rename function GetDevice -> GetDeviceForUser to make intention clearer
* Have a nice stringer for the queue
* User Prepared boolean for now to allow pause/unpause
* Skipping works, but without offsets
* Make ChildFromMediaFile public to be used in jukebox get() implementation
* Return position in seconds and implement offset-skip in seconds
* Default offset to 0
* Adding a simple setGain implementation
* Prepare for transcoding AAC
* WIP: transcode to WAV to use beeps wav decoder. Not done yet.
* WIP: out of sheer desparation: convert to MP3 (which works) rather than WAV to troubleshoot issue.
* Use FLAC as intermediate format to play Apple AAC
* A bit of cleanup
* Catching the end-of-stream event for further reactions
* Have a trackSwitching goroutine waiting on channel when track ends
* Move decoder code into own file. Restructure code a bit
* Now with going on to play the next song in the playlist
* Adding shuffle feature
* Implementing remove action
* Cleanup code
* Remove templates for ffmpeg mp3 generation. Not needed anymore.
* Adding some documentation
* Check whether offset into track is in range. Fixing potential remove track bug. Documentation
* Make golangci-lint happy: handling return values
* Adding test suite and example dummy for playback package
* Adding some basic queue tests
* Only use Jukebox.Enabled config option
* Adding stream closing handling
* Pass context.Context to all PlaybackDevice methods
* Remove unneeded function
* Correct spelling
* Reduce visibility of ChildFromMediaFile
* Decomplicate action-parsing
* Adding simple tempfile-based AAC->FLAC transcoding. No parallel reading and writing yet.
* Try to optimize pipe-writing, tempfile-handling and reading. Not done yet.
* Do a synchronous copy of the tempfile. Racecondition detected
* More debugging statements and fixing the play/pause bug. More work needed
* Start the trackSwitcher() with each device once. Return JSON position even if its 0. More debug-output
* Moving all track-handling code into own module
* Fix typo. Do not pass ctx around when not applicable
* WIP: More refactoring, debugging output
* Fix nil pointer
* Repairing MP3 playback by pinning indirect dependencies: hajimehoshi/go-mp3 and hajimehoshi/oto
* Do not forget to cleanup after a skip action
* Make resync with master easy
* Adding missing mocks
* Adding missing error-handling found by linter
* Updating github.com/hajimehoshi/oto
* Removing duplicate function
* Move BEEP-related code into own package
* Juggle beep-related code around as preparation for interface access
* More refactoring for interface separation
* Gather CloseDevice() behind Track interface.
* Adding skeleton, draft audio-interface using mpv.io
* Adding majority of interface commands using messages to mpv socket.
* Adding end-of-stream handling
* MPV: start/stop are working
* postition is given in float in mpv
* Unify Close() and CloseDevice(). Using temp filename for controlling socket
* Wait until control-socket shows up. Cleanup socket in Close()
* Use canceable command. Rename to Executor
* Skipping tracks works now
* Now with actually setting the position
* Fix regain
* Add missing error-handling found by linter
* Adding retry mode on time-pos property getter
* Remove unneeded code on queue
* Putting build-tag beep onto beep files
* Remove deprecated call to rand.Seed()
"As of Go 1.20 there is no reason to call Seed with a random value. Programs that call Seed with a known value to get a specific sequence of results should use New(NewSource(seed)) to obtain a local random generator."
* Using int32 to conform to Subsonic API spec
* Fix merge error
* Minor style changes
* Get username from context
---------
Co-authored-by: Deluan <deluan@navidrome.org>
2023-09-10 17:25:22 +02:00
viper . SetDefault ( "jukebox.enabled" , false )
viper . SetDefault ( "jukebox.devices" , [ ] AudioDeviceDefinition { } )
viper . SetDefault ( "jukebox.default" , "" )
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" , ";/," )
2023-05-31 21:40:20 +02:00
viper . SetDefault ( "scanner.groupalbumreleases" , false )
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" )
2023-12-11 03:11:40 +01:00
viper . SetDefault ( "lastfm.apikey" , "" )
viper . SetDefault ( "lastfm.secret" , "" )
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 )
2023-03-15 15:56:13 +01:00
viper . SetDefault ( "devenableprofiler" , false )
2020-07-02 22:34:21 +02:00
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 )
2023-01-30 02:33:10 +01:00
viper . SetDefault ( "enablesharing" , false )
2023-03-11 05:01:03 +01:00
viper . SetDefault ( "defaultdownloadableshare" , 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-11-27 19:06:23 +01:00
viper . SetDefault ( "devoffsetoptimize" , 50000 )
2023-02-02 19:59:04 +01:00
viper . SetDefault ( "devartworkmaxrequests" , number . Max ( 2 , runtime . NumCPU ( ) / 3 ) )
2023-01-14 00:10:32 +01:00
viper . SetDefault ( "devartworkthrottlebackloglimit" , consts . RequestThrottleBacklogLimit )
viper . SetDefault ( "devartworkthrottlebacklogtimeout" , consts . RequestThrottleBacklogTimeout )
2023-02-02 22:55:12 +01:00
viper . SetDefault ( "devartistinfotimetolive" , consts . ArtistInfoTimeToLive )
viper . SetDefault ( "devalbuminfotimetolive" , consts . AlbumInfoTimeToLive )
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 )
2023-02-06 19:00:07 +01:00
os . Exit ( 1 )
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" )
}