mirror of
https://github.com/navidrome/navidrome.git
synced 2026-10-29 22:47:48 +01:00
Adds server/jellyfin: the Router (mirrors server/subsonic and server/public), its Wire-friendly New(...) constructor, a chi routes() table, and the ok() JSON response helper. Implements the unauthenticated handshake surface Jellyfin clients probe first: GET /System/Info/Public, GET+POST /System/Ping, and GET /QuickConnect/Enabled (quick connect is unsupported, so it always reports disabled). The public info payload's Id must be stable across restarts (Jellyfin clients cache ServerId), so it reuses the get-or-create Property pattern already used for InsightsID in core/metrics/insights.go: fetch consts.JellyfinServerIDKey from the Property repository, generating and persisting a new UUID on first read. A dedicated key (rather than the existing InsightsID) keeps the anonymous telemetry identifier from being exposed on this unauthenticated endpoint.
212 lines
7.3 KiB
Go
212 lines
7.3 KiB
Go
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"
|
|
|
|
UIAuthorizationHeader = "X-ND-Authorization"
|
|
UIClientUniqueIDHeader = "X-ND-Client-Unique-Id"
|
|
JWTSecretKey = "JWTSecret"
|
|
JWTIssuer = "ND"
|
|
DefaultSessionTimeout = 48 * time.Hour
|
|
CookieExpiry = 365 * 24 * 3600 // One year
|
|
|
|
OptimizeDBSchedule = "@every 24h"
|
|
|
|
// 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
|
|
|
|
DevInitialUserName = "admin"
|
|
DevInitialName = "Dev Admin"
|
|
|
|
URLPathUI = "/app"
|
|
URLPathNativeAPI = "/api"
|
|
URLPathSubsonicAPI = "/rest"
|
|
URLPathPublic = "/share"
|
|
URLPathPublicImages = URLPathPublic + "/img"
|
|
URLPathJellyfinAPI = "/jellyfin"
|
|
|
|
// 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
|
|
|
|
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"
|
|
|
|
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')
|
|
)
|
|
|
|
const (
|
|
DefaultUICoverArtSize = 300
|
|
DefaultMaxImageUploadSize = "10MB"
|
|
)
|
|
|
|
// 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"
|
|
PIDAlbumKey = "PIDAlbum"
|
|
PIDTrackKey = "PIDTrack"
|
|
)
|
|
|
|
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
|
|
|
|
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
|
|
}()
|
|
)
|