prismcast 1.10.1 → 1.10.3

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.
Files changed (220) hide show
  1. package/dist/app.d.ts +5 -3
  2. package/dist/app.js +48 -43
  3. package/dist/app.js.map +1 -1
  4. package/dist/browser/hlsPlaylistObserver.d.ts +38 -0
  5. package/dist/browser/hlsPlaylistObserver.js +113 -0
  6. package/dist/browser/hlsPlaylistObserver.js.map +1 -0
  7. package/dist/browser/index.d.ts +26 -16
  8. package/dist/browser/index.js +97 -72
  9. package/dist/browser/index.js.map +1 -1
  10. package/dist/browser/manifestInterceptor.d.ts +29 -29
  11. package/dist/browser/manifestInterceptor.js +106 -212
  12. package/dist/browser/manifestInterceptor.js.map +1 -1
  13. package/dist/browser/tabNetworkObserver.d.ts +48 -0
  14. package/dist/browser/tabNetworkObserver.js +140 -0
  15. package/dist/browser/tabNetworkObserver.js.map +1 -0
  16. package/dist/browser/tuning/hbo.js +31 -123
  17. package/dist/browser/tuning/hbo.js.map +1 -1
  18. package/dist/browser/tuning/hulu.js +70 -7
  19. package/dist/browser/tuning/hulu.js.map +1 -1
  20. package/dist/browser/video.js +1 -1
  21. package/dist/browser/video.js.map +1 -1
  22. package/dist/config/consistencyProbe.js +13 -11
  23. package/dist/config/consistencyProbe.js.map +1 -1
  24. package/dist/config/index.d.ts +6 -2
  25. package/dist/config/index.js +35 -21
  26. package/dist/config/index.js.map +1 -1
  27. package/dist/config/paths.d.ts +29 -8
  28. package/dist/config/paths.js +54 -10
  29. package/dist/config/paths.js.map +1 -1
  30. package/dist/config/presets.js +2 -2
  31. package/dist/config/presets.js.map +1 -1
  32. package/dist/config/profiles.d.ts +1 -1
  33. package/dist/config/profiles.js +12 -12
  34. package/dist/config/profiles.js.map +1 -1
  35. package/dist/config/services.d.ts +5 -5
  36. package/dist/config/services.js +8 -8
  37. package/dist/config/services.js.map +1 -1
  38. package/dist/config/sites.d.ts +2 -2
  39. package/dist/config/sites.js +6 -6
  40. package/dist/config/sites.js.map +1 -1
  41. package/dist/config/userChannels.d.ts +108 -14
  42. package/dist/config/userChannels.js +292 -38
  43. package/dist/config/userChannels.js.map +1 -1
  44. package/dist/config/userConfig.js +2 -2
  45. package/dist/config/userConfig.js.map +1 -1
  46. package/dist/config/userProfiles.d.ts +3 -3
  47. package/dist/config/userProfiles.js +19 -19
  48. package/dist/config/userProfiles.js.map +1 -1
  49. package/dist/hdhr/discover.js +3 -4
  50. package/dist/hdhr/discover.js.map +1 -1
  51. package/dist/identity.d.ts +2 -0
  52. package/dist/identity.js +19 -0
  53. package/dist/identity.js.map +1 -0
  54. package/dist/index.js +7 -9
  55. package/dist/index.js.map +1 -1
  56. package/dist/native/index.d.ts +1 -1
  57. package/dist/native/index.js +9 -18
  58. package/dist/native/index.js.map +1 -1
  59. package/dist/native/proxy.d.ts +1 -4
  60. package/dist/native/proxy.js +0 -9
  61. package/dist/native/proxy.js.map +1 -1
  62. package/dist/routes/cdp.d.ts +171 -0
  63. package/dist/routes/cdp.js +702 -0
  64. package/dist/routes/cdp.js.map +1 -0
  65. package/dist/routes/clientActions.d.ts +97 -0
  66. package/dist/routes/clientActions.js +113 -0
  67. package/dist/routes/clientActions.js.map +1 -0
  68. package/dist/routes/components.d.ts +4 -3
  69. package/dist/routes/components.js +36 -39
  70. package/dist/routes/components.js.map +1 -1
  71. package/dist/routes/config/channels/endpoints/browse.js.map +1 -1
  72. package/dist/routes/config/channels/endpoints/crud.js +37 -72
  73. package/dist/routes/config/channels/endpoints/crud.js.map +1 -1
  74. package/dist/routes/config/channels/endpoints/importExport.js.map +1 -1
  75. package/dist/routes/config/channels/healthBridge.d.ts +23 -0
  76. package/dist/routes/config/channels/healthBridge.js +71 -0
  77. package/dist/routes/config/channels/healthBridge.js.map +1 -0
  78. package/dist/routes/config/channels/table.js +97 -84
  79. package/dist/routes/config/channels/table.js.map +1 -1
  80. package/dist/routes/config/services.js +26 -23
  81. package/dist/routes/config/services.js.map +1 -1
  82. package/dist/routes/config/settings.js +9 -6
  83. package/dist/routes/config/settings.js.map +1 -1
  84. package/dist/routes/debug.js +277 -139
  85. package/dist/routes/debug.js.map +1 -1
  86. package/dist/routes/index.js +2 -0
  87. package/dist/routes/index.js.map +1 -1
  88. package/dist/routes/logs.js +1 -1
  89. package/dist/routes/logs.js.map +1 -1
  90. package/dist/routes/root/content.js +18 -14
  91. package/dist/routes/root/content.js.map +1 -1
  92. package/dist/routes/root/index.js +25 -28
  93. package/dist/routes/root/index.js.map +1 -1
  94. package/dist/routes/root/scripts/channels.js +50 -21
  95. package/dist/routes/root/scripts/channels.js.map +1 -1
  96. package/dist/routes/root/scripts/config.js +72 -3
  97. package/dist/routes/root/scripts/config.js.map +1 -1
  98. package/dist/routes/root/scripts/shared.js +60 -0
  99. package/dist/routes/root/scripts/shared.js.map +1 -1
  100. package/dist/routes/root/scripts/status.handlers.d.ts +5 -27
  101. package/dist/routes/root/scripts/status.handlers.js +8 -60
  102. package/dist/routes/root/scripts/status.handlers.js.map +1 -1
  103. package/dist/routes/root/scripts/status.js +6 -1
  104. package/dist/routes/root/scripts/status.js.map +1 -1
  105. package/dist/routes/streams.js +5 -3
  106. package/dist/routes/streams.js.map +1 -1
  107. package/dist/service/commands.context.js +2 -2
  108. package/dist/service/commands.context.js.map +1 -1
  109. package/dist/service/commands.d.ts +1 -1
  110. package/dist/service/commands.js +1 -10
  111. package/dist/service/commands.js.map +1 -1
  112. package/dist/service/generators.context.js +2 -1
  113. package/dist/service/generators.context.js.map +1 -1
  114. package/dist/service/generators.d.ts +1 -1
  115. package/dist/service/generators.js +6 -19
  116. package/dist/service/generators.js.map +1 -1
  117. package/dist/streaming/clients.js +3 -11
  118. package/dist/streaming/clients.js.map +1 -1
  119. package/dist/streaming/fmp4Segmenter.js +1 -1
  120. package/dist/streaming/fmp4Segmenter.js.map +1 -1
  121. package/dist/streaming/hls.js +2 -6
  122. package/dist/streaming/hls.js.map +1 -1
  123. package/dist/streaming/hlsSegments.d.ts +1 -1
  124. package/dist/streaming/hlsSegments.js.map +1 -1
  125. package/dist/streaming/mpegts.js +1 -1
  126. package/dist/streaming/mpegts.js.map +1 -1
  127. package/dist/streaming/preroll.d.ts +1 -1
  128. package/dist/streaming/preroll.js +1 -5
  129. package/dist/streaming/preroll.js.map +1 -1
  130. package/dist/streaming/setup.js +6 -6
  131. package/dist/streaming/setup.js.map +1 -1
  132. package/dist/streaming/showInfo.js +2 -4
  133. package/dist/streaming/showInfo.js.map +1 -1
  134. package/dist/streaming/statusEmitter.d.ts +5 -8
  135. package/dist/streaming/statusEmitter.js +0 -5
  136. package/dist/streaming/statusEmitter.js.map +1 -1
  137. package/dist/types/channels.d.ts +9 -0
  138. package/dist/types/channels.js.map +1 -1
  139. package/dist/types/profiles.d.ts +2 -2
  140. package/dist/upgrade/commands.context.js +14 -10
  141. package/dist/upgrade/commands.context.js.map +1 -1
  142. package/dist/upgrade/commands.d.ts +7 -15
  143. package/dist/upgrade/commands.js +27 -28
  144. package/dist/upgrade/commands.js.map +1 -1
  145. package/dist/upgrade/detection.context.js +2 -1
  146. package/dist/upgrade/detection.context.js.map +1 -1
  147. package/dist/upgrade/detection.d.ts +2 -1
  148. package/dist/upgrade/detection.js +20 -51
  149. package/dist/upgrade/detection.js.map +1 -1
  150. package/dist/upgrade/index.d.ts +1 -0
  151. package/dist/upgrade/index.js +1 -0
  152. package/dist/upgrade/index.js.map +1 -1
  153. package/dist/upgrade/lifecycle.context.d.ts +19 -0
  154. package/dist/upgrade/lifecycle.context.js +47 -0
  155. package/dist/upgrade/lifecycle.context.js.map +1 -0
  156. package/dist/upgrade/lifecycle.d.ts +72 -0
  157. package/dist/upgrade/lifecycle.js +176 -0
  158. package/dist/upgrade/lifecycle.js.map +1 -0
  159. package/dist/upgrade/pathHandle.d.ts +28 -0
  160. package/dist/upgrade/pathHandle.js +101 -0
  161. package/dist/upgrade/pathHandle.js.map +1 -0
  162. package/dist/utils/bootSession.context.d.ts +15 -0
  163. package/dist/utils/bootSession.context.js +59 -0
  164. package/dist/utils/bootSession.context.js.map +1 -0
  165. package/dist/utils/bootSession.d.ts +16 -0
  166. package/dist/utils/bootSession.js +20 -0
  167. package/dist/utils/bootSession.js.map +1 -0
  168. package/dist/utils/clock.js +1 -1
  169. package/dist/utils/debugFilter.js +1 -0
  170. package/dist/utils/debugFilter.js.map +1 -1
  171. package/dist/utils/ffmpeg.js +1 -1
  172. package/dist/utils/ffmpeg.js.map +1 -1
  173. package/dist/utils/fileLogger.js +3 -3
  174. package/dist/utils/fileLogger.js.map +1 -1
  175. package/dist/utils/format.js +3 -3
  176. package/dist/utils/format.js.map +1 -1
  177. package/dist/utils/index.d.ts +5 -1
  178. package/dist/utils/index.js +5 -1
  179. package/dist/utils/index.js.map +1 -1
  180. package/dist/utils/logger.d.ts +9 -0
  181. package/dist/utils/logger.js +53 -1
  182. package/dist/utils/logger.js.map +1 -1
  183. package/dist/utils/markup.d.ts +22 -0
  184. package/dist/utils/markup.js +77 -0
  185. package/dist/utils/markup.js.map +1 -0
  186. package/dist/utils/morganStream.js +1 -1
  187. package/dist/utils/morganStream.js.map +1 -1
  188. package/dist/utils/network.d.ts +8 -0
  189. package/dist/utils/network.js +22 -0
  190. package/dist/utils/network.js.map +1 -0
  191. package/dist/utils/pid.d.ts +2 -25
  192. package/dist/utils/pid.js +10 -49
  193. package/dist/utils/pid.js.map +1 -1
  194. package/dist/utils/platform.d.ts +0 -27
  195. package/dist/utils/platform.js +0 -57
  196. package/dist/utils/platform.js.map +1 -1
  197. package/dist/utils/processInspector.context.d.ts +7 -0
  198. package/dist/utils/processInspector.context.js +104 -0
  199. package/dist/utils/processInspector.context.js.map +1 -0
  200. package/dist/utils/processInspector.d.ts +54 -0
  201. package/dist/utils/processInspector.js +125 -0
  202. package/dist/utils/processInspector.js.map +1 -0
  203. package/dist/utils/retry.d.ts +1 -1
  204. package/dist/utils/retry.js +1 -1
  205. package/dist/utils/retry.js.map +1 -1
  206. package/dist/utils/runtimeIdentity.context.d.ts +6 -0
  207. package/dist/utils/runtimeIdentity.context.js +13 -0
  208. package/dist/utils/runtimeIdentity.context.js.map +1 -0
  209. package/dist/utils/runtimeIdentity.d.ts +96 -0
  210. package/dist/utils/runtimeIdentity.js +171 -0
  211. package/dist/utils/runtimeIdentity.js.map +1 -0
  212. package/dist/utils/timing.d.ts +1 -1
  213. package/dist/utils/timing.js +0 -5
  214. package/dist/utils/timing.js.map +1 -1
  215. package/dist/utils/version.js +40 -10
  216. package/dist/utils/version.js.map +1 -1
  217. package/package.json +7 -5
  218. package/dist/utils/html.d.ts +0 -6
  219. package/dist/utils/html.js +0 -22
  220. package/dist/utils/html.js.map +0 -1
