@bnhf/prismcast 1.3.4-2026.2.19
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/LICENSE.md +7 -0
- package/README.md +347 -0
- package/bin/prismcast +6 -0
- package/dist/app.d.ts +6 -0
- package/dist/app.js +315 -0
- package/dist/app.js.map +1 -0
- package/dist/browser/cdp.d.ts +38 -0
- package/dist/browser/cdp.js +155 -0
- package/dist/browser/cdp.js.map +1 -0
- package/dist/browser/channelSelection.d.ts +65 -0
- package/dist/browser/channelSelection.js +202 -0
- package/dist/browser/channelSelection.js.map +1 -0
- package/dist/browser/display.d.ts +34 -0
- package/dist/browser/display.js +54 -0
- package/dist/browser/display.js.map +1 -0
- package/dist/browser/index.d.ts +205 -0
- package/dist/browser/index.js +1205 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/tuning/fox.d.ts +2 -0
- package/dist/browser/tuning/fox.js +83 -0
- package/dist/browser/tuning/fox.js.map +1 -0
- package/dist/browser/tuning/hbo.d.ts +2 -0
- package/dist/browser/tuning/hbo.js +237 -0
- package/dist/browser/tuning/hbo.js.map +1 -0
- package/dist/browser/tuning/hulu.d.ts +2 -0
- package/dist/browser/tuning/hulu.js +550 -0
- package/dist/browser/tuning/hulu.js.map +1 -0
- package/dist/browser/tuning/sling.d.ts +2 -0
- package/dist/browser/tuning/sling.js +518 -0
- package/dist/browser/tuning/sling.js.map +1 -0
- package/dist/browser/tuning/thumbnailRow.d.ts +2 -0
- package/dist/browser/tuning/thumbnailRow.js +108 -0
- package/dist/browser/tuning/thumbnailRow.js.map +1 -0
- package/dist/browser/tuning/tileClick.d.ts +2 -0
- package/dist/browser/tuning/tileClick.js +103 -0
- package/dist/browser/tuning/tileClick.js.map +1 -0
- package/dist/browser/tuning/youtubeTv.d.ts +2 -0
- package/dist/browser/tuning/youtubeTv.js +182 -0
- package/dist/browser/tuning/youtubeTv.js.map +1 -0
- package/dist/browser/video.d.ts +289 -0
- package/dist/browser/video.js +996 -0
- package/dist/browser/video.js.map +1 -0
- package/dist/channels/index.d.ts +3 -0
- package/dist/channels/index.js +392 -0
- package/dist/channels/index.js.map +1 -0
- package/dist/config/index.d.ts +53 -0
- package/dist/config/index.js +233 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/presets.d.ts +98 -0
- package/dist/config/presets.js +241 -0
- package/dist/config/presets.js.map +1 -0
- package/dist/config/profiles.d.ts +79 -0
- package/dist/config/profiles.js +245 -0
- package/dist/config/profiles.js.map +1 -0
- package/dist/config/providers.d.ts +120 -0
- package/dist/config/providers.js +450 -0
- package/dist/config/providers.js.map +1 -0
- package/dist/config/sites.d.ts +22 -0
- package/dist/config/sites.js +377 -0
- package/dist/config/sites.js.map +1 -0
- package/dist/config/userChannels.d.ts +178 -0
- package/dist/config/userChannels.js +543 -0
- package/dist/config/userChannels.js.map +1 -0
- package/dist/config/userConfig.d.ts +235 -0
- package/dist/config/userConfig.js +913 -0
- package/dist/config/userConfig.js.map +1 -0
- package/dist/hdhr/channelMap.d.ts +21 -0
- package/dist/hdhr/channelMap.js +82 -0
- package/dist/hdhr/channelMap.js.map +1 -0
- package/dist/hdhr/deviceId.d.ts +11 -0
- package/dist/hdhr/deviceId.js +84 -0
- package/dist/hdhr/deviceId.js.map +1 -0
- package/dist/hdhr/discover.d.ts +6 -0
- package/dist/hdhr/discover.js +155 -0
- package/dist/hdhr/discover.js.map +1 -0
- package/dist/hdhr/index.d.ts +9 -0
- package/dist/hdhr/index.js +87 -0
- package/dist/hdhr/index.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +144 -0
- package/dist/index.js.map +1 -0
- package/dist/routes/assets.d.ts +6 -0
- package/dist/routes/assets.js +79 -0
- package/dist/routes/assets.js.map +1 -0
- package/dist/routes/auth.d.ts +6 -0
- package/dist/routes/auth.js +77 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/channels.d.ts +6 -0
- package/dist/routes/channels.js +40 -0
- package/dist/routes/channels.js.map +1 -0
- package/dist/routes/components.d.ts +138 -0
- package/dist/routes/components.js +210 -0
- package/dist/routes/components.js.map +1 -0
- package/dist/routes/config.d.ts +72 -0
- package/dist/routes/config.js +1977 -0
- package/dist/routes/config.js.map +1 -0
- package/dist/routes/debug.d.ts +6 -0
- package/dist/routes/debug.js +274 -0
- package/dist/routes/debug.js.map +1 -0
- package/dist/routes/health.d.ts +6 -0
- package/dist/routes/health.js +85 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/hls.d.ts +6 -0
- package/dist/routes/hls.js +25 -0
- package/dist/routes/hls.js.map +1 -0
- package/dist/routes/index.d.ts +19 -0
- package/dist/routes/index.js +49 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/logs.d.ts +6 -0
- package/dist/routes/logs.js +164 -0
- package/dist/routes/logs.js.map +1 -0
- package/dist/routes/mpegts.d.ts +6 -0
- package/dist/routes/mpegts.js +19 -0
- package/dist/routes/mpegts.js.map +1 -0
- package/dist/routes/play.d.ts +6 -0
- package/dist/routes/play.js +18 -0
- package/dist/routes/play.js.map +1 -0
- package/dist/routes/playlist.d.ts +36 -0
- package/dist/routes/playlist.js +134 -0
- package/dist/routes/playlist.js.map +1 -0
- package/dist/routes/root.d.ts +6 -0
- package/dist/routes/root.js +2920 -0
- package/dist/routes/root.js.map +1 -0
- package/dist/routes/streams.d.ts +6 -0
- package/dist/routes/streams.js +88 -0
- package/dist/routes/streams.js.map +1 -0
- package/dist/routes/theme.d.ts +15 -0
- package/dist/routes/theme.js +275 -0
- package/dist/routes/theme.js.map +1 -0
- package/dist/routes/ui.d.ts +56 -0
- package/dist/routes/ui.js +354 -0
- package/dist/routes/ui.js.map +1 -0
- package/dist/service/commands.d.ts +41 -0
- package/dist/service/commands.js +391 -0
- package/dist/service/commands.js.map +1 -0
- package/dist/service/generators.d.ts +33 -0
- package/dist/service/generators.js +432 -0
- package/dist/service/generators.js.map +1 -0
- package/dist/service/index.d.ts +2 -0
- package/dist/service/index.js +7 -0
- package/dist/service/index.js.map +1 -0
- package/dist/streaming/clients.d.ts +48 -0
- package/dist/streaming/clients.js +114 -0
- package/dist/streaming/clients.js.map +1 -0
- package/dist/streaming/fmp4Segmenter.d.ts +61 -0
- package/dist/streaming/fmp4Segmenter.js +461 -0
- package/dist/streaming/fmp4Segmenter.js.map +1 -0
- package/dist/streaming/hls.d.ts +120 -0
- package/dist/streaming/hls.js +722 -0
- package/dist/streaming/hls.js.map +1 -0
- package/dist/streaming/hlsSegments.d.ts +54 -0
- package/dist/streaming/hlsSegments.js +162 -0
- package/dist/streaming/hlsSegments.js.map +1 -0
- package/dist/streaming/lifecycle.d.ts +33 -0
- package/dist/streaming/lifecycle.js +185 -0
- package/dist/streaming/lifecycle.js.map +1 -0
- package/dist/streaming/monitor.d.ts +74 -0
- package/dist/streaming/monitor.js +1310 -0
- package/dist/streaming/monitor.js.map +1 -0
- package/dist/streaming/mp4Parser.d.ts +74 -0
- package/dist/streaming/mp4Parser.js +566 -0
- package/dist/streaming/mp4Parser.js.map +1 -0
- package/dist/streaming/mpegts.d.ts +14 -0
- package/dist/streaming/mpegts.js +248 -0
- package/dist/streaming/mpegts.js.map +1 -0
- package/dist/streaming/registry.d.ts +119 -0
- package/dist/streaming/registry.js +127 -0
- package/dist/streaming/registry.js.map +1 -0
- package/dist/streaming/setup.d.ts +135 -0
- package/dist/streaming/setup.js +670 -0
- package/dist/streaming/setup.js.map +1 -0
- package/dist/streaming/showInfo.d.ts +30 -0
- package/dist/streaming/showInfo.js +362 -0
- package/dist/streaming/showInfo.js.map +1 -0
- package/dist/streaming/statusEmitter.d.ts +125 -0
- package/dist/streaming/statusEmitter.js +139 -0
- package/dist/streaming/statusEmitter.js.map +1 -0
- package/dist/types/index.d.ts +403 -0
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/debugFilter.d.ts +38 -0
- package/dist/utils/debugFilter.js +157 -0
- package/dist/utils/debugFilter.js.map +1 -0
- package/dist/utils/delay.d.ts +6 -0
- package/dist/utils/delay.js +15 -0
- package/dist/utils/delay.js.map +1 -0
- package/dist/utils/errors.d.ts +15 -0
- package/dist/utils/errors.js +40 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/evaluate.d.ts +51 -0
- package/dist/utils/evaluate.js +124 -0
- package/dist/utils/evaluate.js.map +1 -0
- package/dist/utils/ffmpeg.d.ts +65 -0
- package/dist/utils/ffmpeg.js +317 -0
- package/dist/utils/ffmpeg.js.map +1 -0
- package/dist/utils/fileLogger.d.ts +25 -0
- package/dist/utils/fileLogger.js +248 -0
- package/dist/utils/fileLogger.js.map +1 -0
- package/dist/utils/format.d.ts +16 -0
- package/dist/utils/format.js +46 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/html.d.ts +6 -0
- package/dist/utils/html.js +24 -0
- package/dist/utils/html.js.map +1 -0
- package/dist/utils/index.d.ts +15 -0
- package/dist/utils/index.js +20 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logEmitter.d.ts +17 -0
- package/dist/utils/logEmitter.js +30 -0
- package/dist/utils/logEmitter.js.map +1 -0
- package/dist/utils/logger.d.ts +82 -0
- package/dist/utils/logger.js +219 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/m3u.d.ts +32 -0
- package/dist/utils/m3u.js +148 -0
- package/dist/utils/m3u.js.map +1 -0
- package/dist/utils/morganStream.d.ts +7 -0
- package/dist/utils/morganStream.js +33 -0
- package/dist/utils/morganStream.js.map +1 -0
- package/dist/utils/platform.d.ts +64 -0
- package/dist/utils/platform.js +157 -0
- package/dist/utils/platform.js.map +1 -0
- package/dist/utils/retry.d.ts +15 -0
- package/dist/utils/retry.js +82 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/streamContext.d.ts +28 -0
- package/dist/utils/streamContext.js +33 -0
- package/dist/utils/streamContext.js.map +1 -0
- package/dist/utils/version.d.ts +37 -0
- package/dist/utils/version.js +228 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +92 -0
- package/prismcast.png +0 -0
- package/prismcast.svg +74 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { DEFAULTS, loadUserConfig, mergeConfiguration } from "./userConfig.js";
|
|
2
|
+
import { formatPresetStatus, getEffectivePreset, getValidPresetIds } from "./presets.js";
|
|
3
|
+
import { LOG } from "../utils/index.js";
|
|
4
|
+
/* The CONFIG object centralizes all tunable parameters for the application. Configuration uses a layered approach with the following priority (highest to lowest):
|
|
5
|
+
*
|
|
6
|
+
* 1. Environment variables (SCREAMING_SNAKE_CASE naming)
|
|
7
|
+
* 2. User config file (~/.prismcast/config.json)
|
|
8
|
+
* 3. Hard-coded defaults (defined in userConfig.ts)
|
|
9
|
+
*
|
|
10
|
+
* This design allows Docker deployments to use environment variables for overrides while standalone installations can use the web UI at /config for convenience.
|
|
11
|
+
*
|
|
12
|
+
* The settings are organized by functional area:
|
|
13
|
+
*
|
|
14
|
+
* - server: Network binding for the HTTP server (port, host)
|
|
15
|
+
* - browser: Chrome launch settings (executable path, init timeout)
|
|
16
|
+
* - streaming: Media capture quality (preset, bitrates, frame rate) and timeout limits
|
|
17
|
+
* - playback: Health monitoring intervals and recovery timing thresholds
|
|
18
|
+
* - recovery: Retry backoff parameters and circuit breaker configuration
|
|
19
|
+
* - paths: Filesystem locations for Chrome profile and extension data
|
|
20
|
+
*
|
|
21
|
+
* Configuration is initialized at startup via initializeConfiguration(), which loads the user config file, merges with defaults, applies environment overrides, and
|
|
22
|
+
* validates all values. If validation fails, the process exits with a descriptive error message.
|
|
23
|
+
*/
|
|
24
|
+
// The CONFIG object is initialized during startup. It starts as a copy of DEFAULTS and is replaced by the merged configuration.
|
|
25
|
+
export let CONFIG = JSON.parse(JSON.stringify(DEFAULTS));
|
|
26
|
+
/**
|
|
27
|
+
* Indicates whether a user config file parse error occurred during initialization. The web UI displays a warning when this is true.
|
|
28
|
+
*/
|
|
29
|
+
export let configParseError = false;
|
|
30
|
+
/**
|
|
31
|
+
* The parse error message if configParseError is true.
|
|
32
|
+
*/
|
|
33
|
+
export let configParseErrorMessage;
|
|
34
|
+
/**
|
|
35
|
+
* Initializes the configuration by loading the user config file, merging with defaults, and applying environment variable overrides. This must be called at startup
|
|
36
|
+
* before any code accesses CONFIG. After initialization, the CONFIG object contains the final merged values.
|
|
37
|
+
*/
|
|
38
|
+
export async function initializeConfiguration() {
|
|
39
|
+
// Load user configuration from file.
|
|
40
|
+
const result = await loadUserConfig();
|
|
41
|
+
configParseError = result.parseError;
|
|
42
|
+
configParseErrorMessage = result.parseErrorMessage;
|
|
43
|
+
// Merge defaults, user config, and environment variables.
|
|
44
|
+
CONFIG = mergeConfiguration(result.config);
|
|
45
|
+
// Validate quality preset. Viewport is derived on-demand via getViewport() rather than stored in CONFIG.
|
|
46
|
+
const validPresets = getValidPresetIds();
|
|
47
|
+
if (!validPresets.includes(CONFIG.streaming.qualityPreset)) {
|
|
48
|
+
LOG.warn("Invalid quality preset '%s'. Using default '%s'.", CONFIG.streaming.qualityPreset, DEFAULTS.streaming.qualityPreset);
|
|
49
|
+
CONFIG.streaming.qualityPreset = DEFAULTS.streaming.qualityPreset;
|
|
50
|
+
}
|
|
51
|
+
LOG.info("Configuration initialized from defaults, user config, and environment variables.");
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Returns a deep copy of the default configuration. Used by the web UI to display default values and handle reset operations.
|
|
55
|
+
* @returns A copy of the default configuration.
|
|
56
|
+
*/
|
|
57
|
+
export function getDefaults() {
|
|
58
|
+
return JSON.parse(JSON.stringify(DEFAULTS));
|
|
59
|
+
}
|
|
60
|
+
/* Before starting the server, we validate all configuration values to catch errors early. Invalid configurations like negative timeouts or out-of-range bitrates
|
|
61
|
+
* would cause subtle runtime failures that are difficult to diagnose. By validating upfront, we provide clear error messages and prevent the server from starting
|
|
62
|
+
* in a misconfigured state.
|
|
63
|
+
*
|
|
64
|
+
* Validation runs at startup after configuration initialization. If validation fails, the process exits with a non-zero code and a descriptive error message listing
|
|
65
|
+
* all invalid values.
|
|
66
|
+
*/
|
|
67
|
+
/**
|
|
68
|
+
* Validates that a configuration value is a positive integer within an optional range. This helper performs the common validation pattern of checking for valid
|
|
69
|
+
* integers and enforcing minimum/maximum bounds. It returns an error message if validation fails, allowing the caller to collect all errors before reporting them.
|
|
70
|
+
* @param name - The configuration name for error messages, typically the environment variable name.
|
|
71
|
+
* @param value - The value to validate, typically parsed from an environment variable.
|
|
72
|
+
* @param min - Optional minimum allowed value (inclusive).
|
|
73
|
+
* @param max - Optional maximum allowed value (inclusive).
|
|
74
|
+
* @returns Error message if invalid, null if valid.
|
|
75
|
+
*/
|
|
76
|
+
export function validatePositiveInt(name, value, min, max) {
|
|
77
|
+
// Check for NaN (from parseInt of invalid input) and non-positive values.
|
|
78
|
+
if (!Number.isInteger(value) || (value < 1)) {
|
|
79
|
+
return [name, " must be a positive integer, got: ", String(value)].join("");
|
|
80
|
+
}
|
|
81
|
+
// Check minimum bound if specified.
|
|
82
|
+
if ((min !== undefined) && (value < min)) {
|
|
83
|
+
return [name, " must be at least ", String(min), ", got: ", String(value)].join("");
|
|
84
|
+
}
|
|
85
|
+
// Check maximum bound if specified.
|
|
86
|
+
if ((max !== undefined) && (value > max)) {
|
|
87
|
+
return [name, " must be at most ", String(max), ", got: ", String(value)].join("");
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Validates that a configuration value is a positive number (including floats) within an optional range.
|
|
93
|
+
* @param name - The configuration name for error messages.
|
|
94
|
+
* @param value - The value to validate.
|
|
95
|
+
* @param min - Optional minimum allowed value (inclusive).
|
|
96
|
+
* @param max - Optional maximum allowed value (inclusive).
|
|
97
|
+
* @returns Error message if invalid, null if valid.
|
|
98
|
+
*/
|
|
99
|
+
export function validatePositiveNumber(name, value, min, max) {
|
|
100
|
+
// Check for NaN and non-positive values.
|
|
101
|
+
if (Number.isNaN(value) || (value <= 0)) {
|
|
102
|
+
return [name, " must be a positive number, got: ", String(value)].join("");
|
|
103
|
+
}
|
|
104
|
+
// Check minimum bound if specified.
|
|
105
|
+
if ((min !== undefined) && (value < min)) {
|
|
106
|
+
return [name, " must be at least ", String(min), ", got: ", String(value)].join("");
|
|
107
|
+
}
|
|
108
|
+
// Check maximum bound if specified.
|
|
109
|
+
if ((max !== undefined) && (value > max)) {
|
|
110
|
+
return [name, " must be at most ", String(max), ", got: ", String(value)].join("");
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Validates all configuration values and throws an error if any are invalid. This function runs at startup after configuration initialization. We collect all
|
|
116
|
+
* validation errors before throwing to provide complete feedback rather than failing on the first error and requiring multiple restart cycles to find all problems.
|
|
117
|
+
* @throws If any configuration value is invalid. The error message lists all invalid values.
|
|
118
|
+
*/
|
|
119
|
+
export function validateConfiguration() {
|
|
120
|
+
const errors = [];
|
|
121
|
+
// Validate server configuration. Port must be within the valid TCP port range (1-65535). Port 0 is reserved.
|
|
122
|
+
const portError = validatePositiveInt("PORT", CONFIG.server.port, 1, 65535);
|
|
123
|
+
if (portError) {
|
|
124
|
+
errors.push(portError);
|
|
125
|
+
}
|
|
126
|
+
// Validate streaming bitrates. Minimum video bitrate (100kbps) ensures basic video quality. Maximum (50Mbps) prevents unreasonable resource consumption.
|
|
127
|
+
// Audio range (32-512kbps) covers all common audio quality levels.
|
|
128
|
+
const videoBitrateError = validatePositiveInt("VIDEO_BITRATE", CONFIG.streaming.videoBitsPerSecond, 100000, 50000000);
|
|
129
|
+
if (videoBitrateError) {
|
|
130
|
+
errors.push(videoBitrateError);
|
|
131
|
+
}
|
|
132
|
+
const audioBitrateError = validatePositiveInt("AUDIO_BITRATE", CONFIG.streaming.audioBitsPerSecond, 32000, 512000);
|
|
133
|
+
if (audioBitrateError) {
|
|
134
|
+
errors.push(audioBitrateError);
|
|
135
|
+
}
|
|
136
|
+
// Validate timeouts. Minimum (1 second) prevents premature failures. Maximum (10 minutes) prevents indefinite hangs while allowing for very slow networks.
|
|
137
|
+
const navTimeoutError = validatePositiveInt("NAV_TIMEOUT", CONFIG.streaming.navigationTimeout, 1000, 600000);
|
|
138
|
+
if (navTimeoutError) {
|
|
139
|
+
errors.push(navTimeoutError);
|
|
140
|
+
}
|
|
141
|
+
const videoTimeoutError = validatePositiveInt("VIDEO_TIMEOUT", CONFIG.streaming.videoTimeout, 1000, 600000);
|
|
142
|
+
if (videoTimeoutError) {
|
|
143
|
+
errors.push(videoTimeoutError);
|
|
144
|
+
}
|
|
145
|
+
// Validate concurrent stream limit. At least 1 stream must be allowed. Maximum of 100 prevents resource exhaustion on most systems.
|
|
146
|
+
const concurrentError = validatePositiveInt("MAX_CONCURRENT_STREAMS", CONFIG.streaming.maxConcurrentStreams, 1, 100);
|
|
147
|
+
if (concurrentError) {
|
|
148
|
+
errors.push(concurrentError);
|
|
149
|
+
}
|
|
150
|
+
// Validate circuit breaker. At least 1 failure required to trip. Maximum of 100 prevents the breaker from never tripping.
|
|
151
|
+
const circuitBreakerError = validatePositiveInt("CIRCUIT_BREAKER_THRESHOLD", CONFIG.recovery.circuitBreakerThreshold, 1, 100);
|
|
152
|
+
if (circuitBreakerError) {
|
|
153
|
+
errors.push(circuitBreakerError);
|
|
154
|
+
}
|
|
155
|
+
// Validate stall threshold (float).
|
|
156
|
+
const stallThresholdError = validatePositiveNumber("STALL_THRESHOLD", CONFIG.playback.stallThreshold, 0.01, 5);
|
|
157
|
+
if (stallThresholdError) {
|
|
158
|
+
errors.push(stallThresholdError);
|
|
159
|
+
}
|
|
160
|
+
// Validate logging configuration. Minimum size (10KB) ensures meaningful log content. Maximum (100MB) prevents excessive disk usage.
|
|
161
|
+
const logMaxSizeError = validatePositiveInt("LOG_MAX_SIZE", CONFIG.logging.maxSize, 10240, 104857600);
|
|
162
|
+
if (logMaxSizeError) {
|
|
163
|
+
errors.push(logMaxSizeError);
|
|
164
|
+
}
|
|
165
|
+
// Validate HLS configuration. Segment duration and max segments have sensible ranges.
|
|
166
|
+
const hlsSegmentDurationError = validatePositiveInt("HLS_SEGMENT_DURATION", CONFIG.hls.segmentDuration, 1, 10);
|
|
167
|
+
if (hlsSegmentDurationError) {
|
|
168
|
+
errors.push(hlsSegmentDurationError);
|
|
169
|
+
}
|
|
170
|
+
const hlsMaxSegmentsError = validatePositiveInt("HLS_MAX_SEGMENTS", CONFIG.hls.maxSegments, 3, 60);
|
|
171
|
+
if (hlsMaxSegmentsError) {
|
|
172
|
+
errors.push(hlsMaxSegmentsError);
|
|
173
|
+
}
|
|
174
|
+
const hlsIdleTimeoutError = validatePositiveInt("HLS_IDLE_TIMEOUT", CONFIG.hls.idleTimeout, 10000, 300000);
|
|
175
|
+
if (hlsIdleTimeoutError) {
|
|
176
|
+
errors.push(hlsIdleTimeoutError);
|
|
177
|
+
}
|
|
178
|
+
// Force FFmpeg capture mode. Chrome's native fMP4 MediaRecorder produces corrupt output after 20-30 minutes of continuous recording. Until a future Chrome
|
|
179
|
+
// release resolves this, native capture mode is disabled entirely.
|
|
180
|
+
if (CONFIG.streaming.captureMode !== "ffmpeg") {
|
|
181
|
+
LOG.warn("Native capture mode is disabled due to a Chrome fMP4 MediaRecorder bug. Forcing FFmpeg capture mode.");
|
|
182
|
+
CONFIG.streaming.captureMode = "ffmpeg";
|
|
183
|
+
}
|
|
184
|
+
// Validate HDHomeRun configuration when enabled.
|
|
185
|
+
if (CONFIG.hdhr.enabled) {
|
|
186
|
+
// HDHR requires FFmpeg for MPEG-TS remuxing. In native mode, FFmpeg is not guaranteed to be available. Disable HDHR and warn the operator. The string cast
|
|
187
|
+
// suppresses TS2367 because captureMode is currently forced to "ffmpeg" above — this guard will become reachable again when native mode is re-enabled.
|
|
188
|
+
if (CONFIG.streaming.captureMode === "native") {
|
|
189
|
+
CONFIG.hdhr.enabled = false;
|
|
190
|
+
LOG.warn("HDHomeRun emulation requires FFmpeg mode. Disabling HDHR because capture mode is set to native.");
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
const hdhrPortError = validatePositiveInt("HDHR_PORT", CONFIG.hdhr.port, 1, 65535);
|
|
194
|
+
if (hdhrPortError) {
|
|
195
|
+
errors.push(hdhrPortError);
|
|
196
|
+
}
|
|
197
|
+
// Warn if HDHR port conflicts with the main server port (same host).
|
|
198
|
+
if ((CONFIG.hdhr.port === CONFIG.server.port) && ((CONFIG.server.host === "0.0.0.0") || (CONFIG.server.host === "::"))) {
|
|
199
|
+
errors.push("HDHR_PORT (" + String(CONFIG.hdhr.port) + ") conflicts with the main server port.");
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// If any validation errors occurred, throw with complete list for operator to fix all issues at once.
|
|
204
|
+
if (errors.length > 0) {
|
|
205
|
+
throw new Error(["Configuration validation failed:\n ", errors.join("\n ")].join(""));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Displays the active configuration at startup. This helps operators verify their settings and diagnose connection issues. We log only the most commonly adjusted
|
|
210
|
+
* values to keep output concise while providing useful debugging information.
|
|
211
|
+
*
|
|
212
|
+
* This function also checks for preset degradation and logs a warning if the configured preset exceeds display capabilities. The warning helps users understand why
|
|
213
|
+
* their stream resolution may be lower than configured.
|
|
214
|
+
*/
|
|
215
|
+
export function displayConfiguration() {
|
|
216
|
+
const presetResult = getEffectivePreset(CONFIG);
|
|
217
|
+
const presetStatus = formatPresetStatus(presetResult);
|
|
218
|
+
LOG.info("Starting PrismCast with configuration:");
|
|
219
|
+
LOG.info(" Server port: %s", CONFIG.server.port);
|
|
220
|
+
LOG.info(" Quality preset: %s", presetStatus);
|
|
221
|
+
LOG.info(" Video bitrate: %s", CONFIG.streaming.videoBitsPerSecond);
|
|
222
|
+
LOG.info(" Max retries: %s", CONFIG.streaming.maxNavigationRetries);
|
|
223
|
+
LOG.info(" Max concurrent streams: %s", CONFIG.streaming.maxConcurrentStreams);
|
|
224
|
+
LOG.info(" Circuit breaker threshold: %s failures in %s minutes", CONFIG.recovery.circuitBreakerThreshold, Math.round(CONFIG.recovery.circuitBreakerWindow / 60000));
|
|
225
|
+
LOG.info(" Chrome executable: %s", CONFIG.browser.executablePath ?? "autodetect");
|
|
226
|
+
LOG.info(" HLS segment duration: %ss, max segments: %s", CONFIG.hls.segmentDuration, CONFIG.hls.maxSegments);
|
|
227
|
+
LOG.info(" HDHomeRun emulation: %s", CONFIG.hdhr.enabled ? "enabled (port " + String(CONFIG.hdhr.port) + ")" : "disabled");
|
|
228
|
+
// Log a prominent warning if preset was degraded due to display limitations.
|
|
229
|
+
if (presetResult.degraded && presetResult.maxViewport) {
|
|
230
|
+
LOG.warn("Display supports maximum %s\u00d7%s. Configured %s preset will use %s instead.", presetResult.maxViewport.width, presetResult.maxViewport.height, presetResult.configuredPreset.id, presetResult.effectivePreset.id);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { Config, Nullable } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* A quality preset that sets multiple configuration values at once.
|
|
4
|
+
*/
|
|
5
|
+
export interface QualityPreset {
|
|
6
|
+
description: string;
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
values: Record<string, number>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Available video quality presets. These presets configure viewport dimensions, video bitrate, and frame rate for common use cases. The presets are ordered from lowest
|
|
13
|
+
* to highest quality.
|
|
14
|
+
*/
|
|
15
|
+
export declare const VIDEO_QUALITY_PRESETS: QualityPreset[];
|
|
16
|
+
/**
|
|
17
|
+
* Returns the list of valid preset IDs.
|
|
18
|
+
* @returns Array of preset ID strings.
|
|
19
|
+
*/
|
|
20
|
+
export declare function getValidPresetIds(): string[];
|
|
21
|
+
/**
|
|
22
|
+
* Returns the viewport dimensions for the currently configured quality preset. This function derives viewport on-demand from the preset rather than storing it in
|
|
23
|
+
* CONFIG. The CONFIG parameter is passed explicitly to avoid circular dependency issues between presets.ts and config/index.ts.
|
|
24
|
+
*
|
|
25
|
+
* WARNING: This function returns the configured preset viewport without considering display limitations. For runtime operations (stream capture, window sizing), use
|
|
26
|
+
* getEffectiveViewport() instead, which accounts for display size constraints.
|
|
27
|
+
* @param config - The configuration object containing the quality preset.
|
|
28
|
+
* @returns The viewport dimensions for the configured preset, or default 720p dimensions if preset not found.
|
|
29
|
+
*/
|
|
30
|
+
export declare function getPresetViewport(config: Config): {
|
|
31
|
+
height: number;
|
|
32
|
+
width: number;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Result of effective preset resolution, containing both configured and effective presets along with degradation status.
|
|
36
|
+
*/
|
|
37
|
+
export interface EffectivePresetResult {
|
|
38
|
+
configuredPreset: QualityPreset;
|
|
39
|
+
degraded: boolean;
|
|
40
|
+
effectivePreset: QualityPreset;
|
|
41
|
+
maxViewport: Nullable<{
|
|
42
|
+
height: number;
|
|
43
|
+
width: number;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Finds the largest preset that fits within the given maximum dimensions. Presets are checked from highest to lowest resolution (4K down to 480p).
|
|
48
|
+
* @param maxWidth - Maximum available viewport width.
|
|
49
|
+
* @param maxHeight - Maximum available viewport height.
|
|
50
|
+
* @returns The largest fitting preset, or null if no preset fits (extremely small display).
|
|
51
|
+
*/
|
|
52
|
+
export declare function findBestFittingPreset(maxWidth: number, maxHeight: number): Nullable<QualityPreset>;
|
|
53
|
+
/**
|
|
54
|
+
* Resolves the effective preset to use based on the configured preset and display limitations. If the configured preset fits within the display, it is returned
|
|
55
|
+
* unchanged. Otherwise, the largest fitting preset is selected.
|
|
56
|
+
* @param config - The configuration object containing the quality preset.
|
|
57
|
+
* @returns The effective preset result with configured preset, effective preset, and degradation status.
|
|
58
|
+
*/
|
|
59
|
+
export declare function getEffectivePreset(config: Config): EffectivePresetResult;
|
|
60
|
+
/**
|
|
61
|
+
* Returns the effective viewport dimensions accounting for display limitations. This is the primary function for runtime code paths that need viewport dimensions
|
|
62
|
+
* for stream capture and window sizing.
|
|
63
|
+
* @param config - The configuration object containing the quality preset.
|
|
64
|
+
* @returns The effective viewport dimensions.
|
|
65
|
+
*/
|
|
66
|
+
export declare function getEffectiveViewport(config: Config): {
|
|
67
|
+
height: number;
|
|
68
|
+
width: number;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Formats the preset status for display in logs, health endpoint, and UI. Returns a concise string showing the configured preset and any degradation.
|
|
72
|
+
* @param result - The effective preset result from getEffectivePreset().
|
|
73
|
+
* @returns Formatted status string.
|
|
74
|
+
*/
|
|
75
|
+
export declare function formatPresetStatus(result: EffectivePresetResult): string;
|
|
76
|
+
/**
|
|
77
|
+
* A preset option with degradation information for UI display.
|
|
78
|
+
*/
|
|
79
|
+
export interface PresetOption {
|
|
80
|
+
preset: QualityPreset;
|
|
81
|
+
degradedTo: Nullable<QualityPreset>;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Result of preset options with display capability information.
|
|
85
|
+
*/
|
|
86
|
+
export interface PresetOptionsResult {
|
|
87
|
+
maxViewport: Nullable<{
|
|
88
|
+
height: number;
|
|
89
|
+
width: number;
|
|
90
|
+
}>;
|
|
91
|
+
options: PresetOption[];
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Returns all preset options with degradation information for UI display. Each preset includes information about what it will degrade to (if anything) based on
|
|
95
|
+
* current display capabilities. If display detection hasn't completed yet, all presets are returned without degradation information.
|
|
96
|
+
* @returns Preset options with degradation status and max viewport info.
|
|
97
|
+
*/
|
|
98
|
+
export declare function getPresetOptionsWithDegradation(): PresetOptionsResult;
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { getMaxSupportedViewport } from "../browser/display.js";
|
|
2
|
+
/* Presets define video quality profiles that determine capture resolution (viewport) and provide default values for bitrate and frame rate. The selected preset is
|
|
3
|
+
* stored in configuration and determines the viewport dimensions at runtime.
|
|
4
|
+
*
|
|
5
|
+
* Viewport is derived from the preset via getPresetViewport() and is not stored in CONFIG. For runtime operations that need display-aware viewport dimensions, use
|
|
6
|
+
* getEffectiveViewport() which accounts for display limitations.
|
|
7
|
+
*/
|
|
8
|
+
// Default viewport dimensions used as fallback if preset lookup fails. Matches 720p preset.
|
|
9
|
+
const DEFAULT_VIEWPORT = { height: 720, width: 1280 };
|
|
10
|
+
/**
|
|
11
|
+
* Available video quality presets. These presets configure viewport dimensions, video bitrate, and frame rate for common use cases. The presets are ordered from lowest
|
|
12
|
+
* to highest quality.
|
|
13
|
+
*/
|
|
14
|
+
export const VIDEO_QUALITY_PRESETS = [
|
|
15
|
+
{
|
|
16
|
+
description: "Low bandwidth, older devices, minimal resource usage.",
|
|
17
|
+
id: "480p",
|
|
18
|
+
name: "480p",
|
|
19
|
+
values: {
|
|
20
|
+
"browser.viewport.height": 480,
|
|
21
|
+
"browser.viewport.width": 854,
|
|
22
|
+
"streaming.frameRate": 30,
|
|
23
|
+
"streaming.videoBitsPerSecond": 3000000
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
description: "Balanced quality and bandwidth. Good for most content.",
|
|
28
|
+
id: "720p",
|
|
29
|
+
name: "720p",
|
|
30
|
+
values: {
|
|
31
|
+
"browser.viewport.height": 720,
|
|
32
|
+
"browser.viewport.width": 1280,
|
|
33
|
+
"streaming.frameRate": 60,
|
|
34
|
+
"streaming.videoBitsPerSecond": 8000000
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
description: "HD with higher bitrate. Best for sports and fast motion at 720p.",
|
|
39
|
+
id: "720p-high",
|
|
40
|
+
name: "720p High",
|
|
41
|
+
values: {
|
|
42
|
+
"browser.viewport.height": 720,
|
|
43
|
+
"browser.viewport.width": 1280,
|
|
44
|
+
"streaming.frameRate": 60,
|
|
45
|
+
"streaming.videoBitsPerSecond": 12000000
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
description: "Full HD resolution. Requires good bandwidth.",
|
|
50
|
+
id: "1080p",
|
|
51
|
+
name: "1080p",
|
|
52
|
+
values: {
|
|
53
|
+
"browser.viewport.height": 1080,
|
|
54
|
+
"browser.viewport.width": 1920,
|
|
55
|
+
"streaming.frameRate": 60,
|
|
56
|
+
"streaming.videoBitsPerSecond": 15000000
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
description: "Full HD with higher bitrate. Best for sports and fast motion.",
|
|
61
|
+
id: "1080p-high",
|
|
62
|
+
name: "1080p High",
|
|
63
|
+
values: {
|
|
64
|
+
"browser.viewport.height": 1080,
|
|
65
|
+
"browser.viewport.width": 1920,
|
|
66
|
+
"streaming.frameRate": 60,
|
|
67
|
+
"streaming.videoBitsPerSecond": 20000000
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
description: "4K resolution. High resource and bandwidth usage.",
|
|
72
|
+
id: "4k",
|
|
73
|
+
name: "4K",
|
|
74
|
+
values: {
|
|
75
|
+
"browser.viewport.height": 2160,
|
|
76
|
+
"browser.viewport.width": 3840,
|
|
77
|
+
"streaming.frameRate": 60,
|
|
78
|
+
"streaming.videoBitsPerSecond": 35000000
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
];
|
|
82
|
+
/**
|
|
83
|
+
* Returns the list of valid preset IDs.
|
|
84
|
+
* @returns Array of preset ID strings.
|
|
85
|
+
*/
|
|
86
|
+
export function getValidPresetIds() {
|
|
87
|
+
return VIDEO_QUALITY_PRESETS.map((p) => p.id);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Returns the viewport dimensions for the currently configured quality preset. This function derives viewport on-demand from the preset rather than storing it in
|
|
91
|
+
* CONFIG. The CONFIG parameter is passed explicitly to avoid circular dependency issues between presets.ts and config/index.ts.
|
|
92
|
+
*
|
|
93
|
+
* WARNING: This function returns the configured preset viewport without considering display limitations. For runtime operations (stream capture, window sizing), use
|
|
94
|
+
* getEffectiveViewport() instead, which accounts for display size constraints.
|
|
95
|
+
* @param config - The configuration object containing the quality preset.
|
|
96
|
+
* @returns The viewport dimensions for the configured preset, or default 720p dimensions if preset not found.
|
|
97
|
+
*/
|
|
98
|
+
export function getPresetViewport(config) {
|
|
99
|
+
const preset = VIDEO_QUALITY_PRESETS.find((p) => p.id === config.streaming.qualityPreset);
|
|
100
|
+
if (!preset) {
|
|
101
|
+
return DEFAULT_VIEWPORT;
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
height: preset.values["browser.viewport.height"],
|
|
105
|
+
width: preset.values["browser.viewport.width"]
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Finds the largest preset that fits within the given maximum dimensions. Presets are checked from highest to lowest resolution (4K down to 480p).
|
|
110
|
+
* @param maxWidth - Maximum available viewport width.
|
|
111
|
+
* @param maxHeight - Maximum available viewport height.
|
|
112
|
+
* @returns The largest fitting preset, or null if no preset fits (extremely small display).
|
|
113
|
+
*/
|
|
114
|
+
export function findBestFittingPreset(maxWidth, maxHeight) {
|
|
115
|
+
// Iterate from highest to lowest resolution (reverse order since VIDEO_QUALITY_PRESETS is ordered low to high).
|
|
116
|
+
for (let i = VIDEO_QUALITY_PRESETS.length - 1; i >= 0; i--) {
|
|
117
|
+
const preset = VIDEO_QUALITY_PRESETS[i];
|
|
118
|
+
const presetWidth = preset.values["browser.viewport.width"];
|
|
119
|
+
const presetHeight = preset.values["browser.viewport.height"];
|
|
120
|
+
if ((presetWidth <= maxWidth) && (presetHeight <= maxHeight)) {
|
|
121
|
+
return preset;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Resolves the effective preset to use based on the configured preset and display limitations. If the configured preset fits within the display, it is returned
|
|
128
|
+
* unchanged. Otherwise, the largest fitting preset is selected.
|
|
129
|
+
* @param config - The configuration object containing the quality preset.
|
|
130
|
+
* @returns The effective preset result with configured preset, effective preset, and degradation status.
|
|
131
|
+
*/
|
|
132
|
+
export function getEffectivePreset(config) {
|
|
133
|
+
// Find the configured preset.
|
|
134
|
+
const configuredPreset = VIDEO_QUALITY_PRESETS.find((p) => p.id === config.streaming.qualityPreset) ?? VIDEO_QUALITY_PRESETS[1];
|
|
135
|
+
const maxViewport = getMaxSupportedViewport();
|
|
136
|
+
// If display detection hasn't completed yet, use configured preset without degradation.
|
|
137
|
+
if (!maxViewport) {
|
|
138
|
+
return {
|
|
139
|
+
configuredPreset,
|
|
140
|
+
degraded: false,
|
|
141
|
+
effectivePreset: configuredPreset,
|
|
142
|
+
maxViewport: null
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
// Check if configured preset fits within display limits.
|
|
146
|
+
const configuredWidth = configuredPreset.values["browser.viewport.width"];
|
|
147
|
+
const configuredHeight = configuredPreset.values["browser.viewport.height"];
|
|
148
|
+
if ((configuredWidth <= maxViewport.width) && (configuredHeight <= maxViewport.height)) {
|
|
149
|
+
return {
|
|
150
|
+
configuredPreset,
|
|
151
|
+
degraded: false,
|
|
152
|
+
effectivePreset: configuredPreset,
|
|
153
|
+
maxViewport
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
// Configured preset doesn't fit - find the best alternative.
|
|
157
|
+
const bestFitting = findBestFittingPreset(maxViewport.width, maxViewport.height);
|
|
158
|
+
// Edge case: no preset fits (extremely small display). Use 480p as minimum and let Chrome constrain it.
|
|
159
|
+
if (!bestFitting) {
|
|
160
|
+
return {
|
|
161
|
+
configuredPreset,
|
|
162
|
+
degraded: true,
|
|
163
|
+
effectivePreset: VIDEO_QUALITY_PRESETS[0],
|
|
164
|
+
maxViewport
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
configuredPreset,
|
|
169
|
+
degraded: true,
|
|
170
|
+
effectivePreset: bestFitting,
|
|
171
|
+
maxViewport
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Returns the effective viewport dimensions accounting for display limitations. This is the primary function for runtime code paths that need viewport dimensions
|
|
176
|
+
* for stream capture and window sizing.
|
|
177
|
+
* @param config - The configuration object containing the quality preset.
|
|
178
|
+
* @returns The effective viewport dimensions.
|
|
179
|
+
*/
|
|
180
|
+
export function getEffectiveViewport(config) {
|
|
181
|
+
const result = getEffectivePreset(config);
|
|
182
|
+
return {
|
|
183
|
+
height: result.effectivePreset.values["browser.viewport.height"],
|
|
184
|
+
width: result.effectivePreset.values["browser.viewport.width"]
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Formats the preset status for display in logs, health endpoint, and UI. Returns a concise string showing the configured preset and any degradation.
|
|
189
|
+
* @param result - The effective preset result from getEffectivePreset().
|
|
190
|
+
* @returns Formatted status string.
|
|
191
|
+
*/
|
|
192
|
+
export function formatPresetStatus(result) {
|
|
193
|
+
const effectiveWidth = result.effectivePreset.values["browser.viewport.width"];
|
|
194
|
+
const effectiveHeight = result.effectivePreset.values["browser.viewport.height"];
|
|
195
|
+
if (!result.degraded) {
|
|
196
|
+
return [result.configuredPreset.id, " (", String(effectiveWidth), "\u00d7", String(effectiveHeight), ")"].join("");
|
|
197
|
+
}
|
|
198
|
+
return [result.configuredPreset.id, " (limited to ", result.effectivePreset.id, " by display)"].join("");
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Returns all preset options with degradation information for UI display. Each preset includes information about what it will degrade to (if anything) based on
|
|
202
|
+
* current display capabilities. If display detection hasn't completed yet, all presets are returned without degradation information.
|
|
203
|
+
* @returns Preset options with degradation status and max viewport info.
|
|
204
|
+
*/
|
|
205
|
+
export function getPresetOptionsWithDegradation() {
|
|
206
|
+
const maxViewport = getMaxSupportedViewport();
|
|
207
|
+
// If display detection hasn't completed, return all presets without degradation info.
|
|
208
|
+
if (!maxViewport) {
|
|
209
|
+
return {
|
|
210
|
+
maxViewport: null,
|
|
211
|
+
options: VIDEO_QUALITY_PRESETS.map((preset) => ({
|
|
212
|
+
degradedTo: null,
|
|
213
|
+
preset
|
|
214
|
+
}))
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
// Find the best fitting preset for this display.
|
|
218
|
+
const bestFitting = findBestFittingPreset(maxViewport.width, maxViewport.height);
|
|
219
|
+
// Build options with degradation info for each preset.
|
|
220
|
+
const options = VIDEO_QUALITY_PRESETS.map((preset) => {
|
|
221
|
+
const presetWidth = preset.values["browser.viewport.width"];
|
|
222
|
+
const presetHeight = preset.values["browser.viewport.height"];
|
|
223
|
+
// Check if this preset fits within display limits.
|
|
224
|
+
if ((presetWidth <= maxViewport.width) && (presetHeight <= maxViewport.height)) {
|
|
225
|
+
return {
|
|
226
|
+
degradedTo: null,
|
|
227
|
+
preset
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
// This preset doesn't fit - it will degrade to the best fitting preset.
|
|
231
|
+
return {
|
|
232
|
+
degradedTo: bestFitting ?? VIDEO_QUALITY_PRESETS[0],
|
|
233
|
+
preset
|
|
234
|
+
};
|
|
235
|
+
});
|
|
236
|
+
return {
|
|
237
|
+
maxViewport,
|
|
238
|
+
options
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=presets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presets.js","sourceRoot":"","sources":["../../src/config/presets.ts"],"names":[],"mappings":"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"}
|