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.
Files changed (239) hide show
  1. package/README.md +44 -28
  2. package/dist/app.d.ts +5 -0
  3. package/dist/app.js +63 -5
  4. package/dist/app.js.map +1 -1
  5. package/dist/browser/cdp.js +1 -1
  6. package/dist/browser/cdp.js.map +1 -1
  7. package/dist/browser/channelSelection.d.ts +10 -34
  8. package/dist/browser/channelSelection.js +31 -96
  9. package/dist/browser/channelSelection.js.map +1 -1
  10. package/dist/browser/display.d.ts +19 -0
  11. package/dist/browser/display.js +16 -11
  12. package/dist/browser/display.js.map +1 -1
  13. package/dist/browser/index.d.ts +31 -54
  14. package/dist/browser/index.js +334 -272
  15. package/dist/browser/index.js.map +1 -1
  16. package/dist/browser/login.d.ts +65 -0
  17. package/dist/browser/login.js +193 -0
  18. package/dist/browser/login.js.map +1 -0
  19. package/dist/browser/precaching.js +15 -3
  20. package/dist/browser/precaching.js.map +1 -1
  21. package/dist/browser/tuning/comcastPolymer.d.ts +23 -0
  22. package/dist/browser/tuning/comcastPolymer.js +655 -0
  23. package/dist/browser/tuning/comcastPolymer.js.map +1 -0
  24. package/dist/browser/tuning/cox.d.ts +2 -0
  25. package/dist/browser/tuning/cox.js +14 -0
  26. package/dist/browser/tuning/cox.js.map +1 -0
  27. package/dist/browser/tuning/directv.js +14 -1
  28. package/dist/browser/tuning/directv.js.map +1 -1
  29. package/dist/browser/tuning/fox.js +14 -2
  30. package/dist/browser/tuning/fox.js.map +1 -1
  31. package/dist/browser/tuning/hbo.js +12 -1
  32. package/dist/browser/tuning/hbo.js.map +1 -1
  33. package/dist/browser/tuning/hulu.js +24 -1
  34. package/dist/browser/tuning/hulu.js.map +1 -1
  35. package/dist/browser/tuning/shared.d.ts +46 -0
  36. package/dist/browser/tuning/shared.js +100 -0
  37. package/dist/browser/tuning/shared.js.map +1 -0
  38. package/dist/browser/tuning/sling.js +35 -5
  39. package/dist/browser/tuning/sling.js.map +1 -1
  40. package/dist/browser/tuning/spectrum.js +13 -1
  41. package/dist/browser/tuning/spectrum.js.map +1 -1
  42. package/dist/browser/tuning/thumbnailRow.js +1 -1
  43. package/dist/browser/tuning/thumbnailRow.js.map +1 -1
  44. package/dist/browser/tuning/tileClick.js +1 -1
  45. package/dist/browser/tuning/tileClick.js.map +1 -1
  46. package/dist/browser/tuning/xfinity.d.ts +2 -0
  47. package/dist/browser/tuning/xfinity.js +14 -0
  48. package/dist/browser/tuning/xfinity.js.map +1 -0
  49. package/dist/browser/tuning/youtubeTv.js +15 -1
  50. package/dist/browser/tuning/youtubeTv.js.map +1 -1
  51. package/dist/browser/video.d.ts +20 -0
  52. package/dist/browser/video.js +168 -124
  53. package/dist/browser/video.js.map +1 -1
  54. package/dist/channels/index.d.ts +2 -2
  55. package/dist/channels/index.js +2075 -683
  56. package/dist/channels/index.js.map +1 -1
  57. package/dist/config/health.d.ts +28 -28
  58. package/dist/config/health.js +72 -54
  59. package/dist/config/health.js.map +1 -1
  60. package/dist/config/index.js +29 -64
  61. package/dist/config/index.js.map +1 -1
  62. package/dist/config/paths.d.ts +12 -0
  63. package/dist/config/paths.js +16 -0
  64. package/dist/config/paths.js.map +1 -1
  65. package/dist/config/profiles.d.ts +3 -2
  66. package/dist/config/profiles.js +45 -34
  67. package/dist/config/profiles.js.map +1 -1
  68. package/dist/config/providerPacks.js +4 -1
  69. package/dist/config/providerPacks.js.map +1 -1
  70. package/dist/config/providers.d.ts +19 -5
  71. package/dist/config/providers.js +123 -93
  72. package/dist/config/providers.js.map +1 -1
  73. package/dist/config/sites.d.ts +28 -0
  74. package/dist/config/sites.js +104 -127
  75. package/dist/config/sites.js.map +1 -1
  76. package/dist/config/userChannels.d.ts +53 -2
  77. package/dist/config/userChannels.js +222 -13
  78. package/dist/config/userChannels.js.map +1 -1
  79. package/dist/config/userConfig.d.ts +2 -1
  80. package/dist/config/userConfig.js +6 -15
  81. package/dist/config/userConfig.js.map +1 -1
  82. package/dist/config/userProfiles.d.ts +0 -1
  83. package/dist/config/userProfiles.js +18 -10
  84. package/dist/config/userProfiles.js.map +1 -1
  85. package/dist/hdhr/channelMap.d.ts +1 -1
  86. package/dist/hdhr/channelMap.js +4 -4
  87. package/dist/hdhr/channelMap.js.map +1 -1
  88. package/dist/index.js +25 -22
  89. package/dist/index.js.map +1 -1
  90. package/dist/native/decrypt.d.ts +32 -0
  91. package/dist/native/decrypt.js +85 -0
  92. package/dist/native/decrypt.js.map +1 -0
  93. package/dist/native/index.d.ts +61 -0
  94. package/dist/native/index.js +342 -0
  95. package/dist/native/index.js.map +1 -0
  96. package/dist/native/intercept.d.ts +35 -0
  97. package/dist/native/intercept.js +184 -0
  98. package/dist/native/intercept.js.map +1 -0
  99. package/dist/native/probe.d.ts +49 -0
  100. package/dist/native/probe.js +294 -0
  101. package/dist/native/probe.js.map +1 -0
  102. package/dist/native/proxy.d.ts +54 -0
  103. package/dist/native/proxy.js +947 -0
  104. package/dist/native/proxy.js.map +1 -0
  105. package/dist/routes/components.d.ts +43 -0
  106. package/dist/routes/components.js +122 -0
  107. package/dist/routes/components.js.map +1 -1
  108. package/dist/routes/config/channels/index.d.ts +2 -2
  109. package/dist/routes/config/channels/index.js +1 -1
  110. package/dist/routes/config/channels/index.js.map +1 -1
  111. package/dist/routes/config/channels/routes.js +443 -65
  112. package/dist/routes/config/channels/routes.js.map +1 -1
  113. package/dist/routes/config/channels/table.d.ts +76 -0
  114. package/dist/routes/config/channels/table.js +369 -89
  115. package/dist/routes/config/channels/table.js.map +1 -1
  116. package/dist/routes/config/index.d.ts +1 -1
  117. package/dist/routes/config/index.js +1 -1
  118. package/dist/routes/config/index.js.map +1 -1
  119. package/dist/routes/config/providers.d.ts +6 -6
  120. package/dist/routes/config/providers.js +73 -99
  121. package/dist/routes/config/providers.js.map +1 -1
  122. package/dist/routes/debug.js +5 -5
  123. package/dist/routes/debug.js.map +1 -1
  124. package/dist/routes/hls.js +12 -2
  125. package/dist/routes/hls.js.map +1 -1
  126. package/dist/routes/index.d.ts +0 -15
  127. package/dist/routes/index.js +2 -16
  128. package/dist/routes/index.js.map +1 -1
  129. package/dist/routes/providers.js +106 -3
  130. package/dist/routes/providers.js.map +1 -1
  131. package/dist/routes/root/content.js +124 -47
  132. package/dist/routes/root/content.js.map +1 -1
  133. package/dist/routes/root/index.js +2 -1
  134. package/dist/routes/root/index.js.map +1 -1
  135. package/dist/routes/root/scripts/channels.js +772 -312
  136. package/dist/routes/root/scripts/channels.js.map +1 -1
  137. package/dist/routes/root/scripts/config.js +596 -544
  138. package/dist/routes/root/scripts/config.js.map +1 -1
  139. package/dist/routes/root/scripts/index.d.ts +1 -0
  140. package/dist/routes/root/scripts/index.js +1 -0
  141. package/dist/routes/root/scripts/index.js.map +1 -1
  142. package/dist/routes/root/scripts/shared.d.ts +6 -0
  143. package/dist/routes/root/scripts/shared.js +391 -0
  144. package/dist/routes/root/scripts/shared.js.map +1 -0
  145. package/dist/routes/root/scripts/status.js +49 -33
  146. package/dist/routes/root/scripts/status.js.map +1 -1
  147. package/dist/routes/root/styles.js +117 -10
  148. package/dist/routes/root/styles.js.map +1 -1
  149. package/dist/service/commands.js +2 -1
  150. package/dist/service/commands.js.map +1 -1
  151. package/dist/streaming/fmp4Segmenter.d.ts +5 -0
  152. package/dist/streaming/fmp4Segmenter.js +146 -42
  153. package/dist/streaming/fmp4Segmenter.js.map +1 -1
  154. package/dist/streaming/hls.d.ts +24 -19
  155. package/dist/streaming/hls.js +603 -297
  156. package/dist/streaming/hls.js.map +1 -1
  157. package/dist/streaming/hlsResume.d.ts +16 -3
  158. package/dist/streaming/hlsResume.js +35 -8
  159. package/dist/streaming/hlsResume.js.map +1 -1
  160. package/dist/streaming/hlsSegments.d.ts +41 -2
  161. package/dist/streaming/hlsSegments.js +131 -28
  162. package/dist/streaming/hlsSegments.js.map +1 -1
  163. package/dist/streaming/lifecycle.js +27 -13
  164. package/dist/streaming/lifecycle.js.map +1 -1
  165. package/dist/streaming/monitor.d.ts +1 -0
  166. package/dist/streaming/monitor.js +862 -474
  167. package/dist/streaming/monitor.js.map +1 -1
  168. package/dist/streaming/mp4Parser.d.ts +52 -6
  169. package/dist/streaming/mp4Parser.js +156 -30
  170. package/dist/streaming/mp4Parser.js.map +1 -1
  171. package/dist/streaming/mpegts.d.ts +2 -2
  172. package/dist/streaming/mpegts.js +156 -109
  173. package/dist/streaming/mpegts.js.map +1 -1
  174. package/dist/streaming/playlistBuilder.d.ts +38 -0
  175. package/dist/streaming/playlistBuilder.js +66 -0
  176. package/dist/streaming/playlistBuilder.js.map +1 -0
  177. package/dist/streaming/preroll.d.ts +126 -0
  178. package/dist/streaming/preroll.js +434 -0
  179. package/dist/streaming/preroll.js.map +1 -0
  180. package/dist/streaming/pretune.d.ts +8 -0
  181. package/dist/streaming/pretune.js +226 -0
  182. package/dist/streaming/pretune.js.map +1 -0
  183. package/dist/streaming/recovery.d.ts +0 -6
  184. package/dist/streaming/recovery.js +0 -8
  185. package/dist/streaming/recovery.js.map +1 -1
  186. package/dist/streaming/registry.d.ts +57 -9
  187. package/dist/streaming/registry.js +26 -7
  188. package/dist/streaming/registry.js.map +1 -1
  189. package/dist/streaming/setup.d.ts +8 -1
  190. package/dist/streaming/setup.js +71 -27
  191. package/dist/streaming/setup.js.map +1 -1
  192. package/dist/streaming/showInfo.d.ts +29 -4
  193. package/dist/streaming/showInfo.js +210 -38
  194. package/dist/streaming/showInfo.js.map +1 -1
  195. package/dist/streaming/statusEmitter.d.ts +20 -6
  196. package/dist/streaming/statusEmitter.js +31 -7
  197. package/dist/streaming/statusEmitter.js.map +1 -1
  198. package/dist/types/channels.d.ts +32 -0
  199. package/dist/types/config.d.ts +2 -2
  200. package/dist/types/index.d.ts +2 -2
  201. package/dist/types/index.js.map +1 -1
  202. package/dist/types/profiles.d.ts +13 -1
  203. package/dist/types/selection.d.ts +5 -1
  204. package/dist/types/shared.d.ts +1 -1
  205. package/dist/types/streaming.d.ts +7 -0
  206. package/dist/utils/chromeFetch.d.ts +20 -0
  207. package/dist/utils/chromeFetch.js +43 -0
  208. package/dist/utils/chromeFetch.js.map +1 -0
  209. package/dist/utils/debugFilter.d.ts +0 -5
  210. package/dist/utils/debugFilter.js +20 -11
  211. package/dist/utils/debugFilter.js.map +1 -1
  212. package/dist/utils/delay.d.ts +28 -0
  213. package/dist/utils/delay.js +35 -1
  214. package/dist/utils/delay.js.map +1 -1
  215. package/dist/utils/evaluate.js +6 -11
  216. package/dist/utils/evaluate.js.map +1 -1
  217. package/dist/utils/ffmpeg.d.ts +14 -12
  218. package/dist/utils/ffmpeg.js +114 -134
  219. package/dist/utils/ffmpeg.js.map +1 -1
  220. package/dist/utils/format.d.ts +6 -0
  221. package/dist/utils/format.js +8 -0
  222. package/dist/utils/format.js.map +1 -1
  223. package/dist/utils/index.d.ts +3 -0
  224. package/dist/utils/index.js +3 -0
  225. package/dist/utils/index.js.map +1 -1
  226. package/dist/utils/logEmitter.js +1 -1
  227. package/dist/utils/logEmitter.js.map +1 -1
  228. package/dist/utils/pid.d.ts +40 -0
  229. package/dist/utils/pid.js +83 -0
  230. package/dist/utils/pid.js.map +1 -0
  231. package/dist/utils/retry.d.ts +15 -9
  232. package/dist/utils/retry.js +7 -23
  233. package/dist/utils/retry.js.map +1 -1
  234. package/dist/utils/timing.d.ts +5 -0
  235. package/dist/utils/timing.js +13 -0
  236. package/dist/utils/timing.js.map +1 -0
  237. package/dist/utils/version.js +2 -2
  238. package/dist/utils/version.js.map +1 -1
  239. package/package.json +7 -7