package/dist/app.d.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  import type { ParsedArgs } from "./index.ts";
2
2
  /**
3
- * Removes the server PID file if this process owns it. Called during graceful shutdown and from the process exit handler as a fallback. The ownership check
4
- * prevents a rejected duplicate instance from deleting the running instance's PID file during its exit handler.
3
+ * Releases the server instance slot if this process owns it. Called during graceful shutdown and from the process exit handler. The ownership check is
4
+ * structural (release() reads the on-disk record and refuses to remove a file belonging to a different PID), so a rejected-duplicate startup whose exit
5
+ * handler runs this function will correctly leave the legitimate holder's file in place. Tolerates an unresolved data directory: a startup that fails before
6
+ * initializeDataDir() runs still reaches the exit handler, and a slot we never claimed has nothing to release.
5
7
  */
6
- export declare function clearServerPid(): void;
8
+ export declare function releaseInstanceSlot(): void;
7
9
  /**
8
10
  * Initializes and starts the HTTP server. Before accepting connections, we validate configuration, clean up stale Chrome processes, and warm up the browser
9
11
  * instance.
package/dist/app.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * app.ts: Express application builder for PrismCast.
4
4
  */
5
5
  import { CONFIG, displayConfiguration, initializeConfiguration, validateConfiguration } from "./config/index.js";
