🏡 index : quine-global/quintodrome.git

package consts

import (
	"os"
	"strings"
	"time"

	"github.com/navidrome/navidrome/model/id"
)

const (
	AppName = "navidrome"

	DefaultDbPath                 = "navidrome.db?cache=shared&_busy_timeout=15000&_journal_mode=WAL&_foreign_keys=on&synchronous=normal"
	InitialSetupFlagKey           = "InitialSetup"
	FullScanAfterMigrationFlagKey = "FullScanAfterMigration"
	// PlaylistsImportPendingFlagKey marks that playlist import was deferred because
	// no admin user existed yet; the next scan with an admin imports them.
	PlaylistsImportPendingFlagKey = "PlaylistsImportPending"
	LastScanErrorKey              = "LastScanError"
	LastScanTypeKey               = "LastScanType"
	LastScanStartTimeKey          = "LastScanStartTime"
	LastDBAnalyzeAtKey            = "LastDBAnalyzeAt"
	LastDBAnalyzeAttemptAtKey     = "LastDBAnalyzeAttemptAt"
	DBAnalyzePendingKey           = "DBAnalyzePending"
	DBAnalyzeFailureCountKey      = "DBAnalyzeFailureCount"
	// ArtConfFingerprintPropertyKey is the model.PropertyRepository key the artwork config check
	// compares against to detect artwork-affecting config changes across restarts.
	ArtConfFingerprintPropertyKey = "ArtConfFingerprint"

	UIAuthorizationHeader  = "X-ND-Authorization"
	UIClientUniqueIDHeader = "X-ND-Client-Unique-Id"
	JWTSecretKey           = "JWTSecret"
	JWTPublicSecretKey     = "JWTPublicSecret"
	JWTIssuer              = "ND"
	DefaultSessionTimeout  = 48 * time.Hour
	DefaultSmartRefresh    = 5 * time.Second
	DefaultShareExpiration = 8760 * time.Hour
	CookieExpiry           = 365 * 24 * 3600 // One year

	DBAnalyzeCheckSchedule = "@every 30m"
	DBAnalyzeMaxAge        = 24 * time.Hour

	ArtworkEnqueueMissingSchedule = "@every 1h"
	ArtworkPruneSchedule          = "@daily"

	// DefaultEncryptionKey This is the encryption key used if none is specified in the `PasswordEncryptionKey` option
	// Never ever change this! Or it will break all Navidrome installations that don't set the config option
	DefaultEncryptionKey  = "just for obfuscation"
	PasswordsEncryptedKey = "PasswordsEncryptedKey"
	PasswordAutogenPrefix = "__NAVIDROME_AUTOGEN__" //nolint:gosec
	APIKeyPrefix          = "nds_"

	DevInitialUserName = "admin"
	DevInitialName     = "Dev Admin"

	URLPathUI           = "/app"
	URLPathNativeAPI    = "/api"
	URLPathSubsonicAPI  = "/rest"
	URLPathPublic       = "/share"
	URLPathPublicImages = URLPathPublic + "/img"
	URLPathJellyfinAPI  = "/jellyfin"
	URLPathAPIv1        = "/api/v1"

	// JellyfinServerIDKey is the Property key for the stable, persisted server Id reported by the
	// Jellyfin API. Jellyfin clients cache this value, so it must survive process restarts.
	JellyfinServerIDKey = "JellyfinServerID"

	// DefaultUILoginBackgroundURL uses Navidrome curated background images collection,
	// available at https://unsplash.com/collections/20072696/navidrome
	DefaultUILoginBackgroundURL = "/backgrounds"

	// DefaultUILoginBackgroundOffline Background image used in case external integrations are disabled
	DefaultUILoginBackgroundOffline    = "iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAIAAAAiOjnJAAAABGdBTUEAALGPC/xhBQAAAiJJREFUeF7t0IEAAAAAw6D5Ux/khVBhwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDDwMDDVlwABBWcSrQAAAABJRU5ErkJggg=="
	DefaultUILoginBackgroundURLOffline = "data:image/png;base64," + DefaultUILoginBackgroundOffline
	DefaultMaxSidebarPlaylists         = 100

	DefaultAuthWindowLength       = 20 * time.Second
	RequestThrottleBacklogLimit   = 100
	RequestThrottleBacklogTimeout = time.Minute

	ServerReadHeaderTimeout = 3 * time.Second

	DefaultInfoLanguage = "en"

	ArtistInfoTimeToLive      = 24 * time.Hour
	AlbumInfoTimeToLive       = 7 * 24 * time.Hour
	UpdateLastAccessFrequency = time.Minute
	UpdatePlayerFrequency     = time.Minute

	I18nFolder     = "i18n"
	ScanIgnoreFile = ".ndignore"
	ArtworkFolder  = "artwork"
	// HashedArtworkFolder is a subtree of ArtworkFolder, kept apart from the name-addressed
	// upload folders beside it so Prune's sweep never reaches them.
	HashedArtworkFolder = "hashed"

	PlaceholderArtistArt            = "artist-placeholder.webp"
	PlaceholderAlbumArt             = "album-placeholder.webp"
	PlaceholderAvatar               = "logo-192x192.png"
	DefaultUIVolume                 = 100
	DefaultUISearchDebounceMs       = 200
	DefaultUIPlaybackReportInterval = time.Minute

	DefaultHttpClientTimeOut = 10 * time.Second

	DefaultListenBrainzBaseURL         = "https://api.listenbrainz.org/1/"
	DefaultListenBrainzArtistAlgorithm = "session_based_days_9000_session_300_contribution_5_threshold_15_limit_50_skip_30"
	DefaultListenBrainzTrackAlgorithm  = "session_based_days_9000_session_300_contribution_5_threshold_15_limit_50_skip_30"

	DefaultScannerExtractor = "taglib"
	DefaultWatcherWait      = 5 * time.Second
	Zwsp                    = string('\u200b')

	DefaultActivityPanelUpdateRate  = 300 * time.Millisecond
	DefaultPluginCompilationTimeout = time.Minute
)

