prismcast 1.12.1 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.d.ts +88 -7
- package/dist/app.js +295 -161
- package/dist/app.js.map +1 -1
- package/dist/browser/blockedPage.d.ts +0 -1
- package/dist/browser/browserSupervisor.d.ts +4 -4
- package/dist/browser/browserSupervisor.js.map +1 -1
- package/dist/browser/cdp.d.ts +8 -20
- package/dist/browser/cdp.js +11 -61
- package/dist/browser/cdp.js.map +1 -1
- package/dist/browser/channelSelection.d.ts +16 -4
- package/dist/browser/channelSelection.js +42 -31
- package/dist/browser/channelSelection.js.map +1 -1
- package/dist/browser/consent.d.ts +17 -4
- package/dist/browser/consent.js +25 -40
- package/dist/browser/consent.js.map +1 -1
- package/dist/browser/display.d.ts +0 -1
- package/dist/browser/hlsPlaylistObserver.d.ts +3 -2
- package/dist/browser/hlsPlaylistObserver.js +9 -4
- package/dist/browser/hlsPlaylistObserver.js.map +1 -1
- package/dist/browser/index.d.ts +179 -46
- package/dist/browser/index.js +473 -306
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/launchGovernor.d.ts +0 -1
- package/dist/browser/login.d.ts +8 -7
- package/dist/browser/login.js +25 -16
- package/dist/browser/login.js.map +1 -1
- package/dist/browser/manifestInterceptor.d.ts +14 -5
- package/dist/browser/manifestInterceptor.js +25 -12
- package/dist/browser/manifestInterceptor.js.map +1 -1
- package/dist/browser/navigation.d.ts +28 -0
- package/dist/browser/navigation.js +57 -0
- package/dist/browser/navigation.js.map +1 -0
- package/dist/browser/pageStaleness.d.ts +0 -1
- package/dist/browser/precaching.d.ts +27 -11
- package/dist/browser/precaching.js +262 -107
- package/dist/browser/precaching.js.map +1 -1
- package/dist/browser/tabCapture.d.ts +7 -10
- package/dist/browser/tabCapture.js +37 -50
- package/dist/browser/tabCapture.js.map +1 -1
- package/dist/browser/tabNetworkObserver.d.ts +0 -1
- package/dist/browser/tabSelection.d.ts +5 -5
- package/dist/browser/tabSelection.js +11 -10
- package/dist/browser/tabSelection.js.map +1 -1
- package/dist/browser/tuning/cache.d.ts +0 -1
- package/dist/browser/tuning/comcastPolymer.d.ts +0 -1
- package/dist/browser/tuning/comcastPolymer.js +29 -31
- package/dist/browser/tuning/comcastPolymer.js.map +1 -1
- package/dist/browser/tuning/cox.d.ts +0 -1
- package/dist/browser/tuning/directv.d.ts +0 -1
- package/dist/browser/tuning/directv.js +11 -12
- package/dist/browser/tuning/directv.js.map +1 -1
- package/dist/browser/tuning/fox.d.ts +0 -1
- package/dist/browser/tuning/fox.js +2 -2
- package/dist/browser/tuning/fox.js.map +1 -1
- package/dist/browser/tuning/gridSearch.d.ts +0 -1
- package/dist/browser/tuning/hbo.d.ts +0 -1
- package/dist/browser/tuning/hbo.js +34 -17
- package/dist/browser/tuning/hbo.js.map +1 -1
- package/dist/browser/tuning/hulu.d.ts +14 -2
- package/dist/browser/tuning/hulu.js +144 -21
- package/dist/browser/tuning/hulu.js.map +1 -1
- package/dist/browser/tuning/shared.d.ts +0 -1
- package/dist/browser/tuning/sling.d.ts +0 -1
- package/dist/browser/tuning/sling.js +17 -22
- package/dist/browser/tuning/sling.js.map +1 -1
- package/dist/browser/tuning/spectrum.d.ts +0 -1
- package/dist/browser/tuning/thumbnailRow.d.ts +0 -1
- package/dist/browser/tuning/tileClick.d.ts +0 -1
- package/dist/browser/tuning/xfinity.d.ts +0 -1
- package/dist/browser/tuning/youtubeTv.d.ts +0 -1
- package/dist/browser/video.d.ts +22 -20
- package/dist/browser/video.js +102 -119
- package/dist/browser/video.js.map +1 -1
- package/dist/browser/windowSync.d.ts +0 -1
- package/dist/channels/index.d.ts +0 -1
- package/dist/channels/index.js +5 -5
- package/dist/channels/index.js.map +1 -1
- package/dist/cliHelp.d.ts +11 -0
- package/dist/cliHelp.js +148 -0
- package/dist/cliHelp.js.map +1 -0
- package/dist/config/channelForm.d.ts +0 -1
- package/dist/config/channelForm.js +5 -4
- package/dist/config/channelForm.js.map +1 -1
- package/dist/config/channelIdentity.d.ts +27 -0
- package/dist/config/channelIdentity.js +72 -0
- package/dist/config/channelIdentity.js.map +1 -0
- package/dist/config/channelSort.d.ts +25 -0
- package/dist/config/channelSort.js +101 -0
- package/dist/config/channelSort.js.map +1 -0
- package/dist/config/consistencyProbe.d.ts +2 -5
- package/dist/config/consistencyProbe.js +8 -51
- package/dist/config/consistencyProbe.js.map +1 -1
- package/dist/config/health.d.ts +3 -2
- package/dist/config/health.js +41 -28
- package/dist/config/health.js.map +1 -1
- package/dist/config/index.d.ts +107 -56
- package/dist/config/index.js +499 -267
- package/dist/config/index.js.map +1 -1
- package/dist/config/paths.d.ts +13 -9
- package/dist/config/paths.js +21 -25
- package/dist/config/paths.js.map +1 -1
- package/dist/config/persistence.context.d.ts +0 -1
- package/dist/config/persistence.d.ts +57 -12
- package/dist/config/persistence.js +127 -44
- package/dist/config/persistence.js.map +1 -1
- package/dist/config/presets.d.ts +0 -1
- package/dist/config/profiles.d.ts +9 -4
- package/dist/config/profiles.js +37 -3
- package/dist/config/profiles.js.map +1 -1
- package/dist/config/providerLineups.d.ts +0 -1
- package/dist/config/reactivity.d.ts +60 -37
- package/dist/config/reactivity.js +89 -116
- package/dist/config/reactivity.js.map +1 -1
- package/dist/config/servicePacks.d.ts +0 -1
- package/dist/config/services.d.ts +62 -73
- package/dist/config/services.js +140 -202
- package/dist/config/services.js.map +1 -1
- package/dist/config/sites.d.ts +0 -9
- package/dist/config/sites.js +5 -40
- package/dist/config/sites.js.map +1 -1
- package/dist/config/streamSettings.d.ts +24 -0
- package/dist/config/streamSettings.js +17 -0
- package/dist/config/streamSettings.js.map +1 -0
- package/dist/config/userChannels.d.ts +82 -80
- package/dist/config/userChannels.js +184 -248
- package/dist/config/userChannels.js.map +1 -1
- package/dist/config/userConfig.d.ts +225 -59
- package/dist/config/userConfig.js +352 -131
- package/dist/config/userConfig.js.map +1 -1
- package/dist/config/userProfiles.d.ts +2 -3
- package/dist/config/userProfiles.js +6 -2
- package/dist/config/userProfiles.js.map +1 -1
- package/dist/hdhr/channelMap.d.ts +0 -1
- package/dist/hdhr/discover.d.ts +7 -2
- package/dist/hdhr/discover.js +12 -6
- package/dist/hdhr/discover.js.map +1 -1
- package/dist/hdhr/getHandlers.d.ts +0 -1
- package/dist/hdhr/identity.d.ts +0 -1
- package/dist/hdhr/index.d.ts +51 -11
- package/dist/hdhr/index.js +108 -112
- package/dist/hdhr/index.js.map +1 -1
- package/dist/hdhr/protocol.d.ts +0 -1
- package/dist/hdhr/tunerState.d.ts +0 -1
- package/dist/hdhr/udp.d.ts +11 -5
- package/dist/hdhr/udp.js +25 -20
- package/dist/hdhr/udp.js.map +1 -1
- package/dist/identity.d.ts +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +16 -143
- package/dist/index.js.map +1 -1
- package/dist/native/codecInference.d.ts +5 -4
- package/dist/native/codecInference.js +12 -7
- package/dist/native/codecInference.js.map +1 -1
- package/dist/native/decrypt.d.ts +10 -3
- package/dist/native/decrypt.js +9 -5
- package/dist/native/decrypt.js.map +1 -1
- package/dist/native/index.d.ts +25 -23
- package/dist/native/index.js +27 -20
- package/dist/native/index.js.map +1 -1
- package/dist/native/probe.d.ts +7 -10
- package/dist/native/probe.js +40 -36
- package/dist/native/probe.js.map +1 -1
- package/dist/native/proxy.d.ts +3 -4
- package/dist/native/proxy.js +122 -92
- package/dist/native/proxy.js.map +1 -1
- package/dist/native/tokenExpiry.d.ts +0 -1
- package/dist/routes/assets.d.ts +0 -1
- package/dist/routes/auth.d.ts +0 -1
- package/dist/routes/auth.js +1 -1
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/cdp.d.ts +18 -11
- package/dist/routes/cdp.js +99 -70
- package/dist/routes/cdp.js.map +1 -1
- package/dist/routes/channels.d.ts +0 -1
- package/dist/routes/clientActions.d.ts +0 -1
- package/dist/routes/components.d.ts +23 -6
- package/dist/routes/components.js +21 -4
- package/dist/routes/components.js.map +1 -1
- package/dist/routes/config/channels/endpoints/browse.d.ts +0 -1
- package/dist/routes/config/channels/endpoints/browse.js +39 -29
- package/dist/routes/config/channels/endpoints/browse.js.map +1 -1
- package/dist/routes/config/channels/endpoints/bulk.d.ts +0 -1
- package/dist/routes/config/channels/endpoints/bulk.js +3 -7
- package/dist/routes/config/channels/endpoints/bulk.js.map +1 -1
- package/dist/routes/config/channels/endpoints/crud.d.ts +0 -1
- package/dist/routes/config/channels/endpoints/crud.js +3 -2
- package/dist/routes/config/channels/endpoints/crud.js.map +1 -1
- package/dist/routes/config/channels/endpoints/importExport.d.ts +0 -1
- package/dist/routes/config/channels/endpoints/importExport.js +7 -3
- package/dist/routes/config/channels/endpoints/importExport.js.map +1 -1
- package/dist/routes/config/channels/endpoints/predefined.d.ts +0 -1
- package/dist/routes/config/channels/endpoints/predefined.js +5 -15
- package/dist/routes/config/channels/endpoints/predefined.js.map +1 -1
- package/dist/routes/config/channels/endpoints/prefs.d.ts +0 -1
- package/dist/routes/config/channels/endpoints/prefs.js +8 -7
- package/dist/routes/config/channels/endpoints/prefs.js.map +1 -1
- package/dist/routes/config/channels/endpoints/service.d.ts +0 -1
- package/dist/routes/config/channels/endpoints/service.js +13 -9
- package/dist/routes/config/channels/endpoints/service.js.map +1 -1
- package/dist/routes/config/channels/endpoints/tags.d.ts +0 -1
- package/dist/routes/config/channels/endpoints/tags.js +4 -16
- package/dist/routes/config/channels/endpoints/tags.js.map +1 -1
- package/dist/routes/config/channels/healthBridge.d.ts +0 -1
- package/dist/routes/config/channels/http/handler.d.ts +0 -1
- package/dist/routes/config/channels/http/playlistHint.d.ts +0 -1
- package/dist/routes/config/channels/http/serviceWarning.d.ts +0 -1
- package/dist/routes/config/channels/http/serviceWarning.js +1 -1
- package/dist/routes/config/channels/http/serviceWarning.js.map +1 -1
- package/dist/routes/config/channels/index.d.ts +0 -1
- package/dist/routes/config/channels/setup.d.ts +0 -1
- package/dist/routes/config/channels/table.d.ts +2 -2
- package/dist/routes/config/channels/table.js +13 -14
- package/dist/routes/config/channels/table.js.map +1 -1
- package/dist/routes/config/http/envelope.d.ts +6 -4
- package/dist/routes/config/http/envelope.js +18 -5
- package/dist/routes/config/http/envelope.js.map +1 -1
- package/dist/routes/config/index.d.ts +21 -14
- package/dist/routes/config/index.js +49 -35
- package/dist/routes/config/index.js.map +1 -1
- package/dist/routes/config/services.d.ts +0 -1
- package/dist/routes/config/services.js +4 -3
- package/dist/routes/config/services.js.map +1 -1
- package/dist/routes/config/settings.d.ts +43 -7
- package/dist/routes/config/settings.js +227 -126
- package/dist/routes/config/settings.js.map +1 -1
- package/dist/routes/config/vocabulary.d.ts +24 -0
- package/dist/routes/config/vocabulary.js +33 -0
- package/dist/routes/config/vocabulary.js.map +1 -0
- package/dist/routes/debug.d.ts +0 -1
- package/dist/routes/debug.js +28 -13
- package/dist/routes/debug.js.map +1 -1
- package/dist/routes/health.d.ts +1 -2
- package/dist/routes/health.js +5 -2
- package/dist/routes/health.js.map +1 -1
- package/dist/routes/hls.d.ts +0 -1
- package/dist/routes/icons.d.ts +0 -1
- package/dist/routes/index.d.ts +0 -1
- package/dist/routes/logs.d.ts +3 -2
- package/dist/routes/logs.js +4 -2
- package/dist/routes/logs.js.map +1 -1
- package/dist/routes/mpegts.d.ts +0 -1
- package/dist/routes/play.d.ts +0 -1
- package/dist/routes/playlist.d.ts +0 -1
- package/dist/routes/playlist.js +2 -1
- package/dist/routes/playlist.js.map +1 -1
- package/dist/routes/root/content.d.ts +0 -1
- package/dist/routes/root/content.js +33 -26
- package/dist/routes/root/content.js.map +1 -1
- package/dist/routes/root/index.d.ts +0 -1
- package/dist/routes/root/scripts/channels.d.ts +0 -1
- package/dist/routes/root/scripts/channels.js +25 -13
- package/dist/routes/root/scripts/channels.js.map +1 -1
- package/dist/routes/root/scripts/clientEscape.d.ts +0 -1
- package/dist/routes/root/scripts/clientUrl.d.ts +0 -1
- package/dist/routes/root/scripts/config.d.ts +1 -2
- package/dist/routes/root/scripts/config.js +132 -105
- package/dist/routes/root/scripts/config.js.map +1 -1
- package/dist/routes/root/scripts/index.d.ts +0 -1
- package/dist/routes/root/scripts/shared.d.ts +0 -1
- package/dist/routes/root/scripts/shared.js +34 -23
- package/dist/routes/root/scripts/shared.js.map +1 -1
- package/dist/routes/root/scripts/status.d.ts +0 -1
- package/dist/routes/root/scripts/status.handlers.d.ts +8 -14
- package/dist/routes/root/scripts/status.handlers.js +38 -23
- package/dist/routes/root/scripts/status.handlers.js.map +1 -1
- package/dist/routes/root/styles.d.ts +0 -1
- package/dist/routes/root/styles.js +13 -16
- package/dist/routes/root/styles.js.map +1 -1
- package/dist/routes/services.d.ts +0 -1
- package/dist/routes/services.js +13 -17
- package/dist/routes/services.js.map +1 -1
- package/dist/routes/sse.d.ts +3 -2
- package/dist/routes/sse.js +7 -3
- package/dist/routes/sse.js.map +1 -1
- package/dist/routes/streams.d.ts +4 -2
- package/dist/routes/streams.js +8 -5
- package/dist/routes/streams.js.map +1 -1
- package/dist/routes/theme.d.ts +0 -1
- package/dist/routes/theme.js +12 -12
- package/dist/routes/theme.js.map +1 -1
- package/dist/routes/ui.d.ts +4 -4
- package/dist/routes/ui.js +17 -13
- package/dist/routes/ui.js.map +1 -1
- package/dist/routes/upgrade.d.ts +0 -1
- package/dist/routes/upgrade.js +2 -1
- package/dist/routes/upgrade.js.map +1 -1
- package/dist/service/commands.context.d.ts +0 -1
- package/dist/service/commands.context.js +7 -1
- package/dist/service/commands.context.js.map +1 -1
- package/dist/service/commands.d.ts +0 -1
- package/dist/service/generators.context.d.ts +0 -1
- package/dist/service/generators.d.ts +0 -1
- package/dist/service/generators.js +12 -9
- package/dist/service/generators.js.map +1 -1
- package/dist/service/index.d.ts +0 -1
- package/dist/streaming/captureLock.d.ts +1 -2
- package/dist/streaming/captureLock.js +6 -5
- package/dist/streaming/captureLock.js.map +1 -1
- package/dist/streaming/captureSession.d.ts +4 -5
- package/dist/streaming/captureSession.js +6 -7
- package/dist/streaming/captureSession.js.map +1 -1
- package/dist/streaming/clients.d.ts +4 -3
- package/dist/streaming/clients.js +5 -4
- package/dist/streaming/clients.js.map +1 -1
- package/dist/streaming/codec.d.ts +12 -3
- package/dist/streaming/codec.js +40 -28
- package/dist/streaming/codec.js.map +1 -1
- package/dist/streaming/fmp4Segmenter.d.ts +16 -5
- package/dist/streaming/fmp4Segmenter.js +110 -89
- package/dist/streaming/fmp4Segmenter.js.map +1 -1
- package/dist/streaming/hls.d.ts +55 -15
- package/dist/streaming/hls.js +255 -162
- package/dist/streaming/hls.js.map +1 -1
- package/dist/streaming/hlsResume.d.ts +42 -16
- package/dist/streaming/hlsResume.js +75 -42
- package/dist/streaming/hlsResume.js.map +1 -1
- package/dist/streaming/hlsSegments.d.ts +9 -7
- package/dist/streaming/hlsSegments.js +19 -14
- package/dist/streaming/hlsSegments.js.map +1 -1
- package/dist/streaming/lifecycle.d.ts +5 -4
- package/dist/streaming/lifecycle.js +25 -13
- package/dist/streaming/lifecycle.js.map +1 -1
- package/dist/streaming/monitor.d.ts +8 -5
- package/dist/streaming/monitor.js +109 -93
- package/dist/streaming/monitor.js.map +1 -1
- package/dist/streaming/mp4Parser.d.ts +29 -5
- package/dist/streaming/mp4Parser.js +49 -7
- package/dist/streaming/mp4Parser.js.map +1 -1
- package/dist/streaming/mpegts.d.ts +0 -1
- package/dist/streaming/mpegts.js +23 -7
- package/dist/streaming/mpegts.js.map +1 -1
- package/dist/streaming/playlistBuilder.d.ts +1 -3
- package/dist/streaming/playlistBuilder.js +2 -3
- package/dist/streaming/playlistBuilder.js.map +1 -1
- package/dist/streaming/preroll.d.ts +76 -30
- package/dist/streaming/preroll.js +141 -89
- package/dist/streaming/preroll.js.map +1 -1
- package/dist/streaming/pretune.d.ts +3 -4
- package/dist/streaming/pretune.js +50 -46
- package/dist/streaming/pretune.js.map +1 -1
- package/dist/streaming/pretuneTimers.d.ts +16 -12
- package/dist/streaming/pretuneTimers.js +27 -37
- package/dist/streaming/pretuneTimers.js.map +1 -1
- package/dist/streaming/recovery.d.ts +36 -7
- package/dist/streaming/recovery.js +53 -11
- package/dist/streaming/recovery.js.map +1 -1
- package/dist/streaming/registry.d.ts +35 -14
- package/dist/streaming/registry.js +23 -8
- package/dist/streaming/registry.js.map +1 -1
- package/dist/streaming/setup.d.ts +66 -25
- package/dist/streaming/setup.js +305 -229
- package/dist/streaming/setup.js.map +1 -1
- package/dist/streaming/showInfo.d.ts +29 -13
- package/dist/streaming/showInfo.js +156 -140
- package/dist/streaming/showInfo.js.map +1 -1
- package/dist/streaming/statusEmitter.d.ts +16 -12
- package/dist/streaming/statusEmitter.js +25 -21
- package/dist/streaming/statusEmitter.js.map +1 -1
- package/dist/types/channels.d.ts +0 -1
- package/dist/types/config.d.ts +24 -14
- package/dist/types/config.js +5 -1
- package/dist/types/config.js.map +1 -1
- package/dist/types/index.d.ts +4 -4
- package/dist/types/index.js +2 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/profiles.d.ts +3 -3
- package/dist/types/selection.d.ts +19 -8
- package/dist/types/shared.d.ts +0 -1
- package/dist/types/streaming.d.ts +12 -6
- package/dist/types/streaming.js +12 -5
- package/dist/types/streaming.js.map +1 -1
- package/dist/upgrade/commands.context.d.ts +0 -1
- package/dist/upgrade/commands.d.ts +0 -1
- package/dist/upgrade/detection.context.d.ts +0 -1
- package/dist/upgrade/detection.d.ts +0 -1
- package/dist/upgrade/index.d.ts +0 -1
- package/dist/upgrade/lifecycle.context.d.ts +2 -1
- package/dist/upgrade/lifecycle.context.js +9 -4
- package/dist/upgrade/lifecycle.context.js.map +1 -1
- package/dist/upgrade/lifecycle.d.ts +0 -1
- package/dist/upgrade/pathHandle.d.ts +0 -1
- package/dist/utils/bootSession.context.d.ts +0 -1
- package/dist/utils/bootSession.context.js +2 -1
- package/dist/utils/bootSession.context.js.map +1 -1
- package/dist/utils/bootSession.d.ts +0 -1
- package/dist/utils/chromeFetch.d.ts +0 -1
- package/dist/utils/cliOutput.d.ts +0 -1
- package/dist/utils/debugFilter.d.ts +3 -3
- package/dist/utils/debugFilter.js +7 -5
- package/dist/utils/debugFilter.js.map +1 -1
- package/dist/utils/delay.d.ts +47 -25
- package/dist/utils/delay.js +74 -37
- package/dist/utils/delay.js.map +1 -1
- package/dist/{hdhr → utils}/deviceId.d.ts +1 -2
- package/dist/{hdhr → utils}/deviceId.js +11 -29
- package/dist/utils/deviceId.js.map +1 -0
- package/dist/utils/errors.d.ts +7 -1
- package/dist/utils/errors.js +12 -3
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/evaluate.d.ts +11 -3
- package/dist/utils/evaluate.js +4 -3
- package/dist/utils/evaluate.js.map +1 -1
- package/dist/utils/ffmpeg.context.d.ts +0 -1
- package/dist/utils/ffmpeg.d.ts +0 -1
- package/dist/utils/fileLogger.d.ts +26 -14
- package/dist/utils/fileLogger.js +89 -49
- package/dist/utils/fileLogger.js.map +1 -1
- package/dist/utils/format.d.ts +12 -3
- package/dist/utils/format.js +29 -11
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/httpErrors.d.ts +13 -0
- package/dist/utils/httpErrors.js +22 -0
- package/dist/utils/httpErrors.js.map +1 -0
- package/dist/utils/index.d.ts +3 -2
- package/dist/utils/index.js +3 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/logEmitter.d.ts +0 -1
- package/dist/utils/logger.d.ts +9 -9
- package/dist/utils/logger.js +64 -20
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/m3u.d.ts +0 -1
- package/dist/utils/markup.d.ts +0 -1
- package/dist/utils/markup.js +8 -3
- package/dist/utils/markup.js.map +1 -1
- package/dist/utils/memo.d.ts +0 -1
- package/dist/utils/morganStream.d.ts +0 -1
- package/dist/utils/network.d.ts +0 -1
- package/dist/utils/never.d.ts +0 -1
- package/dist/utils/pid.d.ts +0 -1
- package/dist/utils/plainObject.d.ts +6 -0
- package/dist/utils/plainObject.js +18 -0
- package/dist/utils/plainObject.js.map +1 -0
- package/dist/utils/platform.d.ts +0 -1
- package/dist/utils/processInspector.context.d.ts +32 -2
- package/dist/utils/processInspector.context.js +117 -1
- package/dist/utils/processInspector.context.js.map +1 -1
- package/dist/utils/processInspector.d.ts +0 -32
- package/dist/utils/processInspector.js +10 -117
- package/dist/utils/processInspector.js.map +1 -1
- package/dist/utils/retry.d.ts +5 -4
- package/dist/utils/retry.js +43 -33
- package/dist/utils/retry.js.map +1 -1
- package/dist/utils/runtimeIdentity.context.d.ts +0 -1
- package/dist/utils/runtimeIdentity.context.js +4 -30
- package/dist/utils/runtimeIdentity.context.js.map +1 -1
- package/dist/utils/runtimeIdentity.d.ts +18 -7
- package/dist/utils/runtimeIdentity.js +51 -18
- package/dist/utils/runtimeIdentity.js.map +1 -1
- package/dist/utils/sanitize.d.ts +0 -1
- package/dist/utils/streamContext.d.ts +0 -1
- package/dist/utils/timing.d.ts +3 -4
- package/dist/utils/timing.js +4 -4
- package/dist/utils/timing.js.map +1 -1
- package/dist/utils/version.d.ts +5 -4
- package/dist/utils/version.js +35 -24
- package/dist/utils/version.js.map +1 -1
- package/package.json +9 -9
- package/dist/app.d.ts.map +0 -1
- package/dist/browser/blockedPage.d.ts.map +0 -1
- package/dist/browser/browserSupervisor.d.ts.map +0 -1
- package/dist/browser/cdp.d.ts.map +0 -1
- package/dist/browser/channelSelection.d.ts.map +0 -1
- package/dist/browser/consent.d.ts.map +0 -1
- package/dist/browser/display.d.ts.map +0 -1
- package/dist/browser/hlsPlaylistObserver.d.ts.map +0 -1
- package/dist/browser/index.d.ts.map +0 -1
- package/dist/browser/launchGovernor.d.ts.map +0 -1
- package/dist/browser/login.d.ts.map +0 -1
- package/dist/browser/manifestInterceptor.d.ts.map +0 -1
- package/dist/browser/pageStaleness.d.ts.map +0 -1
- package/dist/browser/precaching.d.ts.map +0 -1
- package/dist/browser/tabCapture.d.ts.map +0 -1
- package/dist/browser/tabNetworkObserver.d.ts.map +0 -1
- package/dist/browser/tabSelection.d.ts.map +0 -1
- package/dist/browser/tuning/cache.d.ts.map +0 -1
- package/dist/browser/tuning/comcastPolymer.d.ts.map +0 -1
- package/dist/browser/tuning/cox.d.ts.map +0 -1
- package/dist/browser/tuning/directv.d.ts.map +0 -1
- package/dist/browser/tuning/fox.d.ts.map +0 -1
- package/dist/browser/tuning/gridSearch.d.ts.map +0 -1
- package/dist/browser/tuning/hbo.d.ts.map +0 -1
- package/dist/browser/tuning/hulu.d.ts.map +0 -1
- package/dist/browser/tuning/shared.d.ts.map +0 -1
- package/dist/browser/tuning/sling.d.ts.map +0 -1
- package/dist/browser/tuning/spectrum.d.ts.map +0 -1
- package/dist/browser/tuning/thumbnailRow.d.ts.map +0 -1
- package/dist/browser/tuning/tileClick.d.ts.map +0 -1
- package/dist/browser/tuning/xfinity.d.ts.map +0 -1
- package/dist/browser/tuning/youtubeTv.d.ts.map +0 -1
- package/dist/browser/video.d.ts.map +0 -1
- package/dist/browser/windowSync.d.ts.map +0 -1
- package/dist/channels/index.d.ts.map +0 -1
- package/dist/config/channelForm.d.ts.map +0 -1
- package/dist/config/consistencyProbe.d.ts.map +0 -1
- package/dist/config/health.d.ts.map +0 -1
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/paths.d.ts.map +0 -1
- package/dist/config/persistence.context.d.ts.map +0 -1
- package/dist/config/persistence.d.ts.map +0 -1
- package/dist/config/presets.d.ts.map +0 -1
- package/dist/config/profiles.d.ts.map +0 -1
- package/dist/config/providerLineups.d.ts.map +0 -1
- package/dist/config/reactivity.d.ts.map +0 -1
- package/dist/config/servicePacks.d.ts.map +0 -1
- package/dist/config/services.d.ts.map +0 -1
- package/dist/config/sites.d.ts.map +0 -1
- package/dist/config/userChannels.d.ts.map +0 -1
- package/dist/config/userConfig.d.ts.map +0 -1
- package/dist/config/userProfiles.d.ts.map +0 -1
- package/dist/hdhr/channelMap.d.ts.map +0 -1
- package/dist/hdhr/deviceId.d.ts.map +0 -1
- package/dist/hdhr/deviceId.js.map +0 -1
- package/dist/hdhr/discover.d.ts.map +0 -1
- package/dist/hdhr/getHandlers.d.ts.map +0 -1
- package/dist/hdhr/identity.d.ts.map +0 -1
- package/dist/hdhr/index.d.ts.map +0 -1
- package/dist/hdhr/protocol.d.ts.map +0 -1
- package/dist/hdhr/tunerState.d.ts.map +0 -1
- package/dist/hdhr/udp.d.ts.map +0 -1
- package/dist/identity.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/native/codecInference.d.ts.map +0 -1
- package/dist/native/decrypt.d.ts.map +0 -1
- package/dist/native/index.d.ts.map +0 -1
- package/dist/native/probe.d.ts.map +0 -1
- package/dist/native/proxy.d.ts.map +0 -1
- package/dist/native/tokenExpiry.d.ts.map +0 -1
- package/dist/routes/assets.d.ts.map +0 -1
- package/dist/routes/auth.d.ts.map +0 -1
- package/dist/routes/cdp.d.ts.map +0 -1
- package/dist/routes/channels.d.ts.map +0 -1
- package/dist/routes/clientActions.d.ts.map +0 -1
- package/dist/routes/components.d.ts.map +0 -1
- package/dist/routes/config/channels/endpoints/browse.d.ts.map +0 -1
- package/dist/routes/config/channels/endpoints/bulk.d.ts.map +0 -1
- package/dist/routes/config/channels/endpoints/crud.d.ts.map +0 -1
- package/dist/routes/config/channels/endpoints/importExport.d.ts.map +0 -1
- package/dist/routes/config/channels/endpoints/predefined.d.ts.map +0 -1
- package/dist/routes/config/channels/endpoints/prefs.d.ts.map +0 -1
- package/dist/routes/config/channels/endpoints/service.d.ts.map +0 -1
- package/dist/routes/config/channels/endpoints/tags.d.ts.map +0 -1
- package/dist/routes/config/channels/healthBridge.d.ts.map +0 -1
- package/dist/routes/config/channels/http/handler.d.ts.map +0 -1
- package/dist/routes/config/channels/http/playlistHint.d.ts.map +0 -1
- package/dist/routes/config/channels/http/serviceWarning.d.ts.map +0 -1
- package/dist/routes/config/channels/index.d.ts.map +0 -1
- package/dist/routes/config/channels/setup.d.ts.map +0 -1
- package/dist/routes/config/channels/table.d.ts.map +0 -1
- package/dist/routes/config/http/envelope.d.ts.map +0 -1
- package/dist/routes/config/index.d.ts.map +0 -1
- package/dist/routes/config/services.d.ts.map +0 -1
- package/dist/routes/config/settings.d.ts.map +0 -1
- package/dist/routes/debug.d.ts.map +0 -1
- package/dist/routes/health.d.ts.map +0 -1
- package/dist/routes/hls.d.ts.map +0 -1
- package/dist/routes/icons.d.ts.map +0 -1
- package/dist/routes/index.d.ts.map +0 -1
- package/dist/routes/logs.d.ts.map +0 -1
- package/dist/routes/mpegts.d.ts.map +0 -1
- package/dist/routes/play.d.ts.map +0 -1
- package/dist/routes/playlist.d.ts.map +0 -1
- package/dist/routes/root/content.d.ts.map +0 -1
- package/dist/routes/root/index.d.ts.map +0 -1
- package/dist/routes/root/scripts/channels.d.ts.map +0 -1
- package/dist/routes/root/scripts/clientEscape.d.ts.map +0 -1
- package/dist/routes/root/scripts/clientUrl.d.ts.map +0 -1
- package/dist/routes/root/scripts/config.d.ts.map +0 -1
- package/dist/routes/root/scripts/index.d.ts.map +0 -1
- package/dist/routes/root/scripts/shared.d.ts.map +0 -1
- package/dist/routes/root/scripts/status.d.ts.map +0 -1
- package/dist/routes/root/scripts/status.handlers.d.ts.map +0 -1
- package/dist/routes/root/styles.d.ts.map +0 -1
- package/dist/routes/services.d.ts.map +0 -1
- package/dist/routes/sse.d.ts.map +0 -1
- package/dist/routes/streams.d.ts.map +0 -1
- package/dist/routes/theme.d.ts.map +0 -1
- package/dist/routes/ui.d.ts.map +0 -1
- package/dist/routes/upgrade.d.ts.map +0 -1
- package/dist/service/commands.context.d.ts.map +0 -1
- package/dist/service/commands.d.ts.map +0 -1
- package/dist/service/generators.context.d.ts.map +0 -1
- package/dist/service/generators.d.ts.map +0 -1
- package/dist/service/index.d.ts.map +0 -1
- package/dist/streaming/captureLock.d.ts.map +0 -1
- package/dist/streaming/captureSession.d.ts.map +0 -1
- package/dist/streaming/clients.d.ts.map +0 -1
- package/dist/streaming/codec.d.ts.map +0 -1
- package/dist/streaming/fmp4Segmenter.d.ts.map +0 -1
- package/dist/streaming/hls.d.ts.map +0 -1
- package/dist/streaming/hlsResume.d.ts.map +0 -1
- package/dist/streaming/hlsSegments.d.ts.map +0 -1
- package/dist/streaming/lifecycle.d.ts.map +0 -1
- package/dist/streaming/monitor.d.ts.map +0 -1
- package/dist/streaming/mp4Parser.d.ts.map +0 -1
- package/dist/streaming/mpegts.d.ts.map +0 -1
- package/dist/streaming/playlistBuilder.d.ts.map +0 -1
- package/dist/streaming/preroll.d.ts.map +0 -1
- package/dist/streaming/pretune.d.ts.map +0 -1
- package/dist/streaming/pretuneTimers.d.ts.map +0 -1
- package/dist/streaming/recovery.d.ts.map +0 -1
- package/dist/streaming/registry.d.ts.map +0 -1
- package/dist/streaming/setup.d.ts.map +0 -1
- package/dist/streaming/showInfo.d.ts.map +0 -1
- package/dist/streaming/statusEmitter.d.ts.map +0 -1
- package/dist/types/channels.d.ts.map +0 -1
- package/dist/types/config.d.ts.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/profiles.d.ts.map +0 -1
- package/dist/types/selection.d.ts.map +0 -1
- package/dist/types/shared.d.ts.map +0 -1
- package/dist/types/streaming.d.ts.map +0 -1
- package/dist/upgrade/commands.context.d.ts.map +0 -1
- package/dist/upgrade/commands.d.ts.map +0 -1
- package/dist/upgrade/detection.context.d.ts.map +0 -1
- package/dist/upgrade/detection.d.ts.map +0 -1
- package/dist/upgrade/index.d.ts.map +0 -1
- package/dist/upgrade/lifecycle.context.d.ts.map +0 -1
- package/dist/upgrade/lifecycle.d.ts.map +0 -1
- package/dist/upgrade/pathHandle.d.ts.map +0 -1
- package/dist/utils/bootSession.context.d.ts.map +0 -1
- package/dist/utils/bootSession.d.ts.map +0 -1
- package/dist/utils/chromeFetch.d.ts.map +0 -1
- package/dist/utils/cliOutput.d.ts.map +0 -1
- package/dist/utils/clock.d.ts +0 -15
- package/dist/utils/clock.d.ts.map +0 -1
- package/dist/utils/clock.js +0 -31
- package/dist/utils/clock.js.map +0 -1
- package/dist/utils/debugFilter.d.ts.map +0 -1
- package/dist/utils/delay.d.ts.map +0 -1
- package/dist/utils/errors.d.ts.map +0 -1
- package/dist/utils/evaluate.d.ts.map +0 -1
- package/dist/utils/ffmpeg.context.d.ts.map +0 -1
- package/dist/utils/ffmpeg.d.ts.map +0 -1
- package/dist/utils/fileLogger.d.ts.map +0 -1
- package/dist/utils/format.d.ts.map +0 -1
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/logEmitter.d.ts.map +0 -1
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/m3u.d.ts.map +0 -1
- package/dist/utils/markup.d.ts.map +0 -1
- package/dist/utils/memo.d.ts.map +0 -1
- package/dist/utils/morganStream.d.ts.map +0 -1
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/never.d.ts.map +0 -1
- package/dist/utils/pid.d.ts.map +0 -1
- package/dist/utils/platform.d.ts.map +0 -1
- package/dist/utils/processInspector.context.d.ts.map +0 -1
- package/dist/utils/processInspector.d.ts.map +0 -1
- package/dist/utils/retry.d.ts.map +0 -1
- package/dist/utils/runtimeIdentity.context.d.ts.map +0 -1
- package/dist/utils/runtimeIdentity.d.ts.map +0 -1
- package/dist/utils/sanitize.d.ts.map +0 -1
- package/dist/utils/streamContext.d.ts.map +0 -1
- package/dist/utils/timing.d.ts.map +0 -1
- package/dist/utils/version.d.ts.map +0 -1
|
@@ -1,22 +1,35 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { CONFIG_METADATA, getAdvancedSections, getEnvOverrides, getNestedValue, getSettingsTabSections, getUITabs, isEqualToDefault,
|
|
3
|
-
import {
|
|
1
|
+
import { CAPTURE_BASELINE_CODEC, RECOGNIZED_CODECS } from "../../types/index.js";
|
|
2
|
+
import { CONFIG_METADATA, getAdvancedSections, getEnvOverrides, getNestedValue, getSettingsTabSections, getUITabs, isEqualToDefault, readConfig, setNestedValue } from "../../config/userConfig.js";
|
|
3
|
+
import { ConfigurationRejectedError, getConfigurationGap, getDefaults, getLoadedConfiguration, validateInteger, validateNumber } from "../../config/index.js";
|
|
4
|
+
import { LOG, escapeHtml, isRunningAsService, sanitizeString, serializeAttrs, stringifySorted } from "../../utils/index.js";
|
|
5
|
+
import { PENDING_PATH_ATTRIBUTE, REACTIVITY_BADGES, formatSettingCount } from "./vocabulary.js";
|
|
4
6
|
import { applyConfigurationChange, describeConfigurationOutcome } from "./index.js";
|
|
5
7
|
import { sendErrorResponse, sendFormErrors, sendSuccess, sendValidationError } from "./http/envelope.js";
|
|
6
8
|
import { ACTIONS } from "../clientActions.js";
|
|
7
9
|
import { VIDEO_QUALITY_PRESETS } from "../../config/presets.js";
|
|
10
|
+
import { generateBadge } from "../components.js";
|
|
8
11
|
import { getConfigFilePath } from "../../config/paths.js";
|
|
9
|
-
import { getGpuCapabilities } from "../../browser/display.js";
|
|
10
12
|
import { getProviderModuleInfo } from "../../browser/channelSelection.js";
|
|
13
|
+
import { isCaptureCodecSupported } from "../../streaming/codec.js";
|
|
14
|
+
import { systemClock } from "homebridge-plugin-utils";
|
|
15
|
+
/* The capture-codec items' words: each recognized codec's label, and the reason each codec other than the baseline states when this GPU cannot capture it. Each
|
|
16
|
+
* table is keyed by the codec types, so a codec added to the recognized list cannot compile until the form decides its label and, unless it is the baseline,
|
|
17
|
+
* its reason. Which codecs are fixed and which are available is the codec module's answer, so the form holds only these words. A label names its codec alone,
|
|
18
|
+
* and the " (always enabled)" suffix is the fixed item's, which the provider below appends to whichever codec is the baseline.
|
|
19
|
+
*/
|
|
20
|
+
const CAPTURE_CODEC_LABELS = {
|
|
21
|
+
h264: "H.264",
|
|
22
|
+
hevc: "HEVC"
|
|
23
|
+
};
|
|
24
|
+
const CAPTURE_CODEC_REASONS = {
|
|
25
|
+
hevc: "Requires GPU with HEVC hardware encoding."
|
|
26
|
+
};
|
|
11
27
|
// Registry of list item providers for checkboxList settings. Each key matches a listItemsKey value in CONFIG_METADATA.
|
|
12
28
|
const LIST_ITEM_PROVIDERS = {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
{ disabled: !gpuCaps?.hevcHardwareEncoding, disabledReason: "Requires GPU with HEVC hardware encoding.", label: "HEVC", value: "hevc" }
|
|
18
|
-
];
|
|
19
|
-
},
|
|
29
|
+
// Every recognized codec in order: the baseline as the fixed item, and every other codec disabled while this GPU cannot capture it.
|
|
30
|
+
captureCodecs: () => RECOGNIZED_CODECS.map((codec) => (codec === CAPTURE_BASELINE_CODEC) ?
|
|
31
|
+
{ fixed: true, label: CAPTURE_CODEC_LABELS[codec] + " (always enabled)", value: codec } :
|
|
32
|
+
{ disabled: !isCaptureCodecSupported(codec), disabledReason: CAPTURE_CODEC_REASONS[codec], label: CAPTURE_CODEC_LABELS[codec], value: codec }),
|
|
20
33
|
providerModules: () => getProviderModuleInfo().map((p) => ({ label: p.label, value: p.slug }))
|
|
21
34
|
};
|
|
22
35
|
/**
|
|
@@ -185,15 +198,83 @@ function getFieldWidthClass(setting) {
|
|
|
185
198
|
// Generic strings get wide width.
|
|
186
199
|
return "field-wide";
|
|
187
200
|
}
|
|
201
|
+
// The marker sentence of each kind, given the running value as the form displays it. Keyed by the kind union, so a kind added later cannot compile until
|
|
202
|
+
// it decides its sentence.
|
|
203
|
+
const PENDING_SENTENCES = {
|
|
204
|
+
restart: (running) => REACTIVITY_BADGES.restart.title + " Running value: " + running + ".",
|
|
205
|
+
unrealized: (running) => "Saved, but not applied yet. Running value: " + running + ". The next save retries it."
|
|
206
|
+
};
|
|
188
207
|
/**
|
|
189
|
-
*
|
|
208
|
+
* Reads a setting's value for a person: the one reading the default line and the pending markers both use, so a value reads the same wherever the form names
|
|
209
|
+
* it. A checkbox list reads as its members joined, or none when it has no member; a missing value reads as autodetect; a number reads in display units, grouped
|
|
210
|
+
* unless the setting is a port, with its unit pluralized by the number; any other value reads as its display text with its unit appended as declared.
|
|
211
|
+
* @param value - The value, in storage units.
|
|
190
212
|
* @param setting - The setting metadata.
|
|
191
|
-
* @
|
|
192
|
-
|
|
193
|
-
|
|
213
|
+
* @returns The value's text with its display unit.
|
|
214
|
+
*/
|
|
215
|
+
function formatSettingValue(value, setting) {
|
|
216
|
+
const displayUnit = getDisplayUnit(setting);
|
|
217
|
+
const displayValue = toDisplayValue(value, setting);
|
|
218
|
+
let text;
|
|
219
|
+
if (setting.type === "checkboxList") {
|
|
220
|
+
const members = Array.isArray(value) ? value : [];
|
|
221
|
+
text = (members.length > 0) ? members.join(", ") : "none";
|
|
222
|
+
}
|
|
223
|
+
else if (displayValue === null) {
|
|
224
|
+
text = "autodetect";
|
|
225
|
+
}
|
|
226
|
+
else if (typeof displayValue === "number") {
|
|
227
|
+
text = formatValueForDisplay(displayValue, setting.type);
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
text = displayValue;
|
|
231
|
+
}
|
|
232
|
+
if (!displayUnit) {
|
|
233
|
+
return text;
|
|
234
|
+
}
|
|
235
|
+
return text + " " + ((typeof displayValue === "number") ? formatUnitForValue(displayValue, displayUnit) : displayUnit);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Derives the pending view of a configuration gap: one entry for each saved setting the running process has not taken, keyed by path in the settings' declared
|
|
239
|
+
* order, each carrying the marker sentence the form shows. The view is derived from the gap on every call and stored nowhere, which is why a save's response
|
|
240
|
+
* carries all of it rather than a change against an earlier view.
|
|
241
|
+
* @param gap - The gap to project, the live gap between the running configuration and the loaded snapshot by default.
|
|
242
|
+
* @returns The pending settings, keyed by path.
|
|
243
|
+
*/
|
|
244
|
+
export function collectPendingSettings(gap = getConfigurationGap()) {
|
|
245
|
+
const buckets = {
|
|
246
|
+
held: { changes: gap.held, kind: "restart" },
|
|
247
|
+
live: { changes: gap.live, kind: "unrealized" },
|
|
248
|
+
nextStream: { changes: gap.nextStream, kind: "unrealized" }
|
|
249
|
+
};
|
|
250
|
+
const filed = new Map();
|
|
251
|
+
for (const bucket of Object.values(buckets)) {
|
|
252
|
+
for (const change of bucket.changes) {
|
|
253
|
+
filed.set(change.path, { change, kind: bucket.kind });
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// Walking the metadata settings rather than looking each gap path up means no lookup can miss, and the view comes out in the order the form renders.
|
|
257
|
+
const pending = new Map();
|
|
258
|
+
for (const setting of Object.values(CONFIG_METADATA).flat()) {
|
|
259
|
+
const entry = filed.get(setting.path);
|
|
260
|
+
if (!entry) {
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
// The gap accessor diffs the running configuration against the loaded snapshot, so a change's previous value is the running one the marker names.
|
|
264
|
+
const running = entry.change.previous;
|
|
265
|
+
pending.set(setting.path, { kind: entry.kind, path: setting.path, text: PENDING_SENTENCES[entry.kind](formatSettingValue(running, setting)) });
|
|
266
|
+
}
|
|
267
|
+
return pending;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Generates HTML for a single setting form field. The setting's validValues and type choose the control: a select dropdown when validValues is non-empty,
|
|
271
|
+
* otherwise a hidden-input-plus-checkbox pair for a boolean, a hidden JSON input with a checkbox grid for a checkboxList, and a text or number input for every
|
|
272
|
+
* other type. The field also carries its pending marker, its default hint and, where they apply, the reset button and the environment override notice.
|
|
273
|
+
* @param options - The setting, the saved value the form shows, its default, its environment override, and its pending entry.
|
|
194
274
|
* @returns HTML string for the form field.
|
|
195
275
|
*/
|
|
196
|
-
function generateSettingField(
|
|
276
|
+
function generateSettingField(options) {
|
|
277
|
+
const { currentValue, defaultValue, envOverride, pending, setting } = options;
|
|
197
278
|
const isDisabled = (envOverride !== undefined) || (setting.disabledReason !== undefined);
|
|
198
279
|
const inputId = setting.path.replaceAll(".", "-");
|
|
199
280
|
const isModified = !isDisabled && !isEqualToDefault(currentValue, defaultValue);
|
|
@@ -205,10 +286,11 @@ function generateSettingField(setting, currentValue, defaultValue, envOverride)
|
|
|
205
286
|
const displayMax = getDisplayMax(setting);
|
|
206
287
|
// Determine if this should be a select dropdown.
|
|
207
288
|
const hasValidValues = setting.validValues && (setting.validValues.length > 0);
|
|
208
|
-
// Check if this setting depends on a boolean toggle
|
|
209
|
-
// disabling the inputs, so values are still submitted during save.
|
|
289
|
+
// Check if this setting depends on a boolean toggle the saved configuration turns off. The depends-disabled class applies a visual grey-out without actually
|
|
290
|
+
// disabling the inputs, so values are still submitted during save. The toggle is read from the saved configuration every field renders from, so the grey-out
|
|
291
|
+
// agrees with the toggle the form shows.
|
|
210
292
|
const dependsOnId = setting.dependsOn ? setting.dependsOn.replaceAll(".", "-") : undefined;
|
|
211
|
-
const isDependencyDisabled = setting.dependsOn ? !getNestedValue(
|
|
293
|
+
const isDependencyDisabled = setting.dependsOn ? !getNestedValue(getLoadedConfiguration(), setting.dependsOn) : false;
|
|
212
294
|
// Build CSS classes for the form group.
|
|
213
295
|
const groupClasses = ["form-group"];
|
|
214
296
|
if (isDisabled) {
|
|
@@ -232,8 +314,13 @@ function generateSettingField(setting, currentValue, defaultValue, envOverride)
|
|
|
232
314
|
lines.push("<span class=\"modified-dot\" title=\"Modified from default\"></span>");
|
|
233
315
|
}
|
|
234
316
|
lines.push(escapeHtml(setting.label));
|
|
317
|
+
// The class badge tells the user when a save of the setting takes effect, before the click. A class whose promise a save already makes carries none.
|
|
318
|
+
const classBadge = REACTIVITY_BADGES[setting.reactivity];
|
|
319
|
+
if (classBadge) {
|
|
320
|
+
lines.push(generateBadge(classBadge.label, { title: classBadge.title, variant: classBadge.variant }));
|
|
321
|
+
}
|
|
235
322
|
if (envOverride !== undefined) {
|
|
236
|
-
lines.push("
|
|
323
|
+
lines.push(generateBadge("ENV", { variant: "env" }));
|
|
237
324
|
}
|
|
238
325
|
lines.push("</label>");
|
|
239
326
|
// Block-level content that must appear outside the form-row flex container. Type branches that produce content too large for the inline flex layout (grids,
|
|
@@ -277,8 +364,8 @@ function generateSettingField(setting, currentValue, defaultValue, envOverride)
|
|
|
277
364
|
lines.push("</select>");
|
|
278
365
|
}
|
|
279
366
|
else if (setting.type === "boolean") {
|
|
280
|
-
// Render boolean as a checkbox. A hidden input with value "false" precedes the checkbox so that unchecking submits "false"
|
|
281
|
-
//
|
|
367
|
+
// Render boolean as a checkbox. A hidden input with value "false" precedes the checkbox so that unchecking submits "false". Without it an unchecked box
|
|
368
|
+
// submits nothing, the save handler skips the path, and the merge keeps the saved true, so the uncheck would be lost.
|
|
282
369
|
const isChecked = (currentValue === true) || (currentValue === "true");
|
|
283
370
|
const defaultStr = defaultValue ? "true" : "false";
|
|
284
371
|
lines.push("<input type=\"hidden\" name=\"" + setting.path + "\" value=\"false\">");
|
|
@@ -335,9 +422,10 @@ function generateSettingField(setting, currentValue, defaultValue, envOverride)
|
|
|
335
422
|
else {
|
|
336
423
|
// Render as input field.
|
|
337
424
|
const inputType = (setting.type === "float") ? "number" : (((setting.type === "integer") || (setting.type === "port")) ? "number" : "text");
|
|
338
|
-
// Calculate step for arrow key increments.
|
|
339
|
-
// step (e.g., 500ms -> 0.5s step); otherwise step is 1 whole display unit.
|
|
340
|
-
//
|
|
425
|
+
// Calculate step for arrow key increments. With a displayDivisor and a min, the step is auto-derived: when the min in display units is between 0 and 1
|
|
426
|
+
// (exclusive), use it as the step (e.g., 500ms -> 0.5s step); otherwise the step is 1 whole display unit. A float with no displayDivisor steps by 0.01 so
|
|
427
|
+
// two-decimal values stay reachable, and every other number steps by one display unit. This gives meaningful arrow increments and constrains input to a
|
|
428
|
+
// sensible value grid (e.g., 0.5, 1.0, 1.5, ... for half-second steps).
|
|
341
429
|
let step = "1";
|
|
342
430
|
if (setting.displayDivisor && (setting.min !== undefined)) {
|
|
343
431
|
const displayMin = setting.min / setting.displayDivisor;
|
|
@@ -393,35 +481,16 @@ function generateSettingField(setting, currentValue, defaultValue, envOverride)
|
|
|
393
481
|
for (const content of postDescription) {
|
|
394
482
|
lines.push(content);
|
|
395
483
|
}
|
|
484
|
+
// Every field carries one marker slot, holding the server's sentence when the saved value is not the running one and hidden otherwise, so a save's response
|
|
485
|
+
// fills or clears it as text without creating markup.
|
|
486
|
+
lines.push("<div" + serializeAttrs({ "class": "form-pending" }) + serializeAttrs({ [PENDING_PATH_ATTRIBUTE]: setting.path, hidden: pending === undefined }) +
|
|
487
|
+
">" + escapeHtml(pending?.text ?? "") + "</div>");
|
|
396
488
|
// Add disabled reason warning when a setting is locked out due to an upstream issue.
|
|
397
489
|
if (setting.disabledReason) {
|
|
398
490
|
lines.push("<div class=\"form-warning\">" + escapeHtml(setting.disabledReason) + "</div>");
|
|
399
491
|
}
|
|
400
|
-
// Add default value hint
|
|
401
|
-
|
|
402
|
-
if (setting.type === "checkboxList") {
|
|
403
|
-
// For checkbox lists, show the default items as a comma-separated list, or "none" for empty arrays.
|
|
404
|
-
const defaultArr = Array.isArray(defaultValue) ? defaultValue : [];
|
|
405
|
-
defaultDisplay = defaultArr.length > 0 ? defaultArr.join(", ") : "none";
|
|
406
|
-
}
|
|
407
|
-
else if (displayDefault === null) {
|
|
408
|
-
defaultDisplay = "autodetect";
|
|
409
|
-
}
|
|
410
|
-
else if (typeof displayDefault === "number") {
|
|
411
|
-
defaultDisplay = formatValueForDisplay(displayDefault, setting.type);
|
|
412
|
-
}
|
|
413
|
-
else {
|
|
414
|
-
defaultDisplay = displayDefault;
|
|
415
|
-
}
|
|
416
|
-
// Format the unit with correct pluralization based on the default value.
|
|
417
|
-
let formattedUnit = "";
|
|
418
|
-
if (displayUnit && (typeof displayDefault === "number")) {
|
|
419
|
-
formattedUnit = " " + formatUnitForValue(displayDefault, displayUnit);
|
|
420
|
-
}
|
|
421
|
-
else if (displayUnit) {
|
|
422
|
-
formattedUnit = " " + displayUnit;
|
|
423
|
-
}
|
|
424
|
-
lines.push("<div class=\"form-default\">Default: " + escapeHtml(defaultDisplay) + formattedUnit + "</div>");
|
|
492
|
+
// Add the default value hint, read as the pending markers read a value.
|
|
493
|
+
lines.push("<div class=\"form-default\">Default: " + escapeHtml(formatSettingValue(defaultValue, setting)) + "</div>");
|
|
425
494
|
// Add env var override notice if applicable.
|
|
426
495
|
if (isDisabled && setting.envVar && envOverride) {
|
|
427
496
|
lines.push("<div class=\"form-env\">Overridden by environment variable: <code>" + escapeHtml(setting.envVar) + "=" +
|
|
@@ -449,7 +518,7 @@ function validateSettingValue(setting, value) {
|
|
|
449
518
|
// Validate string type with validValues.
|
|
450
519
|
if ((setting.type === "string") && setting.validValues && (setting.validValues.length > 0)) {
|
|
451
520
|
if (!setting.validValues.includes(value)) {
|
|
452
|
-
return setting.label + " must be one of: " + setting.validValues.join(", ");
|
|
521
|
+
return setting.label + " must be one of: " + setting.validValues.join(", ") + ".";
|
|
453
522
|
}
|
|
454
523
|
return undefined;
|
|
455
524
|
}
|
|
@@ -457,7 +526,7 @@ function validateSettingValue(setting, value) {
|
|
|
457
526
|
switch (setting.type) {
|
|
458
527
|
case "boolean": {
|
|
459
528
|
if (typeof value !== "boolean") {
|
|
460
|
-
return setting.label + " must be true or false";
|
|
529
|
+
return setting.label + " must be true or false.";
|
|
461
530
|
}
|
|
462
531
|
return undefined;
|
|
463
532
|
}
|
|
@@ -478,35 +547,35 @@ function validateSettingValue(setting, value) {
|
|
|
478
547
|
case "integer":
|
|
479
548
|
case "port": {
|
|
480
549
|
if (typeof value !== "number") {
|
|
481
|
-
return setting.label + " must be a number";
|
|
550
|
+
return setting.label + " must be a number.";
|
|
482
551
|
}
|
|
483
|
-
const error =
|
|
552
|
+
const error = validateInteger(setting.label, value, setting.min, setting.max);
|
|
484
553
|
return error ?? undefined;
|
|
485
554
|
}
|
|
486
555
|
case "float": {
|
|
487
556
|
if (typeof value !== "number") {
|
|
488
|
-
return setting.label + " must be a number";
|
|
557
|
+
return setting.label + " must be a number.";
|
|
489
558
|
}
|
|
490
|
-
const error =
|
|
559
|
+
const error = validateNumber(setting.label, value, setting.min, setting.max);
|
|
491
560
|
return error ?? undefined;
|
|
492
561
|
}
|
|
493
562
|
case "host": {
|
|
494
563
|
if ((typeof value !== "string") || (value.trim() === "")) {
|
|
495
|
-
return setting.label + " must be a non-empty string";
|
|
564
|
+
return setting.label + " must be a non-empty string.";
|
|
496
565
|
}
|
|
497
566
|
return undefined;
|
|
498
567
|
}
|
|
499
568
|
case "path": {
|
|
500
569
|
// A path is any string. The empty and null forms mean autodetect and were already accepted by the guard at the top.
|
|
501
570
|
if (typeof value !== "string") {
|
|
502
|
-
return setting.label + " must be a string";
|
|
571
|
+
return setting.label + " must be a string.";
|
|
503
572
|
}
|
|
504
573
|
return undefined;
|
|
505
574
|
}
|
|
506
575
|
case "string": {
|
|
507
576
|
// A free-form string carries no value constraint beyond being one. Settings that restrict their values declare validValues and were handled above.
|
|
508
577
|
if (typeof value !== "string") {
|
|
509
|
-
return setting.label + " must be a string";
|
|
578
|
+
return setting.label + " must be a string.";
|
|
510
579
|
}
|
|
511
580
|
return undefined;
|
|
512
581
|
}
|
|
@@ -568,16 +637,34 @@ function parseFormValue(setting, value) {
|
|
|
568
637
|
}
|
|
569
638
|
}
|
|
570
639
|
}
|
|
640
|
+
/**
|
|
641
|
+
* Generates the fields of a run of settings, the one per-field loop the Settings tab and every collapsible section share.
|
|
642
|
+
* @param settings - The settings to render, in order.
|
|
643
|
+
* @param context - The environment overrides and the pending view the page render resolved once.
|
|
644
|
+
* @returns One HTML string per field.
|
|
645
|
+
*/
|
|
646
|
+
function generateSettingFields(settings, context) {
|
|
647
|
+
const defaults = getDefaults();
|
|
648
|
+
// The form shows the saved configuration, the loaded snapshot, rather than the running one. After a save that holds a change for a restart, a page load shows
|
|
649
|
+
// that change, so submitting the page cannot post the running value back over it.
|
|
650
|
+
const saved = getLoadedConfiguration();
|
|
651
|
+
return settings.map((setting) => generateSettingField({
|
|
652
|
+
currentValue: getNestedValue(saved, setting.path),
|
|
653
|
+
defaultValue: getNestedValue(defaults, setting.path),
|
|
654
|
+
envOverride: context.envOverrides.get(setting.path),
|
|
655
|
+
pending: context.pending.get(setting.path),
|
|
656
|
+
setting
|
|
657
|
+
}));
|
|
658
|
+
}
|
|
571
659
|
/**
|
|
572
660
|
* Generates the content for the Settings tab with non-collapsible section headers.
|
|
573
|
-
* @param
|
|
661
|
+
* @param context - The environment overrides and the pending view the page render resolved once and shares with every section it draws.
|
|
574
662
|
* @returns HTML string for the Settings tab content.
|
|
575
663
|
*/
|
|
576
|
-
export function generateSettingsTabContent(
|
|
664
|
+
export function generateSettingsTabContent(context) {
|
|
577
665
|
const sections = getSettingsTabSections();
|
|
578
666
|
const tabs = getUITabs();
|
|
579
667
|
const settingsTab = tabs.find((t) => t.id === "settings");
|
|
580
|
-
const defaults = getDefaults();
|
|
581
668
|
const lines = [];
|
|
582
669
|
// Panel header with description and reset button.
|
|
583
670
|
lines.push("<div class=\"panel-header\">");
|
|
@@ -590,12 +677,7 @@ export function generateSettingsTabContent(envOverrides) {
|
|
|
590
677
|
lines.push("<div class=\"settings-section\">");
|
|
591
678
|
lines.push("<div class=\"settings-section-header\">" + escapeHtml(section.displayName) + "</div>");
|
|
592
679
|
// Generate setting fields for this section.
|
|
593
|
-
|
|
594
|
-
const currentValue = getNestedValue(CONFIG, setting.path);
|
|
595
|
-
const defaultValue = getNestedValue(defaults, setting.path);
|
|
596
|
-
const envOverride = envOverrides.get(setting.path);
|
|
597
|
-
lines.push(generateSettingField(setting, currentValue, defaultValue, envOverride));
|
|
598
|
-
}
|
|
680
|
+
lines.push(...generateSettingFields(section.settings, context));
|
|
599
681
|
lines.push("</div>");
|
|
600
682
|
}
|
|
601
683
|
return lines.join("\n");
|
|
@@ -603,30 +685,28 @@ export function generateSettingsTabContent(envOverrides) {
|
|
|
603
685
|
/**
|
|
604
686
|
* Generates the content for a collapsible section within the Advanced tab.
|
|
605
687
|
* @param section - The section definition.
|
|
606
|
-
* @param
|
|
688
|
+
* @param context - The environment overrides and the pending view the page render resolved once and shares with every section it draws.
|
|
607
689
|
* @returns HTML string for the section.
|
|
608
690
|
*/
|
|
609
|
-
export function generateCollapsibleSection(section,
|
|
610
|
-
const defaults = getDefaults();
|
|
691
|
+
export function generateCollapsibleSection(section, context) {
|
|
611
692
|
const lines = [];
|
|
612
|
-
|
|
693
|
+
/* A section holding a pending setting is opened on the server, so a pending marker is never hidden behind a collapsed header, and nothing on the client has
|
|
694
|
+
* to remember it. The client's stored section state only ever adds the expanded class, so a section opened here stays open on load.
|
|
695
|
+
*/
|
|
696
|
+
const expanded = section.settings.some((setting) => context.pending.has(setting.path)) ? " expanded" : "";
|
|
613
697
|
// Section container.
|
|
614
698
|
lines.push("<div class=\"advanced-section\" data-section=\"" + escapeHtml(section.id) + "\">");
|
|
615
699
|
// Section header with chevron, title, and count.
|
|
616
|
-
lines.push("<div class=\"section-header\" data-click-action=\"" + ACTIONS.toggleSection + "\" data-section-id=\"" + escapeHtml(section.id) +
|
|
700
|
+
lines.push("<div class=\"section-header" + expanded + "\" data-click-action=\"" + ACTIONS.toggleSection + "\" data-section-id=\"" + escapeHtml(section.id) +
|
|
701
|
+
"\">");
|
|
617
702
|
lines.push("<span class=\"section-chevron\">▶</span>");
|
|
618
703
|
lines.push("<span class=\"section-title\">" + escapeHtml(section.displayName) + "</span>");
|
|
619
|
-
lines.push("<span class=\"section-count\">(" +
|
|
704
|
+
lines.push("<span class=\"section-count\">(" + formatSettingCount(section.settings.length) + ")</span>");
|
|
620
705
|
lines.push("</div>");
|
|
621
|
-
// Section content
|
|
622
|
-
lines.push("<div class=\"section-content\">");
|
|
706
|
+
// Section content, collapsed unless the section holds a pending setting.
|
|
707
|
+
lines.push("<div class=\"section-content" + expanded + "\">");
|
|
623
708
|
// Generate setting fields for this section.
|
|
624
|
-
|
|
625
|
-
const currentValue = getNestedValue(CONFIG, setting.path);
|
|
626
|
-
const defaultValue = getNestedValue(defaults, setting.path);
|
|
627
|
-
const envOverride = envOverrides.get(setting.path);
|
|
628
|
-
lines.push(generateSettingField(setting, currentValue, defaultValue, envOverride));
|
|
629
|
-
}
|
|
709
|
+
lines.push(...generateSettingFields(section.settings, context));
|
|
630
710
|
// Close section-content, then advanced-section, in reverse order of the opens above.
|
|
631
711
|
lines.push("</div>");
|
|
632
712
|
lines.push("</div>");
|
|
@@ -634,10 +714,10 @@ export function generateCollapsibleSection(section, envOverrides) {
|
|
|
634
714
|
}
|
|
635
715
|
/**
|
|
636
716
|
* Generates the content for the Advanced tab with collapsible sections.
|
|
637
|
-
* @param
|
|
717
|
+
* @param context - The environment overrides and the pending view the page render resolved once and shares with every section it draws.
|
|
638
718
|
* @returns HTML string for the Advanced tab content.
|
|
639
719
|
*/
|
|
640
|
-
export function generateAdvancedTabContent(
|
|
720
|
+
export function generateAdvancedTabContent(context) {
|
|
641
721
|
const sections = getAdvancedSections();
|
|
642
722
|
const tabs = getUITabs();
|
|
643
723
|
const advancedTab = tabs.find((t) => t.id === "advanced");
|
|
@@ -650,7 +730,7 @@ export function generateAdvancedTabContent(envOverrides) {
|
|
|
650
730
|
lines.push("</div>");
|
|
651
731
|
// Generate each collapsible section.
|
|
652
732
|
for (const section of sections) {
|
|
653
|
-
lines.push(generateCollapsibleSection(section,
|
|
733
|
+
lines.push(generateCollapsibleSection(section, context));
|
|
654
734
|
}
|
|
655
735
|
return lines.join("\n");
|
|
656
736
|
}
|
|
@@ -661,17 +741,37 @@ export function generateAdvancedTabContent(envOverrides) {
|
|
|
661
741
|
export function generateSettingsFormFooter() {
|
|
662
742
|
return "<div class=\"config-path\">Configuration file: <code>" + escapeHtml(getConfigFilePath()) + "</code></div>";
|
|
663
743
|
}
|
|
744
|
+
/**
|
|
745
|
+
* The keys that name an object's prototype machinery rather than a setting. A document parsed from JSON carries any of them as an ordinary key, and assigning
|
|
746
|
+
* through one reaches a prototype instead of the configuration, so the merge refuses each of them at every level it walks.
|
|
747
|
+
*/
|
|
748
|
+
const PROTOTYPE_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
|
749
|
+
/**
|
|
750
|
+
* Refuses a document key that would reach an object's prototype rather than name a setting. The merge runs inside the save's store mutation, so the refusal
|
|
751
|
+
* writes nothing, and the route answers it as the validation error it is.
|
|
752
|
+
* @param key - The key the merge is about to assign through.
|
|
753
|
+
* @throws ConfigurationRejectedError when the key names prototype machinery.
|
|
754
|
+
*/
|
|
755
|
+
function refusePrototypeKey(key) {
|
|
756
|
+
if (PROTOTYPE_KEYS.has(key)) {
|
|
757
|
+
throw new ConfigurationRejectedError("The configuration document carries the key " + key + ", which is not a setting.");
|
|
758
|
+
}
|
|
759
|
+
}
|
|
664
760
|
/**
|
|
665
761
|
* Shallow-merges a source config into a target config. Top-level object values are merged one level deep (preserving sibling keys within each category).
|
|
666
|
-
* Non-object values are assigned directly. This is the single merge strategy
|
|
762
|
+
* Non-object values are assigned directly. This is the single merge strategy the settings form save and the config import share. A key that names prototype
|
|
763
|
+
* machinery, at any level the merge walks, is refused before anything is assigned through it.
|
|
667
764
|
* @param target - The existing config to merge into (modified in place).
|
|
668
765
|
* @param source - The new values to merge.
|
|
766
|
+
* @throws ConfigurationRejectedError when the source carries a key that is not a setting.
|
|
669
767
|
*/
|
|
670
768
|
function mergeConfigValues(target, source) {
|
|
671
769
|
for (const [path, value] of Object.entries(source)) {
|
|
770
|
+
refusePrototypeKey(path);
|
|
672
771
|
if ((typeof value === "object") && (value !== null) && !Array.isArray(value)) {
|
|
673
772
|
target[path] ??= {};
|
|
674
773
|
for (const [subPath, subValue] of Object.entries(value)) {
|
|
774
|
+
refusePrototypeKey(subPath);
|
|
675
775
|
target[path][subPath] = subValue;
|
|
676
776
|
}
|
|
677
777
|
}
|
|
@@ -680,6 +780,31 @@ function mergeConfigValues(target, source) {
|
|
|
680
780
|
}
|
|
681
781
|
}
|
|
682
782
|
}
|
|
783
|
+
/**
|
|
784
|
+
* Builds the data envelope of a save response from the save's outcome. The settings save and the import share it, so each answers with the same fields: the
|
|
785
|
+
* restart members the API reference documents, the outcome by setting under changes, and the pending view under pending. changes.applied and
|
|
786
|
+
* changes.nextStream are everything this save's reconcile realized, a value an earlier save introduced and this save's retry realized included, while
|
|
787
|
+
* changes.deferred and changes.rejected are this save's own. pending is the whole view after the save, read here once the save has resolved, and the client
|
|
788
|
+
* replaces its markers from it rather than merging them. Exported so a unit row holds the mapping of every bucket, the next-stream bucket included, which a
|
|
789
|
+
* route reaches only through a setting of that class.
|
|
790
|
+
* @param outcome - The save's apply and restart result.
|
|
791
|
+
* @returns The response data.
|
|
792
|
+
*/
|
|
793
|
+
export function buildSaveResponseData(outcome) {
|
|
794
|
+
const paths = (changes) => changes.map((change) => change.path);
|
|
795
|
+
return {
|
|
796
|
+
activeStreams: outcome.restart?.activeStreams ?? 0,
|
|
797
|
+
changes: {
|
|
798
|
+
applied: paths(outcome.apply.applied),
|
|
799
|
+
deferred: paths(outcome.apply.deferred),
|
|
800
|
+
nextStream: paths(outcome.apply.nextStream),
|
|
801
|
+
rejected: outcome.apply.rejected.map(({ change, reason }) => ({ path: change.path, reason }))
|
|
802
|
+
},
|
|
803
|
+
deferred: outcome.restart?.deferred ?? false,
|
|
804
|
+
pending: Array.from(collectPendingSettings().values()),
|
|
805
|
+
willRestart: outcome.restart?.willRestart ?? false
|
|
806
|
+
};
|
|
807
|
+
}
|
|
683
808
|
/**
|
|
684
809
|
* Installs all settings-related route handlers on the Express application.
|
|
685
810
|
* @param app - The Express application.
|
|
@@ -725,28 +850,15 @@ export function setupSettingsRoutes(app) {
|
|
|
725
850
|
sendFormErrors(res, validationErrors);
|
|
726
851
|
return;
|
|
727
852
|
}
|
|
728
|
-
// Merge form values into the existing config
|
|
729
|
-
// managed by separate endpoints (disabledPredefined, enabledServices, visibleColumns, setupCompleted, channelSortField,
|
|
730
|
-
// channelsDvr.host, hdhr.deviceId, etc.). Merging form values into the existing config preserves all non-form fields
|
|
731
|
-
// list to maintain.
|
|
732
|
-
|
|
853
|
+
// Merge form values into the existing config through the validated save. The settings form only manages CONFIG_METADATA fields, but config.json also
|
|
854
|
+
// stores fields managed by separate endpoints (disabledPredefined, enabledServices, visibleColumns, setupCompleted, channelSortField,
|
|
855
|
+
// channelSortDirection, channelsDvr.host, hdhr.deviceId, etc.). Merging form values into the existing config preserves all non-form fields
|
|
856
|
+
// automatically - no carry-forward list to maintain. The save then reconciles the running configuration against the file: each change takes effect as
|
|
857
|
+
// its reactivity class allows, and a restart is scheduled only when this save holds a restart-class change for one.
|
|
858
|
+
const outcome = await applyConfigurationChange("to apply configuration changes", (existing) => {
|
|
733
859
|
mergeConfigValues(existing, newConfig);
|
|
734
860
|
});
|
|
735
|
-
|
|
736
|
-
// live application (HDHR is the first) make their changes immediately; everything else is reported as deferred and triggers a service restart so the
|
|
737
|
-
// change lands on the next boot.
|
|
738
|
-
const outcome = await applyConfigurationChange("to apply configuration changes");
|
|
739
|
-
sendSuccess(res, {
|
|
740
|
-
data: {
|
|
741
|
-
activeStreams: outcome.restart?.activeStreams ?? 0,
|
|
742
|
-
appliedCount: outcome.apply.applied.length,
|
|
743
|
-
deferred: outcome.restart?.deferred ?? false,
|
|
744
|
-
deferredCount: outcome.apply.deferred.length,
|
|
745
|
-
rejectedCount: outcome.apply.rejected.length,
|
|
746
|
-
willRestart: outcome.restart?.willRestart ?? false
|
|
747
|
-
},
|
|
748
|
-
message: describeConfigurationOutcome(outcome)
|
|
749
|
-
});
|
|
861
|
+
sendSuccess(res, { data: buildSaveResponseData(outcome), message: describeConfigurationOutcome(outcome) });
|
|
750
862
|
}
|
|
751
863
|
catch (error) {
|
|
752
864
|
sendErrorResponse(res, error, "save configuration");
|
|
@@ -814,11 +926,13 @@ export function setupSettingsRoutes(app) {
|
|
|
814
926
|
sendValidationError(res, "Validation errors:\n" + validationErrors.join("\n"));
|
|
815
927
|
return;
|
|
816
928
|
}
|
|
817
|
-
// Import replaces the user settings layer
|
|
818
|
-
//
|
|
819
|
-
//
|
|
820
|
-
//
|
|
821
|
-
|
|
929
|
+
// Import replaces the user settings layer. CONFIG_METADATA is the SSOT for which fields are user settings (port, timeouts, quality preset, etc.) vs
|
|
930
|
+
// system state (channelsDvr.host, deviceId, disabledPredefined, enabledServices, etc.). Clearing all CONFIG_METADATA-tracked paths before merging
|
|
931
|
+
// ensures that settings not present in the import file revert to defaults rather than surviving from the previous config. The merge then copies every
|
|
932
|
+
// key the document carries, so a system-state field missing from the document keeps its current value, while one present in the document - as in a
|
|
933
|
+
// file written by the export, which carries the whole configuration - is merged over the current one. The save validates the result before anything
|
|
934
|
+
// reaches disk and reconciles the running configuration against it, the same path every write to the settings takes.
|
|
935
|
+
const outcome = await applyConfigurationChange("after configuration import", (existing) => {
|
|
822
936
|
// Clear all user settings from the existing config, leaving only system state.
|
|
823
937
|
for (const settings of Object.values(CONFIG_METADATA)) {
|
|
824
938
|
for (const setting of settings) {
|
|
@@ -835,20 +949,7 @@ export function setupSettingsRoutes(app) {
|
|
|
835
949
|
// Apply imported settings on top of the preserved system state.
|
|
836
950
|
mergeConfigValues(existing, importedConfig);
|
|
837
951
|
});
|
|
838
|
-
|
|
839
|
-
// "restart required" path - but the same live-apply machinery still picks off any subsystem that opted in.
|
|
840
|
-
const outcome = await applyConfigurationChange("after configuration import");
|
|
841
|
-
sendSuccess(res, {
|
|
842
|
-
data: {
|
|
843
|
-
activeStreams: outcome.restart?.activeStreams ?? 0,
|
|
844
|
-
appliedCount: outcome.apply.applied.length,
|
|
845
|
-
deferred: outcome.restart?.deferred ?? false,
|
|
846
|
-
deferredCount: outcome.apply.deferred.length,
|
|
847
|
-
rejectedCount: outcome.apply.rejected.length,
|
|
848
|
-
willRestart: outcome.restart?.willRestart ?? false
|
|
849
|
-
},
|
|
850
|
-
message: describeConfigurationOutcome(outcome)
|
|
851
|
-
});
|
|
952
|
+
sendSuccess(res, { data: buildSaveResponseData(outcome), message: describeConfigurationOutcome(outcome) });
|
|
852
953
|
}
|
|
853
954
|
catch (error) {
|
|
854
955
|
sendErrorResponse(res, error, "import configuration");
|
|
@@ -864,7 +965,7 @@ export function setupSettingsRoutes(app) {
|
|
|
864
965
|
sendSuccess(res, { message: "Server is restarting..." });
|
|
865
966
|
// Close the browser first to avoid orphan Chrome processes. The delay gives the success response above time to
|
|
866
967
|
// reach the client before the process exits.
|
|
867
|
-
|
|
968
|
+
systemClock.schedule(() => {
|
|
868
969
|
LOG.info("Exiting for forced service manager restart.");
|
|
869
970
|
void import("../../browser/index.js").then(async (mod) => mod.closeBrowser()).then(() => { process.exit(0); }).catch(() => { process.exit(1); });
|
|
870
971
|
}, 500);
|