6
- import { LOG, clearPidFile, createMorganStream, formatError, formatTimestamp, getCurrentPattern, getPackageVersion, isDebugLogging, isProcessRunning, readPidFile, resolveFFmpegPath, setConsoleLogging, startUpdateChecking, stopUpdateChecking, writePidFile } from "./utils/index.js";
6
+ import { LOG, claim, createMorganStream, formatError, formatTimestamp, getCurrentPattern, getPackageVersion, isDebugLogging, release, resolveFFmpegPath, setConsoleLogging, startUpdateChecking, stopUpdateChecking } from "./utils/index.js";
7
7
  import { closeBrowser, ensureDataDirectory, getCurrentBrowser, killStaleChrome, minimizeBrowserWindow, prepareExtension, setGracefulShutdown, startBrowserRestartChecking, startStalePageCleanup, stopBrowserRestartChecking, stopStalePageCleanup } from "./browser/index.js";
8
8
  import { ensureAllMigrated, snapshotAllForRelease } from "./config/persistence.js";
9
9
  import { getDebugEnv, getLogFilePath, getServerPidFilePath } from "./config/paths.js";
@@ -12,6 +12,7 @@ import { loadResumeState, saveResumeState } from "./streaming/hlsResume.js";
12
12
  import { startHdhrServer, stopHdhrServer } from "./hdhr/index.js";
