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.
- package/dist/app.d.ts +5 -3
- package/dist/app.js +42 -41
- package/dist/app.js.map +1 -1
- package/dist/browser/hlsPlaylistObserver.d.ts +38 -0
- package/dist/browser/hlsPlaylistObserver.js +113 -0
- package/dist/browser/hlsPlaylistObserver.js.map +1 -0
- package/dist/browser/index.d.ts +26 -16
- package/dist/browser/index.js +95 -70
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/manifestInterceptor.d.ts +29 -29
- package/dist/browser/manifestInterceptor.js +106 -212
- package/dist/browser/manifestInterceptor.js.map +1 -1
- package/dist/browser/tabNetworkObserver.d.ts +48 -0
- package/dist/browser/tabNetworkObserver.js +140 -0
- package/dist/browser/tabNetworkObserver.js.map +1 -0
- package/dist/config/consistencyProbe.js +13 -11
- package/dist/config/consistencyProbe.js.map +1 -1
- package/dist/config/paths.d.ts +3 -8
- package/dist/config/paths.js +3 -10
- package/dist/config/paths.js.map +1 -1
- package/dist/config/profiles.d.ts +1 -1
- package/dist/config/profiles.js +6 -6
- package/dist/config/profiles.js.map +1 -1
- package/dist/config/services.d.ts +5 -5
- package/dist/config/services.js +8 -8
- package/dist/config/services.js.map +1 -1
- package/dist/config/sites.d.ts +2 -2
- package/dist/config/sites.js +6 -6
- package/dist/config/sites.js.map +1 -1
- package/dist/config/userChannels.d.ts +108 -14
- package/dist/config/userChannels.js +291 -37
- package/dist/config/userChannels.js.map +1 -1
- package/dist/config/userConfig.js +1 -1
- package/dist/config/userConfig.js.map +1 -1
- package/dist/config/userProfiles.d.ts +3 -3
- package/dist/config/userProfiles.js +19 -19
- package/dist/config/userProfiles.js.map +1 -1
- package/dist/index.js +7 -9
- package/dist/index.js.map +1 -1
- package/dist/native/index.d.ts +1 -1
- package/dist/native/index.js +9 -18
- package/dist/native/index.js.map +1 -1
- package/dist/native/proxy.d.ts +1 -4
- package/dist/native/proxy.js +0 -9
- package/dist/native/proxy.js.map +1 -1
- package/dist/routes/clientActions.d.ts +97 -0
- package/dist/routes/clientActions.js +113 -0
- package/dist/routes/clientActions.js.map +1 -0
- package/dist/routes/components.d.ts +4 -3
- package/dist/routes/components.js +36 -39
- package/dist/routes/components.js.map +1 -1
- package/dist/routes/config/channels/endpoints/browse.js.map +1 -1
- package/dist/routes/config/channels/endpoints/crud.js +37 -72
- package/dist/routes/config/channels/endpoints/crud.js.map +1 -1
- package/dist/routes/config/channels/endpoints/importExport.js.map +1 -1
- package/dist/routes/config/channels/healthBridge.d.ts +23 -0
- package/dist/routes/config/channels/healthBridge.js +71 -0
- package/dist/routes/config/channels/healthBridge.js.map +1 -0
- package/dist/routes/config/channels/table.js +97 -84
- package/dist/routes/config/channels/table.js.map +1 -1
- package/dist/routes/config/services.js +25 -22
- package/dist/routes/config/services.js.map +1 -1
- package/dist/routes/config/settings.js +9 -6
- package/dist/routes/config/settings.js.map +1 -1
- package/dist/routes/debug.js +277 -139
- package/dist/routes/debug.js.map +1 -1
- package/dist/routes/logs.js +1 -1
- package/dist/routes/logs.js.map +1 -1
- package/dist/routes/root/content.js +18 -14
- package/dist/routes/root/content.js.map +1 -1
- package/dist/routes/root/index.js +17 -14
- package/dist/routes/root/index.js.map +1 -1
- package/dist/routes/root/scripts/channels.js +50 -21
- package/dist/routes/root/scripts/channels.js.map +1 -1
- package/dist/routes/root/scripts/config.js +72 -3
- package/dist/routes/root/scripts/config.js.map +1 -1
- package/dist/routes/root/scripts/shared.js +60 -0
- package/dist/routes/root/scripts/shared.js.map +1 -1
- package/dist/routes/root/scripts/status.handlers.d.ts +5 -27
- package/dist/routes/root/scripts/status.handlers.js +8 -60
- package/dist/routes/root/scripts/status.handlers.js.map +1 -1
- package/dist/routes/root/scripts/status.js +6 -1
- package/dist/routes/root/scripts/status.js.map +1 -1
- package/dist/routes/streams.js +5 -3
- package/dist/routes/streams.js.map +1 -1
- package/dist/service/commands.context.js +1 -1
- package/dist/service/commands.context.js.map +1 -1
- package/dist/service/generators.context.js +1 -1
- package/dist/service/generators.context.js.map +1 -1
- package/dist/service/generators.d.ts +1 -1
- package/dist/service/generators.js +3 -11
- package/dist/service/generators.js.map +1 -1
- package/dist/streaming/hls.js +1 -5
- package/dist/streaming/hls.js.map +1 -1
- package/dist/streaming/hlsSegments.d.ts +1 -1
- package/dist/streaming/hlsSegments.js.map +1 -1
- package/dist/streaming/preroll.d.ts +1 -1
- package/dist/streaming/preroll.js +1 -5
- package/dist/streaming/preroll.js.map +1 -1
- package/dist/streaming/statusEmitter.d.ts +5 -8
- package/dist/streaming/statusEmitter.js +0 -5
- package/dist/streaming/statusEmitter.js.map +1 -1
- package/dist/types/channels.d.ts +9 -0
- package/dist/types/channels.js.map +1 -1
- package/dist/types/profiles.d.ts +2 -2
- package/dist/upgrade/commands.context.js +14 -10
- package/dist/upgrade/commands.context.js.map +1 -1
- package/dist/upgrade/commands.d.ts +7 -15
- package/dist/upgrade/commands.js +27 -28
- package/dist/upgrade/commands.js.map +1 -1
- package/dist/upgrade/detection.context.js +2 -1
- package/dist/upgrade/detection.context.js.map +1 -1
- package/dist/upgrade/detection.d.ts +2 -1
- package/dist/upgrade/detection.js +20 -51
- package/dist/upgrade/detection.js.map +1 -1
- package/dist/upgrade/index.d.ts +1 -0
- package/dist/upgrade/index.js +1 -0
- package/dist/upgrade/index.js.map +1 -1
- package/dist/upgrade/lifecycle.context.d.ts +19 -0
- package/dist/upgrade/lifecycle.context.js +47 -0
- package/dist/upgrade/lifecycle.context.js.map +1 -0
- package/dist/upgrade/lifecycle.d.ts +72 -0
- package/dist/upgrade/lifecycle.js +176 -0
- package/dist/upgrade/lifecycle.js.map +1 -0
- package/dist/upgrade/pathHandle.d.ts +28 -0
- package/dist/upgrade/pathHandle.js +101 -0
- package/dist/upgrade/pathHandle.js.map +1 -0
- package/dist/utils/bootSession.context.d.ts +15 -0
- package/dist/utils/bootSession.context.js +59 -0
- package/dist/utils/bootSession.context.js.map +1 -0
- package/dist/utils/bootSession.d.ts +16 -0
- package/dist/utils/bootSession.js +20 -0
- package/dist/utils/bootSession.js.map +1 -0
- package/dist/utils/clock.js +1 -1
- package/dist/utils/ffmpeg.js +1 -1
- package/dist/utils/ffmpeg.js.map +1 -1
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/markup.d.ts +10 -0
- package/dist/utils/markup.js +23 -0
- package/dist/utils/markup.js.map +1 -1
- package/dist/utils/pid.d.ts +2 -25
- package/dist/utils/pid.js +10 -49
- package/dist/utils/pid.js.map +1 -1
- package/dist/utils/processInspector.context.d.ts +7 -0
- package/dist/utils/processInspector.context.js +104 -0
- package/dist/utils/processInspector.context.js.map +1 -0
- package/dist/utils/processInspector.d.ts +54 -0
- package/dist/utils/processInspector.js +125 -0
- package/dist/utils/processInspector.js.map +1 -0
- package/dist/utils/retry.d.ts +1 -1
- package/dist/utils/retry.js +1 -1
- package/dist/utils/retry.js.map +1 -1
- package/dist/utils/runtimeIdentity.context.d.ts +6 -0
- package/dist/utils/runtimeIdentity.context.js +13 -0
- package/dist/utils/runtimeIdentity.context.js.map +1 -0
- package/dist/utils/runtimeIdentity.d.ts +96 -0
- package/dist/utils/runtimeIdentity.js +171 -0
- package/dist/utils/runtimeIdentity.js.map +1 -0
- package/dist/utils/timing.d.ts +1 -1
- package/dist/utils/timing.js +0 -5
- package/dist/utils/timing.js.map +1 -1
- package/dist/utils/version.js +40 -10
- package/dist/utils/version.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,32 +1,24 @@
|
|
|
1
|
-
import { LOG,
|
|
2
|
-
import {
|
|
3
|
-
/* This module
|
|
4
|
-
*
|
|
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
|
-
* -
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
20
|
-
*
|
|
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
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
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
|
|
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
|
|
59
|
-
*
|
|
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
|
|
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
|
|
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
|
-
*
|
|
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
|
|
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
|
-
//
|
|
163
|
-
//
|
|
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
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
196
|
-
|
|
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
|
-
|
|
202
|
-
const
|
|
203
|
-
if (
|
|
204
|
-
LOG.debug("native:intercept", "
|
|
205
|
-
observer.dispose(
|
|
206
|
-
resolve({
|
|
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", "
|
|
210
|
-
observer.dispose(
|
|
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
|
-
}
|
|
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:
|
|
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
|
-
//
|
|
225
|
-
|
|
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
|
-
|
|
141
|
+
settle(true, "finalized");
|
|
248
142
|
}
|
|
249
143
|
else {
|
|
250
|
-
setTimeout(
|
|
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
|
|
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
|
|
261
|
-
* in both cases - the verification path does not hand off
|
|
262
|
-
*
|
|
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
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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
|
-
}
|
|
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(
|
|
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":"AAKA,OAAO,EAAE,GAAG,EAAE,
|
|
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>>;
|