@@ -1,33 +1,30 @@
1
+ /* Copyright(C) 2024-2026, HJD (https://github.com/hjdhjd). All rights reserved.
2
+ *
3
+ * mpegts.ts: MPEG-TS streaming handler for PrismCast.
4
+ */
1
5
  import { LOG, formatError, spawnMpegTsRemuxer } from "../utils/index.js";
2
- import { awaitStreamReadySilent, initializeStream, sendValidationError, validateChannel } from "./hls.js";
3
6
  import { getStream, updateLastAccess } from "./registry.js";
7
+ import { initializeStream, sendValidationError, validateChannel } from "./hls.js";
4
8
  import { registerClient, unregisterClient } from "./clients.js";
5
9
  import { CONFIG } from "../config/index.js";
6
10
  import { StreamSetupError } from "./setup.js";
7
11
  import { getChannelStreamId } from "./lifecycle.js";
8
12
  import { waitForInitSegment } from "./hlsSegments.js";
9
- /* This module provides a continuous MPEG-TS byte stream from the same capture pipeline used for HLS. It is designed for HDHomeRun-compatible clients (such as Plex)
10
- * that expect raw MPEG-TS when tuning a channel. The existing capture → segmenter → HLS segments flow is unchanged. Each MPEG-TS client gets its own FFmpeg remuxer
11
- * that converts stored fMP4 segments to MPEG-TS with codec copy (no transcoding).
13
+ /* This module provides a continuous MPEG-TS byte stream for HDHomeRun-compatible clients (such as Plex) that expect raw MPEG-TS when tuning a channel. Two delivery
14
+ * modes are supported:
15
+ *
16
+ * Capture mode (fMP4 → MPEG-TS): The capture pipeline produces fMP4 segments. Each MPEG-TS client gets its own FFmpeg remuxer that converts fMP4 to MPEG-TS with
17
+ * codec copy (no transcoding). FFmpeg reads the init segment + media segments from stdin and outputs a continuous MPEG-TS stream on stdout, piped to the HTTP response.
12
18
  *
13
- * Data flow per client:
14
- * 1. Validate channel and check for existing stream
15
- * 2. If new stream needed, flush HTTP 200 headers immediately (so the client sees "connection accepted")
16
- * 3. initializeStream() starts the capture, or awaitStreamReadySilent() waits for an in-progress startup
17
- * 4. Wait for the init segment (ftyp+moov codec configuration)
18
- * 5. Spawn FFmpeg: -f mp4 -i pipe:0 -c copy -f mpegts pipe:1
19
- * 6. Write init segment + existing media segments to FFmpeg stdin
20
- * 7. Subscribe to segment events for new segments in real time
21
- * 8. Pipe FFmpeg stdout to the HTTP response as video/mp2t
22
- * 9. On client disconnect or stream termination, kill FFmpeg and clean up
19
+ * Native mode (MPEG-TS pass-through): The native HLS proxy already produces MPEG-TS segments. These are written directly to the HTTP response without any remuxing.
23
20
  *
24
- * The header flush in step 2 prevents client timeouts. Without it, the client receives zero bytes until the entire stream setup completes (4-10+ seconds), which may
25
- * exceed the client's connection timeout.
21
+ * Both modes share the same client lifecycle via connectMpegTsClient(): register the client, subscribe to segment events for real-time delivery, write existing
22
+ * segments for catchup, and clean up on disconnect. The header flush for new streams prevents client timeouts during the 4-10+ second startup sequence.
26
23
  */