13
13
  import { startPretunePolling, stopPretunePolling } from "./streaming/pretune.js";
14
14
  import { startShowInfoPolling, stopShowInfoPolling } from "./streaming/showInfo.js";
15
+ import { attachCdpUpgradeHandler } from "./routes/cdp.js";
15
16
  import { cleanupIdleStreams } from "./streaming/hls.js";
16
17
  import compression from "compression";
17
18
  import express from "express";
@@ -19,6 +20,7 @@ import { generatePreroll } from "./streaming/preroll.js";
19
20
  import { getAllStreams } from "./streaming/registry.js";
20
21
  import { initializeUserChannels } from "./config/userChannels.js";
21
22
  import { initializeUserProfiles } from "./config/userProfiles.js";
23
+ import { installHealthBridge } from "./routes/config/channels/healthBridge.js";
22
24
  import { loadHealthState } from "./config/health.js";
23
25
  import morgan from "morgan";
24
26
  import { runConsistencyProbeAtStartup } from "./config/consistencyProbe.js";
@@ -106,8 +108,8 @@ function setupGracefulShutdown() {
106
108
  }
107
109
  // Close the browser.
108
110
  await closeBrowser();
109
- // Remove the server PID file so the next startup does not see a stale PID.
110
- clearServerPid();
111
+ // Release the server instance slot so the next startup does not see a stale identity file.
112
+ releaseInstanceSlot();
111
113
  // Close the HTTP server.
112
114
  try {
113
115
  if (server) {
@@ -246,46 +248,44 @@ async function buildApp() {
246
248
  });
247
249
  return app;
248
250
  }
249
- /* PrismCast instance guard. Only one server instance should run at a time - a second instance would launch a competing Chrome process, bind to the same port, and
250
- * corrupt shared state. The guard uses a PID file written immediately after the instance check passes, before Chrome launches or the port binds. On the next
251
- * startup, we check whether the stored PID is still alive and exit early if so. Stale PID files (from crashes or container restarts) are handled gracefully via
252
- * isProcessRunning(), and the exit handler removes the file if startup fails.
251
+ /* PrismCast instance guard. Only one server instance should run at a time - a second instance would launch a competing Chrome process, bind to the same port,
252
+ * and corrupt shared state. The guard delegates to the runtime-identity primitive in utils/runtimeIdentity.ts, which composes PID liveness with the current
253
+ * boot session ID to classify the on-disk identity file. Stale records from reboots, container restarts, crashes, and ungraceful shutdowns are recovered
254
+ * transparently: the next claim() overwrites them. A held-live record (same boot, alive PID) is the only state that rejects a startup.
253
255
  *
254
- * The ownsServerPid flag tracks whether this process has written the PID file. Without it, the exit handler would unconditionally remove the file - including when
255
- * a duplicate instance is rejected by the guard. That would delete the legitimately running instance's PID file, allowing a third instance to bypass the check.
256
+ * Ownership is verified structurally inside release(): the function reads the on-disk record and only removes the file when its PID matches this process's
257
+ * PID. The file content is itself the source of truth, so a rejected duplicate startup's exit handler cannot accidentally delete the legitimate holder's file.
256
258
  */
257
- // Tracks whether this process owns the server PID file. Set to true by saveServerPid(), checked by clearServerPid() to avoid removing another instance's file.
258
- let ownsServerPid = false;
259
259
  /**
260
- * Checks for an already-running PrismCast instance by reading the server PID file and verifying the process is alive. If a live instance is detected, logs an error
261
- * and exits. Stale PID files from crashed or terminated instances are silently ignored.
260
+ * Claims the server instance slot for this process. On success, the identity file at getServerPidFilePath() now holds our PID and boot session ID; subsequent
261
+ * starts of PrismCast will recover the file as stale on the next reboot/crash. On failure, a live instance already holds the slot - we surface a precise
262
+ * diagnostic (PID + version + start time of the holder) and exit.
262
263
  */
