prismcast 1.10.2 → 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 (166) hide show
  1. package/dist/app.d.ts +5 -3
  2. package/dist/app.js +42 -41
  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 +95 -70
  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/config/consistencyProbe.js +13 -11
  17. package/dist/config/consistencyProbe.js.map +1 -1
  18. package/dist/config/paths.d.ts +3 -8
  19. package/dist/config/paths.js +3 -10
  20. package/dist/config/paths.js.map +1 -1
  21. package/dist/config/profiles.d.ts +1 -1
  22. package/dist/config/profiles.js +6 -6
  23. package/dist/config/profiles.js.map +1 -1
  24. package/dist/config/services.d.ts +5 -5
  25. package/dist/config/services.js +8 -8
  26. package/dist/config/services.js.map +1 -1
  27. package/dist/config/sites.d.ts +2 -2
  28. package/dist/config/sites.js +6 -6
  29. package/dist/config/sites.js.map +1 -1
  30. package/dist/config/userChannels.d.ts +108 -14
  31. package/dist/config/userChannels.js +291 -37
  32. package/dist/config/userChannels.js.map +1 -1
  33. package/dist/config/userConfig.js +1 -1
  34. package/dist/config/userConfig.js.map +1 -1
  35. package/dist/config/userProfiles.d.ts +3 -3
  36. package/dist/config/userProfiles.js +19 -19
  37. package/dist/config/userProfiles.js.map +1 -1
  38. package/dist/index.js +7 -9
  39. package/dist/index.js.map +1 -1
  40. package/dist/native/index.d.ts +1 -1
  41. package/dist/native/index.js +9 -18
  42. package/dist/native/index.js.map +1 -1
  43. package/dist/native/proxy.d.ts +1 -4
  44. package/dist/native/proxy.js +0 -9
  45. package/dist/native/proxy.js.map +1 -1
  46. package/dist/routes/clientActions.d.ts +97 -0
  47. package/dist/routes/clientActions.js +113 -0
  48. package/dist/routes/clientActions.js.map +1 -0
  49. package/dist/routes/components.d.ts +4 -3
  50. package/dist/routes/components.js +36 -39
  51. package/dist/routes/components.js.map +1 -1
  52. package/dist/routes/config/channels/endpoints/browse.js.map +1 -1
  53. package/dist/routes/config/channels/endpoints/crud.js +37 -72
  54. package/dist/routes/config/channels/endpoints/crud.js.map +1 -1
  55. package/dist/routes/config/channels/endpoints/importExport.js.map +1 -1
  56. package/dist/routes/config/channels/healthBridge.d.ts +23 -0
  57. package/dist/routes/config/channels/healthBridge.js +71 -0
  58. package/dist/routes/config/channels/healthBridge.js.map +1 -0
  59. package/dist/routes/config/channels/table.js +97 -84
  60. package/dist/routes/config/channels/table.js.map +1 -1
  61. package/dist/routes/config/services.js +25 -22
  62. package/dist/routes/config/services.js.map +1 -1
  63. package/dist/routes/config/settings.js +9 -6
  64. package/dist/routes/config/settings.js.map +1 -1
  65. package/dist/routes/debug.js +277 -139
  66. package/dist/routes/debug.js.map +1 -1
  67. package/dist/routes/logs.js +1 -1
  68. package/dist/routes/logs.js.map +1 -1
  69. package/dist/routes/root/content.js +18 -14
  70. package/dist/routes/root/content.js.map +1 -1
  71. package/dist/routes/root/index.js +17 -14
  72. package/dist/routes/root/index.js.map +1 -1
  73. package/dist/routes/root/scripts/channels.js +50 -21
  74. package/dist/routes/root/scripts/channels.js.map +1 -1
  75. package/dist/routes/root/scripts/config.js +72 -3
  76. package/dist/routes/root/scripts/config.js.map +1 -1
  77. package/dist/routes/root/scripts/shared.js +60 -0
  78. package/dist/routes/root/scripts/shared.js.map +1 -1
  79. package/dist/routes/root/scripts/status.handlers.d.ts +5 -27
  80. package/dist/routes/root/scripts/status.handlers.js +8 -60
  81. package/dist/routes/root/scripts/status.handlers.js.map +1 -1
  82. package/dist/routes/root/scripts/status.js +6 -1
  83. package/dist/routes/root/scripts/status.js.map +1 -1
  84. package/dist/routes/streams.js +5 -3
  85. package/dist/routes/streams.js.map +1 -1
  86. package/dist/service/commands.context.js +1 -1
  87. package/dist/service/commands.context.js.map +1 -1
  88. package/dist/service/generators.context.js +1 -1
  89. package/dist/service/generators.context.js.map +1 -1
  90. package/dist/service/generators.d.ts +1 -1
  91. package/dist/service/generators.js +3 -11
  92. package/dist/service/generators.js.map +1 -1
  93. package/dist/streaming/hls.js +1 -5
  94. package/dist/streaming/hls.js.map +1 -1
  95. package/dist/streaming/hlsSegments.d.ts +1 -1
  96. package/dist/streaming/hlsSegments.js.map +1 -1
  97. package/dist/streaming/preroll.d.ts +1 -1
  98. package/dist/streaming/preroll.js +1 -5
  99. package/dist/streaming/preroll.js.map +1 -1
  100. package/dist/streaming/statusEmitter.d.ts +5 -8
  101. package/dist/streaming/statusEmitter.js +0 -5
  102. package/dist/streaming/statusEmitter.js.map +1 -1
  103. package/dist/types/channels.d.ts +9 -0
  104. package/dist/types/channels.js.map +1 -1
  105. package/dist/types/profiles.d.ts +2 -2
  106. package/dist/upgrade/commands.context.js +14 -10
  107. package/dist/upgrade/commands.context.js.map +1 -1
  108. package/dist/upgrade/commands.d.ts +7 -15
  109. package/dist/upgrade/commands.js +27 -28
  110. package/dist/upgrade/commands.js.map +1 -1
  111. package/dist/upgrade/detection.context.js +2 -1
  112. package/dist/upgrade/detection.context.js.map +1 -1
  113. package/dist/upgrade/detection.d.ts +2 -1
  114. package/dist/upgrade/detection.js +20 -51
  115. package/dist/upgrade/detection.js.map +1 -1
  116. package/dist/upgrade/index.d.ts +1 -0
  117. package/dist/upgrade/index.js +1 -0
  118. package/dist/upgrade/index.js.map +1 -1
  119. package/dist/upgrade/lifecycle.context.d.ts +19 -0
  120. package/dist/upgrade/lifecycle.context.js +47 -0
  121. package/dist/upgrade/lifecycle.context.js.map +1 -0
  122. package/dist/upgrade/lifecycle.d.ts +72 -0
  123. package/dist/upgrade/lifecycle.js +176 -0
  124. package/dist/upgrade/lifecycle.js.map +1 -0
  125. package/dist/upgrade/pathHandle.d.ts +28 -0
  126. package/dist/upgrade/pathHandle.js +101 -0
  127. package/dist/upgrade/pathHandle.js.map +1 -0
  128. package/dist/utils/bootSession.context.d.ts +15 -0
  129. package/dist/utils/bootSession.context.js +59 -0
  130. package/dist/utils/bootSession.context.js.map +1 -0
  131. package/dist/utils/bootSession.d.ts +16 -0
  132. package/dist/utils/bootSession.js +20 -0
  133. package/dist/utils/bootSession.js.map +1 -0
  134. package/dist/utils/clock.js +1 -1
  135. package/dist/utils/ffmpeg.js +1 -1
  136. package/dist/utils/ffmpeg.js.map +1 -1
  137. package/dist/utils/index.d.ts +3 -0
  138. package/dist/utils/index.js +3 -0
  139. package/dist/utils/index.js.map +1 -1
  140. package/dist/utils/markup.d.ts +10 -0
  141. package/dist/utils/markup.js +23 -0
  142. package/dist/utils/markup.js.map +1 -1
  143. package/dist/utils/pid.d.ts +2 -25
  144. package/dist/utils/pid.js +10 -49
  145. package/dist/utils/pid.js.map +1 -1
  146. package/dist/utils/processInspector.context.d.ts +7 -0
  147. package/dist/utils/processInspector.context.js +104 -0
  148. package/dist/utils/processInspector.context.js.map +1 -0
  149. package/dist/utils/processInspector.d.ts +54 -0
  150. package/dist/utils/processInspector.js +125 -0
  151. package/dist/utils/processInspector.js.map +1 -0
  152. package/dist/utils/retry.d.ts +1 -1
  153. package/dist/utils/retry.js +1 -1
  154. package/dist/utils/retry.js.map +1 -1
  155. package/dist/utils/runtimeIdentity.context.d.ts +6 -0
  156. package/dist/utils/runtimeIdentity.context.js +13 -0
  157. package/dist/utils/runtimeIdentity.context.js.map +1 -0
  158. package/dist/utils/runtimeIdentity.d.ts +96 -0
  159. package/dist/utils/runtimeIdentity.js +171 -0
  160. package/dist/utils/runtimeIdentity.js.map +1 -0
  161. package/dist/utils/timing.d.ts +1 -1
  162. package/dist/utils/timing.js +0 -5
  163. package/dist/utils/timing.js.map +1 -1
  164. package/dist/utils/version.js +40 -10
  165. package/dist/utils/version.js.map +1 -1
  166. package/package.json +2 -2