27
24
  // Public Endpoint Handler.
28
25
  /**
29
- * Handles MPEG-TS stream requests. Validates the channel, flushes HTTP headers early for new streams, then ensures a capture is running, waits for the init segment,
30
- * spawns a per-client FFmpeg remuxer, and streams the output.
26
+ * Handles MPEG-TS stream requests. Validates the channel, flushes HTTP headers early for new streams, then ensures a stream is running and delegates to the
27
+ * mode-appropriate serving path (FFmpeg remuxer for capture mode, direct pass-through for native mode).
31
28
  *
32
29
  * For new streams, headers are flushed before stream setup begins so the client sees an immediate 200 response. This prevents timeout failures during the 4-10+
33
30
  * second startup sequence. The trade-off is that error responses cannot be sent after the flush — failures are logged server-side and the connection is closed.
@@ -45,21 +42,17 @@ export async function handleMpegTsStream(req, res) {
45
42
  }
46
43
  // Check for an existing stream first. If one exists, we can skip validation and header flushing.
47
44
  const existingStreamId = getChannelStreamId(channelName);
48
- // Fast path: a real stream already exists. No early flush needed — the stream data will flow quickly.
49
- if ((existingStreamId !== undefined) && (existingStreamId !== -1)) {
45
+ // Fast path: a stream already exists (either fully set up or a pending entry from an HLS request). Serve it directly.
46
+ if (existingStreamId !== undefined) {
50
47
  await serveMpegTsStream(existingStreamId, channelName, req, res);
51
48
  return;
52
49
  }
53
- // If no existing stream or startup in progress, validate the channel before flushing headers. This ensures we can still return proper error responses for invalid
54
- // channels, disabled channels, and login mode. Store the validated channel for use during stream initialization below.
55
- let validatedChannel;
56
- if (existingStreamId === undefined) {
57
- const validation = validateChannel(channelName);
58
- if (!validation.valid) {
59
- sendValidationError(validation, res);
60
- return;
61
- }
62
- validatedChannel = validation.channel;
50
+ // No existing stream — validate the channel before flushing headers. This ensures we can still return proper error responses for invalid channels, disabled
51
+ // channels, and login mode.
52
+ const validation = validateChannel(channelName);
53
+ if (!validation.valid) {
54
+ sendValidationError(validation, res);
55
+ return;
63
56
  }
64
57
  // Flush HTTP 200 headers immediately. The client sees "connection accepted, data coming" and waits patiently. After this point, we cannot send error status codes —
65
58
  // failures will close the connection with no data.
@@ -68,55 +61,40 @@ export async function handleMpegTsStream(req, res) {
68
61
  res.setHeader("Content-Type", "video/mpeg");
69
62
  res.setHeader("transferMode.dlna.org", "Streaming");
70
63
  res.flushHeaders();
71
- // Acquire the stream. If a startup is in progress (another request started it), poll silently. Otherwise, start a new stream via initializeStream().
64
+ // Start a new stream directly. initializeStream blocks until setup completes (no preroll for MPEG-TS clients). Since headers are already flushed, errors are logged
65
+ // and the connection is closed.
72
66
  let streamId;
73
- if (existingStreamId === -1) {
74
- // Another request is already starting this stream. Wait silently (no error responses possible after flush).
75
- streamId = await awaitStreamReadySilent(channelName);
76
- if (streamId === null) {
77
- LOG.warn("MPEG-TS stream startup failed for %s (startup did not complete).", channelName);
78
- res.end();
79
- return;
80
- }
67
+ try {
68
+ streamId = await initializeStream({
69
+ channel: validation.channel,
70
+ channelName,
71
+ clientAddress: req.ip ?? req.socket.remoteAddress ?? null,
72
+ mpegTsClient: true,
73
+ profileOverride: req.query.profile,
74
+ url: validation.channel.url
75
+ });
81
76
  }
82
- else {
83
- // Start a new stream directly. validatedChannel is guaranteed set: this branch runs only when existingStreamId === undefined, which requires successful
84
- // validation above. Since headers are already flushed, errors are logged and the connection is closed.
85
- if (!validatedChannel) {
86
- res.end();
87
- return;
88
- }
89
- try {
90
- streamId = await initializeStream({
91
- channel: validatedChannel,
92
- channelName,
93
- clientAddress: req.ip ?? req.socket.remoteAddress ?? null,
94
- profileOverride: req.query.profile,
95
- url: validatedChannel.url
96
- });
97
- }
98
- catch (error) {
99
- if (error instanceof StreamSetupError) {
100
- LOG.warn("MPEG-TS stream startup failed for %s: %s.", channelName, error.userMessage);
101
- }
102
- else {
103
- LOG.warn("MPEG-TS stream startup failed for %s: %s.", channelName, formatError(error));
104
- }
105
- res.end();
106
- return;
77
+ catch (error) {
78
+ if (error instanceof StreamSetupError) {
79
+ LOG.warn("MPEG-TS stream startup failed for %s: %s", channelName, error.userMessage);
107
80
  }
108
- if (streamId === null) {
109
- LOG.warn("MPEG-TS stream startup failed for %s (terminated during setup).", channelName);
110
- res.end();
111
- return;
81
+ else {
82
+ LOG.warn("MPEG-TS stream startup failed for %s: %s.", channelName, formatError(error));
112
83
  }
84
+ res.end();
85
+ return;
86
+ }
87
+ if (streamId === null) {
88
+ LOG.warn("MPEG-TS stream startup failed for %s (terminated during setup).", channelName);
89
+ res.end();
90
+ return;
113
91
  }
114
92
  await serveMpegTsStream(streamId, channelName, req, res);
115
93
  }
116
94
  // Internal Helpers.
117
95
  /**
118
- * Serves the MPEG-TS stream once a stream ID is available. Waits for the init segment, spawns the FFmpeg remuxer, and pipes the output to the response. This is the
119
- * shared implementation used by both the fast path (existing stream) and the flush path (new stream).
96
+ * Serves the MPEG-TS stream once a stream ID is available. Waits for init segment readiness, then delegates to mode-specific serving: capture mode spawns an FFmpeg
97
+ * remuxer to convert fMP4 to MPEG-TS, while native mode passes .ts segments directly to the response. Both paths share client lifecycle via connectMpegTsClient().
120
98
  *
121
99
  * @param streamId - The numeric stream ID.
122
100
  * @param channelName - The channel name for logging.
@@ -124,7 +102,8 @@ export async function handleMpegTsStream(req, res) {
124
102
  * @param res - Express response object.
125
103
  */
