362 lines
9.2 KiB
Go
362 lines
9.2 KiB
Go
package backend
|
|
|
|
import (
|
|
"os"
|
|
"sync"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/pelletier/go-toml/v2"
|
|
)
|
|
|
|
type ServerType string
|
|
|
|
const (
|
|
ServerTypeSubsonic ServerType = "Subsonic"
|
|
ServerTypeJellyfin ServerType = "Jellyfin"
|
|
)
|
|
|
|
type ServerConnection struct {
|
|
ServerType ServerType
|
|
Hostname string
|
|
AltHostname string
|
|
Username string
|
|
LegacyAuth bool
|
|
SkipSSLVerify bool
|
|
}
|
|
|
|
type ServerConfig struct {
|
|
ServerConnection
|
|
ID uuid.UUID
|
|
Nickname string
|
|
Default bool
|
|
SelectedLibrary string
|
|
EnablePodcasts bool
|
|
}
|
|
|
|
type AppConfig struct {
|
|
WindowWidth int
|
|
WindowHeight int
|
|
LastCheckedVersion string
|
|
LastLaunchedVersion string
|
|
EnableSystemTray bool
|
|
CloseToSystemTray bool
|
|
StartupPage string
|
|
SettingsTab string
|
|
AllowMultiInstance bool
|
|
MaxImageCacheSizeMB int
|
|
SavePlayQueue bool
|
|
SaveQueueToServer bool
|
|
DefaultPlaylistID string
|
|
AddToPlaylistSkipDuplicates bool
|
|
ShowTrackChangeNotification bool
|
|
EnableLrcLib bool
|
|
CustomLrcLibUrl string
|
|
EnablePasswordStorage bool
|
|
SkipSSLVerify bool // Deprecated: use per-server SkipSSLVerify. Drop in future version.
|
|
EnqueueBatchSize int
|
|
Language string
|
|
DisableDPIDetection bool
|
|
EnableAutoUpdateChecker bool
|
|
RequestTimeoutSeconds int
|
|
EnableOSMediaPlayerAPIs bool
|
|
ShowSidebar bool
|
|
SidebarWidthFraction float64
|
|
SidebarTab string
|
|
|
|
PreventScreensaverOnNowPlayingPage bool
|
|
|
|
FontNormalTTF string
|
|
FontBoldTTF string
|
|
UIScaleSize string
|
|
}
|
|
|
|
type AlbumPageConfig struct {
|
|
TracklistColumns []string
|
|
CompactHeader bool
|
|
}
|
|
|
|
// shared between Albums and Genre pages
|
|
type AlbumsPageConfig struct {
|
|
SortOrder string // only relevant for Albums page
|
|
ShowYears bool
|
|
ShuffleMode string // only relevant for Genre page
|
|
PlayInOrder bool // only relevant for Albums page
|
|
}
|
|
|
|
type ArtistPageConfig struct {
|
|
InitialView string
|
|
DiscographySort string
|
|
TracklistColumns []string
|
|
CompactHeader bool
|
|
}
|
|
|
|
type ArtistsPageConfig struct {
|
|
SortOrder string
|
|
}
|
|
|
|
type FavoritesPageConfig struct {
|
|
InitialView string
|
|
TracklistColumns []string
|
|
ShowAlbumYears bool
|
|
}
|
|
|
|
type GridViewConfig struct {
|
|
CardSize float32
|
|
}
|
|
|
|
type PlaylistPageConfig struct {
|
|
TracklistColumns []string
|
|
CompactHeader bool
|
|
}
|
|
|
|
type PlaylistsPageConfig struct {
|
|
InitialView string
|
|
}
|
|
|
|
type TracksPageConfig struct {
|
|
TracklistColumns []string
|
|
}
|
|
|
|
type NowPlayingPageConfig struct {
|
|
InitialView string
|
|
UseBackgroundImage bool
|
|
}
|
|
|
|
type PlaybackConfig struct {
|
|
Autoplay bool
|
|
Shuffle bool
|
|
RepeatMode string
|
|
SkipOneStarWhenShuffling bool
|
|
SkipKeywordWhenShuffling string
|
|
UseWaveformSeekbar bool
|
|
}
|
|
|
|
type LocalPlaybackConfig struct {
|
|
AudioDeviceName string
|
|
AudioExclusive bool
|
|
InMemoryCacheSizeMB int
|
|
Volume int
|
|
EqualizerEnabled bool
|
|
EqualizerType string // "ISO10Band" or "ISO15Band"
|
|
EqualizerPreamp float64
|
|
GraphicEqualizerBands []float64
|
|
ActiveEQPresetName string // Name of currently selected EQ preset
|
|
AutoEQProfilePath string // Path to applied AutoEQ profile (e.g., "oratory1990/over-ear/Sennheiser HD 650")
|
|
AutoEQProfileName string // Display name of applied profile (e.g., "Sennheiser HD 650")
|
|
PauseFade bool
|
|
}
|
|
|
|
type ScrobbleConfig struct {
|
|
Enabled bool
|
|
ThresholdTimeSeconds int
|
|
ThresholdPercent int
|
|
}
|
|
|
|
type ReplayGainConfig struct {
|
|
Mode string
|
|
PreampGainDB float64
|
|
PreventClipping bool
|
|
}
|
|
|
|
type ThemeConfig struct {
|
|
ThemeFile string
|
|
Appearance string
|
|
UseRoundedImageCorners bool
|
|
}
|
|
|
|
type TranscodingConfig struct {
|
|
ForceRawFile bool
|
|
RequestTranscode bool
|
|
Codec string
|
|
MaxBitRateKBPS int
|
|
}
|
|
|
|
type PeakMeterConfig struct {
|
|
WindowHeight int
|
|
WindowWidth int
|
|
}
|
|
|
|
type Config struct {
|
|
Application AppConfig
|
|
Servers []*ServerConfig
|
|
AlbumPage AlbumPageConfig
|
|
AlbumsPage AlbumsPageConfig
|
|
ArtistPage ArtistPageConfig
|
|
ArtistsPage ArtistsPageConfig
|
|
FavoritesPage FavoritesPageConfig
|
|
GridView GridViewConfig
|
|
PlaylistPage PlaylistPageConfig
|
|
PlaylistsPage PlaylistsPageConfig
|
|
TracksPage TracksPageConfig
|
|
NowPlayingConfig NowPlayingPageConfig
|
|
Playback PlaybackConfig
|
|
LocalPlayback LocalPlaybackConfig
|
|
Scrobbling ScrobbleConfig
|
|
ReplayGain ReplayGainConfig
|
|
Transcoding TranscodingConfig
|
|
Theme ThemeConfig
|
|
PeakMeter PeakMeterConfig
|
|
}
|
|
|
|
var SupportedStartupPages = []string{"Albums", "Favorites", "Playlists", "Artists", "All Tracks"}
|
|
|
|
func DefaultConfig(appVersionTag string) *Config {
|
|
return &Config{
|
|
Application: AppConfig{
|
|
WindowWidth: 1000,
|
|
WindowHeight: 800,
|
|
LastCheckedVersion: appVersionTag,
|
|
LastLaunchedVersion: "",
|
|
EnableSystemTray: true,
|
|
CloseToSystemTray: false,
|
|
StartupPage: "Albums",
|
|
SettingsTab: "General",
|
|
AllowMultiInstance: false,
|
|
MaxImageCacheSizeMB: 50,
|
|
UIScaleSize: "Normal",
|
|
SavePlayQueue: true,
|
|
SaveQueueToServer: false,
|
|
ShowTrackChangeNotification: false,
|
|
EnableLrcLib: true,
|
|
EnablePasswordStorage: true,
|
|
EnqueueBatchSize: 100,
|
|
Language: "auto",
|
|
EnableAutoUpdateChecker: true,
|
|
RequestTimeoutSeconds: 15,
|
|
EnableOSMediaPlayerAPIs: true,
|
|
SidebarWidthFraction: 0.8,
|
|
PreventScreensaverOnNowPlayingPage: false,
|
|
},
|
|
AlbumPage: AlbumPageConfig{
|
|
TracklistColumns: []string{"Artist", "Time", "Plays", "Favorite", "Rating"},
|
|
},
|
|
AlbumsPage: AlbumsPageConfig{
|
|
SortOrder: string("Recently Added"),
|
|
ShowYears: false,
|
|
ShuffleMode: "Tracks",
|
|
},
|
|
ArtistPage: ArtistPageConfig{
|
|
InitialView: "Discography",
|
|
TracklistColumns: []string{"Album", "Time", "Plays", "Favorite", "Rating"},
|
|
},
|
|
ArtistsPage: ArtistsPageConfig{
|
|
SortOrder: string("Name (A-Z)"),
|
|
},
|
|
FavoritesPage: FavoritesPageConfig{
|
|
TracklistColumns: []string{"Album", "Time", "Plays"},
|
|
InitialView: "Albums",
|
|
ShowAlbumYears: false,
|
|
},
|
|
GridView: GridViewConfig{
|
|
CardSize: 200,
|
|
},
|
|
PlaylistPage: PlaylistPageConfig{
|
|
TracklistColumns: []string{"Album", "Time", "Plays"},
|
|
},
|
|
PlaylistsPage: PlaylistsPageConfig{
|
|
InitialView: "List",
|
|
},
|
|
NowPlayingConfig: NowPlayingPageConfig{
|
|
InitialView: "Play Queue",
|
|
UseBackgroundImage: true,
|
|
},
|
|
TracksPage: TracksPageConfig{
|
|
TracklistColumns: []string{"Album", "Time", "Plays"},
|
|
},
|
|
Playback: PlaybackConfig{
|
|
Autoplay: false,
|
|
Shuffle: false,
|
|
RepeatMode: "None",
|
|
UseWaveformSeekbar: false,
|
|
},
|
|
LocalPlayback: LocalPlaybackConfig{
|
|
// "auto" is the name to pass to MPV for autoselecting the output device
|
|
AudioDeviceName: "auto",
|
|
AudioExclusive: false,
|
|
InMemoryCacheSizeMB: 30,
|
|
Volume: 100,
|
|
EqualizerEnabled: false,
|
|
EqualizerType: "ISO15Band",
|
|
EqualizerPreamp: 0,
|
|
GraphicEqualizerBands: make([]float64, 15),
|
|
PauseFade: true,
|
|
},
|
|
Scrobbling: ScrobbleConfig{
|
|
Enabled: true,
|
|
ThresholdTimeSeconds: 240,
|
|
ThresholdPercent: 50,
|
|
},
|
|
ReplayGain: ReplayGainConfig{
|
|
Mode: ReplayGainNone,
|
|
PreampGainDB: 0.0,
|
|
PreventClipping: true,
|
|
},
|
|
Transcoding: TranscodingConfig{
|
|
ForceRawFile: false,
|
|
RequestTranscode: false,
|
|
Codec: "opus",
|
|
MaxBitRateKBPS: 160,
|
|
},
|
|
Theme: ThemeConfig{
|
|
Appearance: "Dark",
|
|
UseRoundedImageCorners: true,
|
|
},
|
|
PeakMeter: PeakMeterConfig{
|
|
WindowWidth: 375,
|
|
WindowHeight: 100,
|
|
},
|
|
}
|
|
}
|
|
|
|
func ReadConfigFile(filepath, appVersionTag string) (*Config, error) {
|
|
f, err := os.Open(filepath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
|
|
c := DefaultConfig(appVersionTag)
|
|
if err := toml.NewDecoder(f).Decode(c); err != nil {
|
|
return nil, err
|
|
}
|
|
c.migrateDeprecatedSettings()
|
|
|
|
// Backfill Subsonic to empty ServerType fields
|
|
// for updating configs created before multiple MediaProviders were added
|
|
for _, s := range c.Servers {
|
|
if s.ServerType == "" {
|
|
s.ServerType = ServerTypeSubsonic
|
|
}
|
|
}
|
|
|
|
return c, nil
|
|
}
|
|
|
|
var writeLock sync.Mutex
|
|
|
|
func (c *Config) WriteConfigFile(filepath string) error {
|
|
if !writeLock.TryLock() {
|
|
return nil // another write in progress
|
|
}
|
|
defer writeLock.Unlock()
|
|
|
|
// clear deprecated global SkipSSLVerify after migrating to per-server settings
|
|
c.Application.SkipSSLVerify = false
|
|
b, err := toml.Marshal(c)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
os.WriteFile(filepath, b, 0o644)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Config) migrateDeprecatedSettings() {
|
|
// Migrate deprecated global SkipSSLVerify to per-server settings
|
|
if c.Application.SkipSSLVerify {
|
|
for _, s := range c.Servers {
|
|
s.SkipSSLVerify = true
|
|
}
|
|
}
|
|
}
|