@@ -1,32 +1,24 @@
1
- import { LOG, chromeFetch, startTimer } from "../utils/index.js";
2
- import { classifyHlsPlaylist } from "../native/probe.js";
3
- /* This module installs a Chrome DevTools Protocol (CDP) listener on the Network domain to capture HLS manifest URLs as the browser's video player fetches them. The
4
- * listener is shared by two consumers:
1
+ import { LOG, startTimer } from "../utils/index.js";
2
+ import { observeHlsPlaylists } from "./hlsPlaylistObserver.js";
3
+ /* This module hosts two consumer-facing state machines built on top of the HLS playlist observer (hlsPlaylistObserver.ts), which in turn rides on the tab-wide
4
+ * network observer (tabNetworkObserver.ts). The layering:
5
5
  *
6
- * - Native HLS streaming: captures the playlist URL during stream startup and hands the CDP session off to the proxy for subsequent token-refresh tunes. See
7
- * installManifestInterceptor() and the consumers in src/native/. This consumer accepts both master and media playlists; the probe normalizes either kind into a
8
- * MediaFeed so downstream code does not branch on which kind arrived.
9
- * - Tune verification: confirms that the channel a strategy clicked actually loaded by matching the resulting manifest URL against an expected predicate (e.g.,
10
- * call sign in the path). See awaitMatchingManifest() and the consumer in src/browser/tuning/fox.ts. This consumer expects master playlists only because tune
11
- * verification is a multi-channel concept that does not apply to direct-tune media-only sources.
6
+ * tabNetworkObserver -> tab-wide CDP transport (OOPIF-aware via auto-attach + flatten)
7
+ * hlsPlaylistObserver -> HLS filter + body fetch + classify + per-URL dedup
8
+ * manifestInterceptor -> selection: first-vs-latest, master priority, finalize semantics, predicate match
12
9
  *
13
- * Both APIs share the underlying CDP observer that filters .m3u8 URLs, fetches the body, and asks classifyHlsPlaylist() for the playlist kind; the kind is
14
- * forwarded to consumers so each can apply its own kind-specific filter without re-reading the body. Classification lives in exactly one place
15
- * (src/native/probe.ts) so the master/media decision cannot drift between the interceptor's transport-layer gate and the probe's resolution-layer dispatch.
16
- * We use a direct fetch rather than CDP's Network.getResponseBody because Chrome's network cache can evict response bodies before we read them, causing
17
- * spurious "No data found for resource" failures.
10
+ * Two state machines live here:
18
11
  *
19
- * For multi-channel sites, the video player may load manifests for channels other than the one requested. installManifestInterceptor tracks both the first and
20
- * latest master manifest URLs to handle two distinct tuning patterns. The pattern is selected by the directTune flag passed to finalize():
12
+ * - installManifestInterceptor(): the long-lived capture used by native HLS setup. Accepts both master and media playlists, tracks first and latest URLs per
13
+ * kind, and resolves with the URL appropriate for the tune type when finalize() is called. Master playlists take precedence over media playlists when both
14
+ * arrive during the interception window because masters carry richer metadata (variant bandwidth, resolution, separate audio renditions).
15
+ * - awaitMatchingManifest(): the short-lived verification used after a guide-click tune. Accepts master playlists only and resolves with the first URL whose
16
+ * path satisfies a caller-supplied predicate. Media playlists are skipped at the consumer level because tune verification is a multi-channel concept that
17
+ * does not apply to direct-tune media-only sources.
21
18
  *
22
- * - Direct tunes (directTune=true): the navigated URL itself selects the channel, so the first manifest captured is the correct one - the player loaded it for
23
- * the navigated URL. Background prefetches for other channels may arrive later but should be ignored. The first manifest wins.
24
- * - Guide tunes (directTune=false): page navigation loads a default channel's manifest, then a tuning strategy (guide-grid click, JS bridge call, webpack
25
- * injection, etc.) triggers a new manifest fetch for the requested channel. The latest manifest wins.
26
- *
27
- * awaitMatchingManifest takes a different approach: it resolves with the first master manifest whose URL satisfies the caller-supplied predicate. The CDP session
28
- * is torn down automatically on resolution. This is the verification path - the caller has already triggered the tune and now wants confirmation that the
29
- * resulting manifest belongs to the expected channel.
19
+ * Race safety, URL filtering, body fetching, and classification all live below this module - we receive a stream of recognized HLS playlists with their kind
20
+ * and apply selection policy. Public callers do not hold CDP session references; the underlying HLS observer disposes its tab network observer deterministically
21
+ * when finalize, timeout, predicate match, or explicit dispose fires.
30
22
  */
