prismcast 1.5.2 → 1.7.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/README.md +44 -28
- package/dist/app.d.ts +5 -0
- package/dist/app.js +63 -5
- package/dist/app.js.map +1 -1
- package/dist/browser/cdp.js +1 -1
- package/dist/browser/cdp.js.map +1 -1
- package/dist/browser/channelSelection.d.ts +10 -34
- package/dist/browser/channelSelection.js +31 -96
- package/dist/browser/channelSelection.js.map +1 -1
- package/dist/browser/display.d.ts +19 -0
- package/dist/browser/display.js +16 -11
- package/dist/browser/display.js.map +1 -1
- package/dist/browser/index.d.ts +31 -54
- package/dist/browser/index.js +334 -272
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/login.d.ts +65 -0
- package/dist/browser/login.js +193 -0
- package/dist/browser/login.js.map +1 -0
- package/dist/browser/precaching.js +15 -3
- package/dist/browser/precaching.js.map +1 -1
- package/dist/browser/tuning/comcastPolymer.d.ts +23 -0
- package/dist/browser/tuning/comcastPolymer.js +655 -0
- package/dist/browser/tuning/comcastPolymer.js.map +1 -0
- package/dist/browser/tuning/cox.d.ts +2 -0
- package/dist/browser/tuning/cox.js +14 -0
- package/dist/browser/tuning/cox.js.map +1 -0
- package/dist/browser/tuning/directv.js +14 -1
- package/dist/browser/tuning/directv.js.map +1 -1
- package/dist/browser/tuning/fox.js +14 -2
- package/dist/browser/tuning/fox.js.map +1 -1
- package/dist/browser/tuning/hbo.js +12 -1
- package/dist/browser/tuning/hbo.js.map +1 -1
- package/dist/browser/tuning/hulu.js +24 -1
- package/dist/browser/tuning/hulu.js.map +1 -1
- package/dist/browser/tuning/shared.d.ts +46 -0
- package/dist/browser/tuning/shared.js +100 -0
- package/dist/browser/tuning/shared.js.map +1 -0
- package/dist/browser/tuning/sling.js +35 -5
- package/dist/browser/tuning/sling.js.map +1 -1
- package/dist/browser/tuning/spectrum.js +13 -1
- package/dist/browser/tuning/spectrum.js.map +1 -1
- package/dist/browser/tuning/thumbnailRow.js +1 -1
- package/dist/browser/tuning/thumbnailRow.js.map +1 -1
- package/dist/browser/tuning/tileClick.js +1 -1
- package/dist/browser/tuning/tileClick.js.map +1 -1
- package/dist/browser/tuning/xfinity.d.ts +2 -0
- package/dist/browser/tuning/xfinity.js +14 -0
- package/dist/browser/tuning/xfinity.js.map +1 -0
- package/dist/browser/tuning/youtubeTv.js +15 -1
- package/dist/browser/tuning/youtubeTv.js.map +1 -1
- package/dist/browser/video.d.ts +20 -0
- package/dist/browser/video.js +168 -124
- package/dist/browser/video.js.map +1 -1
- package/dist/channels/index.d.ts +2 -2
- package/dist/channels/index.js +2075 -683
- package/dist/channels/index.js.map +1 -1
- package/dist/config/health.d.ts +28 -28
- package/dist/config/health.js +72 -54
- package/dist/config/health.js.map +1 -1
- package/dist/config/index.js +29 -64
- package/dist/config/index.js.map +1 -1
- package/dist/config/paths.d.ts +12 -0
- package/dist/config/paths.js +16 -0
- package/dist/config/paths.js.map +1 -1
- package/dist/config/profiles.d.ts +3 -2
- package/dist/config/profiles.js +45 -34
- package/dist/config/profiles.js.map +1 -1
- package/dist/config/providerPacks.js +4 -1
- package/dist/config/providerPacks.js.map +1 -1
- package/dist/config/providers.d.ts +19 -5
- package/dist/config/providers.js +123 -93
- package/dist/config/providers.js.map +1 -1
- package/dist/config/sites.d.ts +28 -0
- package/dist/config/sites.js +104 -127
- package/dist/config/sites.js.map +1 -1
- package/dist/config/userChannels.d.ts +53 -2
- package/dist/config/userChannels.js +222 -13
- package/dist/config/userChannels.js.map +1 -1
- package/dist/config/userConfig.d.ts +2 -1
- package/dist/config/userConfig.js +6 -15
- package/dist/config/userConfig.js.map +1 -1
- package/dist/config/userProfiles.d.ts +0 -1
- package/dist/config/userProfiles.js +18 -10
- package/dist/config/userProfiles.js.map +1 -1
- package/dist/hdhr/channelMap.d.ts +1 -1
- package/dist/hdhr/channelMap.js +4 -4
- package/dist/hdhr/channelMap.js.map +1 -1
- package/dist/index.js +25 -22
- package/dist/index.js.map +1 -1
- package/dist/native/decrypt.d.ts +32 -0
- package/dist/native/decrypt.js +85 -0
- package/dist/native/decrypt.js.map +1 -0
- package/dist/native/index.d.ts +61 -0
- package/dist/native/index.js +342 -0
- package/dist/native/index.js.map +1 -0
- package/dist/native/intercept.d.ts +35 -0
- package/dist/native/intercept.js +184 -0
- package/dist/native/intercept.js.map +1 -0
- package/dist/native/probe.d.ts +49 -0
- package/dist/native/probe.js +294 -0
- package/dist/native/probe.js.map +1 -0
- package/dist/native/proxy.d.ts +54 -0
- package/dist/native/proxy.js +947 -0
- package/dist/native/proxy.js.map +1 -0
- package/dist/routes/components.d.ts +43 -0
- package/dist/routes/components.js +122 -0
- package/dist/routes/components.js.map +1 -1
- package/dist/routes/config/channels/index.d.ts +2 -2
- package/dist/routes/config/channels/index.js +1 -1
- package/dist/routes/config/channels/index.js.map +1 -1
- package/dist/routes/config/channels/routes.js +443 -65
- package/dist/routes/config/channels/routes.js.map +1 -1
- package/dist/routes/config/channels/table.d.ts +76 -0
- package/dist/routes/config/channels/table.js +369 -89
- package/dist/routes/config/channels/table.js.map +1 -1
- package/dist/routes/config/index.d.ts +1 -1
- package/dist/routes/config/index.js +1 -1
- package/dist/routes/config/index.js.map +1 -1
- package/dist/routes/config/providers.d.ts +6 -6
- package/dist/routes/config/providers.js +73 -99
- package/dist/routes/config/providers.js.map +1 -1
- package/dist/routes/debug.js +5 -5
- package/dist/routes/debug.js.map +1 -1
- package/dist/routes/hls.js +12 -2
- package/dist/routes/hls.js.map +1 -1
- package/dist/routes/index.d.ts +0 -15
- package/dist/routes/index.js +2 -16
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/providers.js +106 -3
- package/dist/routes/providers.js.map +1 -1
- package/dist/routes/root/content.js +124 -47
- package/dist/routes/root/content.js.map +1 -1
- package/dist/routes/root/index.js +2 -1
- package/dist/routes/root/index.js.map +1 -1
- package/dist/routes/root/scripts/channels.js +772 -312
- package/dist/routes/root/scripts/channels.js.map +1 -1
- package/dist/routes/root/scripts/config.js +596 -544
- package/dist/routes/root/scripts/config.js.map +1 -1
- package/dist/routes/root/scripts/index.d.ts +1 -0
- package/dist/routes/root/scripts/index.js +1 -0
- package/dist/routes/root/scripts/index.js.map +1 -1
- package/dist/routes/root/scripts/shared.d.ts +6 -0
- package/dist/routes/root/scripts/shared.js +391 -0
- package/dist/routes/root/scripts/shared.js.map +1 -0
- package/dist/routes/root/scripts/status.js +49 -33
- package/dist/routes/root/scripts/status.js.map +1 -1
- package/dist/routes/root/styles.js +117 -10
- package/dist/routes/root/styles.js.map +1 -1
- package/dist/service/commands.js +2 -1
- package/dist/service/commands.js.map +1 -1
- package/dist/streaming/fmp4Segmenter.d.ts +5 -0
- package/dist/streaming/fmp4Segmenter.js +146 -42
- package/dist/streaming/fmp4Segmenter.js.map +1 -1
- package/dist/streaming/hls.d.ts +24 -19
- package/dist/streaming/hls.js +603 -297
- package/dist/streaming/hls.js.map +1 -1
- package/dist/streaming/hlsResume.d.ts +16 -3
- package/dist/streaming/hlsResume.js +35 -8
- package/dist/streaming/hlsResume.js.map +1 -1
- package/dist/streaming/hlsSegments.d.ts +41 -2
- package/dist/streaming/hlsSegments.js +131 -28
- package/dist/streaming/hlsSegments.js.map +1 -1
- package/dist/streaming/lifecycle.js +27 -13
- package/dist/streaming/lifecycle.js.map +1 -1
- package/dist/streaming/monitor.d.ts +1 -0
- package/dist/streaming/monitor.js +862 -474
- package/dist/streaming/monitor.js.map +1 -1
- package/dist/streaming/mp4Parser.d.ts +52 -6
- package/dist/streaming/mp4Parser.js +156 -30
- package/dist/streaming/mp4Parser.js.map +1 -1
- package/dist/streaming/mpegts.d.ts +2 -2
- package/dist/streaming/mpegts.js +156 -109
- package/dist/streaming/mpegts.js.map +1 -1
- package/dist/streaming/playlistBuilder.d.ts +38 -0
- package/dist/streaming/playlistBuilder.js +66 -0
- package/dist/streaming/playlistBuilder.js.map +1 -0
- package/dist/streaming/preroll.d.ts +126 -0
- package/dist/streaming/preroll.js +434 -0
- package/dist/streaming/preroll.js.map +1 -0
- package/dist/streaming/pretune.d.ts +8 -0
- package/dist/streaming/pretune.js +226 -0
- package/dist/streaming/pretune.js.map +1 -0
- package/dist/streaming/recovery.d.ts +0 -6
- package/dist/streaming/recovery.js +0 -8
- package/dist/streaming/recovery.js.map +1 -1
- package/dist/streaming/registry.d.ts +57 -9
- package/dist/streaming/registry.js +26 -7
- package/dist/streaming/registry.js.map +1 -1
- package/dist/streaming/setup.d.ts +8 -1
- package/dist/streaming/setup.js +71 -27
- package/dist/streaming/setup.js.map +1 -1
- package/dist/streaming/showInfo.d.ts +29 -4
- package/dist/streaming/showInfo.js +210 -38
- package/dist/streaming/showInfo.js.map +1 -1
- package/dist/streaming/statusEmitter.d.ts +20 -6
- package/dist/streaming/statusEmitter.js +31 -7
- package/dist/streaming/statusEmitter.js.map +1 -1
- package/dist/types/channels.d.ts +32 -0
- package/dist/types/config.d.ts +2 -2
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.js.map +1 -1
- package/dist/types/profiles.d.ts +13 -1
- package/dist/types/selection.d.ts +5 -1
- package/dist/types/shared.d.ts +1 -1
- package/dist/types/streaming.d.ts +7 -0
- package/dist/utils/chromeFetch.d.ts +20 -0
- package/dist/utils/chromeFetch.js +43 -0
- package/dist/utils/chromeFetch.js.map +1 -0
- package/dist/utils/debugFilter.d.ts +0 -5
- package/dist/utils/debugFilter.js +20 -11
- package/dist/utils/debugFilter.js.map +1 -1
- package/dist/utils/delay.d.ts +28 -0
- package/dist/utils/delay.js +35 -1
- package/dist/utils/delay.js.map +1 -1
- package/dist/utils/evaluate.js +6 -11
- package/dist/utils/evaluate.js.map +1 -1
- package/dist/utils/ffmpeg.d.ts +14 -12
- package/dist/utils/ffmpeg.js +114 -134
- package/dist/utils/ffmpeg.js.map +1 -1
- package/dist/utils/format.d.ts +6 -0
- package/dist/utils/format.js +8 -0
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/logEmitter.js +1 -1
- package/dist/utils/logEmitter.js.map +1 -1
- package/dist/utils/pid.d.ts +40 -0
- package/dist/utils/pid.js +83 -0
- package/dist/utils/pid.js.map +1 -0
- package/dist/utils/retry.d.ts +15 -9
- package/dist/utils/retry.js +7 -23
- package/dist/utils/retry.js.map +1 -1
- package/dist/utils/timing.d.ts +5 -0
- package/dist/utils/timing.js +13 -0
- package/dist/utils/timing.js.map +1 -0
- package/dist/utils/version.js +2 -2
- package/dist/utils/version.js.map +1 -1
- package/package.json +7 -7
package/dist/utils/ffmpeg.js
CHANGED
|
@@ -6,14 +6,29 @@ import { join } from "node:path";
|
|
|
6
6
|
import { spawn } from "node:child_process";
|
|
7
7
|
// The ffmpeg-for-homebridge package has incorrect type definitions (declares named export but JS uses default export). Cast to the correct type.
|
|
8
8
|
const ffmpegPath = ffmpegForHomebridge;
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
// FFmpeg stderr noise patterns that are suppressed from logging. The -nostats flag suppresses most progress output, but some FFmpeg builds or versions may still
|
|
10
|
+
// emit these patterns. The filter acts as a safety net to keep logs clean.
|
|
11
|
+
const FFMPEG_NOISE_PATTERNS = ["Press [q] to stop", "frame=", "size=", "time=", "bitrate=", "speed="];
|
|
12
|
+
/**
|
|
13
|
+
* Returns the path to the bundled ffmpeg-for-homebridge binary if it exists on disk. This binary ships as an npm dependency and has a full encoder set (libx264,
|
|
14
|
+
* libx265, zoompan, overlay) regardless of what the user has installed. Used by preroll generation to guarantee HEVC encoding availability without depending on
|
|
15
|
+
* the Channels DVR FFmpeg (which has a minimal encoder set) or a system installation.
|
|
16
|
+
* @returns The absolute path to the bundled FFmpeg, or undefined if the package did not resolve or the binary is missing.
|
|
17
|
+
*/
|
|
18
|
+
export function getBundledFFmpegPath() {
|
|
19
|
+
if (ffmpegPath && existsSync(ffmpegPath)) {
|
|
20
|
+
return ffmpegPath;
|
|
21
|
+
}
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
/* When using Matroska capture mode, Chrome's MediaRecorder outputs a Matroska container with video (H264 or HEVC depending on hardware capabilities) and Opus audio.
|
|
25
|
+
* For HLS compatibility, we need an fMP4 container with AAC audio. FFmpeg handles this conversion:
|
|
11
26
|
*
|
|
12
27
|
* - Video: Passed through unchanged (copy codec) - no quality loss, minimal CPU
|
|
13
28
|
* - Audio: Transcoded from Opus to AAC - lightweight operation
|
|
14
|
-
* - Container: Converted from
|
|
29
|
+
* - Container: Converted from Matroska to fragmented MP4 with streaming-friendly flags
|
|
15
30
|
*
|
|
16
|
-
* The FFmpeg process runs for the lifetime of the stream, reading
|
|
31
|
+
* The FFmpeg process runs for the lifetime of the stream, reading Matroska from stdin and writing fMP4 to stdout. This output feeds directly into the existing fMP4
|
|
17
32
|
* segmenter.
|
|
18
33
|
*/
|
|
19
34
|
/* FFmpeg can be located in several places depending on how it was installed. We check in order of preference:
|
|
@@ -102,98 +117,74 @@ export async function resolveFFmpegPath() {
|
|
|
102
117
|
cachedFFmpegPath = undefined;
|
|
103
118
|
return undefined;
|
|
104
119
|
}
|
|
120
|
+
// MPEG-TS Output Flags.
|
|
121
|
+
/* MPEG-TS muxer flags for the fMP4-to-MPEG-TS remuxer. Tuned to produce output resembling a real HDHomeRun CONNECT DUO (HDTC-2US) ATSC transport stream.
|
|
122
|
+
* Plex's transcoder may make assumptions about stream structure based on the reported device model (PID assignments, PAT/PMT frequency). Using ATSC-conventional
|
|
123
|
+
* values avoids "Invalid argument" failures when Plex tries to transcode the live session for remote clients. These are pure container metadata changes — the actual
|
|
124
|
+
* A/V data is untouched by -c copy.
|
|
125
|
+
*/
|
|
126
|
+
const MPEGTS_OUTPUT_FLAGS = [
|
|
127
|
+
"-f", "mpegts",
|
|
128
|
+
"-mpegts_pmt_start_pid", "0x0020",
|
|
129
|
+
"-mpegts_start_pid", "0x0031",
|
|
130
|
+
"-mpegts_service_type", "digital_tv",
|
|
131
|
+
"-pat_period", "0.1",
|
|
132
|
+
"-pcr_period", "40",
|
|
133
|
+
"-flush_packets", "1"
|
|
134
|
+
];
|
|
135
|
+
// Internal Helper.
|
|
105
136
|
/**
|
|
106
|
-
* Spawns an FFmpeg process
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
* FFmpeg
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* - `-i pipe:0`: Read input from stdin
|
|
113
|
-
* - `-c:v copy`: Copy video stream without re-encoding (H264 passthrough)
|
|
114
|
-
* - `-c:a aac -b:a <bitrate>`: Transcode audio to AAC at specified bitrate
|
|
115
|
-
* - `-f mp4`: Output MP4 container format
|
|
116
|
-
* - `-movflags frag_keyframe+empty_moov+default_base_moof`: Streaming-friendly fMP4 flags
|
|
117
|
-
* - `-flush_packets 1`: Flush output immediately after each packet to minimize latency
|
|
118
|
-
* - `pipe:1`: Write output to stdout
|
|
119
|
-
* @param audioBitrate - Audio bitrate in bits per second (e.g., 256000 for 256 kbps).
|
|
120
|
-
* @param onError - Callback invoked when FFmpeg exits unexpectedly or encounters an error.
|
|
121
|
-
* @param streamId - Stream identifier for logging.
|
|
122
|
-
* @param comment - Optional comment metadata (channel name or domain) to embed in the output.
|
|
137
|
+
* Spawns an FFmpeg process with shared lifecycle management. Handles stderr logging with noise filtering, exit and error callbacks, and graceful shutdown for all
|
|
138
|
+
* FFmpeg use cases. Callers provide the FFmpeg arguments and a descriptive label; this helper handles everything else.
|
|
139
|
+
* @param options.args - FFmpeg command-line arguments.
|
|
140
|
+
* @param options.label - Descriptive label for log messages and error strings (e.g., "FFmpeg", "MPEG-TS remuxer").
|
|
141
|
+
* @param options.onError - Callback invoked when FFmpeg exits unexpectedly or encounters an error.
|
|
142
|
+
* @param options.streamId - Optional stream identifier for log prefixing.
|
|
123
143
|
* @returns FFmpeg process wrapper with stdin, stdout, and kill function.
|
|
124
144
|
*/
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const ffmpegPath = cachedFFmpegPath ?? "ffmpeg";
|
|
129
|
-
// Use Apple's AudioToolbox AAC encoder on macOS for better quality and performance. Fall back to FFmpeg's built-in AAC encoder on other platforms.
|
|
130
|
-
const aacEncoder = process.platform === "darwin" ? "aac_at" : "aac";
|
|
131
|
-
const ffmpegArgs = [
|
|
132
|
-
"-hide_banner",
|
|
133
|
-
"-loglevel", "warning",
|
|
134
|
-
"-probesize", "16384",
|
|
135
|
-
"-i", "pipe:0",
|
|
136
|
-
"-c:v", "copy",
|
|
137
|
-
"-c:a", aacEncoder,
|
|
138
|
-
"-b:a", String(audioBitrate),
|
|
139
|
-
"-f", "mp4",
|
|
140
|
-
"-movflags", "frag_keyframe+empty_moov+default_base_moof+skip_sidx+skip_trailer",
|
|
141
|
-
"-flush_packets", "1"
|
|
142
|
-
];
|
|
143
|
-
// Add metadata comment if provided. This embeds "PrismCast - <channel>" in the output for identification.
|
|
144
|
-
if (comment) {
|
|
145
|
-
ffmpegArgs.push("-metadata", "comment=PrismCast - " + comment);
|
|
146
|
-
}
|
|
147
|
-
ffmpegArgs.push("pipe:1");
|
|
148
|
-
const ffmpeg = spawn(ffmpegPath, ffmpegArgs, {
|
|
149
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
150
|
-
});
|
|
145
|
+
function spawnFFmpegProcess({ args, label, onError, streamId }) {
|
|
146
|
+
const ffmpegBin = cachedFFmpegPath ?? "ffmpeg";
|
|
147
|
+
const ffmpeg = spawn(ffmpegBin, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
151
148
|
const logPrefix = streamId ? "[" + streamId + "] " : "";
|
|
152
149
|
// Track whether graceful shutdown has been initiated. When true, we suppress error callbacks because any exit (whether from SIGTERM or stdin close) is expected.
|
|
153
150
|
let shuttingDown = false;
|
|
154
|
-
// Log FFmpeg stderr output (warnings and errors).
|
|
151
|
+
// Log FFmpeg stderr output (warnings and errors). Suppress noise patterns that are not actionable.
|
|
155
152
|
ffmpeg.stderr.on("data", (data) => {
|
|
156
|
-
// Suppress warnings during shutdown - truncated input warnings are expected when the capture stream closes.
|
|
157
153
|
if (shuttingDown) {
|
|
158
154
|
return;
|
|
159
155
|
}
|
|
160
156
|
const message = data.toString().trim();
|
|
161
|
-
|
|
162
|
-
const noisePatterns = ["Press [q] to stop", "frame=", "size=", "time=", "bitrate=", "speed="];
|
|
163
|
-
if (noisePatterns.some((pattern) => message.includes(pattern))) {
|
|
157
|
+
if (FFMPEG_NOISE_PATTERNS.some((pattern) => message.includes(pattern))) {
|
|
164
158
|
return;
|
|
165
159
|
}
|
|
166
160
|
if (message.length > 0) {
|
|
167
|
-
LOG.debug("streaming:ffmpeg", "%
|
|
161
|
+
LOG.debug("streaming:ffmpeg", "%s%s: %s", logPrefix, label, message);
|
|
168
162
|
}
|
|
169
163
|
});
|
|
170
164
|
// Handle FFmpeg process exit.
|
|
171
165
|
ffmpeg.on("exit", (code, signal) => {
|
|
172
|
-
// During graceful shutdown, any exit is expected (whether from SIGTERM or stdin closing).
|
|
173
166
|
if (shuttingDown) {
|
|
174
167
|
return;
|
|
175
168
|
}
|
|
176
169
|
if (signal === "SIGTERM") {
|
|
177
|
-
// Normal termination via kill()
|
|
170
|
+
// Normal termination via kill() — do not treat as error.
|
|
178
171
|
return;
|
|
179
172
|
}
|
|
180
173
|
if ((code !== null) && (code !== 0)) {
|
|
181
|
-
onError(new Error("
|
|
174
|
+
onError(new Error(label + " exited with code " + String(code) + "."));
|
|
182
175
|
}
|
|
183
176
|
else if (signal) {
|
|
184
|
-
onError(new Error("
|
|
177
|
+
onError(new Error(label + " killed by signal " + signal + "."));
|
|
185
178
|
}
|
|
186
179
|
});
|
|
187
180
|
// Handle spawn errors (e.g., FFmpeg not found).
|
|
188
181
|
ffmpeg.on("error", (error) => {
|
|
189
|
-
// During graceful shutdown, suppress errors from stdin pipe closing.
|
|
190
182
|
if (shuttingDown) {
|
|
191
183
|
return;
|
|
192
184
|
}
|
|
193
185
|
onError(error);
|
|
194
186
|
});
|
|
195
|
-
// Kill function for graceful shutdown. Sets the shuttingDown flag before sending SIGTERM so that any exit
|
|
196
|
-
// stream ending) is treated as normal termination.
|
|
187
|
+
// Kill function for graceful shutdown. Sets the shuttingDown flag before sending SIGTERM so that any exit is treated as normal termination.
|
|
197
188
|
const kill = () => {
|
|
198
189
|
shuttingDown = true;
|
|
199
190
|
if (!ffmpeg.killed) {
|
|
@@ -207,104 +198,93 @@ export function spawnFFmpeg(audioBitrate, onError, streamId, comment) {
|
|
|
207
198
|
stdout: ffmpeg.stdout
|
|
208
199
|
};
|
|
209
200
|
}
|
|
201
|
+
// Public FFmpeg Spawners.
|
|
202
|
+
/**
|
|
203
|
+
* Spawns an FFmpeg process configured to remux Matroska to fMP4. Video is passed through unchanged (codec copy); audio is transcoded from Opus to AAC for HLS
|
|
204
|
+
* compatibility. The video codec (H264 or HEVC) is determined by Chrome's MediaRecorder based on hardware capabilities. The process reads from stdin and writes
|
|
205
|
+
* to stdout, allowing it to be integrated into a Node.js stream pipeline.
|
|
206
|
+
*
|
|
207
|
+
* FFmpeg arguments:
|
|
208
|
+
* - `-hide_banner -loglevel warning`: Reduce noise, only show warnings/errors
|
|
209
|
+
* - `-probesize 16384`: Limit input probing to 16KB (Chrome's Matroska header fits well under this) to minimize startup delay
|
|
210
|
+
* - `-i pipe:0`: Read input from stdin
|
|
211
|
+
* - `-c:v copy`: Copy video stream without re-encoding (passthrough)
|
|
212
|
+
* - `-c:a aac -b:a <bitrate>`: Transcode audio to AAC at specified bitrate
|
|
213
|
+
* - `-f mp4`: Output MP4 container format
|
|
214
|
+
* - `-movflags frag_keyframe+empty_moov+default_base_moof`: Streaming-friendly fMP4 flags
|
|
215
|
+
* - `-flush_packets 1`: Flush output immediately after each packet to minimize latency
|
|
216
|
+
* - `pipe:1`: Write output to stdout
|
|
217
|
+
* @param audioBitrate - Audio bitrate in bits per second (e.g., 256000 for 256 kbps).
|
|
218
|
+
* @param onError - Callback invoked when FFmpeg exits unexpectedly or encounters an error.
|
|
219
|
+
* @param streamId - Stream identifier for logging.
|
|
220
|
+
* @param comment - Optional comment metadata (channel name or domain) to embed in the output.
|
|
221
|
+
* @returns FFmpeg process wrapper with stdin, stdout, and kill function.
|
|
222
|
+
*/
|
|
223
|
+
export function spawnFFmpeg(audioBitrate, onError, streamId, comment) {
|
|
224
|
+
// Use Apple's AudioToolbox AAC encoder on macOS for better quality and performance. Fall back to FFmpeg's built-in AAC encoder on other platforms.
|
|
225
|
+
const aacEncoder = process.platform === "darwin" ? "aac_at" : "aac";
|
|
226
|
+
const ffmpegArgs = [
|
|
227
|
+
"-hide_banner",
|
|
228
|
+
"-nostats",
|
|
229
|
+
"-loglevel", "warning",
|
|
230
|
+
"-fflags", "+discardcorrupt",
|
|
231
|
+
"-err_detect", "ignore_err",
|
|
232
|
+
"-probesize", "16384",
|
|
233
|
+
"-i", "pipe:0",
|
|
234
|
+
"-c:v", "copy",
|
|
235
|
+
"-c:a", aacEncoder,
|
|
236
|
+
"-b:a", String(audioBitrate),
|
|
237
|
+
"-f", "mp4",
|
|
238
|
+
"-movflags", "frag_keyframe+empty_moov+default_base_moof+skip_sidx+skip_trailer",
|
|
239
|
+
"-flush_packets", "1"
|
|
240
|
+
];
|
|
241
|
+
// Add metadata comment if provided. This embeds "PrismCast - <channel>" in the output for identification.
|
|
242
|
+
if (comment) {
|
|
243
|
+
ffmpegArgs.push("-metadata", "comment=PrismCast - " + comment);
|
|
244
|
+
}
|
|
245
|
+
ffmpegArgs.push("pipe:1");
|
|
246
|
+
return spawnFFmpegProcess({
|
|
247
|
+
args: ffmpegArgs,
|
|
248
|
+
label: "FFmpeg",
|
|
249
|
+
onError,
|
|
250
|
+
streamId
|
|
251
|
+
});
|
|
252
|
+
}
|
|
210
253
|
/**
|
|
211
254
|
* Spawns an FFmpeg process configured to remux fMP4 input to MPEG-TS output with codec copy. The process reads a continuous fMP4 stream (init segment followed by
|
|
212
|
-
* media segments) from stdin and writes MPEG-TS to stdout. No transcoding occurs — both video
|
|
255
|
+
* media segments) from stdin and writes MPEG-TS to stdout. No transcoding occurs — both video and audio are copied unchanged — so CPU usage is minimal.
|
|
213
256
|
*
|
|
214
257
|
* FFmpeg arguments:
|
|
215
258
|
* - `-hide_banner -loglevel warning`: Reduce noise, only show warnings/errors
|
|
216
259
|
* - `-probesize 16384`: Limit input probing to 16KB (fMP4 init segment is ~1.3KB) to minimize startup delay
|
|
217
260
|
* - `-f mp4 -i pipe:0`: Read fragmented MP4 from stdin
|
|
218
261
|
* - `-c copy`: Copy both video and audio codecs without transcoding
|
|
219
|
-
* -
|
|
220
|
-
* - `-mpegts_pmt_start_pid 0x0020`: Use ATSC-conventional PMT PID range instead of FFmpeg's default (0x1000). Minimum allowed value is 0x0020 (32).
|
|
221
|
-
* - `-mpegts_start_pid 0x0031`: Use ATSC-conventional elementary stream PIDs instead of FFmpeg's defaults (0x100+)
|
|
222
|
-
* - `-mpegts_service_type digital_tv`: Label the service as digital TV in the PMT service descriptor
|
|
223
|
-
* - `-pat_period 0.1`: Repeat PAT/PMT tables every 100ms, matching ATSC broadcast frequency
|
|
224
|
-
* - `-pcr_period 40`: Insert PCR timestamps every 40ms, matching ATSC broadcast convention
|
|
225
|
-
* - `-flush_packets 1`: Flush output immediately after each packet to minimize latency
|
|
262
|
+
* - MPEG-TS output flags: See MPEGTS_OUTPUT_FLAGS for ATSC-conventional settings
|
|
226
263
|
* - `pipe:1`: Write output to stdout
|
|
227
264
|
* @param onError - Callback invoked when FFmpeg exits unexpectedly or encounters an error.
|
|
228
265
|
* @param streamId - Optional stream identifier for logging.
|
|
229
266
|
* @returns FFmpeg process wrapper with stdin, stdout, and kill function.
|
|
230
267
|
*/
|
|
231
268
|
export function spawnMpegTsRemuxer(onError, streamId) {
|
|
232
|
-
const ffmpegBin = cachedFFmpegPath ?? "ffmpeg";
|
|
233
|
-
// MPEG-TS muxer flags are tuned to produce output resembling a real HDHomeRun CONNECT DUO (HDTC-2US) ATSC transport stream. Plex's transcoder may make
|
|
234
|
-
// assumptions about stream structure based on the reported device model (PID assignments, PAT/PMT frequency). Using ATSC-conventional values avoids "Invalid
|
|
235
|
-
// argument" failures when Plex tries to transcode the live session for remote clients. These are pure container metadata changes — the actual A/V data is
|
|
236
|
-
// untouched by -c copy.
|
|
237
269
|
const ffmpegArgs = [
|
|
238
270
|
"-hide_banner",
|
|
271
|
+
"-nostats",
|
|
239
272
|
"-loglevel", "warning",
|
|
273
|
+
"-fflags", "+discardcorrupt",
|
|
274
|
+
"-err_detect", "ignore_err",
|
|
240
275
|
"-probesize", "16384",
|
|
241
276
|
"-f", "mp4",
|
|
242
277
|
"-i", "pipe:0",
|
|
243
278
|
"-c", "copy",
|
|
244
|
-
|
|
245
|
-
"-mpegts_pmt_start_pid", "0x0020",
|
|
246
|
-
"-mpegts_start_pid", "0x0031",
|
|
247
|
-
"-mpegts_service_type", "digital_tv",
|
|
248
|
-
"-pat_period", "0.1",
|
|
249
|
-
"-pcr_period", "40",
|
|
250
|
-
"-flush_packets", "1",
|
|
279
|
+
...MPEGTS_OUTPUT_FLAGS,
|
|
251
280
|
"pipe:1"
|
|
252
281
|
];
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
let shuttingDown = false;
|
|
259
|
-
// Log FFmpeg stderr output (warnings and errors).
|
|
260
|
-
ffmpeg.stderr.on("data", (data) => {
|
|
261
|
-
if (shuttingDown) {
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
const message = data.toString().trim();
|
|
265
|
-
const noisePatterns = ["Press [q] to stop", "frame=", "size=", "time=", "bitrate=", "speed="];
|
|
266
|
-
if (noisePatterns.some((pattern) => message.includes(pattern))) {
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
if (message.length > 0) {
|
|
270
|
-
LOG.debug("streaming:ffmpeg", "%sMPEG-TS remuxer: %s", logPrefix, message);
|
|
271
|
-
}
|
|
272
|
-
});
|
|
273
|
-
// Handle FFmpeg process exit.
|
|
274
|
-
ffmpeg.on("exit", (code, signal) => {
|
|
275
|
-
if (shuttingDown) {
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
if (signal === "SIGTERM") {
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
281
|
-
if ((code !== null) && (code !== 0)) {
|
|
282
|
-
onError(new Error("MPEG-TS remuxer exited with code " + String(code) + "."));
|
|
283
|
-
}
|
|
284
|
-
else if (signal) {
|
|
285
|
-
onError(new Error("MPEG-TS remuxer killed by signal " + signal + "."));
|
|
286
|
-
}
|
|
282
|
+
return spawnFFmpegProcess({
|
|
283
|
+
args: ffmpegArgs,
|
|
284
|
+
label: "MPEG-TS remuxer",
|
|
285
|
+
onError,
|
|
286
|
+
streamId
|
|
287
287
|
});
|
|
288
|
-
// Handle spawn errors (e.g., FFmpeg not found).
|
|
289
|
-
ffmpeg.on("error", (error) => {
|
|
290
|
-
if (shuttingDown) {
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
onError(error);
|
|
294
|
-
});
|
|
295
|
-
// Kill function for graceful shutdown.
|
|
296
|
-
const kill = () => {
|
|
297
|
-
shuttingDown = true;
|
|
298
|
-
if (!ffmpeg.killed) {
|
|
299
|
-
ffmpeg.kill("SIGTERM");
|
|
300
|
-
}
|
|
301
|
-
};
|
|
302
|
-
return {
|
|
303
|
-
kill,
|
|
304
|
-
process: ffmpeg,
|
|
305
|
-
stdin: ffmpeg.stdin,
|
|
306
|
-
stdout: ffmpeg.stdout
|
|
307
|
-
};
|
|
308
288
|
}
|
|
309
289
|
/**
|
|
310
290
|
* Checks if FFmpeg is available on the system. This resolves the FFmpeg path and caches it for use by spawnFFmpeg().
|
package/dist/utils/ffmpeg.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ffmpeg.js","sourceRoot":"","sources":["../../src/utils/ffmpeg.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ffmpeg.js","sourceRoot":"","sources":["../../src/utils/ffmpeg.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/format.d.ts
CHANGED
|
@@ -21,3 +21,9 @@ export declare function formatTimeAgo(timestamp: number): string;
|
|
|
21
21
|
* @returns The concise domain, or the original URL if parsing fails.
|
|
22
22
|
*/
|
|
23
23
|
export declare function extractDomain(url: string): string;
|
|
24
|
+
/**
|
|
25
|
+
* Capitalizes the first letter of a string.
|
|
26
|
+
* @param str - The string to capitalize.
|
|
27
|
+
* @returns The string with the first letter capitalized.
|
|
28
|
+
*/
|
|
29
|
+
export declare function capitalize(str: string): string;
|
package/dist/utils/format.js
CHANGED
|
@@ -65,4 +65,12 @@ export function extractDomain(url) {
|
|
|
65
65
|
return url;
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Capitalizes the first letter of a string.
|
|
70
|
+
* @param str - The string to capitalize.
|
|
71
|
+
* @returns The string with the first letter capitalized.
|
|
72
|
+
*/
|
|
73
|
+
export function capitalize(str) {
|
|
74
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
75
|
+
}
|
|
68
76
|
//# sourceMappingURL=format.js.map
|
package/dist/utils/format.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,EAAU;IAEvC,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,YAAY,GAAG,EAAE,CAAC;IAElC,IAAG,KAAK,GAAG,CAAC,EAAE,CAAC;QAEb,OAAO,CAAE,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,GAAG,CAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,IAAG,OAAO,GAAG,CAAC,EAAE,CAAC;QAEf,OAAO,CAAE,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,GAAG,CAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,OAAO,CAAE,MAAM,CAAC,OAAO,CAAC,EAAE,GAAG,CAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,SAAiB;IAE7C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;IAE5D,IAAG,OAAO,GAAG,EAAE,EAAE,CAAC;QAEhB,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IAEzC,IAAG,OAAO,GAAG,EAAE,EAAE,CAAC;QAEhB,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;IAC5E,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IAEvC,IAAG,KAAK,GAAG,EAAE,EAAE,CAAC;QAEd,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;IACpE,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;IAEpC,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;AAChE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW;IAEvC,IAAI,CAAC;QAEH,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;QACvC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAElC,uHAAuH;QACvH,IAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAEpB,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnC,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAAC,MAAM,CAAC;QAEP,OAAO,GAAG,CAAC;IACb,CAAC;AACH,CAAC"}
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export * from "./chromeFetch.js";
|
|
1
2
|
export * from "./debugFilter.js";
|
|
2
3
|
export * from "./delay.js";
|
|
3
4
|
export * from "./errors.js";
|
|
@@ -9,8 +10,10 @@ export * from "./logEmitter.js";
|
|
|
9
10
|
export * from "./logger.js";
|
|
10
11
|
export * from "./m3u.js";
|
|
11
12
|
export * from "./morganStream.js";
|
|
13
|
+
export * from "./pid.js";
|
|
12
14
|
export * from "./platform.js";
|
|
13
15
|
export * from "./retry.js";
|
|
14
16
|
export * from "./sanitize.js";
|
|
15
17
|
export * from "./streamContext.js";
|
|
18
|
+
export * from "./timing.js";
|
|
16
19
|
export * from "./version.js";
|
package/dist/utils/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
*
|
|
3
3
|
* index.ts: Utility module exports for PrismCast.
|
|
4
4
|
*/
|
|
5
|
+
export * from "./chromeFetch.js";
|
|
5
6
|
export * from "./debugFilter.js";
|
|
6
7
|
export * from "./delay.js";
|
|
7
8
|
export * from "./errors.js";
|
|
@@ -13,9 +14,11 @@ export * from "./logEmitter.js";
|
|
|
13
14
|
export * from "./logger.js";
|
|
14
15
|
export * from "./m3u.js";
|
|
15
16
|
export * from "./morganStream.js";
|
|
17
|
+
export * from "./pid.js";
|
|
16
18
|
export * from "./platform.js";
|
|
17
19
|
export * from "./retry.js";
|
|
18
20
|
export * from "./sanitize.js";
|
|
19
21
|
export * from "./streamContext.js";
|
|
22
|
+
export * from "./timing.js";
|
|
20
23
|
export * from "./version.js";
|
|
21
24
|
//# sourceMappingURL=index.js.map
|
package/dist/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC"}
|
package/dist/utils/logEmitter.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
*
|
|
3
3
|
* logEmitter.ts: Event emitter for real-time log streaming via SSE.
|
|
4
4
|
*/
|
|
5
|
-
import { EventEmitter } from "events";
|
|
5
|
+
import { EventEmitter } from "node:events";
|
|
6
6
|
/* A singleton EventEmitter that broadcasts log entries to all subscribed SSE clients. When a log entry is written via LOG.info/warn/error, the entry is emitted here
|
|
7
7
|
* for real-time streaming to connected browsers.
|
|
8
8
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logEmitter.js","sourceRoot":"","sources":["../../src/utils/logEmitter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"logEmitter.js","sourceRoot":"","sources":["../../src/utils/logEmitter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAc3C;;GAEG;AAEH,MAAM,UAAU,GAAG,IAAI,YAAY,EAAE,CAAC;AAEtC,oJAAoJ;AACpJ,UAAU,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;AAEhC;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,KAAe;IAE1C,UAAU,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAChC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,QAAmC;IAEjE,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAE/B,OAAO,GAAS,EAAE;QAEhB,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAClC,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Nullable } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Checks whether a process with the given PID is still running. Uses the signal 0 technique: process.kill(pid, 0) throws ESRCH if the process does not exist,
|
|
4
|
+
* returns successfully if it does. EPERM (permission denied) means the process exists but belongs to another user — treated as "still running" since we cannot
|
|
5
|
+
* kill it anyway. This is cross-platform and works on macOS, Linux, and Windows without external tools.
|
|
6
|
+
* @param pid - The process ID to check.
|
|
7
|
+
* @returns True if the process is running, false if it has exited.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isProcessRunning(pid: number): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Reads a PID from a file on disk. Returns null if the file does not exist, is empty, or contains non-numeric content. ENOENT is expected on first run or after a
|
|
12
|
+
* clean shutdown and is silently ignored.
|
|
13
|
+
* @param filePath - The absolute path to the PID file.
|
|
14
|
+
* @param label - A descriptive label for log messages (e.g., "Chrome", "server").
|
|
15
|
+
* @param log - Optional logger for non-ENOENT read errors. When omitted (e.g., before the logger is initialized), errors are silently ignored.
|
|
16
|
+
* @returns The stored process ID, or null if unavailable.
|
|
17
|
+
*/
|
|
18
|
+
export declare function readPidFile(filePath: string, label: string, log?: {
|
|
19
|
+
warn: (message: string, ...args: unknown[]) => void;
|
|
20
|
+
}): Nullable<number>;
|
|
21
|
+
/**
|
|
22
|
+
* Writes a PID to a file on disk. Used to persist the current process ID so that the next startup can detect a running instance or clean up an orphaned process.
|
|
23
|
+
* @param filePath - The absolute path to the PID file.
|
|
24
|
+
* @param pid - The process ID to write.
|
|
25
|
+
* @param label - A descriptive label for log messages (e.g., "Chrome", "server").
|
|
26
|
+
* @param log - Optional logger for write errors. When omitted, errors are silently ignored.
|
|
27
|
+
*/
|
|
28
|
+
export declare function writePidFile(filePath: string, pid: number, label: string, log?: {
|
|
29
|
+
warn: (message: string, ...args: unknown[]) => void;
|
|
30
|
+
}): void;
|
|
31
|
+
/**
|
|
32
|
+
* Removes a PID file from disk. Called during graceful shutdown and from exit handlers as a fallback. ENOENT is expected when the file has already been removed and
|
|
33
|
+
* is silently ignored.
|
|
34
|
+
* @param filePath - The absolute path to the PID file.
|
|
35
|
+
* @param label - A descriptive label for log messages (e.g., "Chrome", "server").
|
|
36
|
+
* @param log - Optional logger for non-ENOENT removal errors. When omitted, errors are silently ignored.
|
|
37
|
+
*/
|
|
38
|
+
export declare function clearPidFile(filePath: string, label: string, log?: {
|
|
39
|
+
warn: (message: string, ...args: unknown[]) => void;
|
|
40
|
+
}): void;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
/* PID files provide a lightweight mechanism for tracking running processes across restarts and crashes. Each PID file stores a single process ID as a plain text
|
|
3
|
+
* integer. On startup, the stored PID is checked via signal 0 to determine whether the process is still alive. Stale PID files from crashed or terminated processes
|
|
4
|
+
* are handled gracefully — ENOENT on read and ESRCH on signal check are both expected and silently ignored.
|
|
5
|
+
*
|
|
6
|
+
* This module is intentionally synchronous. PID file operations run in contexts where the event loop may not be available (process.on("exit") handlers), so all
|
|
7
|
+
* I/O uses the synchronous fs API and process.kill() for signaling.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Checks whether a process with the given PID is still running. Uses the signal 0 technique: process.kill(pid, 0) throws ESRCH if the process does not exist,
|
|
11
|
+
* returns successfully if it does. EPERM (permission denied) means the process exists but belongs to another user — treated as "still running" since we cannot
|
|
12
|
+
* kill it anyway. This is cross-platform and works on macOS, Linux, and Windows without external tools.
|
|
13
|
+
* @param pid - The process ID to check.
|
|
14
|
+
* @returns True if the process is running, false if it has exited.
|
|
15
|
+
*/
|
|
16
|
+
export function isProcessRunning(pid) {
|
|
17
|
+
try {
|
|
18
|
+
process.kill(pid, 0);
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
// EPERM means the process exists but we lack permission to signal it. Treat as running.
|
|
23
|
+
return error.code === "EPERM";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Reads a PID from a file on disk. Returns null if the file does not exist, is empty, or contains non-numeric content. ENOENT is expected on first run or after a
|
|
28
|
+
* clean shutdown and is silently ignored.
|
|
29
|
+
* @param filePath - The absolute path to the PID file.
|
|
30
|
+
* @param label - A descriptive label for log messages (e.g., "Chrome", "server").
|
|
31
|
+
* @param log - Optional logger for non-ENOENT read errors. When omitted (e.g., before the logger is initialized), errors are silently ignored.
|
|
32
|
+
* @returns The stored process ID, or null if unavailable.
|
|
33
|
+
*/
|
|
34
|
+
export function readPidFile(filePath, label, log) {
|
|
35
|
+
try {
|
|
36
|
+
const content = fs.readFileSync(filePath, "utf-8").trim();
|
|
37
|
+
const pid = parseInt(content, 10);
|
|
38
|
+
if (!isNaN(pid)) {
|
|
39
|
+
return pid;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if ((error.code !== "ENOENT") && log) {
|
|
44
|
+
log.warn("Failed to read %s PID file: %s.", label, error.message);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Writes a PID to a file on disk. Used to persist the current process ID so that the next startup can detect a running instance or clean up an orphaned process.
|
|
51
|
+
* @param filePath - The absolute path to the PID file.
|
|
52
|
+
* @param pid - The process ID to write.
|
|
53
|
+
* @param label - A descriptive label for log messages (e.g., "Chrome", "server").
|
|
54
|
+
* @param log - Optional logger for write errors. When omitted, errors are silently ignored.
|
|
55
|
+
*/
|
|
56
|
+
export function writePidFile(filePath, pid, label, log) {
|
|
57
|
+
try {
|
|
58
|
+
fs.writeFileSync(filePath, String(pid), "utf-8");
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
if (log) {
|
|
62
|
+
log.warn("Failed to write %s PID file: %s.", label, error.message);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Removes a PID file from disk. Called during graceful shutdown and from exit handlers as a fallback. ENOENT is expected when the file has already been removed and
|
|
68
|
+
* is silently ignored.
|
|
69
|
+
* @param filePath - The absolute path to the PID file.
|
|
70
|
+
* @param label - A descriptive label for log messages (e.g., "Chrome", "server").
|
|
71
|
+
* @param log - Optional logger for non-ENOENT removal errors. When omitted, errors are silently ignored.
|
|
72
|
+
*/
|
|
73
|
+
export function clearPidFile(filePath, label, log) {
|
|
74
|
+
try {
|
|
75
|
+
fs.unlinkSync(filePath);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
if ((error.code !== "ENOENT") && log) {
|
|
79
|
+
log.warn("Failed to remove %s PID file: %s.", label, error.message);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=pid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pid.js","sourceRoot":"","sources":["../../src/utils/pid.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB;;;;;;GAMG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAE1C,IAAI,CAAC;QAEH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAErB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAM,KAAc,EAAE,CAAC;QAEvB,wFAAwF;QACxF,OAAQ,KAA+B,CAAC,IAAI,KAAK,OAAO,CAAC;IAC3D,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,QAAgB,EAAE,KAAa,EAAE,GAA6D;IAExH,IAAI,CAAC;QAEH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1D,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAElC,IAAG,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YAEf,OAAO,GAAG,CAAC;QACb,CAAC;IACH,CAAC;IAAC,OAAM,KAAc,EAAE,CAAC;QAEvB,IAAG,CAAE,KAA+B,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,GAAG,EAAE,CAAC;YAE/D,GAAG,CAAC,IAAI,CAAC,iCAAiC,EAAE,KAAK,EAAG,KAAe,CAAC,OAAO,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,GAAW,EAAE,KAAa,EAAE,GAA6D;IAEtI,IAAI,CAAC;QAEH,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC;IAAC,OAAM,KAAc,EAAE,CAAC;QAEvB,IAAG,GAAG,EAAE,CAAC;YAEP,GAAG,CAAC,IAAI,CAAC,kCAAkC,EAAE,KAAK,EAAG,KAAe,CAAC,OAAO,CAAC,CAAC;QAChF,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,KAAa,EAAE,GAA6D;IAEzH,IAAI,CAAC;QAEH,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC1B,CAAC;IAAC,OAAM,KAAc,EAAE,CAAC;QAEvB,IAAG,CAAE,KAA+B,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,GAAG,EAAE,CAAC;YAE/D,GAAG,CAAC,IAAI,CAAC,mCAAmC,EAAE,KAAK,EAAG,KAAe,CAAC,OAAO,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;AACH,CAAC"}
|