mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
add settings override with env variables
This commit is contained in:
@@ -136,6 +136,8 @@ services:
|
|||||||
|
|
||||||
5. _Optional_ - To disable Umami analytics tracking in the Docker/web version, set the environment variable `ANALYTICS_DISABLED=true`. When enabled, the analytics script will not be loaded and all tracking will be disabled.
|
5. _Optional_ - To disable Umami analytics tracking in the Docker/web version, set the environment variable `ANALYTICS_DISABLED=true`. When enabled, the analytics script will not be loaded and all tracking will be disabled.
|
||||||
|
|
||||||
|
6. _Optional_ - App settings (theme, language, sidebar options, etc.) can be overridden with environment variables on first run. The variables use the `FS_` prefix (e.g. `FS_GENERAL_THEME=defaultDark`, `FS_GENERAL_LANGUAGE=de`). See [the settings environment variable documentation](docs/ENV_SETTINGS.md) for the full list.
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
### MPV is either not working or is rapidly switching between pause/play states
|
### MPV is either not working or is rapidly switching between pause/play states
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
# Environment variables for settings (web / Docker)
|
||||||
|
|
||||||
|
These variables override app settings **on first run** when no persisted settings exist. They are injected via `settings.js` (from `settings.js.template`) and only apply to the **web** build.
|
||||||
|
|
||||||
|
**Format:** All values are strings; booleans use `true`/`false`, numbers are numeric strings. Leave unset or empty to use the default.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## General
|
||||||
|
|
||||||
|
| Setting | Default | Env variable | Available values / Description |
|
||||||
|
|-------------|---------|--------------|--------------------------------|
|
||||||
|
| `general.accent` | `rgb(53, 116, 252)` | `FS_GENERAL_ACCENT` | CSS `rgb(r, g, b)` string (e.g. `rgb(53, 116, 252)`). Invalid values are ignored. |
|
||||||
|
| `general.albumBackground` | `false` | `FS_GENERAL_ALBUM_BACKGROUND` | `true` / `false` — Show album background image. |
|
||||||
|
| `general.albumBackgroundBlur` | `3` | `FS_GENERAL_ALBUM_BACKGROUND_BLUR` | Blur amount for album background (number). |
|
||||||
|
| `general.artistBackground` | `true` | `FS_GENERAL_ARTIST_BACKGROUND` | `true` / `false` — Show artist background image. |
|
||||||
|
| `general.artistBackgroundBlur` | `3` | `FS_GENERAL_ARTIST_BACKGROUND_BLUR` | Blur amount for artist background (number). |
|
||||||
|
| `general.blurExplicitImages` | `false` | `FS_GENERAL_BLUR_EXPLICIT_IMAGES` | `true` / `false` — Blur explicit images. |
|
||||||
|
| `general.combinedLyricsAndVisualizer` | `false` | `FS_GENERAL_COMBINED_LYRICS_AND_VISUALIZER` | `true` / `false` — Combine lyrics and visualizer panel. |
|
||||||
|
| `general.enableGridMultiSelect` | `false` | `FS_GENERAL_ENABLE_GRID_MULTI_SELECT` | `true` / `false` — Enable multi-select in grid views. |
|
||||||
|
| `general.externalLinks` | `true` | `FS_GENERAL_EXTERNAL_LINKS` | `true` / `false` — Show external links in UI. |
|
||||||
|
| `general.followCurrentSong` | `true` | `FS_GENERAL_FOLLOW_CURRENT_SONG` | `true` / `false` — Follow current song in list. |
|
||||||
|
| `general.followSystemTheme` | `false` | `FS_GENERAL_FOLLOW_SYSTEM_THEME` | `true` / `false` — Use OS light/dark preference. |
|
||||||
|
| `general.homeFeature` | `true` | `FS_GENERAL_HOME_FEATURE` | `true` / `false` — Show home featured carousel. |
|
||||||
|
| `general.homeFeatureStyle` | `single` | `FS_GENERAL_HOME_FEATURE_STYLE` | `multiple` / `single` — Home featured carousel style. |
|
||||||
|
| `general.language` | `en` | `FS_GENERAL_LANGUAGE` | UI language code (e.g. `en`, `de`, `fr`). |
|
||||||
|
| `general.theme` | `defaultDark` | `FS_GENERAL_THEME` | One of: `ayuDark`, `ayuLight`, `catppuccinLatte`, `catppuccinMocha`, `defaultDark`, `defaultLight`, `dracula`, `githubDark`, `githubLight`, `glassyDark`, `gruvboxDark`, `gruvboxLight`, `highContrastDark`, `highContrastLight`, `materialDark`, `materialLight`, `monokai`, `nightOwl`, `nord`, `oneDark`, `rosePine`, `rosePineDawn`, `rosePineMoon`, `shadesOfPurple`, `solarizedDark`, `solarizedLight`, `tokyoNight`, `vscodeDarkPlus`, `vscodeLightPlus`. |
|
||||||
|
| `general.themeDark` | `defaultDark` | `FS_GENERAL_THEME_DARK` | Same as theme (used when system is dark). |
|
||||||
|
| `general.themeLight` | `defaultLight` | `FS_GENERAL_THEME_LIGHT` | Same as theme (used when system is light). |
|
||||||
|
| `general.lastfmApiKey` | *(empty)* | `FS_GENERAL_LASTFM_API_KEY` | Last.fm API key. |
|
||||||
|
| `general.lastFM` | `true` | `FS_GENERAL_LAST_FM` | `true` / `false` — Enable Last.fm. |
|
||||||
|
| `general.musicBrainz` | `true` | `FS_GENERAL_MUSIC_BRAINZ` | `true` / `false` — MusicBrainz links. |
|
||||||
|
| `general.nativeAspectRatio` | `false` | `FS_GENERAL_NATIVE_ASPECT_RATIO` | `true` / `false` — Use native cover art aspect ratio. |
|
||||||
|
| `general.pathReplace` | *(empty)* | `FS_GENERAL_PATH_REPLACE` | Path pattern to replace (e.g. server path in Docker). |
|
||||||
|
| `general.pathReplaceWith` | *(empty)* | `FS_GENERAL_PATH_REPLACE_WITH` | Replacement path. |
|
||||||
|
| `general.playerbarOpenDrawer` | `false` | `FS_GENERAL_PLAYERBAR_OPEN_DRAWER` | `true` / `false` — Open queue/lyrics as drawer from player bar. |
|
||||||
|
| `general.primaryShade` | `6` | `FS_GENERAL_PRIMARY_SHADE` | Mantine primary shade 0–9 (number). |
|
||||||
|
| `general.resume` | `true` | `FS_GENERAL_RESUME` | `true` / `false` — Resume playback on load. |
|
||||||
|
| `general.showLyricsInSidebar` | `true` | `FS_GENERAL_SHOW_LYRICS_IN_SIDEBAR` | `true` / `false` — Show lyrics in sidebar. |
|
||||||
|
| `general.showRatings` | `true` | `FS_GENERAL_SHOW_RATINGS` | `true` / `false` — Show star ratings. |
|
||||||
|
| `general.showVisualizerInSidebar` | `true` | `FS_GENERAL_SHOW_VISUALIZER_IN_SIDEBAR` | `true` / `false` — Show visualizer in sidebar. |
|
||||||
|
| `general.sidebarCollapsedNavigation` | `true` | `FS_GENERAL_SIDEBAR_COLLAPSED_NAVIGATION` | `true` / `false` — Start with collapsed sidebar nav. |
|
||||||
|
| `general.sidebarCollapseShared` | `false` | `FS_GENERAL_SIDEBAR_COLLAPSE_SHARED` | `true` / `false` — Share sidebar collapse state. |
|
||||||
|
| `general.sidebarPlaylistList` | `true` | `FS_GENERAL_SIDEBAR_PLAYLIST_LIST` | `true` / `false` — Show playlist list in sidebar. |
|
||||||
|
| `general.sidebarPlaylistSorting` | `false` | `FS_GENERAL_SIDEBAR_PLAYLIST_SORTING` | `true` / `false` — Enable playlist sorting in sidebar. |
|
||||||
|
| `general.sideQueueType` | `sideQueue` | `FS_GENERAL_SIDE_QUEUE_TYPE` | `sideDrawerQueue` / `sideQueue` — Side play queue style. |
|
||||||
|
| `general.useThemeAccentColor` | `false` | `FS_GENERAL_USE_THEME_ACCENT_COLOR` | `true` / `false` — Use theme’s accent color instead of custom. |
|
||||||
|
| `general.useThemePrimaryShade` | `true` | `FS_GENERAL_USE_THEME_PRIMARY_SHADE` | `true` / `false` — Use theme’s primary shade. |
|
||||||
|
| `general.zoomFactor` | `100` | `FS_GENERAL_ZOOM_FACTOR` | UI zoom percentage (number). |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Playback
|
||||||
|
|
||||||
|
| Setting path | Default | Env variable | Available values / Description |
|
||||||
|
|-------------|---------|--------------|--------------------------------|
|
||||||
|
| `playback.mediaSession` | `false` | `FS_PLAYBACK_MEDIA_SESSION` | `true` / `false` — Media Session API (e.g. browser/media keys). |
|
||||||
|
| `playback.webAudio` | `true` | `FS_PLAYBACK_WEB_AUDIO` | `true` / `false` — Use Web Audio for playback. |
|
||||||
|
| `playback.audioFadeOnStatusChange` | `true` | `FS_PLAYBACK_AUDIO_FADE_ON_STATUS_CHANGE` | `true` / `false` — Fade on play/pause. |
|
||||||
|
| `playback.preservePitch` | `true` | `FS_PLAYBACK_PRESERVE_PITCH` | `true` / `false` — Preserve pitch when changing speed. |
|
||||||
|
| `playback.scrobble.enabled` | `true` | `FS_PLAYBACK_SCROBBLE_ENABLED` | `true` / `false` — Enable scrobbling. |
|
||||||
|
| `playback.scrobble.notify` | `false` | `FS_PLAYBACK_SCROBBLE_NOTIFY` | `true` / `false` — Scrobble notifications. |
|
||||||
|
| `playback.scrobble.scrobbleAtDuration` | `240` | `FS_PLAYBACK_SCROBBLE_AT_DURATION` | Seconds of playback before scrobble. |
|
||||||
|
| `playback.scrobble.scrobbleAtPercentage` | `75` | `FS_PLAYBACK_SCROBBLE_AT_PERCENTAGE` | Percentage of track before scrobble. |
|
||||||
|
| `playback.transcode.enabled` | `false` | `FS_PLAYBACK_TRANSCODE_ENABLED` | `true` / `false` — Enable transcoding. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Discord
|
||||||
|
|
||||||
|
| Setting path | Default | Env variable | Available values / Description |
|
||||||
|
|-------------|---------|--------------|--------------------------------|
|
||||||
|
| `discord.enabled` | `false` | `FS_DISCORD_ENABLED` | `true` / `false` — Discord rich presence. |
|
||||||
|
| `discord.clientId` | *(built-in)* | `FS_DISCORD_CLIENT_ID` | Custom Discord application ID. |
|
||||||
|
| `discord.displayType` | `feishin` | `FS_DISCORD_DISPLAY_TYPE` | `artist` / `feishin` / `song`. |
|
||||||
|
| `discord.linkType` | `none` | `FS_DISCORD_LINK_TYPE` | `last_fm` / `musicbrainz` / `musicbrainz_last_fm` / `none`. |
|
||||||
|
| `discord.showAsListening` | `false` | `FS_DISCORD_SHOW_AS_LISTENING` | `true` / `false`. |
|
||||||
|
| `discord.showPaused` | `true` | `FS_DISCORD_SHOW_PAUSED` | `true` / `false` — Show paused state. |
|
||||||
|
| `discord.showServerImage` | `false` | `FS_DISCORD_SHOW_SERVER_IMAGE` | `true` / `false`. |
|
||||||
|
| `discord.showStateIcon` | `true` | `FS_DISCORD_SHOW_STATE_ICON` | `true` / `false`. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Lyrics
|
||||||
|
|
||||||
|
| Setting path | Default | Env variable | Available values / Description |
|
||||||
|
|-------------|---------|--------------|--------------------------------|
|
||||||
|
| `lyrics.fetch` | `true` | `FS_LYRICS_FETCH` | `true` / `false` — Fetch lyrics. |
|
||||||
|
| `lyrics.follow` | `true` | `FS_LYRICS_FOLLOW` | `true` / `false` — Follow current line. |
|
||||||
|
| `lyrics.delayMs` | `0` | `FS_LYRICS_DELAY_MS` | Sync delay in milliseconds. |
|
||||||
|
| `lyrics.preferLocalLyrics` | `true` | `FS_LYRICS_PREFER_LOCAL` | `true` / `false` — Prefer local lyric files. |
|
||||||
|
| `lyrics.showMatch` | `true` | `FS_LYRICS_SHOW_MATCH` | `true` / `false`. |
|
||||||
|
| `lyrics.showProvider` | `true` | `FS_LYRICS_SHOW_PROVIDER` | `true` / `false`. |
|
||||||
|
| `lyrics.enableAutoTranslation` | `false` | `FS_LYRICS_ENABLE_AUTO_TRANSLATION` | `true` / `false`. |
|
||||||
|
| `lyrics.translationApiKey` | *(empty)* | `FS_LYRICS_TRANSLATION_API_KEY` | API key for lyric translation. |
|
||||||
|
| `lyrics.translationTargetLanguage` | `en` | `FS_LYRICS_TRANSLATION_TARGET_LANGUAGE` | Target language code. |
|
||||||
|
| `lyrics.alignment` | `center` | `FS_LYRICS_ALIGNMENT` | `center` / `left` / `right`. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Auto DJ
|
||||||
|
|
||||||
|
| Setting path | Default | Env variable | Available values / Description |
|
||||||
|
|-------------|---------|--------------|--------------------------------|
|
||||||
|
| `autoDJ.enabled` | `false` | `FS_AUTO_DJ_ENABLED` | `true` / `false`. |
|
||||||
|
| `autoDJ.itemCount` | `5` | `FS_AUTO_DJ_ITEM_COUNT` | Number of items to add. |
|
||||||
|
| `autoDJ.timing` | `1` | `FS_AUTO_DJ_TIMING` | Timing value (number). |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CSS
|
||||||
|
|
||||||
|
| Setting path | Default | Env variable | Available values / Description |
|
||||||
|
|-------------|---------|--------------|--------------------------------|
|
||||||
|
| `css.content` | *(empty)* | `FS_CSS_CONTENT` | Custom CSS string (sanitized like in-app custom CSS). Set `FS_CSS_ENABLED=true` to apply. |
|
||||||
|
| `css.enabled` | `false` | `FS_CSS_ENABLED` | `true` / `false` — Enable custom CSS. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Font
|
||||||
|
|
||||||
|
| Setting path | Default | Env variable | Available values / Description |
|
||||||
|
|-------------|---------|--------------|--------------------------------|
|
||||||
|
| `font.type` | `builtIn` | `FS_FONT_TYPE` | `builtIn` / `system` / `custom`. |
|
||||||
|
| `font.builtIn` | `Inter` | `FS_FONT_BUILT_IN` | Built-in font name. |
|
||||||
|
| `font.system` | *(empty)* | `FS_FONT_SYSTEM` | System font name (when type is `system`). |
|
||||||
+86
-1
@@ -1 +1,86 @@
|
|||||||
"use strict";window.SERVER_URL="${SERVER_URL}";window.SERVER_NAME="${SERVER_NAME}";window.SERVER_TYPE="${SERVER_TYPE}";window.SERVER_LOCK="${SERVER_LOCK}";window.LEGACY_AUTHENTICATION="${LEGACY_AUTHENTICATION}";window.ANALYTICS_DISABLED="${ANALYTICS_DISABLED}";
|
"use strict";
|
||||||
|
|
||||||
|
window.SERVER_URL = "${SERVER_URL}";
|
||||||
|
window.SERVER_NAME = "${SERVER_NAME}";
|
||||||
|
window.SERVER_TYPE = "${SERVER_TYPE}";
|
||||||
|
window.SERVER_LOCK = "${SERVER_LOCK}";
|
||||||
|
window.LEGACY_AUTHENTICATION = "${LEGACY_AUTHENTICATION}";
|
||||||
|
window.ANALYTICS_DISABLED = "${ANALYTICS_DISABLED}";
|
||||||
|
|
||||||
|
window.FS_GENERAL_ACCENT = "${FS_GENERAL_ACCENT}";
|
||||||
|
window.FS_GENERAL_ALBUM_BACKGROUND = "${FS_GENERAL_ALBUM_BACKGROUND}";
|
||||||
|
window.FS_GENERAL_ALBUM_BACKGROUND_BLUR = "${FS_GENERAL_ALBUM_BACKGROUND_BLUR}";
|
||||||
|
window.FS_GENERAL_ARTIST_BACKGROUND = "${FS_GENERAL_ARTIST_BACKGROUND}";
|
||||||
|
window.FS_GENERAL_ARTIST_BACKGROUND_BLUR = "${FS_GENERAL_ARTIST_BACKGROUND_BLUR}";
|
||||||
|
window.FS_GENERAL_BLUR_EXPLICIT_IMAGES = "${FS_GENERAL_BLUR_EXPLICIT_IMAGES}";
|
||||||
|
window.FS_GENERAL_COMBINED_LYRICS_AND_VISUALIZER = "${FS_GENERAL_COMBINED_LYRICS_AND_VISUALIZER}";
|
||||||
|
window.FS_GENERAL_ENABLE_GRID_MULTI_SELECT = "${FS_GENERAL_ENABLE_GRID_MULTI_SELECT}";
|
||||||
|
window.FS_GENERAL_EXTERNAL_LINKS = "${FS_GENERAL_EXTERNAL_LINKS}";
|
||||||
|
window.FS_GENERAL_FOLLOW_CURRENT_SONG = "${FS_GENERAL_FOLLOW_CURRENT_SONG}";
|
||||||
|
window.FS_GENERAL_FOLLOW_SYSTEM_THEME = "${FS_GENERAL_FOLLOW_SYSTEM_THEME}";
|
||||||
|
window.FS_GENERAL_HOME_FEATURE = "${FS_GENERAL_HOME_FEATURE}";
|
||||||
|
window.FS_GENERAL_HOME_FEATURE_STYLE = "${FS_GENERAL_HOME_FEATURE_STYLE}";
|
||||||
|
window.FS_GENERAL_LANGUAGE = "${FS_GENERAL_LANGUAGE}";
|
||||||
|
window.FS_GENERAL_LAST_FM = "${FS_GENERAL_LAST_FM}";
|
||||||
|
window.FS_GENERAL_LASTFM_API_KEY = "${FS_GENERAL_LASTFM_API_KEY}";
|
||||||
|
window.FS_GENERAL_MUSIC_BRAINZ = "${FS_GENERAL_MUSIC_BRAINZ}";
|
||||||
|
window.FS_GENERAL_NATIVE_ASPECT_RATIO = "${FS_GENERAL_NATIVE_ASPECT_RATIO}";
|
||||||
|
window.FS_GENERAL_PATH_REPLACE = "${FS_GENERAL_PATH_REPLACE}";
|
||||||
|
window.FS_GENERAL_PATH_REPLACE_WITH = "${FS_GENERAL_PATH_REPLACE_WITH}";
|
||||||
|
window.FS_GENERAL_PLAYERBAR_OPEN_DRAWER = "${FS_GENERAL_PLAYERBAR_OPEN_DRAWER}";
|
||||||
|
window.FS_GENERAL_PRIMARY_SHADE = "${FS_GENERAL_PRIMARY_SHADE}";
|
||||||
|
window.FS_GENERAL_RESUME = "${FS_GENERAL_RESUME}";
|
||||||
|
window.FS_GENERAL_SHOW_LYRICS_IN_SIDEBAR = "${FS_GENERAL_SHOW_LYRICS_IN_SIDEBAR}";
|
||||||
|
window.FS_GENERAL_SHOW_RATINGS = "${FS_GENERAL_SHOW_RATINGS}";
|
||||||
|
window.FS_GENERAL_SHOW_VISUALIZER_IN_SIDEBAR = "${FS_GENERAL_SHOW_VISUALIZER_IN_SIDEBAR}";
|
||||||
|
window.FS_GENERAL_SIDEBAR_COLLAPSED_NAVIGATION = "${FS_GENERAL_SIDEBAR_COLLAPSED_NAVIGATION}";
|
||||||
|
window.FS_GENERAL_SIDEBAR_COLLAPSE_SHARED = "${FS_GENERAL_SIDEBAR_COLLAPSE_SHARED}";
|
||||||
|
window.FS_GENERAL_SIDEBAR_PLAYLIST_LIST = "${FS_GENERAL_SIDEBAR_PLAYLIST_LIST}";
|
||||||
|
window.FS_GENERAL_SIDEBAR_PLAYLIST_SORTING = "${FS_GENERAL_SIDEBAR_PLAYLIST_SORTING}";
|
||||||
|
window.FS_GENERAL_SIDE_QUEUE_TYPE = "${FS_GENERAL_SIDE_QUEUE_TYPE}";
|
||||||
|
window.FS_GENERAL_THEME = "${FS_GENERAL_THEME}";
|
||||||
|
window.FS_GENERAL_THEME_DARK = "${FS_GENERAL_THEME_DARK}";
|
||||||
|
window.FS_GENERAL_THEME_LIGHT = "${FS_GENERAL_THEME_LIGHT}";
|
||||||
|
window.FS_GENERAL_USE_THEME_ACCENT_COLOR = "${FS_GENERAL_USE_THEME_ACCENT_COLOR}";
|
||||||
|
window.FS_GENERAL_USE_THEME_PRIMARY_SHADE = "${FS_GENERAL_USE_THEME_PRIMARY_SHADE}";
|
||||||
|
window.FS_GENERAL_ZOOM_FACTOR = "${FS_GENERAL_ZOOM_FACTOR}";
|
||||||
|
|
||||||
|
window.FS_PLAYBACK_MEDIA_SESSION = "${FS_PLAYBACK_MEDIA_SESSION}";
|
||||||
|
window.FS_PLAYBACK_WEB_AUDIO = "${FS_PLAYBACK_WEB_AUDIO}";
|
||||||
|
window.FS_PLAYBACK_AUDIO_FADE_ON_STATUS_CHANGE = "${FS_PLAYBACK_AUDIO_FADE_ON_STATUS_CHANGE}";
|
||||||
|
window.FS_PLAYBACK_PRESERVE_PITCH = "${FS_PLAYBACK_PRESERVE_PITCH}";
|
||||||
|
window.FS_PLAYBACK_SCROBBLE_ENABLED = "${FS_PLAYBACK_SCROBBLE_ENABLED}";
|
||||||
|
window.FS_PLAYBACK_SCROBBLE_NOTIFY = "${FS_PLAYBACK_SCROBBLE_NOTIFY}";
|
||||||
|
window.FS_PLAYBACK_SCROBBLE_AT_DURATION = "${FS_PLAYBACK_SCROBBLE_AT_DURATION}";
|
||||||
|
window.FS_PLAYBACK_SCROBBLE_AT_PERCENTAGE = "${FS_PLAYBACK_SCROBBLE_AT_PERCENTAGE}";
|
||||||
|
window.FS_PLAYBACK_TRANSCODE_ENABLED = "${FS_PLAYBACK_TRANSCODE_ENABLED}";
|
||||||
|
|
||||||
|
window.FS_DISCORD_ENABLED = "${FS_DISCORD_ENABLED}";
|
||||||
|
window.FS_DISCORD_CLIENT_ID = "${FS_DISCORD_CLIENT_ID}";
|
||||||
|
window.FS_DISCORD_DISPLAY_TYPE = "${FS_DISCORD_DISPLAY_TYPE}";
|
||||||
|
window.FS_DISCORD_LINK_TYPE = "${FS_DISCORD_LINK_TYPE}";
|
||||||
|
window.FS_DISCORD_SHOW_AS_LISTENING = "${FS_DISCORD_SHOW_AS_LISTENING}";
|
||||||
|
window.FS_DISCORD_SHOW_PAUSED = "${FS_DISCORD_SHOW_PAUSED}";
|
||||||
|
window.FS_DISCORD_SHOW_SERVER_IMAGE = "${FS_DISCORD_SHOW_SERVER_IMAGE}";
|
||||||
|
window.FS_DISCORD_SHOW_STATE_ICON = "${FS_DISCORD_SHOW_STATE_ICON}";
|
||||||
|
|
||||||
|
window.FS_LYRICS_FETCH = "${FS_LYRICS_FETCH}";
|
||||||
|
window.FS_LYRICS_FOLLOW = "${FS_LYRICS_FOLLOW}";
|
||||||
|
window.FS_LYRICS_DELAY_MS = "${FS_LYRICS_DELAY_MS}";
|
||||||
|
window.FS_LYRICS_PREFER_LOCAL = "${FS_LYRICS_PREFER_LOCAL}";
|
||||||
|
window.FS_LYRICS_SHOW_MATCH = "${FS_LYRICS_SHOW_MATCH}";
|
||||||
|
window.FS_LYRICS_SHOW_PROVIDER = "${FS_LYRICS_SHOW_PROVIDER}";
|
||||||
|
window.FS_LYRICS_ENABLE_AUTO_TRANSLATION = "${FS_LYRICS_ENABLE_AUTO_TRANSLATION}";
|
||||||
|
window.FS_LYRICS_TRANSLATION_API_KEY = "${FS_LYRICS_TRANSLATION_API_KEY}";
|
||||||
|
window.FS_LYRICS_TRANSLATION_TARGET_LANGUAGE = "${FS_LYRICS_TRANSLATION_TARGET_LANGUAGE}";
|
||||||
|
window.FS_LYRICS_ALIGNMENT = "${FS_LYRICS_ALIGNMENT}";
|
||||||
|
|
||||||
|
window.FS_AUTO_DJ_ENABLED = "${FS_AUTO_DJ_ENABLED}";
|
||||||
|
window.FS_AUTO_DJ_ITEM_COUNT = "${FS_AUTO_DJ_ITEM_COUNT}";
|
||||||
|
window.FS_AUTO_DJ_TIMING = "${FS_AUTO_DJ_TIMING}";
|
||||||
|
|
||||||
|
window.FS_CSS_CONTENT = "${FS_CSS_CONTENT}";
|
||||||
|
window.FS_CSS_ENABLED = "${FS_CSS_ENABLED}";
|
||||||
|
window.FS_FONT_TYPE = "${FS_FONT_TYPE}";
|
||||||
|
window.FS_FONT_BUILT_IN = "${FS_FONT_BUILT_IN}";
|
||||||
|
window.FS_FONT_SYSTEM = "${FS_FONT_SYSTEM}";
|
||||||
|
|||||||
Vendored
+72
@@ -1,6 +1,78 @@
|
|||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
ANALYTICS_DISABLED?: boolean | string;
|
ANALYTICS_DISABLED?: boolean | string;
|
||||||
|
FS_AUTO_DJ_ENABLED?: string;
|
||||||
|
FS_AUTO_DJ_ITEM_COUNT?: string;
|
||||||
|
FS_AUTO_DJ_TIMING?: string;
|
||||||
|
FS_CSS_CONTENT?: string;
|
||||||
|
FS_CSS_ENABLED?: string;
|
||||||
|
FS_DISCORD_CLIENT_ID?: string;
|
||||||
|
FS_DISCORD_DISPLAY_TYPE?: string;
|
||||||
|
FS_DISCORD_ENABLED?: string;
|
||||||
|
FS_DISCORD_LINK_TYPE?: string;
|
||||||
|
FS_DISCORD_SHOW_AS_LISTENING?: string;
|
||||||
|
FS_DISCORD_SHOW_PAUSED?: string;
|
||||||
|
FS_DISCORD_SHOW_SERVER_IMAGE?: string;
|
||||||
|
FS_DISCORD_SHOW_STATE_ICON?: string;
|
||||||
|
FS_FONT_BUILT_IN?: string;
|
||||||
|
FS_FONT_SYSTEM?: string;
|
||||||
|
FS_FONT_TYPE?: string;
|
||||||
|
FS_GENERAL_ACCENT?: string;
|
||||||
|
FS_GENERAL_ALBUM_BACKGROUND?: string;
|
||||||
|
FS_GENERAL_ALBUM_BACKGROUND_BLUR?: string;
|
||||||
|
FS_GENERAL_ARTIST_BACKGROUND?: string;
|
||||||
|
FS_GENERAL_ARTIST_BACKGROUND_BLUR?: string;
|
||||||
|
FS_GENERAL_BLUR_EXPLICIT_IMAGES?: string;
|
||||||
|
FS_GENERAL_COMBINED_LYRICS_AND_VISUALIZER?: string;
|
||||||
|
FS_GENERAL_ENABLE_GRID_MULTI_SELECT?: string;
|
||||||
|
FS_GENERAL_EXTERNAL_LINKS?: string;
|
||||||
|
FS_GENERAL_FOLLOW_CURRENT_SONG?: string;
|
||||||
|
FS_GENERAL_FOLLOW_SYSTEM_THEME?: string;
|
||||||
|
FS_GENERAL_HOME_FEATURE?: string;
|
||||||
|
FS_GENERAL_HOME_FEATURE_STYLE?: string;
|
||||||
|
FS_GENERAL_LANGUAGE?: string;
|
||||||
|
FS_GENERAL_LAST_FM?: string;
|
||||||
|
FS_GENERAL_LASTFM_API_KEY?: string;
|
||||||
|
FS_GENERAL_MUSIC_BRAINZ?: string;
|
||||||
|
FS_GENERAL_NATIVE_ASPECT_RATIO?: string;
|
||||||
|
FS_GENERAL_PATH_REPLACE?: string;
|
||||||
|
FS_GENERAL_PATH_REPLACE_WITH?: string;
|
||||||
|
FS_GENERAL_PLAYERBAR_OPEN_DRAWER?: string;
|
||||||
|
FS_GENERAL_PRIMARY_SHADE?: string;
|
||||||
|
FS_GENERAL_RESUME?: string;
|
||||||
|
FS_GENERAL_SHOW_LYRICS_IN_SIDEBAR?: string;
|
||||||
|
FS_GENERAL_SHOW_RATINGS?: string;
|
||||||
|
FS_GENERAL_SHOW_VISUALIZER_IN_SIDEBAR?: string;
|
||||||
|
FS_GENERAL_SIDE_QUEUE_TYPE?: string;
|
||||||
|
FS_GENERAL_SIDEBAR_COLLAPSE_SHARED?: string;
|
||||||
|
FS_GENERAL_SIDEBAR_COLLAPSED_NAVIGATION?: string;
|
||||||
|
FS_GENERAL_SIDEBAR_PLAYLIST_LIST?: string;
|
||||||
|
FS_GENERAL_SIDEBAR_PLAYLIST_SORTING?: string;
|
||||||
|
FS_GENERAL_THEME?: string;
|
||||||
|
FS_GENERAL_THEME_DARK?: string;
|
||||||
|
FS_GENERAL_THEME_LIGHT?: string;
|
||||||
|
FS_GENERAL_USE_THEME_ACCENT_COLOR?: string;
|
||||||
|
FS_GENERAL_USE_THEME_PRIMARY_SHADE?: string;
|
||||||
|
FS_GENERAL_ZOOM_FACTOR?: string;
|
||||||
|
FS_LYRICS_ALIGNMENT?: string;
|
||||||
|
FS_LYRICS_DELAY_MS?: string;
|
||||||
|
FS_LYRICS_ENABLE_AUTO_TRANSLATION?: string;
|
||||||
|
FS_LYRICS_FETCH?: string;
|
||||||
|
FS_LYRICS_FOLLOW?: string;
|
||||||
|
FS_LYRICS_PREFER_LOCAL?: string;
|
||||||
|
FS_LYRICS_SHOW_MATCH?: string;
|
||||||
|
FS_LYRICS_SHOW_PROVIDER?: string;
|
||||||
|
FS_LYRICS_TRANSLATION_API_KEY?: string;
|
||||||
|
FS_LYRICS_TRANSLATION_TARGET_LANGUAGE?: string;
|
||||||
|
FS_PLAYBACK_AUDIO_FADE_ON_STATUS_CHANGE?: string;
|
||||||
|
FS_PLAYBACK_MEDIA_SESSION?: string;
|
||||||
|
FS_PLAYBACK_PRESERVE_PITCH?: string;
|
||||||
|
FS_PLAYBACK_SCROBBLE_AT_DURATION?: string;
|
||||||
|
FS_PLAYBACK_SCROBBLE_AT_PERCENTAGE?: string;
|
||||||
|
FS_PLAYBACK_SCROBBLE_ENABLED?: string;
|
||||||
|
FS_PLAYBACK_SCROBBLE_NOTIFY?: string;
|
||||||
|
FS_PLAYBACK_TRANSCODE_ENABLED?: string;
|
||||||
|
FS_PLAYBACK_WEB_AUDIO?: string;
|
||||||
LEGACY_AUTHENTICATION?: boolean | string;
|
LEGACY_AUTHENTICATION?: boolean | string;
|
||||||
SERVER_LOCK?: boolean | string;
|
SERVER_LOCK?: boolean | string;
|
||||||
SERVER_NAME?: string;
|
SERVER_NAME?: string;
|
||||||
|
|||||||
@@ -0,0 +1,376 @@
|
|||||||
|
import type { SettingsState } from './settings.store';
|
||||||
|
|
||||||
|
import { sanitizeCss } from '/@/renderer/utils/sanitize';
|
||||||
|
|
||||||
|
const APP_THEMES = new Set([
|
||||||
|
'ayuDark',
|
||||||
|
'ayuLight',
|
||||||
|
'catppuccinLatte',
|
||||||
|
'catppuccinMocha',
|
||||||
|
'defaultDark',
|
||||||
|
'defaultLight',
|
||||||
|
'dracula',
|
||||||
|
'githubDark',
|
||||||
|
'githubLight',
|
||||||
|
'glassyDark',
|
||||||
|
'gruvboxDark',
|
||||||
|
'gruvboxLight',
|
||||||
|
'highContrastDark',
|
||||||
|
'highContrastLight',
|
||||||
|
'materialDark',
|
||||||
|
'materialLight',
|
||||||
|
'monokai',
|
||||||
|
'nightOwl',
|
||||||
|
'nord',
|
||||||
|
'oneDark',
|
||||||
|
'rosePine',
|
||||||
|
'rosePineDawn',
|
||||||
|
'rosePineMoon',
|
||||||
|
'shadesOfPurple',
|
||||||
|
'solarizedDark',
|
||||||
|
'solarizedLight',
|
||||||
|
'tokyoNight',
|
||||||
|
'vscodeDarkPlus',
|
||||||
|
'vscodeLightPlus',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const DISCORD_DISPLAY_TYPES = new Set(['artist', 'feishin', 'song']);
|
||||||
|
const DISCORD_LINK_TYPES = new Set(['last_fm', 'musicbrainz', 'musicbrainz_last_fm', 'none']);
|
||||||
|
const LYRICS_ALIGNMENTS = new Set(['center', 'left', 'right']);
|
||||||
|
const FONT_TYPES = new Set(['builtIn', 'custom', 'system']);
|
||||||
|
const HOME_FEATURE_STYLES = new Set(['multiple', 'single']);
|
||||||
|
const SIDE_QUEUE_TYPES = new Set(['sideDrawerQueue', 'sideQueue']);
|
||||||
|
|
||||||
|
export type EnvSettingsOverrides = DeepPartial<
|
||||||
|
Pick<SettingsState, 'autoDJ' | 'css' | 'discord' | 'font' | 'general' | 'lyrics' | 'playback'>
|
||||||
|
>;
|
||||||
|
|
||||||
|
type DeepPartial<T> = {
|
||||||
|
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
|
||||||
|
};
|
||||||
|
|
||||||
|
interface EnvSettingSpec {
|
||||||
|
enumSet?: Set<string>;
|
||||||
|
key: string;
|
||||||
|
path: [string, string, string] | [string, string];
|
||||||
|
skipIfEmpty?: boolean;
|
||||||
|
transform?: (raw: string) => unknown;
|
||||||
|
type: 'bool' | 'enum' | 'num' | 'string';
|
||||||
|
}
|
||||||
|
|
||||||
|
function setAtPath(
|
||||||
|
obj: EnvSettingsOverrides,
|
||||||
|
path: [string, string, string] | [string, string],
|
||||||
|
value: unknown,
|
||||||
|
): void {
|
||||||
|
const [a, b, c] = path;
|
||||||
|
const root = (obj as Record<string, unknown>)[a] ?? {};
|
||||||
|
(obj as Record<string, unknown>)[a] = root;
|
||||||
|
const branch = root as Record<string, unknown>;
|
||||||
|
if (c === undefined) {
|
||||||
|
branch[b] = value;
|
||||||
|
} else {
|
||||||
|
const nested = branch[b] ?? {};
|
||||||
|
branch[b] = nested;
|
||||||
|
(nested as Record<string, unknown>)[c] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const RGB_ACCENT_REGEX = /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/;
|
||||||
|
|
||||||
|
const ENV_SETTING_SPECS: EnvSettingSpec[] = [
|
||||||
|
{
|
||||||
|
key: 'FS_GENERAL_ACCENT',
|
||||||
|
path: ['general', 'accent'],
|
||||||
|
transform: (s) => (RGB_ACCENT_REGEX.test(s) ? s : undefined),
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
{ key: 'FS_GENERAL_ALBUM_BACKGROUND', path: ['general', 'albumBackground'], type: 'bool' },
|
||||||
|
{
|
||||||
|
key: 'FS_GENERAL_ALBUM_BACKGROUND_BLUR',
|
||||||
|
path: ['general', 'albumBackgroundBlur'],
|
||||||
|
type: 'num',
|
||||||
|
},
|
||||||
|
{ key: 'FS_GENERAL_ARTIST_BACKGROUND', path: ['general', 'artistBackground'], type: 'bool' },
|
||||||
|
{
|
||||||
|
key: 'FS_GENERAL_ARTIST_BACKGROUND_BLUR',
|
||||||
|
path: ['general', 'artistBackgroundBlur'],
|
||||||
|
type: 'num',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'FS_GENERAL_BLUR_EXPLICIT_IMAGES',
|
||||||
|
path: ['general', 'blurExplicitImages'],
|
||||||
|
type: 'bool',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'FS_GENERAL_COMBINED_LYRICS_AND_VISUALIZER',
|
||||||
|
path: ['general', 'combinedLyricsAndVisualizer'],
|
||||||
|
type: 'bool',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'FS_GENERAL_ENABLE_GRID_MULTI_SELECT',
|
||||||
|
path: ['general', 'enableGridMultiSelect'],
|
||||||
|
type: 'bool',
|
||||||
|
},
|
||||||
|
{ key: 'FS_GENERAL_FOLLOW_CURRENT_SONG', path: ['general', 'followCurrentSong'], type: 'bool' },
|
||||||
|
{ key: 'FS_GENERAL_HOME_FEATURE', path: ['general', 'homeFeature'], type: 'bool' },
|
||||||
|
{
|
||||||
|
enumSet: HOME_FEATURE_STYLES,
|
||||||
|
key: 'FS_GENERAL_HOME_FEATURE_STYLE',
|
||||||
|
path: ['general', 'homeFeatureStyle'],
|
||||||
|
type: 'enum',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'FS_GENERAL_LANGUAGE',
|
||||||
|
path: ['general', 'language'],
|
||||||
|
skipIfEmpty: true,
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'FS_GENERAL_PRIMARY_SHADE',
|
||||||
|
path: ['general', 'primaryShade'],
|
||||||
|
transform: (s) => {
|
||||||
|
const n = parseNum(s);
|
||||||
|
return n !== undefined ? Math.min(9, Math.max(0, Math.round(n))) : undefined;
|
||||||
|
},
|
||||||
|
type: 'num',
|
||||||
|
},
|
||||||
|
{ enumSet: APP_THEMES, key: 'FS_GENERAL_THEME', path: ['general', 'theme'], type: 'enum' },
|
||||||
|
{
|
||||||
|
enumSet: APP_THEMES,
|
||||||
|
key: 'FS_GENERAL_THEME_DARK',
|
||||||
|
path: ['general', 'themeDark'],
|
||||||
|
type: 'enum',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enumSet: APP_THEMES,
|
||||||
|
key: 'FS_GENERAL_THEME_LIGHT',
|
||||||
|
path: ['general', 'themeLight'],
|
||||||
|
type: 'enum',
|
||||||
|
},
|
||||||
|
{ key: 'FS_GENERAL_FOLLOW_SYSTEM_THEME', path: ['general', 'followSystemTheme'], type: 'bool' },
|
||||||
|
{ key: 'FS_GENERAL_PATH_REPLACE', path: ['general', 'pathReplace'], type: 'string' },
|
||||||
|
{ key: 'FS_GENERAL_PATH_REPLACE_WITH', path: ['general', 'pathReplaceWith'], type: 'string' },
|
||||||
|
{ key: 'FS_GENERAL_LASTFM_API_KEY', path: ['general', 'lastfmApiKey'], type: 'string' },
|
||||||
|
{ key: 'FS_GENERAL_LAST_FM', path: ['general', 'lastFM'], type: 'bool' },
|
||||||
|
{ key: 'FS_GENERAL_MUSIC_BRAINZ', path: ['general', 'musicBrainz'], type: 'bool' },
|
||||||
|
{ key: 'FS_GENERAL_NATIVE_ASPECT_RATIO', path: ['general', 'nativeAspectRatio'], type: 'bool' },
|
||||||
|
{
|
||||||
|
key: 'FS_GENERAL_PLAYERBAR_OPEN_DRAWER',
|
||||||
|
path: ['general', 'playerbarOpenDrawer'],
|
||||||
|
type: 'bool',
|
||||||
|
},
|
||||||
|
{ key: 'FS_GENERAL_EXTERNAL_LINKS', path: ['general', 'externalLinks'], type: 'bool' },
|
||||||
|
{
|
||||||
|
key: 'FS_GENERAL_SHOW_LYRICS_IN_SIDEBAR',
|
||||||
|
path: ['general', 'showLyricsInSidebar'],
|
||||||
|
type: 'bool',
|
||||||
|
},
|
||||||
|
{ key: 'FS_GENERAL_SHOW_RATINGS', path: ['general', 'showRatings'], type: 'bool' },
|
||||||
|
{
|
||||||
|
key: 'FS_GENERAL_SHOW_VISUALIZER_IN_SIDEBAR',
|
||||||
|
path: ['general', 'showVisualizerInSidebar'],
|
||||||
|
type: 'bool',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'FS_GENERAL_SIDEBAR_COLLAPSED_NAVIGATION',
|
||||||
|
path: ['general', 'sidebarCollapsedNavigation'],
|
||||||
|
type: 'bool',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'FS_GENERAL_SIDEBAR_COLLAPSE_SHARED',
|
||||||
|
path: ['general', 'sidebarCollapseShared'],
|
||||||
|
type: 'bool',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'FS_GENERAL_SIDEBAR_PLAYLIST_LIST',
|
||||||
|
path: ['general', 'sidebarPlaylistList'],
|
||||||
|
type: 'bool',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'FS_GENERAL_SIDEBAR_PLAYLIST_SORTING',
|
||||||
|
path: ['general', 'sidebarPlaylistSorting'],
|
||||||
|
type: 'bool',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enumSet: SIDE_QUEUE_TYPES,
|
||||||
|
key: 'FS_GENERAL_SIDE_QUEUE_TYPE',
|
||||||
|
path: ['general', 'sideQueueType'],
|
||||||
|
type: 'enum',
|
||||||
|
},
|
||||||
|
{ key: 'FS_GENERAL_RESUME', path: ['general', 'resume'], type: 'bool' },
|
||||||
|
{
|
||||||
|
key: 'FS_GENERAL_USE_THEME_ACCENT_COLOR',
|
||||||
|
path: ['general', 'useThemeAccentColor'],
|
||||||
|
type: 'bool',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'FS_GENERAL_USE_THEME_PRIMARY_SHADE',
|
||||||
|
path: ['general', 'useThemePrimaryShade'],
|
||||||
|
type: 'bool',
|
||||||
|
},
|
||||||
|
{ key: 'FS_GENERAL_ZOOM_FACTOR', path: ['general', 'zoomFactor'], type: 'num' },
|
||||||
|
{ key: 'FS_PLAYBACK_MEDIA_SESSION', path: ['playback', 'mediaSession'], type: 'bool' },
|
||||||
|
{ key: 'FS_PLAYBACK_WEB_AUDIO', path: ['playback', 'webAudio'], type: 'bool' },
|
||||||
|
{
|
||||||
|
key: 'FS_PLAYBACK_AUDIO_FADE_ON_STATUS_CHANGE',
|
||||||
|
path: ['playback', 'audioFadeOnStatusChange'],
|
||||||
|
type: 'bool',
|
||||||
|
},
|
||||||
|
{ key: 'FS_PLAYBACK_PRESERVE_PITCH', path: ['playback', 'preservePitch'], type: 'bool' },
|
||||||
|
{
|
||||||
|
key: 'FS_PLAYBACK_SCROBBLE_ENABLED',
|
||||||
|
path: ['playback', 'scrobble', 'enabled'],
|
||||||
|
type: 'bool',
|
||||||
|
},
|
||||||
|
{ key: 'FS_PLAYBACK_SCROBBLE_NOTIFY', path: ['playback', 'scrobble', 'notify'], type: 'bool' },
|
||||||
|
{
|
||||||
|
key: 'FS_PLAYBACK_SCROBBLE_AT_DURATION',
|
||||||
|
path: ['playback', 'scrobble', 'scrobbleAtDuration'],
|
||||||
|
type: 'num',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'FS_PLAYBACK_SCROBBLE_AT_PERCENTAGE',
|
||||||
|
path: ['playback', 'scrobble', 'scrobbleAtPercentage'],
|
||||||
|
type: 'num',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'FS_PLAYBACK_TRANSCODE_ENABLED',
|
||||||
|
path: ['playback', 'transcode', 'enabled'],
|
||||||
|
type: 'bool',
|
||||||
|
},
|
||||||
|
{ key: 'FS_DISCORD_ENABLED', path: ['discord', 'enabled'], type: 'bool' },
|
||||||
|
{
|
||||||
|
key: 'FS_DISCORD_CLIENT_ID',
|
||||||
|
path: ['discord', 'clientId'],
|
||||||
|
skipIfEmpty: true,
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enumSet: DISCORD_DISPLAY_TYPES,
|
||||||
|
key: 'FS_DISCORD_DISPLAY_TYPE',
|
||||||
|
path: ['discord', 'displayType'],
|
||||||
|
type: 'enum',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enumSet: DISCORD_LINK_TYPES,
|
||||||
|
key: 'FS_DISCORD_LINK_TYPE',
|
||||||
|
path: ['discord', 'linkType'],
|
||||||
|
type: 'enum',
|
||||||
|
},
|
||||||
|
{ key: 'FS_DISCORD_SHOW_AS_LISTENING', path: ['discord', 'showAsListening'], type: 'bool' },
|
||||||
|
{ key: 'FS_DISCORD_SHOW_PAUSED', path: ['discord', 'showPaused'], type: 'bool' },
|
||||||
|
{ key: 'FS_DISCORD_SHOW_SERVER_IMAGE', path: ['discord', 'showServerImage'], type: 'bool' },
|
||||||
|
{ key: 'FS_DISCORD_SHOW_STATE_ICON', path: ['discord', 'showStateIcon'], type: 'bool' },
|
||||||
|
{ key: 'FS_LYRICS_FETCH', path: ['lyrics', 'fetch'], type: 'bool' },
|
||||||
|
{ key: 'FS_LYRICS_FOLLOW', path: ['lyrics', 'follow'], type: 'bool' },
|
||||||
|
{ key: 'FS_LYRICS_DELAY_MS', path: ['lyrics', 'delayMs'], type: 'num' },
|
||||||
|
{ key: 'FS_LYRICS_PREFER_LOCAL', path: ['lyrics', 'preferLocalLyrics'], type: 'bool' },
|
||||||
|
{ key: 'FS_LYRICS_SHOW_MATCH', path: ['lyrics', 'showMatch'], type: 'bool' },
|
||||||
|
{ key: 'FS_LYRICS_SHOW_PROVIDER', path: ['lyrics', 'showProvider'], type: 'bool' },
|
||||||
|
{
|
||||||
|
key: 'FS_LYRICS_ENABLE_AUTO_TRANSLATION',
|
||||||
|
path: ['lyrics', 'enableAutoTranslation'],
|
||||||
|
type: 'bool',
|
||||||
|
},
|
||||||
|
{ key: 'FS_LYRICS_TRANSLATION_API_KEY', path: ['lyrics', 'translationApiKey'], type: 'string' },
|
||||||
|
{
|
||||||
|
key: 'FS_LYRICS_TRANSLATION_TARGET_LANGUAGE',
|
||||||
|
path: ['lyrics', 'translationTargetLanguage'],
|
||||||
|
skipIfEmpty: true,
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enumSet: LYRICS_ALIGNMENTS,
|
||||||
|
key: 'FS_LYRICS_ALIGNMENT',
|
||||||
|
path: ['lyrics', 'alignment'],
|
||||||
|
type: 'enum',
|
||||||
|
},
|
||||||
|
{ key: 'FS_AUTO_DJ_ENABLED', path: ['autoDJ', 'enabled'], type: 'bool' },
|
||||||
|
{ key: 'FS_AUTO_DJ_ITEM_COUNT', path: ['autoDJ', 'itemCount'], type: 'num' },
|
||||||
|
{ key: 'FS_AUTO_DJ_TIMING', path: ['autoDJ', 'timing'], type: 'num' },
|
||||||
|
{
|
||||||
|
key: 'FS_CSS_CONTENT',
|
||||||
|
path: ['css', 'content'],
|
||||||
|
transform: (s) => (s.trim() === '' ? undefined : sanitizeCss(`<style>${s}`)),
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
{ key: 'FS_CSS_ENABLED', path: ['css', 'enabled'], type: 'bool' },
|
||||||
|
{ enumSet: FONT_TYPES, key: 'FS_FONT_TYPE', path: ['font', 'type'], type: 'enum' },
|
||||||
|
{ key: 'FS_FONT_BUILT_IN', path: ['font', 'builtIn'], skipIfEmpty: true, type: 'string' },
|
||||||
|
{
|
||||||
|
key: 'FS_FONT_SYSTEM',
|
||||||
|
path: ['font', 'system'],
|
||||||
|
transform: (s) => (s === '' ? null : s),
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function getEnvSettingsOverrides(): EnvSettingsOverrides {
|
||||||
|
const w = getWin();
|
||||||
|
const get = (key: string): string | undefined => {
|
||||||
|
const v = w[key];
|
||||||
|
if (typeof v !== 'string') return undefined;
|
||||||
|
if (isUnsubstitutedPlaceholder(v)) return undefined;
|
||||||
|
return v;
|
||||||
|
};
|
||||||
|
|
||||||
|
const overrides: EnvSettingsOverrides = {};
|
||||||
|
|
||||||
|
for (const spec of ENV_SETTING_SPECS) {
|
||||||
|
const raw = get(spec.key);
|
||||||
|
const value = parseValue(raw, spec);
|
||||||
|
if (value !== undefined) {
|
||||||
|
setAtPath(overrides, spec.path, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return overrides;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWin(): Record<string, unknown> & Window {
|
||||||
|
if (typeof window === 'undefined') return {} as Record<string, unknown> & Window;
|
||||||
|
return window as unknown as Record<string, unknown> & Window;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isUnsubstitutedPlaceholder(s: string): boolean {
|
||||||
|
return s.length > 0 && s.startsWith('${FS_') && s.endsWith('}');
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseBool(s: string | undefined): boolean | undefined {
|
||||||
|
if (s === undefined || s === '') return undefined;
|
||||||
|
const lower = s.toLowerCase();
|
||||||
|
if (lower === 'true' || lower === '1') return true;
|
||||||
|
if (lower === 'false' || lower === '0') return false;
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseEnum<T extends string>(s: string | undefined, allowed: Set<string>): T | undefined {
|
||||||
|
if (s === undefined || s === '') return undefined;
|
||||||
|
const v = s.trim();
|
||||||
|
return allowed.has(v) ? (v as T) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseNum(s: string | undefined): number | undefined {
|
||||||
|
if (s === undefined || s === '') return undefined;
|
||||||
|
const n = Number(s);
|
||||||
|
return Number.isFinite(n) ? n : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseValue(raw: string | undefined, spec: EnvSettingSpec): unknown {
|
||||||
|
if (raw === undefined) return undefined;
|
||||||
|
if (spec.transform) return spec.transform(raw);
|
||||||
|
switch (spec.type) {
|
||||||
|
case 'bool':
|
||||||
|
return parseBool(raw);
|
||||||
|
case 'enum':
|
||||||
|
return spec.enumSet ? parseEnum(raw, spec.enumSet) : undefined;
|
||||||
|
case 'num':
|
||||||
|
return parseNum(raw);
|
||||||
|
case 'string':
|
||||||
|
if (spec.skipIfEmpty && raw === '') return undefined;
|
||||||
|
return raw;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import isElectron from 'is-electron';
|
import isElectron from 'is-electron';
|
||||||
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
import mergeWith from 'lodash/mergeWith';
|
import mergeWith from 'lodash/mergeWith';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
@@ -22,6 +23,7 @@ import {
|
|||||||
} from '/@/renderer/components/item-list/item-table-list/default-columns';
|
} from '/@/renderer/components/item-list/item-table-list/default-columns';
|
||||||
import { audiomotionanalyzerPresets } from '/@/renderer/features/visualizer/components/audiomotionanalyzer/presets';
|
import { audiomotionanalyzerPresets } from '/@/renderer/features/visualizer/components/audiomotionanalyzer/presets';
|
||||||
import { AppRoute } from '/@/renderer/router/routes';
|
import { AppRoute } from '/@/renderer/router/routes';
|
||||||
|
import { getEnvSettingsOverrides } from '/@/renderer/store/env-settings-overrides';
|
||||||
import { mergeOverridingColumns } from '/@/renderer/store/utils';
|
import { mergeOverridingColumns } from '/@/renderer/store/utils';
|
||||||
import { FontValueSchema } from '/@/renderer/types/fonts';
|
import { FontValueSchema } from '/@/renderer/types/fonts';
|
||||||
import { randomString } from '/@/renderer/utils';
|
import { randomString } from '/@/renderer/utils';
|
||||||
@@ -1889,6 +1891,11 @@ const initialState: SettingsState = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const initialStateWithEnv = mergeWith(
|
||||||
|
cloneDeep(initialState),
|
||||||
|
getEnvSettingsOverrides(),
|
||||||
|
) as SettingsState;
|
||||||
|
|
||||||
export const useSettingsStore = createWithEqualityFn<SettingsSlice>()(
|
export const useSettingsStore = createWithEqualityFn<SettingsSlice>()(
|
||||||
persist(
|
persist(
|
||||||
devtools(
|
devtools(
|
||||||
@@ -2035,7 +2042,7 @@ export const useSettingsStore = createWithEqualityFn<SettingsSlice>()(
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
...initialState,
|
...initialStateWithEnv,
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
{ name: 'store_settings' },
|
{ name: 'store_settings' },
|
||||||
|
|||||||
Reference in New Issue
Block a user