const (
	DefaultUICoverArtSize     = 300
	DefaultMaxImageUploadSize = "10MB"
	DefaultMaxImageSize       = "20MB"
)

// Prometheus options
const (
	PrometheusDefaultPath = "/metrics"
	PrometheusAuthUser    = "navidrome"
)

// Cache options
const (
	TranscodingCacheDir             = "transcoding"
	DefaultTranscodingCacheMaxItems = 0 // Unlimited

	ImageCacheDir             = "images"
	DefaultImageCacheMaxItems = 0 // Unlimited

	DefaultCacheSize            = 100 * 1024 * 1024 // 100MB
	DefaultCacheCleanUpInterval = 10 * time.Minute
)

// Entity types
const (
	EntityArtist   = "artist"
	EntityPlaylist = "playlist"
	EntityRadio    = "radio"
)

const (
	AlbumPlayCountModeAbsolute   = "absolute"
	AlbumPlayCountModeNormalized = "normalized"
)

const (
	//DefaultAlbumPID = "album_legacy"
	DefaultAlbumPID = "musicbrainz_albumid|albumartistid,album,albumversion,releasedate"
	DefaultTrackPID = "musicbrainz_trackid|albumid,discnumber,tracknumber,title"
)

const (
	InsightsIDKey          = "InsightsID"
	InsightsEndpoint       = "https://insights.navidrome.org/collect"
	InsightsUpdateInterval = 24 * time.Hour
	InsightsInitialDelay   = 30 * time.Minute
)

const (
	PurgeMissingNever  = "never"
	PurgeMissingAlways = "always"
	PurgeMissingFull   = "full"
)

var (
	DefaultDownsamplingFormat = "opus"
	DefaultTranscodings       = []struct {
		Name           string
		TargetFormat   string
		DefaultBitRate int
		Command        string
	}{
		{
			Name:           "mp3 audio",
			TargetFormat:   "mp3",
			DefaultBitRate: 192,
			Command:        "ffmpeg -ss %t -i %s -map 0:a:0 -map_metadata 0 -map_metadata 0:s:a:0 -b:a %bk -v 0 -f mp3 -",
		},
		{
			Name:           "opus audio",
			TargetFormat:   "opus",
			DefaultBitRate: 128,
			Command:        "ffmpeg -ss %t -i %s -map 0:a:0 -map_metadata 0 -map_metadata 0:s:a:0 -b:a %bk -v 0 -c:a libopus -f opus -",
		},
		{
			Name:           "aac audio",
			TargetFormat:   "aac",
			DefaultBitRate: 256,
			Command:        "ffmpeg -ss %t -i %s -map 0:a:0 -map_metadata 0 -map_metadata 0:s:a:0 -b:a %bk -v 0 -c:a aac -f adts -",
		},
		{
			Name:           "flac audio",
			TargetFormat:   "flac",
			DefaultBitRate: 0,
			Command:        "ffmpeg -ss %t -i %s -map 0:a:0 -map_metadata 0 -map_metadata 0:s:a:0 -v 0 -c:a flac -f flac -",
		},
	}
)

var HTTPUserAgent = "Navidrome/" + Version + " - https://github.com/navidrome"

var (
	VariousArtists = "Various Artists"
	// TODO This will be dynamic when using disambiguation
	VariousArtistsID = "63sqASlAfjbGMuLP4JhnZU"
	UnknownAlbum     = "[Unknown Album]"
	UnknownArtist    = "[Unknown Artist]"
	// TODO This will be dynamic when using disambiguation
	UnknownArtistID     = id.NewHash(strings.ToLower(UnknownArtist))
	VariousArtistsMbzId = "89ad4ac3-39f7-470e-963a-56509c546377"

	ArtistJoiner = " • "
)

var (
	ServerStart = time.Now()

	InContainer = func() bool {
		// Check if the /.nddockerenv file exists
		if _, err := os.Stat("/.nddockerenv"); err == nil {
			return true
		}
		return false
	}()
)