126
104
  async function serveMpegTsStream(streamId, channelName, req, res) {
127
- // Wait for the init segment to be available. The init segment contains codec configuration (ftyp+moov boxes) that FFmpeg needs before it can process media segments.
105
+ // Wait for the init segment to be available. For capture-mode streams, this waits for the fMP4 init segment (ftyp+moov). For native-mode streams,
106
+ // signalInitSegmentReady() was called immediately during setup, so this returns instantly.
128
107
  const initReady = await waitForInitSegment(streamId, CONFIG.streaming.navigationTimeout);
129
108
  if (!initReady) {
130
109
  if (!res.headersSent) {
@@ -137,9 +116,40 @@ async function serveMpegTsStream(streamId, channelName, req, res) {
137
116
  }
138
117
  return;
139
118
  }
140
- // Get the stream from the registry and verify it's still alive with a valid init segment.
119
+ // Get the stream from the registry.
141
120
  const stream = getStream(streamId);
142
- if (!stream?.hls.initSegment) {
121
+ if (!stream) {
122
+ if (!res.headersSent) {
123
+ res.status(500).send("Stream no longer available.");
124
+ }
125
+ else {
126
+ res.end();
127
+ }
128
+ return;
129
+ }
130
+ // For native-mode streams, segments are already MPEG-TS — write them directly to the response without FFmpeg.
131
+ if (stream.streamingMode === "native") {
132
+ connectMpegTsClient({
133
+ logLabel: "Native MPEG-TS",
134
+ onStreamTerminated: () => {
135
+ if (!res.writableEnded) {
136
+ res.end();
137
+ }
138
+ },
139
+ req,
140
+ res,
141
+ stream,
142
+ streamId,
143
+ writeSegment: (data) => {
144
+ if (!res.writableEnded) {
145
+ res.write(data);
146
+ }
147
+ }
148
+ });
149
+ return;
150
+ }
151
+ // For capture-mode streams, verify the init segment is available (required by FFmpeg).
152
+ if (!stream.hls.initSegment) {
143
153
  if (!res.headersSent) {
144
154
  res.status(500).send("Stream no longer available.");
145
155
  }
@@ -148,23 +158,11 @@ async function serveMpegTsStream(streamId, channelName, req, res) {
148
158
  }
149
159
  return;
150
160
  }
151
- // Capture client address for client tracking. Captured before any async operations so it remains consistent in the cleanup closure, even if the request object
152
- // becomes unreliable after disconnect.
153
- const clientAddress = req.ip ?? req.socket.remoteAddress ?? "unknown";
154
- // Increment the MPEG-TS client counter to prevent idle timeout while this client is connected.
155
- stream.mpegTsClientCount++;
156
- updateLastAccess(streamId);
157
- registerClient(streamId, clientAddress, "mpegts");
158
- const streamLog = LOG.withStreamId(stream.streamIdStr);
159
- // Track which segments have been written to FFmpeg stdin to avoid duplicates during the catchup phase. When we subscribe to segment events and then write
160
- // existing segments, a new segment could arrive via the event that we also encounter in the existing segment iteration. The Set prevents writing it twice.
161
- const sentSegments = new Set();
162
- let cleanedUp = false;
163
- // We declare cleanup as a let initialized to a no-op so the error callback and stdin error handler can reference it before the real implementation is assigned. It
164
- // is reassigned to the real cleanup function immediately after all handlers are defined, before any asynchronous events can fire.
165
- let cleanup = () => { };
166
161
  // Spawn an FFmpeg process to remux fMP4 to MPEG-TS. The process reads concatenated fMP4 (init segment + media segments) from stdin and outputs a continuous
167
- // MPEG-TS stream on stdout. Video (H264) and audio (AAC) are copied without transcoding.
162
+ // MPEG-TS stream on stdout. Video (H264) and audio (AAC) are copied without transcoding. We declare cleanup as a let so the error callback can reference it before
163
+ // connectMpegTsClient assigns the real implementation — all assignments happen synchronously before any async events can fire.
164
+ let cleanup = () => { };
165
+ const streamLog = LOG.withStreamId(stream.streamIdStr);
168
166
  const remuxer = spawnMpegTsRemuxer((error) => {
169
167
  streamLog.debug("streaming:mpegts", "MPEG-TS remuxer error: %s.", formatError(error));
170
168
  cleanup();
@@ -172,29 +170,79 @@ async function serveMpegTsStream(streamId, channelName, req, res) {
172
170
  res.end();
173
171
  }
174
172
  }, stream.streamIdStr);
175
- // Handler for new media segments. Writes each segment to FFmpeg stdin and updates the last access timestamp to prevent idle timeout.
173
+ // Suppress errors from writing to a closed FFmpeg stdin. This can happen during cleanup when the capture stream closes before we stop writing.
174
+ remuxer.stdin.on("error", () => {
175
+ cleanup();
176
+ });
177
+ cleanup = connectMpegTsClient({
178
+ beforeCatchup: () => {
179
+ // Pipe FFmpeg stdout to the HTTP response. When FFmpeg exits (either from stdin ending or being killed), stdout closes and the response ends automatically.
180
+ remuxer.stdout.pipe(res);
181
+ // Write the init segment first — FFmpeg needs the ftyp and moov boxes before it can process any media segments.
182
+ remuxer.stdin.write(stream.hls.initSegment);
183
+ },
184
+ extraCleanup: () => {
185
+ remuxer.kill();
186
+ },
187
+ logLabel: "MPEG-TS",
188
+ onStreamTerminated: () => {
189
+ remuxer.stdin.end();
190
+ },
191
+ req,
192
+ res,
193
+ stream,
194
+ streamId,
195
+ writeSegment: (data) => {
196
+ remuxer.stdin.write(data);
197
+ }
198
+ });
199
+ }
200
+ /**
201
+ * Sets up a single MPEG-TS client session with shared lifecycle management. Handles client registration, segment event subscription, response headers, catchup
202
+ * delivery of existing segments, and cleanup on disconnect. The caller provides mode-specific callbacks for segment writing and stream termination.
203
+ *
204
+ * @param options.beforeCatchup - Optional callback invoked after event subscription and headers but before writing existing segments. Used by the capture path to
205
+ * pipe FFmpeg output and write the init segment before catchup begins.
206
+ * @param options.extraCleanup - Optional callback invoked during cleanup for mode-specific teardown (e.g., killing the FFmpeg remuxer).
207
+ * @param options.logLabel - Label for connect/disconnect debug messages (e.g., "MPEG-TS", "Native MPEG-TS").
208
+ * @param options.onStreamTerminated - Callback invoked when the stream emits a "terminated" event. Capture mode ends FFmpeg stdin; native mode ends the response.
209
+ * @param options.req - Express request object.
210
+ * @param options.res - Express response object.
211
+ * @param options.stream - The stream registry entry.
212
+ * @param options.streamId - The numeric stream ID.
213
+ * @param options.writeSegment - Callback to write segment data to the output target (FFmpeg stdin or HTTP response).
214
+ * @returns Cleanup function. The capture path wires this to the FFmpeg error handler; the native path does not need it.
215
+ */
216
+ function connectMpegTsClient({ beforeCatchup, extraCleanup, logLabel, onStreamTerminated, req, res, stream, streamId, writeSegment }) {
217
+ const clientAddress = req.ip ?? req.socket.remoteAddress ?? "unknown";
218
+ // Increment the MPEG-TS client counter to prevent idle timeout while this client is connected.
219
+ stream.mpegTsClientCount++;
220
+ updateLastAccess(streamId);
221
+ registerClient(streamId, clientAddress, "mpegts");
222
+ const streamLog = LOG.withStreamId(stream.streamIdStr);
223
+ // Track which segments have been written to avoid duplicates during the catchup phase. When we subscribe to segment events and then write existing segments, a new
224
+ // segment could arrive via the event that we also encounter in the existing segment iteration. The Set prevents writing it twice.
225
+ const sentSegments = new Set();
226
+ let cleanedUp = false;
227
+ // Handler for new media segments. Writes each segment to the output target and updates the last access timestamp to prevent idle timeout.
176
228
  const onSegment = (filename, data) => {
177
229
  if (cleanedUp || sentSegments.has(filename)) {
178
230
  return;
179
231
  }
180
232
  sentSegments.add(filename);
181
- remuxer.stdin.write(data);
233
+ writeSegment(data);
182
234
  updateLastAccess(streamId);
183
235
  };
184
- // Handler for stream termination. Ends FFmpeg stdin gracefully so it can flush remaining data and exit cleanly.
236
+ // Handler for stream termination.
185
237
  const onTerminated = () => {
186
238
  if (cleanedUp) {
187
239
  return;
188
240
  }
189
- remuxer.stdin.end();
241
+ onStreamTerminated();
190
242
  };
191
- // Suppress errors from writing to a closed FFmpeg stdin. This can happen during cleanup when the capture stream closes before we stop writing.
192
- remuxer.stdin.on("error", () => {
193
- cleanup();
194
- });
195
- // Assign the real cleanup function. This is idempotent — the cleanedUp flag ensures it only runs once regardless of which event triggers it first (client
196
- // disconnect, stream termination, or FFmpeg error).
197
- cleanup = () => {
243
+ // Idempotent cleanup function. The cleanedUp flag ensures it runs only once regardless of which event triggers it first (client disconnect, stream termination, or
244
+ // output error).
245
+ const cleanup = () => {
198
246
  if (cleanedUp) {
199
247
  return;
200
248
  }
@@ -212,10 +260,10 @@ async function serveMpegTsStream(streamId, channelName, req, res) {
212
260
  unregisterClient(streamId, clientAddress, "mpegts");
213
261
  stream.hls.segmentEmitter.off("segment", onSegment);
214
262
  stream.hls.segmentEmitter.off("terminated", onTerminated);
215
- remuxer.kill();
216
- streamLog.debug("streaming:mpegts", "MPEG-TS client disconnected.");
263
+ extraCleanup?.();
264
+ streamLog.debug("streaming:mpegts", "%s client disconnected.", logLabel);
217
265
  };
218
- // Clean up when the client disconnects. Registered immediately after cleanup is assigned to minimize the window where a disconnect could be missed.
266
+ // Clean up when the client disconnects.
219
267
  req.on("close", () => {
220
268
  cleanup();
221
269
  });
@@ -229,10 +277,8 @@ async function serveMpegTsStream(streamId, channelName, req, res) {
229
277
  res.setHeader("Content-Type", "video/mpeg");
230
278
  res.setHeader("transferMode.dlna.org", "Streaming");
231
279
  }
232
- // Pipe FFmpeg stdout to the HTTP response. When FFmpeg exits (either from stdin ending or being killed), stdout closes and the response ends automatically.
233
- remuxer.stdout.pipe(res);
234
- // Write the init segment first — FFmpeg needs the ftyp and moov boxes before it can process any media segments.
235
- remuxer.stdin.write(stream.hls.initSegment);
280
+ // Run any mode-specific initialization before writing catchup segments.
281
+ beforeCatchup?.();
236
282
  // Write all existing media segments to provide immediate playback catchup. The sentSegments Set deduplicates against any segments received via the event handler
237
283
  // during this iteration.
238
284
  for (const [filename, data] of stream.hls.segments) {
@@ -241,8 +287,9 @@ async function serveMpegTsStream(streamId, channelName, req, res) {
241
287
  break;
242
288
  }
243
289
  sentSegments.add(filename);
244
- remuxer.stdin.write(data);
290
+ writeSegment(data);
245
291
  }
246
- streamLog.debug("streaming:mpegts", "MPEG-TS client connected.");
292
+ streamLog.debug("streaming:mpegts", "%s client connected.", logLabel);
293
+ return cleanup;
247
294
  }
248
295
  //# sourceMappingURL=mpegts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mpegts.js","sourceRoot":"","sources":["../../src/streaming/mpegts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mpegts.js","sourceRoot":"","sources":["../../src/streaming/mpegts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * A single segment entry in the playlist. Each entry carries everything the builder needs to emit that segment's tags. The caller constructs entries from its own data
3
+ * source (fMP4 segmenter state, native proxy metadata, or preroll segment cache) and passes them to buildPlaylist(). Properties are listed alphabetically per project
4
+ * convention.
5
+ */
6
+ export interface PlaylistSegmentEntry {
7
+ cueIn?: boolean;
8
+ cueOut?: string;
9
+ cueOutCont?: string;
10
+ discontinuity?: boolean;
11
+ duration: number;
12
+ mapUri?: string;
13
+ programDateTime?: string;
14
+ url: string;
15
+ }
16
+ /**
17
+ * Playlist-level options for buildPlaylist(). Properties are listed alphabetically per project convention.
18
+ */
19
+ export interface PlaylistOptions {
20
+ discontinuitySequence?: number;
21
+ initialMapUri?: string;
22
+ mediaSequence: number;
23
+ targetDuration?: number;
24
+ version: number;
25
+ }
26
+ /**
27
+ * Builds an HLS playlist string from playlist options and an ordered array of segment entries. This is the single formatting function used by all playlist generators
28
+ * in PrismCast — the capture-mode fMP4 segmenter, the native HLS proxy, and the standalone preroll playlist. The builder is stateless and pure: it iterates the
29
+ * entries, emits the appropriate HLS tags for each, and returns the formatted m3u8 string.
30
+ *
31
+ * Per-segment tag ordering follows the HLS spec and preserves the ordering used by both existing generators: DISCONTINUITY, MAP, PROGRAM-DATE-TIME, CUE-IN,
32
+ * CUE-OUT, CUE-OUT-CONT, EXTINF, then the segment URL.
33
+ *
34
+ * @param options - Playlist-level configuration (version, sequence numbers, init segment, target duration).
35
+ * @param entries - Ordered array of segment entries to format.
36
+ * @returns The complete m3u8 playlist string with a trailing newline.
37
+ */
38
+ export declare function buildPlaylist(options: PlaylistOptions, entries: PlaylistSegmentEntry[]): string;
@@ -0,0 +1,66 @@
1
+ /* Copyright(C) 2024-2026, HJD (https://github.com/hjdhjd). All rights reserved.
2
+ *
3
+ * playlistBuilder.ts: Shared HLS playlist builder for PrismCast.
4
+ */
5
+ // Playlist Builder.
6
+ /**
7
+ * Builds an HLS playlist string from playlist options and an ordered array of segment entries. This is the single formatting function used by all playlist generators
8
+ * in PrismCast — the capture-mode fMP4 segmenter, the native HLS proxy, and the standalone preroll playlist. The builder is stateless and pure: it iterates the
9
+ * entries, emits the appropriate HLS tags for each, and returns the formatted m3u8 string.
10
+ *
11
+ * Per-segment tag ordering follows the HLS spec and preserves the ordering used by both existing generators: DISCONTINUITY, MAP, PROGRAM-DATE-TIME, CUE-IN,
12
+ * CUE-OUT, CUE-OUT-CONT, EXTINF, then the segment URL.
13
+ *
14
+ * @param options - Playlist-level configuration (version, sequence numbers, init segment, target duration).
15
+ * @param entries - Ordered array of segment entries to format.
16
+ * @returns The complete m3u8 playlist string with a trailing newline.
17
+ */
18
+ export function buildPlaylist(options, entries) {
19
+ // Compute TARGETDURATION as the maximum of the provided floor value and the maximum entry duration, rounded up to the nearest integer per RFC 8216 Section 4.3.3.1.
20
+ let maxDuration = options.targetDuration ?? 0;
21
+ for (const entry of entries) {
22
+ if (entry.duration > maxDuration) {
23
+ maxDuration = entry.duration;
24
+ }
25
+ }
26
+ const lines = [
27
+ "#EXTM3U",
28
+ "#EXT-X-VERSION:" + String(options.version),
29
+ "#EXT-X-TARGETDURATION:" + String(Math.ceil(maxDuration)),
30
+ "#EXT-X-MEDIA-SEQUENCE:" + String(options.mediaSequence)
31
+ ];
32
+ // Emit DISCONTINUITY-SEQUENCE when the caller provides a value. The distinction between undefined (no discontinuities in the stream's history) and 0
33
+ // (discontinuities exist but none have scrolled off the window) is meaningful for spec compliance — both capture and native paths control this independently.
34
+ if (options.discontinuitySequence !== undefined) {
35
+ lines.push("#EXT-X-DISCONTINUITY-SEQUENCE:" + String(options.discontinuitySequence));
36
+ }
37
+ // Emit the initial EXT-X-MAP for fMP4 streams. This applies to all segments until a per-segment mapUri overrides it at a discontinuity boundary.
38
+ if (options.initialMapUri) {
39
+ lines.push("#EXT-X-MAP:URI=\"" + options.initialMapUri + "\"");
40
+ }
41
+ // Emit per-segment tags and EXTINF for each entry.
42
+ for (const entry of entries) {
43
+ if (entry.discontinuity) {
44
+ lines.push("#EXT-X-DISCONTINUITY");
45
+ }
46
+ if (entry.mapUri) {
47
+ lines.push("#EXT-X-MAP:URI=\"" + entry.mapUri + "\"");
48
+ }
49
+ if (entry.programDateTime) {
50
+ lines.push("#EXT-X-PROGRAM-DATE-TIME:" + entry.programDateTime);
51
+ }
52
+ if (entry.cueIn) {
53
+ lines.push("#EXT-X-CUE-IN");
54
+ }
55
+ if (entry.cueOut !== undefined) {
56
+ lines.push((entry.cueOut.length > 0) ? "#EXT-X-CUE-OUT:" + entry.cueOut : "#EXT-X-CUE-OUT");
57
+ }
58
+ if (entry.cueOutCont !== undefined) {
59
+ lines.push("#EXT-X-CUE-OUT-CONT:" + entry.cueOutCont);
60
+ }
61
+ lines.push("#EXTINF:" + entry.duration.toFixed(3) + ",");
62
+ lines.push(entry.url);
63
+ }
64
+ return lines.join("\n") + "\n";
65
+ }
66
+ //# sourceMappingURL=playlistBuilder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playlistBuilder.js","sourceRoot":"","sources":["../../src/streaming/playlistBuilder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAqEH,oBAAoB;AAEpB;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAAC,OAAwB,EAAE,OAA+B;IAErF,oKAAoK;IACpK,IAAI,WAAW,GAAG,OAAO,CAAC,cAAc,IAAI,CAAC,CAAC;IAE9C,KAAI,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAE3B,IAAG,KAAK,CAAC,QAAQ,GAAG,WAAW,EAAE,CAAC;YAEhC,WAAW,GAAG,KAAK,CAAC,QAAQ,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAa;QACtB,SAAS;QACT,iBAAiB,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;QAC3C,wBAAwB,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACzD,wBAAwB,GAAG,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC;KACzD,CAAC;IAEF,qJAAqJ;IACrJ,8JAA8J;IAC9J,IAAG,OAAO,CAAC,qBAAqB,KAAK,SAAS,EAAE,CAAC;QAE/C,KAAK,CAAC,IAAI,CAAC,gCAAgC,GAAG,MAAM,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAAC;IACvF,CAAC;IAED,iJAAiJ;IACjJ,IAAG,OAAO,CAAC,aAAa,EAAE,CAAC;QAEzB,KAAK,CAAC,IAAI,CAAC,mBAAmB,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IACjE,CAAC;IAED,mDAAmD;IACnD,KAAI,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAE3B,IAAG,KAAK,CAAC,aAAa,EAAE,CAAC;YAEvB,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QACrC,CAAC;QAED,IAAG,KAAK,CAAC,MAAM,EAAE,CAAC;YAEhB,KAAK,CAAC,IAAI,CAAC,mBAAmB,GAAG,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;QACxD,CAAC;QAED,IAAG,KAAK,CAAC,eAAe,EAAE,CAAC;YAEzB,KAAK,CAAC,IAAI,CAAC,2BAA2B,GAAG,KAAK,CAAC,eAAe,CAAC,CAAC;QAClE,CAAC;QAED,IAAG,KAAK,CAAC,KAAK,EAAE,CAAC;YAEf,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC9B,CAAC;QAED,IAAG,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAE9B,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC;QAC9F,CAAC;QAED,IAAG,KAAK,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YAElC,KAAK,CAAC,IAAI,CAAC,sBAAsB,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC;QACxD,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;QACzD,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AACjC,CAAC"}