31
23
  // Default timeout for the long-lived interception used by native HLS.
32
24
  const INTERCEPTION_TIMEOUT = 15000;
@@ -35,15 +27,13 @@ const INTERCEPTION_TIMEOUT = 15000;
35
27
  const FINALIZE_SETTLE_DELAY = 1500;
36
28
  // Default timeout for awaitMatchingManifest. Tune verification is a short-lived check after a click, so the budget is tighter than the native interception path.
37
29
  const VERIFICATION_TIMEOUT = 8000;
38
- // 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
39
- // cache evicts the body or the CDN serves a slow response, we abandon and treat the response as non-master.
40
- const MANIFEST_BODY_FETCH_TIMEOUT = 5000;
41
30
  /**
42
31
  * Selects the playlist URL appropriate for a given resolution mode from the per-kind first/latest state captured by the manifest observer. Master URLs always
43
32
  * outrank media URLs when both kinds were observed because masters declare richer metadata (variant bandwidth, resolution, separate audio renditions). Within
44
33
  * the chosen kind, direct tunes pick the first URL and guide tunes pick the latest URL. Returns null when no qualifying URL is available.
45
34
  *
46
- * Pure function exported for unit testing; the closure inside installManifestInterceptor() consumes it through the same shape.
35
+ * Pure selection policy exported so the rule is testable in isolation from the network plumbing; the closure inside installManifestInterceptor() consumes it
36
+ * through the same shape.
47
37
  *
48
38
  * @param state - The captured-URL state at resolution time.
49
39
  * @returns The selected URL, or null when no URL matches the resolution mode.
@@ -55,112 +45,29 @@ export function selectInterceptedManifest(state) {
55
45
  return state.latestMasterUrl ?? state.latestMediaUrl;
56
46
  }
57
47
  /**
58
- * Installs a CDP Network.responseReceived listener that calls onPlaylist() for every recognized HLS playlist URL observed. The listener filters to .m3u8 URLs,
59
- * fetches the body, and asks classifyHlsPlaylist() for the playlist kind; recognized kinds (master, media) fire the callback with the kind label, while
60
- * unrecognized bodies are skipped. Consumers apply their own kind-specific filtering on top of the callback - installManifestInterceptor accepts both kinds
61
- * with master priority, while awaitMatchingManifest accepts master only.
62
- *
63
- * Returns null when the page is closed or the CDP session cannot be created. The caller is responsible for calling dispose() to clean up.
64
- * @param page - The Puppeteer page to monitor.
65
- * @param onPlaylist - Callback invoked for each verified HLS playlist URL together with its kind.
66
- * @param logCategory - Debug log category for this observer's lifecycle messages.
67
- * @returns The observer handle, or null if installation failed.
68
- */
69
- async function startManifestObserver(page, onPlaylist, logCategory) {
70
- if (page.isClosed()) {
71
- return null;
72
- }
73
- let cdpSession;
74
- try {
75
- cdpSession = await page.createCDPSession();
76
- }
77
- catch (error) {
78
- LOG.debug(logCategory, "Failed to create CDP session: %s.", String(error));
79
- return null;
80
- }
81
- try {
82
- await cdpSession.send("Network.enable");
83
- }
84
- catch (error) {
85
- LOG.debug(logCategory, "Failed to enable Network domain: %s.", String(error));
86
- return null;
87
- }
88
- LOG.debug(logCategory, "CDP manifest observer installed.");
89
- let disposed = false;
90
- // Listen for completed responses. We use Network.responseReceived to capture .m3u8 URLs, then fetch the manifest body directly from Node.js to verify it is
91
- // a master manifest. This avoids CDP's Network.getResponseBody which is unreliable - Chrome's network cache can evict response bodies before we read them.
92
- const onResponseReceived = async (params) => {
93
- if (disposed) {
94
- return;
95
- }
96
- const url = params.response.url;
97
- // Filter for .m3u8 URLs. Strip query parameters before checking the extension.
98
- const urlPath = url.split("?")[0] ?? "";
99
- if (!urlPath.endsWith(".m3u8")) {
100
- return;
101
- }
102
- LOG.debug(logCategory, "Observed .m3u8 response: %s.", url.slice(0, 120));
103
- // Fetch the manifest body directly to inspect whether this is a master manifest. The URL contains embedded CDN auth tokens, so no cookies or special
104
- // headers are needed beyond the Chrome User-Agent injected by chromeFetch().
105
- try {
106
- const response = await chromeFetch(url, { signal: AbortSignal.timeout(MANIFEST_BODY_FETCH_TIMEOUT) });
107
- if (!response.ok) {
108
- LOG.debug(logCategory, "Manifest fetch returned HTTP %s for %s.", response.status, url.slice(0, 120));
109
- return;
110
- }
111
- const body = await response.text();
112
- // Delegate the master/media decision to the canonical classifier. Recognized kinds (master, media) are forwarded to the callback together with the kind
113
- // label; consumers apply their own filter. Both consumers of onPlaylist maintain their own resolved-flag guard, so a callback that arrives after dispose
114
- // is harmless - the downstream resolve is idempotent.
115
- const kind = classifyHlsPlaylist(body);
116
- if (kind === "unknown") {
117
- LOG.debug(logCategory, "Skipping .m3u8 classified as unknown (no recognizable HLS directives).");
118
- return;
119
- }
120
- onPlaylist(url, kind);
121
- }
122
- catch (error) {
123
- LOG.debug(logCategory, "Could not fetch .m3u8 body: %s.", String(error));
124
- }
125
- };
126
- // Wrap the async handler to avoid no-misused-promises - EventEmitter.on() does not handle returned promises.
127
- const wrappedHandler = (...args) => { void onResponseReceived(...args); };
128
- cdpSession.on("Network.responseReceived", wrappedHandler);
129
- const dispose = (keepSession) => {
130
- if (disposed) {
131
- return;
132
- }
133
- disposed = true;
134
- cdpSession.off("Network.responseReceived", wrappedHandler);
135
- if (!keepSession) {
136
- removeManifestInterceptor(cdpSession);
137
- }
138
- };
139
- return { cdpSession, dispose };
140
- }
141
- /**
142
- * Installs a long-lived CDP listener that tracks observed HLS playlist URLs on the given page. Both master and media playlists are accepted; the listener tracks
143
- * first/latest URLs separately per kind so master-based and media-only sites both work without separate code paths. Master playlists take precedence over media
48
+ * Installs a long-lived HLS playlist observer that tracks observed playlist URLs on the given page. Both master and media playlists are accepted; first/latest
49
+ * URLs are tracked separately per kind so master-based and media-only sites both work without separate code paths. Master playlists take precedence over media
144
50
  * playlists when both have arrived during the interception window because masters carry richer metadata (variant bandwidth, resolution, separate audio
145
- * renditions) that improves the resulting MediaFeed. The returned handle provides a finalize(directTune) callback - when called, the interceptor resolves with
51
+ * renditions) that improves the resulting MediaFeed. The returned handle provides a finalize(directTune) callback - when called, the observer resolves with
146
52
  * whichever URL is appropriate for the tune type:
147
53
  *
148
54
  * - directTune=true: resolves immediately (or after the settle delay if no manifest has arrived yet) with the first manifest captured. Used by sites where the
149
55
  * navigated URL itself selects the channel and the player loads its manifest before the click handler returns. Master-first URL preferred over media-first.
150
- * - directTune=false: resolves after the settle delay with the latest manifest captured. Used by guide-based sites where the channel-switch click triggers a new
151
- * manifest fetch that may arrive milliseconds after the click handler returns. Master-latest URL preferred over media-latest.
56
+ * - directTune=false: resolves after the settle delay with the latest manifest captured. Used by guide-based sites where the channel-switch click triggers a
57
+ * new manifest fetch that may arrive milliseconds after the click handler returns. Master-latest URL preferred over media-latest.
58
+ *
59
+ * On timeout (finalize never called), the observer resolves with the latest captured URL using the same master-priority rule, or null if none arrived. The
60
+ * underlying HLS observer is disposed automatically on resolution; callers do not hold CDP session references.
152
61
  *
153
- * On timeout (finalize never called), the listener resolves with the latest captured URL using the same master-priority rule, or null if none arrived. The CDP
154
- * session is preserved in the result and ownership transfers to the caller; callers must call removeManifestInterceptor() when done.
155
- * @param page - The Puppeteer page to monitor.
62
+ * @param page - The puppeteer page to monitor.
156
63
  * @param timeout - Maximum time in milliseconds to wait for a manifest. Acts as a safety net if finalize() is never called.
157
- * @returns The interceptor handle, or null if the CDP session could not be created.
64
+ * @returns The interceptor handle, or null if the observer could not be installed.
158
65
  */