263
- function checkForRunningInstance() {
264
- const pid = readPidFile(getServerPidFilePath(), "server", LOG);
265
- if ((pid !== null) && isProcessRunning(pid)) {
266
- // eslint-disable-next-line no-console
267
- console.error("Error: another PrismCast instance is already running (PID " + String(pid) + "). Stop it before starting a new one.");
268
- process.exit(1);
264
+ function claimInstanceSlot() {
265
+ const result = claim(getServerPidFilePath(), { version: getPackageVersion() });
266
+ if (result.ok) {
267
+ return;
269
268
  }
269
+ const conflict = result.conflict;
270
+ // eslint-disable-next-line no-console
271
+ console.error("Error: another PrismCast instance is already running (PID " + String(conflict.pid) + ", version " + conflict.version + ", started " +
272
+ conflict.startedAt + "). Stop it before starting a new one.");
273
+ process.exit(1);
270
274
  }
271
275
  /**
272
- * Writes the current process PID to the server PID file. Called immediately after the instance check passes to claim the slot before Chrome launches or the port
273
- * binds. If startup subsequently fails, the exit handler calls clearServerPid() to remove the stale file.
274
- */
275
- function saveServerPid() {
276
- writePidFile(getServerPidFilePath(), process.pid, "server", LOG);
277
- ownsServerPid = true;
278
- }
279
- /**
280
- * Removes the server PID file if this process owns it. Called during graceful shutdown and from the process exit handler as a fallback. The ownership check
281
- * prevents a rejected duplicate instance from deleting the running instance's PID file during its exit handler.
276
+ * Releases the server instance slot if this process owns it. Called during graceful shutdown and from the process exit handler. The ownership check is
277
+ * structural (release() reads the on-disk record and refuses to remove a file belonging to a different PID), so a rejected-duplicate startup whose exit
278
+ * handler runs this function will correctly leave the legitimate holder's file in place. Tolerates an unresolved data directory: a startup that fails before
279
+ * initializeDataDir() runs still reaches the exit handler, and a slot we never claimed has nothing to release.
282
280
  */
283
- export function clearServerPid() {
284
- if (!ownsServerPid) {
285
- return;
281
+ export function releaseInstanceSlot() {
282
+ try {
283
+ release(getServerPidFilePath());
284
+ }
285
+ catch {
286
+ // The data directory may not have been initialized when this runs (early-startup failure paths). Without a resolvable path there is no slot to release; the
287
+ // legitimate holder's file, if any, is in the configured data directory which we cannot address - so we never could have touched it. Silent no-op is safe.
286
288
  }
287
- clearPidFile(getServerPidFilePath(), "server", LOG);
288
- ownsServerPid = false;
289
289
  }
290
290
  /* The startServer function initializes and starts the HTTP server. It validates configuration, cleans up stale processes, warms up the browser, and starts the
291
291
  * Express application.
@@ -328,14 +328,14 @@ export async function startServer(parsedArgs) {
328
328
  }
329
329
  // Release boot coordinator: snapshot every persistence-managed file before any reads or migrations run, then apply any pending schema migrations across all
330
330
  // stores. Both operations are idempotent within a release (snapshots skip when the labeled copy already exists; ensureMigrated skips when the file is at the
331
- // current schema version). Running them up front guarantees a guaranteed restore point exists for every file at the start of every release boot, even if a
332
- // subsequent initialize* function or migration discovers a problem and aborts startup.
331
+ // current schema version). Running them up front guarantees a restore point exists for every file at the start of every release boot, even if a subsequent
332
+ // initialize* function or migration discovers a problem and aborts startup.
333
333
  try {
334
334
  await snapshotAllForRelease("pre-v" + getPackageVersion());
335
335
  await ensureAllMigrated();
336
336
  }
337
337
  catch (error) {
338
- LOG.error("Failed during release boot coordinator: %s", formatError(error));
338
+ LOG.error("Failed during release boot coordinator: %s.", formatError(error));
339
339
  process.exit(1);
340
340
  }
341
341
  // Initialize configuration from file and environment variables, then validate. CLI overrides are applied as the highest-priority merge pass. User profiles are
@@ -353,11 +353,10 @@ export async function startServer(parsedArgs) {
353
353
  setupGracefulShutdown();
354
354
  // Ensure the data directory exists before any operations that depend on it.
355
355
  await ensureDataDirectory();
356
- // Check for an already-running instance before launching Chrome or binding the port. This must come after ensureDataDirectory() since the PID file lives there.
357
- // We write the PID immediately after the check passes to close the race window - without this, a second instance launched during Chrome startup or port binding
358
- // could pass the same check. If startup subsequently fails, the exit handler calls clearServerPid() to remove the stale file.
359
- checkForRunningInstance();
360
- saveServerPid();
356
+ // Claim the server instance slot before launching Chrome or binding the port. This must come after ensureDataDirectory() since the identity file lives there.
357
+ // claimInstanceSlot() exits with a precise diagnostic when a live holder is detected; on success the identity file holds our PID and boot session ID. If
358
+ // startup subsequently fails, the exit handler calls releaseInstanceSlot() to remove the stale file.
359
+ claimInstanceSlot();
361
360
  // Initialize file logger if not using console logging. This must happen after config loading (to resolve the log file path) and after ensureDataDirectory()
362
361
  // (to create the parent directory). All startup log messages that should appear in the log file must come after this point.
363
362
  if (!parsedArgs.consoleLogging) {
@@ -386,12 +385,15 @@ export async function startServer(parsedArgs) {
386
385
  LOG.error("Either install FFmpeg or change the capture mode to 'native' in the configuration.");
387
386
  process.exit(1);
388
387
  }
389
- LOG.info("Using FFmpeg at: %s", ffmpegPath);
388
+ LOG.info("Using FFmpeg at: %s.", ffmpegPath);
390
389
  }
391
390
  // Load user channels from channels.json in the data directory if it exists.
392
391
  await initializeUserChannels();
393
392
  // Load persisted health state (channel health + domain auth) from health.json.
394
393
  await loadHealthState();
394
+ // Install the reactive bridge that translates health/auth state changes into channel table patches over SSE. Channel row HTML has a single source of truth -
395
+ // generateChannelRowHtml on the server - and every reactive update flows through this bridge so the client never composes channel row state imperatively.
396
+ installHealthBridge();
395
397
  // Run the cross-store consistency probe now that every store is loaded. Validates foreign-key-style invariants spanning multiple stores (service selections,
396
398
  // variant canonicalKey targets, domain profile mappings, service tag filter) and auto-fixes warnings where safe. Errors do not block startup.
397
399
  await runConsistencyProbeAtStartup();
@@ -440,6 +442,9 @@ export async function startServer(parsedArgs) {
440
442
  server = app.listen(CONFIG.server.port, CONFIG.server.host, () => {
441
443
  LOG.info("PrismCast is now listening on %s:%s.", CONFIG.server.host, CONFIG.server.port);
442
444
  });
445
+ // Attach the CDP proxy upgrade handler once the underlying http.Server exists. The handler is gated on the `cdp` debug category at request time, so it sits
446
+ // dormant until the user enables CDP via /debug. We attach unconditionally so the toggle takes effect without requiring a restart.
447
+ attachCdpUpgradeHandler(server);
443
448
  }
444
449
  catch (error) {
445
450
  LOG.error("Failed to build application: %s.", formatError(error));
package/dist/app.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import type { HlsPlaylistKind } from "../native/probe.ts";
2
+ import type { Nullable } from "../types/index.ts";
3
+ import type { Page } from "puppeteer-core";
4
+ /**
5
+ * A recognized HLS playlist observed by the tab. Carries the URL the player loaded and the classification result (master multivariant playlist or media segment
6
+ * playlist). Unknown bodies (no `#EXT-X-STREAM-INF` and no `#EXTINF`/`#EXT-X-TARGETDURATION`) are not delivered.
7
+ */
8
+ export interface ObservedHlsPlaylist {
9
+ readonly kind: HlsPlaylistKind;
10
+ readonly url: string;
11
+ }
12
+ /**
13
+ * Options accepted by observeHlsPlaylists(). The callback fires once per unique HLS playlist URL observed during the observer's lifetime.
14
+ */
15
+ export interface HlsPlaylistObserverOptions {
16
+ readonly logCategory: string;
17
+ readonly onPlaylist: (playlist: ObservedHlsPlaylist) => void;
18
+ }
19
+ /**
20
+ * Handle returned by observeHlsPlaylists(). Implements both the project's dispose() convention and TC39 Symbol.dispose so callers may use either an explicit
21
+ * dispose() call or the "using" keyword. Disposal is idempotent and tears down the underlying tab network observer.
22
+ */
23
+ export interface HlsPlaylistObserver extends Disposable {
24
+ readonly dispose: () => void;
25
+ readonly [Symbol.dispose]: () => void;
26
+ }
27
+ /**
28
+ * Installs an HLS playlist observer on the given page. The callback fires once per unique recognized HLS playlist URL observed across the tab's entire target
29
+ * tree (top frame, iframes including OOPIFs, workers, service workers). The OOPIF-aware transport is provided by tabNetworkObserver underneath; this observer
30
+ * adds the HLS-specific filter, body fetch, classification dispatch, and per-URL dedup.
31
+ *
32
+ * Returns null when the underlying tab network observer could not be installed (page closed, root CDP session creation failed).
33
+ *
34
+ * @param page - The puppeteer page to observe.
35
+ * @param options - Observer options including the playlist callback and the debug log category.
36
+ * @returns The observer handle, or null if installation failed.
37
+ */
38
+ export declare function observeHlsPlaylists(page: Page, options: HlsPlaylistObserverOptions): Promise<Nullable<HlsPlaylistObserver>>;
@@ -0,0 +1,113 @@
1
+ /* Copyright(C) 2024-2026, HJD (https://github.com/hjdhjd). All rights reserved.
2
+ *
3
+ * hlsPlaylistObserver.ts: HLS-aware observer layered on top of the tab-wide network observer.
4
+ */
5
+ import { LOG, chromeFetch } from "../utils/index.js";
6
+ import { classifyHlsPlaylist } from "../native/probe.js";
7
+ import { observeTabResponses } from "./tabNetworkObserver.js";
8
+ /* This module is the HLS-aware layer on top of tabNetworkObserver. Its job is to deliver every recognized HLS playlist (URL + kind) observed anywhere in the tab
9
+ * to a single callback. It encapsulates:
10
+ *
11
+ * - URL filter: only `.m3u8` URLs (query string ignored) are eligible
12
+ * - Body fetch: Node-side fetch via chromeFetch (CDP's Network.getResponseBody is unreliable because Chrome's network cache can evict response bodies)
13
+ * - Classification: dispatch to classifyHlsPlaylist() to decide master vs media vs unknown
14
+ * - Per-URL deduplication: the same `.m3u8` URL fired multiple times during the observation window (typical: hls.js re-polling the chunklist every ~2s)
15
+ * produces exactly one body fetch and exactly one callback delivery
16
+ *
17
+ * It does not know about first vs latest URL selection, master priority, finalize semantics, or predicate-based verification - those are consumer concerns and
18
+ * live in manifestInterceptor.ts (the long-lived interception path and the short-lived predicate-match path). This module is the single source of truth for
19
+ * "given a tab, observe its HLS playlists"; any future feature that needs the same primitive (debug logging of every observed playlist, playlist content
20
+ * archiving, etc.) consumes this module rather than re-implementing the filter + fetch + classify pipeline.
21
+ *
22
+ * Dedup rationale. During a typical interception window the hls.js player inside an OOPIF re-polls its chunklist multiple times. Without dedup we would fetch
23
+ * and classify the chunklist body once per poll, wasting CPU and network. Dedup is applied at the URL gate (before fetch is initiated) so an in-flight fetch is
24
+ * not duplicated by a near-simultaneous second observation. URLs are kept in the set across both successful and failed fetches: a manifest URL that failed to
25
+ * fetch is unlikely to succeed on an immediate retry, and the interceptor timeout in the consumer is the safety net for genuinely missed manifests. A consumer
26
+ * that needs different dedup semantics (e.g., refetch every N seconds) constructs its own observer rather than parameterizing this one.
27
+ */
28
+ // Timeout for the per-response manifest body fetch. The body is fed to classifyHlsPlaylist() to determine whether it is a master playlist; if Chrome's network
29
+ // cache evicts the body or the CDN serves a slow response, we abandon and treat the response as unclassifiable.
30
+ const MANIFEST_BODY_FETCH_TIMEOUT = 5000;
31
+ /**
32
+ * Installs an HLS playlist observer on the given page. The callback fires once per unique recognized HLS playlist URL observed across the tab's entire target
33
+ * tree (top frame, iframes including OOPIFs, workers, service workers). The OOPIF-aware transport is provided by tabNetworkObserver underneath; this observer
34
+ * adds the HLS-specific filter, body fetch, classification dispatch, and per-URL dedup.
35
+ *
36
+ * Returns null when the underlying tab network observer could not be installed (page closed, root CDP session creation failed).
37
+ *
38
+ * @param page - The puppeteer page to observe.
39
+ * @param options - Observer options including the playlist callback and the debug log category.
40
+ * @returns The observer handle, or null if installation failed.
41
+ */
42
+ export async function observeHlsPlaylists(page, options) {
43
+ const { logCategory, onPlaylist } = options;
44
+ let disposed = false;
45
+ // Per-URL dedup state. We add a URL to seenUrls the moment we decide to fetch it - before the asynchronous chromeFetch resolves - so a second observation of
46
+ // the same URL arriving while the first fetch is in flight does not initiate a duplicate fetch. URLs remain in the set for the observer's lifetime; the
47
+ // consumer's interception window timeout bounds the memory footprint.
48
+ const seenUrls = new Set();
49
+ // Handles a single response delivered by the tab network observer. Filters to .m3u8 URLs, applies per-URL dedup, fetches the body for classification, and
50
+ // dispatches the kind to the consumer callback. Wrapped in try/catch so a body-fetch failure on one URL does not interfere with subsequent responses arriving
51
+ // on other URLs.
52
+ const handleResponse = async (url) => {
53
+ if (disposed) {
54
+ return;
55
+ }
56
+ // Strip query parameters before checking the extension. Many CDNs append cache-busting or tokenization query strings to .m3u8 URLs.
57
+ const urlPath = url.split("?")[0] ?? "";
58
+ if (!urlPath.endsWith(".m3u8")) {
59
+ return;
60
+ }
61
+ // Dedup gate. We use the full URL (with query string) as the dedup key so two different tokenized URLs to the same playlist do not collapse. The chunklist
62
+ // poll case - where hls.js fires the same exact URL repeatedly - is the workload this optimization targets.
63
+ if (seenUrls.has(url)) {
64
+ return;
65
+ }
66
+ seenUrls.add(url);
67
+ LOG.debug(logCategory, "Observed .m3u8 response: %s.", url.slice(0, 120));
68
+ try {
69
+ const response = await chromeFetch(url, { signal: AbortSignal.timeout(MANIFEST_BODY_FETCH_TIMEOUT) });
70
+ if (!response.ok) {
71
+ LOG.debug(logCategory, "Manifest fetch returned HTTP %s for %s.", response.status, url.slice(0, 120));
72
+ return;
73
+ }
74
+ const body = await response.text();
75
+ // Delegate the master/media decision to the canonical classifier. Recognized kinds (master, media) are forwarded to the callback together with the kind
76
+ // label; consumers apply their own filter. Both consumers (installManifestInterceptor and awaitMatchingManifest) maintain their own resolved-flag guards,
77
+ // but we still gate locally on the disposed flag so a callback that arrives after dispose is never invoked even if the consumer guard were absent.
78
+ const kind = classifyHlsPlaylist(body);
79
+ if (kind === "unknown") {
80
+ LOG.debug(logCategory, "Skipping .m3u8 classified as unknown (no recognizable HLS directives).");
81
+ return;
82
+ }
83
+ // The disposed flag may have flipped during the awaits above. Closure-captured mutation across awaits is invisible to ESLint's flow analysis (which is why
84
+ // the next line carries an explicit disable), but it is observable at runtime and matters for race-safety: we never deliver a callback after dispose, full
85
+ // stop. This guard lives here because race safety is the observer's responsibility, not the consumer's - consumers should not have to repeat it.
86
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
87
+ if (disposed) {
88
+ return;
89
+ }
90
+ onPlaylist({ kind, url });
91
+ }
92
+ catch (error) {
93
+ LOG.debug(logCategory, "Could not fetch .m3u8 body: %s.", String(error));
94
+ }
95
+ };
96
+ const tabObserver = await observeTabResponses(page, {
97
+ onResponse: (response) => { void handleResponse(response.url); }
98
+ });
99
+ if (!tabObserver) {
100
+ return null;
101
+ }
102
+ LOG.debug(logCategory, "HLS playlist observer installed.");
103
+ const dispose = () => {
104
+ if (disposed) {
105
+ return;
106
+ }
107
+ disposed = true;
108
+ tabObserver.dispose();
109
+ seenUrls.clear();
110
+ };
111
+ return { dispose, [Symbol.dispose]: dispose };
112
+ }
113
+ //# sourceMappingURL=hlsPlaylistObserver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hlsPlaylistObserver.js","sourceRoot":"","sources":["../../src/browser/hlsPlaylistObserver.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ import type { Browser, LaunchOptions, Page } from "puppeteer-core";
2
2
  import { clearLoginState, isLoginModeActive } from "./login.ts";
3
3
  import { getStream } from "puppeteer-stream";
4
4
  import type { Nullable } from "../types/index.ts";
5
+ import type { ProcessInfo } from "../utils/index.ts";
5
6
  /**
6
7
  * Returns true if graceful shutdown is in progress.
7
8
  */
@@ -41,30 +42,39 @@ export declare function unregisterManagedPage(page: Page): void;
41
42
  */
42
43
  export declare function ensureDataDirectory(): Promise<void>;
43
44
  /**
44
- * Ensures a clean slate for browser launch by terminating any stale Chrome processes and removing orphaned profile lock files. Chrome locks its profile directory
45
- * while running, and if a previous instance crashed without releasing the lock, we cannot launch a new browser with the same profile. This function uses the
46
- * saved Chrome PID to find and terminate the process via process.kill(), then polls for exit using signal 0. This approach is fully cross-platform - it does not
47
- * rely on Unix-only tools like pkill or pgrep.
45
+ * Identifies Chrome processes that this PrismCast instance is responsible for terminating. The filter has two stages: command-line discovery (anything using
46
+ * our profile directory) followed by ownership verification (we spawned it, or its parent is no longer alive). The ownership stage is structural - we do not
47
+ * rely on an in-memory flag; the parent-child relationship in the OS process table IS the ownership proof, which means this function is safe to call from any
48
+ * code path, including from a rejected-duplicate startup's exit handler.
49
+ *
50
+ * Why ownership matters. A duplicate PrismCast that the instance guard rejects must NOT signal Chrome that belongs to the legitimate holder. With PID-based
51
+ * cleanup the only defense was an in-memory ownsChromeCleanup flag set at startup. With process-table discovery, the OS itself tells us who owns what: a Chrome
52
+ * whose ppid is a live unrelated PID belongs to that parent, not to us.
53
+ * @param processes - The current process table snapshot.
54
+ * @param profileDir - The Chrome user-data-dir to match against.
55
+ * @param ownPid - The current process's PID (anything whose parent is us is ours to kill).
56
+ * @param isProcessAlive - Predicate that returns true when the given PID is currently a live process. Injected so tests do not depend on real PIDs.
57
+ * @returns The PIDs to terminate, in process-table order.
58
+ */
59
+ export declare function findChromeProcessesUsingProfile(processes: readonly ProcessInfo[], profileDir: string, ownPid: number, isProcessAlive: (pid: number) => boolean): number[];
60
+ /**
61
+ * Ensures a clean slate for browser launch by terminating any stale Chrome processes and removing orphaned profile lock files. Chrome locks its profile
62
+ * directory while running; if a previous instance crashed without releasing the lock, we cannot launch a new browser with the same profile. Discovery is done
63
+ * via the OS process table (utils/processInspector) and filtered to Chrome processes using our profile directory whose ownership belongs to us - either because
64
+ * we spawned them (ppid === process.pid) or because their parent is no longer alive (orphaned from a previous instance).
48
65
  *
49
66
  * The termination strategy escalates from SIGTERM to SIGKILL. SIGTERM is sent first, giving Chrome up to 5 seconds to flush its profile databases (LevelDB,
50
67
  * extension state, session storage) and exit cleanly. If Chrome does not exit, SIGKILL is sent as a fallback. This escalation is critical when called from the
51
- * process exit handler - Chrome may be running normally (e.g., after a capture probe timeout), and an immediate SIGKILL would corrupt its profile databases,
68
+ * process exit handler: Chrome may be running normally (e.g., after a capture probe timeout) and an immediate SIGKILL would corrupt its profile databases,
52
69
  * poisoning the Docker volume for subsequent container restarts.
53
70
  *
54
- * If no PID is available (first run or clean shutdown where the PID file was already removed), process killing is skipped entirely and only lock file cleanup
55
- * runs. When a PID file does exist but the process is gone (Docker restart with a mounted volume - the PID belongs to the previous container's PID namespace),
56
- * process.kill() throws ESRCH, which is caught gracefully.
71
+ * The ownership filter makes this function safe to call from any context, including a rejected-duplicate startup's exit handler: a duplicate that never
72
+ * spawned Chrome will find nothing matching its ownership criteria and signal nothing.
57
73
  *
58
- * This is called at startup before launching the browser and from the process exit handler as a crash recovery fallback. It's safe to call even when no stale
59
- * processes or files exist.
74
+ * Called at startup before launching the browser and from the process exit handler as a crash recovery fallback. Safe to call when no stale processes or files
75
+ * exist - the discovery and lock-file cleanup are both no-ops in the empty case.
60
76
  */
61
77
  export declare function killStaleChrome(): void;
62
- /**
63
- * Returns whether this process has taken ownership of Chrome cleanup by running killStaleChrome() during startup. Used by the exit handler to avoid killing
64
- * Chrome that belongs to another running PrismCast instance.
65
- * @returns True if killStaleChrome() has run in this process.
66
- */
67
- export declare function canCleanupChrome(): boolean;
68
78
  /**
69
79
  * Locates the Google Chrome executable on the system. The CHROME_BIN environment variable takes precedence, allowing operators to specify a non-standard
70
80
  * installation. Otherwise, we search common installation paths across macOS, Linux, and Windows.