159
66
  export async function installManifestInterceptor(page, timeout = INTERCEPTION_TIMEOUT) {
160
67
  const elapsed = startTimer();
161
- // Track first/latest URLs separately per playlist kind. Master URLs take precedence at selection time because masters carry richer metadata. For direct
162
- // tunes, the first URL of the higher-priority kind wins; for guide tunes, the latest URL of the higher-priority kind wins. Without separate per-kind
163
- // tracking, a master-based site whose player happens to also load a media playlist for a different channel would risk picking the wrong one.
68
+ // Track first/latest URLs separately per playlist kind. Master URLs take precedence at selection time because masters carry richer metadata. For direct tunes,
69
+ // the first URL of the higher-priority kind wins; for guide tunes, the latest URL of the higher-priority kind wins. Without separate per-kind tracking, a
70
+ // master-based site whose player happens to also load a media playlist for a different channel would risk picking the wrong one.
164
71
  let firstMasterUrl = null;
165
72
  let latestMasterUrl = null;
166
73
  let firstMediaUrl = null;
@@ -168,18 +75,21 @@ export async function installManifestInterceptor(page, timeout = INTERCEPTION_TI
168
75
  let resolved = false;
169
76
  let manifestCount = 0;
170
77
  const { promise, resolve } = Promise.withResolvers();
171
- const observer = await startManifestObserver(page, (url, kind) => {
172
- manifestCount++;
173
- if (kind === "master") {
174
- firstMasterUrl ??= url;
175
- latestMasterUrl = url;
176
- }
177
- else if (kind === "media") {
178
- firstMediaUrl ??= url;
179
- latestMediaUrl = url;
78
+ const observer = await observeHlsPlaylists(page, {
79
+ logCategory: "native:intercept",
80
+ onPlaylist: (playlist) => {
81
+ manifestCount++;
82
+ if (playlist.kind === "master") {
83
+ firstMasterUrl ??= playlist.url;
84
+ latestMasterUrl = playlist.url;
85
+ }
86
+ else if (playlist.kind === "media") {
87
+ firstMediaUrl ??= playlist.url;
88
+ latestMediaUrl = playlist.url;
89
+ }
90
+ LOG.debug("native:intercept", "%s playlist captured (#%s) in %sms: %s.", playlist.kind, manifestCount, elapsed(), playlist.url.slice(0, 120));
180
91
  }
181
- LOG.debug("native:intercept", "%s playlist captured (#%s) in %sms: %s.", kind, manifestCount, elapsed(), url.slice(0, 120));
182
- }, "native:intercept");
92
+ });
183
93
  if (!observer) {
184
94
  return null;
185
95
  }
@@ -192,74 +102,71 @@ export async function installManifestInterceptor(page, timeout = INTERCEPTION_TI
192
102
  latestMasterUrl,
193
103
  latestMediaUrl
194
104
  });
195
- // Timeout guard. If finalize() is never called (defensive), resolve with whatever we have after the timeout.
196
- const timer = setTimeout(() => {
105
+ // Centralized resolution helper. Used by the timeout, finalize, and dispose paths to settle the promise exactly once and tear down the observer regardless of
106
+ // which path won. The selectedUrl computation honors both the master-priority rule and the directTune first/latest semantics via selectInterceptedManifest.
107
+ const settle = (directTune, reason) => {
197
108
  if (resolved) {
198
109
  return;
199
110
  }
200
111
  resolved = true;
201
- // The timeout path mirrors the latest-URL semantics of a guide tune; if no finalize() ever arrived we err on the side of the most recent capture.
202
- const selected = selectUrl(false);
203
- if (selected) {
204
- LOG.debug("native:intercept", "Manifest interception timed out after %sms. Resolving with latest URL (%s captured).", elapsed(), manifestCount);
205
- observer.dispose(true);
206
- resolve({ cdpSession: observer.cdpSession, masterManifestUrl: selected });
112
+ clearTimeout(timer);
113
+ const selectedUrl = selectUrl(directTune);
114
+ if (selectedUrl) {
115
+ LOG.debug("native:intercept", "Interception %s in %sms with %s capture(s). Using %s: %s.", reason, elapsed(), manifestCount, directTune ? "first" : "latest", selectedUrl.slice(0, 120));
116
+ observer.dispose();
117
+ resolve({ masterManifestUrl: selectedUrl });
207
118
  }
208
119
  else {
209
- LOG.debug("native:intercept", "Manifest interception timed out after %sms. No HLS playlist captured.", elapsed());
210
- observer.dispose(false);
120
+ LOG.debug("native:intercept", "Interception %s in %sms but no HLS playlist was captured.", reason, elapsed());
121
+ observer.dispose();
211
122
  resolve(null);
212
123
  }
213
- }, timeout);
124
+ };
125
+ // Timeout guard. If finalize() is never called (defensive), settle with whatever we have after the timeout. The timeout path mirrors the latest-URL semantics
126
+ // of a guide tune; if no finalize() ever arrived we err on the side of the most recent capture.
127
+ const timer = setTimeout(() => { settle(false, "timed out"); }, timeout);
214
128
  // Finalize function exposed on the returned handle. Called by the stream setup code after channel selection is complete. The resolution strategy depends on
215
129
  // two factors: whether a qualifying manifest has already been captured, and whether the tune is direct or guide-based.
216
130
  //
217
- // - Manifest captured + direct tune: resolve immediately (A&E, most TVE sites - manifest arrived during page load).
131
+ // - Manifest captured + direct tune: settle immediately (A&E, most TVE sites - manifest arrived during page load).
218
132
  // - Manifest captured + guide tune: wait FINALIZE_SETTLE_DELAY (Fox guide, Hulu - a newer manifest from the channel switch may still arrive).
219
133
  // - No manifest captured + either: wait FINALIZE_SETTLE_DELAY (Fox Sports - manifest fetch starts after video element appears, hasn't arrived yet).
220
134
  const finalize = (directTune) => {
221
135
  if (resolved) {
222
136
  return;
223
137
  }
224
- // Helper that resolves the promise with the current state. The selectUrl helper applies the master-first priority and the directTune first/latest semantics.
225
- const resolveNow = () => {
226
- if (resolved) {
227
- return;
228
- }
229
- resolved = true;
230
- clearTimeout(timer);
231
- const selectedUrl = selectUrl(directTune);
232
- if (selectedUrl) {
233
- LOG.debug("native:intercept", "Interception finalized in %sms with %s playlist capture(s). Using %s: %s.", elapsed(), manifestCount, directTune ? "first" : "latest", selectedUrl.slice(0, 120));
234
- observer.dispose(true);
235
- resolve({ cdpSession: observer.cdpSession, masterManifestUrl: selectedUrl });
236
- }
237
- else {
238
- LOG.debug("native:intercept", "Interception finalized in %sms but no HLS playlist was captured.", elapsed());
239
- observer.dispose(false);
240
- resolve(null);
241
- }
242
- };
243
- // For direct tunes, resolve immediately if a master URL has already arrived. We do not short-circuit on a media-only first URL because a master may still
244
- // be in flight - waiting the settle delay gives master priority a chance to take effect. Once the settle elapses we resolve with whichever URL ranks
245
- // highest under selectUrl.
138
+ // For direct tunes, settle immediately if a master URL has already arrived. We do not short-circuit on a media-only first URL because a master may still
139
+ // be in flight - waiting the settle delay gives master priority a chance to take effect.
246
140
  if (directTune && firstMasterUrl) {
247
- resolveNow();
141
+ settle(true, "finalized");
248
142
  }
249
143
  else {
250
- setTimeout(resolveNow, FINALIZE_SETTLE_DELAY);
144
+ setTimeout(() => { settle(directTune, "finalized"); }, FINALIZE_SETTLE_DELAY);
145
+ }
146
+ };
147
+ // Explicit dispose path for "using" syntax or caller-driven cancellation. Idempotent. Resolves the promise with null if it has not already settled, so
148
+ // callers awaiting the promise are never left hanging on a disposed observer.
149
+ const dispose = () => {
150
+ if (resolved) {
151
+ return;
251
152
  }
153
+ resolved = true;
154
+ clearTimeout(timer);
155
+ LOG.debug("native:intercept", "Interception disposed in %sms with %s capture(s) and no resolution selected.", elapsed(), manifestCount);
156
+ observer.dispose();
157
+ resolve(null);
252
158
  };
253
- return { finalize, promise };
159
+ return { dispose, finalize, promise, [Symbol.dispose]: dispose };
254
160
  }
255
161
  /**
256
- * Awaits the first master HLS manifest URL whose URL satisfies the supplied predicate. Used by tune-verification paths where the caller has just clicked or
162
+ * Awaits the first master HLS manifest whose URL satisfies the supplied predicate. Used by tune-verification paths where the caller has just clicked or
257
163
  * navigated to a target channel and wants to confirm the resulting stream belongs to that channel rather than to whatever default the page was previously
258
164
  * showing.
259
165
  *
260
- * Resolves with the matching URL on success. Resolves with null if no matching manifest arrives within the timeout. The CDP session is torn down automatically
261
- * in both cases - the verification path does not hand off the session to a downstream consumer.
262
- * @param page - The Puppeteer page to monitor.
166
+ * Resolves with the matching URL on success. Resolves with null if no matching manifest arrives within the timeout. The underlying observer is torn down
167
+ * automatically in both cases - the verification path does not hand off a session to a downstream consumer.
168
+ *
169
+ * @param page - The puppeteer page to monitor.
263
170
  * @param predicate - Test applied to each verified master manifest URL. Return true to accept the URL and resolve.
264
171
  * @param timeout - Maximum time in milliseconds to wait for a matching manifest.
265
172
  * @returns The matching URL, or null on timeout.
@@ -271,25 +178,28 @@ export async function awaitMatchingManifest(page, predicate, timeout = VERIFICAT
271
178
  // Tune verification is a multi-channel concept that only applies to master playlists - direct-tune media-only sources do not run a guide-click verification
272
179
  // step because the page navigation itself selects the channel. We reject media playlists at the consumer level rather than asking the observer to filter so
273
180
  // the observer keeps a single canonical contract (forward all recognized HLS playlists with their kind).
274
- const observer = await startManifestObserver(page, (url, kind) => {
275
- if (resolved) {
276
- return;
277
- }
278
- if (kind !== "master") {
279
- LOG.debug("native:intercept", "Tune verification ignoring non-master playlist (%s).", kind);
280
- return;
281
- }
282
- if (predicate(url)) {
283
- resolved = true;
284
- LOG.debug("native:intercept", "Matching manifest found in %sms: %s.", elapsed(), url.slice(0, 120));
285
- observer?.dispose(false);
286
- clearTimeout(timer);
287
- resolve(url);
288
- }
289
- else {
290
- LOG.debug("native:intercept", "Master manifest did not match predicate: %s.", url.slice(0, 120));
181
+ const observer = await observeHlsPlaylists(page, {
182
+ logCategory: "native:intercept",
183
+ onPlaylist: (playlist) => {
184
+ if (resolved) {
185
+ return;
186
+ }
187
+ if (playlist.kind !== "master") {
188
+ LOG.debug("native:intercept", "Tune verification ignoring non-master playlist (%s).", playlist.kind);
189
+ return;
190
+ }
191
+ if (predicate(playlist.url)) {
192
+ resolved = true;
193
+ LOG.debug("native:intercept", "Matching manifest found in %sms: %s.", elapsed(), playlist.url.slice(0, 120));
194
+ observer?.dispose();
195
+ clearTimeout(timer);
196
+ resolve(playlist.url);
197
+ }
198
+ else {
199
+ LOG.debug("native:intercept", "Master manifest did not match predicate: %s.", playlist.url.slice(0, 120));
200
+ }
291
201
  }
292
- }, "native:intercept");
202
+ });
293
203
  if (!observer) {
294
204
  return null;
295
205
  }
@@ -299,25 +209,9 @@ export async function awaitMatchingManifest(page, predicate, timeout = VERIFICAT
299
209
  }
300
210
  resolved = true;
301
211
  LOG.debug("native:intercept", "Awaiting matching manifest timed out after %sms.", elapsed());
302
- observer.dispose(false);
212
+ observer.dispose();
303
213
  resolve(null);
304
214
  }, timeout);
305
215
  return promise;
306
216
  }
307
- /**
308
- * Removes the Network domain listener and detaches the CDP session. Safe to call multiple times or on an already-detached session. Public so that native HLS
309
- * consumers (which receive the CDP session through ManifestInterceptionResult) can clean up after token-refresh handoffs and proxy shutdown.
310
- * @param cdpSession - The CDP session to clean up.
311
- */
312
- export function removeManifestInterceptor(cdpSession) {
313
- try {
314
- cdpSession.removeAllListeners("Network.responseReceived");
315
- void cdpSession.send("Network.disable").catch(() => { });
316
- void cdpSession.detach().catch(() => { });
317
- }
318
- catch (_error) {
319
- // Ignore errors during cleanup - the session may already be detached.
320
- }
321
- LOG.debug("native:intercept", "CDP manifest interceptor removed.");
322
- }
323
217
  //# sourceMappingURL=manifestInterceptor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"manifestInterceptor.js","sourceRoot":"","sources":["../../src/browser/manifestInterceptor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"manifestInterceptor.js","sourceRoot":"","sources":["../../src/browser/manifestInterceptor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import type { Page } from "puppeteer-core";
2
+ import type { Nullable } from "../types/index.ts";
3
+ /**
4
+ * A network response observed anywhere in the tab's target tree. Carries the response URL, the headers as delivered by CDP, and the sessionId of the originating
5
+ * target. The sessionId lets a consumer route follow-up CDP commands (e.g., Network.getResponseBody) to the correct session if it ever needs to; the default
6
+ * consumer in hlsPlaylistObserver does not, since it fetches manifest bodies directly via Node fetch to avoid Chrome's response-body cache eviction.
7
+ */
8
+ export interface ObservedResponse {
9
+ readonly headers: Readonly<Record<string, string>>;
10
+ readonly sessionId: string;
11
+ readonly url: string;
12
+ }
13
+ /**
14
+ * Information about a target the observer encountered. Passed to the consumer-supplied targetFilter predicate so consumers can decide which target types to
15
+ * observe. Modeled as a record rather than puppeteer's full TargetInfo so the predicate API is stable across puppeteer versions.
16
+ */
17
+ export interface ObservedTargetInfo {
18
+ readonly type: string;
19
+ }
20
+ /**
21
+ * Options accepted by observeTabResponses(). The onResponse callback receives every response observed across every target accepted by targetFilter; consumers
22
+ * apply their own URL or content-type filtering on top.
23
+ */
24
+ export interface TabNetworkObserverOptions {
25
+ readonly onResponse: (response: ObservedResponse) => void;
26
+ readonly targetFilter?: (info: ObservedTargetInfo) => boolean;
27
+ }
28
+ /**
29
+ * Handle returned by observeTabResponses(). Implements both the project's dispose() convention and TC39 Symbol.dispose so callers may use either an explicit
30
+ * dispose() call or the "using" keyword for scope-bound cleanup. Disposal is idempotent.
31
+ */
32
+ export interface TabNetworkObserver extends Disposable {
33
+ readonly dispose: () => void;
34
+ readonly [Symbol.dispose]: () => void;
35
+ }
36
+ /**
37
+ * Installs a tab-wide network observer on the given page. The returned handle delivers every response observed across every target accepted by the targetFilter
38
+ * predicate (top page, iframes including OOPIFs, workers, service workers by default) to the supplied callback. Disposal is the caller's responsibility; the
39
+ * returned handle supports both an explicit dispose() call and the "using" keyword via Symbol.dispose.
40
+ *
41
+ * Returns null when the page is already closed, the root CDP session could not be created, or the puppeteer Connection backing the session is not available
42
+ * (e.g., the session detached during construction).
43
+ *
44
+ * @param page - The puppeteer page to observe.
45
+ * @param options - Observer options including the response callback and optional target-type predicate.
46
+ * @returns The observer handle, or null if installation failed.
47
+ */
48
+ export declare function observeTabResponses(page: Page, options: TabNetworkObserverOptions): Promise<Nullable<TabNetworkObserver>>;