prismcast 1.5.2 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -28
- package/dist/app.d.ts +5 -0
- package/dist/app.js +63 -5
- package/dist/app.js.map +1 -1
- package/dist/browser/cdp.js +1 -1
- package/dist/browser/cdp.js.map +1 -1
- package/dist/browser/channelSelection.d.ts +10 -34
- package/dist/browser/channelSelection.js +31 -96
- package/dist/browser/channelSelection.js.map +1 -1
- package/dist/browser/display.d.ts +19 -0
- package/dist/browser/display.js +16 -11
- package/dist/browser/display.js.map +1 -1
- package/dist/browser/index.d.ts +31 -54
- package/dist/browser/index.js +334 -272
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/login.d.ts +65 -0
- package/dist/browser/login.js +193 -0
- package/dist/browser/login.js.map +1 -0
- package/dist/browser/precaching.js +15 -3
- package/dist/browser/precaching.js.map +1 -1
- package/dist/browser/tuning/comcastPolymer.d.ts +23 -0
- package/dist/browser/tuning/comcastPolymer.js +655 -0
- package/dist/browser/tuning/comcastPolymer.js.map +1 -0
- package/dist/browser/tuning/cox.d.ts +2 -0
- package/dist/browser/tuning/cox.js +14 -0
- package/dist/browser/tuning/cox.js.map +1 -0
- package/dist/browser/tuning/directv.js +14 -1
- package/dist/browser/tuning/directv.js.map +1 -1
- package/dist/browser/tuning/fox.js +14 -2
- package/dist/browser/tuning/fox.js.map +1 -1
- package/dist/browser/tuning/hbo.js +12 -1
- package/dist/browser/tuning/hbo.js.map +1 -1
- package/dist/browser/tuning/hulu.js +24 -1
- package/dist/browser/tuning/hulu.js.map +1 -1
- package/dist/browser/tuning/shared.d.ts +46 -0
- package/dist/browser/tuning/shared.js +100 -0
- package/dist/browser/tuning/shared.js.map +1 -0
- package/dist/browser/tuning/sling.js +35 -5
- package/dist/browser/tuning/sling.js.map +1 -1
- package/dist/browser/tuning/spectrum.js +13 -1
- package/dist/browser/tuning/spectrum.js.map +1 -1
- package/dist/browser/tuning/thumbnailRow.js +1 -1
- package/dist/browser/tuning/thumbnailRow.js.map +1 -1
- package/dist/browser/tuning/tileClick.js +1 -1
- package/dist/browser/tuning/tileClick.js.map +1 -1
- package/dist/browser/tuning/xfinity.d.ts +2 -0
- package/dist/browser/tuning/xfinity.js +14 -0
- package/dist/browser/tuning/xfinity.js.map +1 -0
- package/dist/browser/tuning/youtubeTv.js +15 -1
- package/dist/browser/tuning/youtubeTv.js.map +1 -1
- package/dist/browser/video.d.ts +20 -0
- package/dist/browser/video.js +168 -124
- package/dist/browser/video.js.map +1 -1
- package/dist/channels/index.d.ts +2 -2
- package/dist/channels/index.js +2075 -683
- package/dist/channels/index.js.map +1 -1
- package/dist/config/health.d.ts +28 -28
- package/dist/config/health.js +72 -54
- package/dist/config/health.js.map +1 -1
- package/dist/config/index.js +29 -64
- package/dist/config/index.js.map +1 -1
- package/dist/config/paths.d.ts +12 -0
- package/dist/config/paths.js +16 -0
- package/dist/config/paths.js.map +1 -1
- package/dist/config/profiles.d.ts +3 -2
- package/dist/config/profiles.js +45 -34
- package/dist/config/profiles.js.map +1 -1
- package/dist/config/providerPacks.js +4 -1
- package/dist/config/providerPacks.js.map +1 -1
- package/dist/config/providers.d.ts +19 -5
- package/dist/config/providers.js +123 -93
- package/dist/config/providers.js.map +1 -1
- package/dist/config/sites.d.ts +28 -0
- package/dist/config/sites.js +104 -127
- package/dist/config/sites.js.map +1 -1
- package/dist/config/userChannels.d.ts +53 -2
- package/dist/config/userChannels.js +222 -13
- package/dist/config/userChannels.js.map +1 -1
- package/dist/config/userConfig.d.ts +2 -1
- package/dist/config/userConfig.js +6 -15
- package/dist/config/userConfig.js.map +1 -1
- package/dist/config/userProfiles.d.ts +0 -1
- package/dist/config/userProfiles.js +18 -10
- package/dist/config/userProfiles.js.map +1 -1
- package/dist/hdhr/channelMap.d.ts +1 -1
- package/dist/hdhr/channelMap.js +4 -4
- package/dist/hdhr/channelMap.js.map +1 -1
- package/dist/index.js +25 -22
- package/dist/index.js.map +1 -1
- package/dist/native/decrypt.d.ts +32 -0
- package/dist/native/decrypt.js +85 -0
- package/dist/native/decrypt.js.map +1 -0
- package/dist/native/index.d.ts +61 -0
- package/dist/native/index.js +342 -0
- package/dist/native/index.js.map +1 -0
- package/dist/native/intercept.d.ts +35 -0
- package/dist/native/intercept.js +184 -0
- package/dist/native/intercept.js.map +1 -0
- package/dist/native/probe.d.ts +49 -0
- package/dist/native/probe.js +294 -0
- package/dist/native/probe.js.map +1 -0
- package/dist/native/proxy.d.ts +54 -0
- package/dist/native/proxy.js +947 -0
- package/dist/native/proxy.js.map +1 -0
- package/dist/routes/components.d.ts +43 -0
- package/dist/routes/components.js +122 -0
- package/dist/routes/components.js.map +1 -1
- package/dist/routes/config/channels/index.d.ts +2 -2
- package/dist/routes/config/channels/index.js +1 -1
- package/dist/routes/config/channels/index.js.map +1 -1
- package/dist/routes/config/channels/routes.js +443 -65
- package/dist/routes/config/channels/routes.js.map +1 -1
- package/dist/routes/config/channels/table.d.ts +76 -0
- package/dist/routes/config/channels/table.js +369 -89
- package/dist/routes/config/channels/table.js.map +1 -1
- package/dist/routes/config/index.d.ts +1 -1
- package/dist/routes/config/index.js +1 -1
- package/dist/routes/config/index.js.map +1 -1
- package/dist/routes/config/providers.d.ts +6 -6
- package/dist/routes/config/providers.js +73 -99
- package/dist/routes/config/providers.js.map +1 -1
- package/dist/routes/debug.js +5 -5
- package/dist/routes/debug.js.map +1 -1
- package/dist/routes/hls.js +12 -2
- package/dist/routes/hls.js.map +1 -1
- package/dist/routes/index.d.ts +0 -15
- package/dist/routes/index.js +2 -16
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/providers.js +106 -3
- package/dist/routes/providers.js.map +1 -1
- package/dist/routes/root/content.js +124 -47
- package/dist/routes/root/content.js.map +1 -1
- package/dist/routes/root/index.js +2 -1
- package/dist/routes/root/index.js.map +1 -1
- package/dist/routes/root/scripts/channels.js +772 -312
- package/dist/routes/root/scripts/channels.js.map +1 -1
- package/dist/routes/root/scripts/config.js +596 -544
- package/dist/routes/root/scripts/config.js.map +1 -1
- package/dist/routes/root/scripts/index.d.ts +1 -0
- package/dist/routes/root/scripts/index.js +1 -0
- package/dist/routes/root/scripts/index.js.map +1 -1
- package/dist/routes/root/scripts/shared.d.ts +6 -0
- package/dist/routes/root/scripts/shared.js +391 -0
- package/dist/routes/root/scripts/shared.js.map +1 -0
- package/dist/routes/root/scripts/status.js +49 -33
- package/dist/routes/root/scripts/status.js.map +1 -1
- package/dist/routes/root/styles.js +117 -10
- package/dist/routes/root/styles.js.map +1 -1
- package/dist/service/commands.js +2 -1
- package/dist/service/commands.js.map +1 -1
- package/dist/streaming/fmp4Segmenter.d.ts +5 -0
- package/dist/streaming/fmp4Segmenter.js +146 -42
- package/dist/streaming/fmp4Segmenter.js.map +1 -1
- package/dist/streaming/hls.d.ts +24 -19
- package/dist/streaming/hls.js +603 -297
- package/dist/streaming/hls.js.map +1 -1
- package/dist/streaming/hlsResume.d.ts +16 -3
- package/dist/streaming/hlsResume.js +35 -8
- package/dist/streaming/hlsResume.js.map +1 -1
- package/dist/streaming/hlsSegments.d.ts +41 -2
- package/dist/streaming/hlsSegments.js +131 -28
- package/dist/streaming/hlsSegments.js.map +1 -1
- package/dist/streaming/lifecycle.js +27 -13
- package/dist/streaming/lifecycle.js.map +1 -1
- package/dist/streaming/monitor.d.ts +1 -0
- package/dist/streaming/monitor.js +862 -474
- package/dist/streaming/monitor.js.map +1 -1
- package/dist/streaming/mp4Parser.d.ts +52 -6
- package/dist/streaming/mp4Parser.js +156 -30
- package/dist/streaming/mp4Parser.js.map +1 -1
- package/dist/streaming/mpegts.d.ts +2 -2
- package/dist/streaming/mpegts.js +156 -109
- package/dist/streaming/mpegts.js.map +1 -1
- package/dist/streaming/playlistBuilder.d.ts +38 -0
- package/dist/streaming/playlistBuilder.js +66 -0
- package/dist/streaming/playlistBuilder.js.map +1 -0
- package/dist/streaming/preroll.d.ts +126 -0
- package/dist/streaming/preroll.js +434 -0
- package/dist/streaming/preroll.js.map +1 -0
- package/dist/streaming/pretune.d.ts +8 -0
- package/dist/streaming/pretune.js +226 -0
- package/dist/streaming/pretune.js.map +1 -0
- package/dist/streaming/recovery.d.ts +0 -6
- package/dist/streaming/recovery.js +0 -8
- package/dist/streaming/recovery.js.map +1 -1
- package/dist/streaming/registry.d.ts +57 -9
- package/dist/streaming/registry.js +26 -7
- package/dist/streaming/registry.js.map +1 -1
- package/dist/streaming/setup.d.ts +8 -1
- package/dist/streaming/setup.js +71 -27
- package/dist/streaming/setup.js.map +1 -1
- package/dist/streaming/showInfo.d.ts +29 -4
- package/dist/streaming/showInfo.js +210 -38
- package/dist/streaming/showInfo.js.map +1 -1
- package/dist/streaming/statusEmitter.d.ts +20 -6
- package/dist/streaming/statusEmitter.js +31 -7
- package/dist/streaming/statusEmitter.js.map +1 -1
- package/dist/types/channels.d.ts +32 -0
- package/dist/types/config.d.ts +2 -2
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.js.map +1 -1
- package/dist/types/profiles.d.ts +13 -1
- package/dist/types/selection.d.ts +5 -1
- package/dist/types/shared.d.ts +1 -1
- package/dist/types/streaming.d.ts +7 -0
- package/dist/utils/chromeFetch.d.ts +20 -0
- package/dist/utils/chromeFetch.js +43 -0
- package/dist/utils/chromeFetch.js.map +1 -0
- package/dist/utils/debugFilter.d.ts +0 -5
- package/dist/utils/debugFilter.js +20 -11
- package/dist/utils/debugFilter.js.map +1 -1
- package/dist/utils/delay.d.ts +28 -0
- package/dist/utils/delay.js +35 -1
- package/dist/utils/delay.js.map +1 -1
- package/dist/utils/evaluate.js +6 -11
- package/dist/utils/evaluate.js.map +1 -1
- package/dist/utils/ffmpeg.d.ts +14 -12
- package/dist/utils/ffmpeg.js +114 -134
- package/dist/utils/ffmpeg.js.map +1 -1
- package/dist/utils/format.d.ts +6 -0
- package/dist/utils/format.js +8 -0
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/logEmitter.js +1 -1
- package/dist/utils/logEmitter.js.map +1 -1
- package/dist/utils/pid.d.ts +40 -0
- package/dist/utils/pid.js +83 -0
- package/dist/utils/pid.js.map +1 -0
- package/dist/utils/retry.d.ts +15 -9
- package/dist/utils/retry.js +7 -23
- package/dist/utils/retry.js.map +1 -1
- package/dist/utils/timing.d.ts +5 -0
- package/dist/utils/timing.js +13 -0
- package/dist/utils/timing.js.map +1 -0
- package/dist/utils/version.js +2 -2
- package/dist/utils/version.js.map +1 -1
- package/package.json +7 -7
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/* Copyright(C) 2024-2026, HJD (https://github.com/hjdhjd). All rights reserved.
|
|
2
|
+
*
|
|
3
|
+
* pretune.ts: Predictive channel pretuning from Channels DVR schedule.
|
|
4
|
+
*/
|
|
5
|
+
import { LOG, delay, formatError } from "../utils/index.js";
|
|
6
|
+
import { fetchFromDvr, getDeviceMappings, getDvrHost } from "./showInfo.js";
|
|
7
|
+
import { getChannelStreamId, terminateStream } from "./lifecycle.js";
|
|
8
|
+
import { initializeStream, validateChannel } from "./hls.js";
|
|
9
|
+
import { emitCurrentSystemStatus } from "../browser/index.js";
|
|
10
|
+
import { getStream } from "./registry.js";
|
|
11
|
+
/* This module polls the Channels DVR schedule API to discover upcoming recordings and pretunes channels 30 seconds before they start. When the DVR requests the
|
|
12
|
+
* stream, it's already live with buffered segments — achieving near-instant tuning instead of 3-7 second cold starts.
|
|
13
|
+
*
|
|
14
|
+
* The polling loop runs every 60 seconds, checking for jobs starting within a 5-minute scheduling horizon. For each eligible job (PrismCast channel as the DVR's
|
|
15
|
+
* preferred source), a precise setTimeout is scheduled for 30 seconds before the recording start time. When the timer fires, the module validates the channel,
|
|
16
|
+
* checks for conflicts, and calls initializeStream() with the preTuned flag. A safety timeout tears down unclaimed streams 90 seconds after the scheduled start.
|
|
17
|
+
*
|
|
18
|
+
* Key design decisions:
|
|
19
|
+
* - Only pretune when a PrismCast guide number is the FIRST entry in the job's channels array (DVR's preferred source).
|
|
20
|
+
* - Skip channels that are already streaming — no duplicate streams for the same channel.
|
|
21
|
+
* - Pretune alongside existing streams freely; capacity enforcement is handled by initializeStream().
|
|
22
|
+
* - Retry up to 5 times within the pretune window on failure.
|
|
23
|
+
* - Safety timeout at start_time + 90s handles cancelled jobs or missed client connections.
|
|
24
|
+
*/
|
|
25
|
+
// Constants.
|
|
26
|
+
// How often to poll for upcoming jobs (60 seconds).
|
|
27
|
+
const POLL_INTERVAL_MS = 60000;
|
|
28
|
+
// How far ahead to look for upcoming jobs (5 minutes).
|
|
29
|
+
const SCHEDULING_HORIZON_MS = 300000;
|
|
30
|
+
// How far before the recording start to begin pretuning (30 seconds).
|
|
31
|
+
const PRETUNE_LEAD_MS = 30000;
|
|
32
|
+
// Safety timeout for unclaimed pretuned streams (90 seconds past scheduled start).
|
|
33
|
+
const SAFETY_TIMEOUT_MS = 90000;
|
|
34
|
+
// Maximum retry attempts for failed pretune.
|
|
35
|
+
const MAX_RETRIES = 5;
|
|
36
|
+
// Delay between retry attempts (5 seconds).
|
|
37
|
+
const RETRY_DELAY_MS = 5000;
|
|
38
|
+
// State.
|
|
39
|
+
// Interval handle for periodic job polling.
|
|
40
|
+
let pollInterval = null;
|
|
41
|
+
// Active pretune timers keyed by job ID. Used to avoid duplicate scheduling and to clear timers when jobs disappear.
|
|
42
|
+
const activeTimers = new Map();
|
|
43
|
+
// Safety timers keyed by stream ID. Used to tear down unclaimed pretuned streams after the scheduled start time.
|
|
44
|
+
const safetyTimers = new Map();
|
|
45
|
+
// Public API.
|
|
46
|
+
/**
|
|
47
|
+
* Starts the pretune polling loop. Should be called on server startup after show info polling is initialized.
|
|
48
|
+
*/
|
|
49
|
+
export function startPretunePolling() {
|
|
50
|
+
if (pollInterval) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
// Run the first poll after a short delay to allow the persisted DVR host to load.
|
|
54
|
+
setTimeout(() => {
|
|
55
|
+
void pollForUpcomingJobs();
|
|
56
|
+
}, 5000);
|
|
57
|
+
pollInterval = setInterval(() => {
|
|
58
|
+
void pollForUpcomingJobs();
|
|
59
|
+
}, POLL_INTERVAL_MS);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Stops the pretune polling loop and clears all pending timers. Should be called on server shutdown.
|
|
63
|
+
*/
|
|
64
|
+
export function stopPretunePolling() {
|
|
65
|
+
if (pollInterval) {
|
|
66
|
+
clearInterval(pollInterval);
|
|
67
|
+
pollInterval = null;
|
|
68
|
+
}
|
|
69
|
+
// Clear all pending pretune timers.
|
|
70
|
+
for (const timer of activeTimers.values()) {
|
|
71
|
+
clearTimeout(timer);
|
|
72
|
+
}
|
|
73
|
+
activeTimers.clear();
|
|
74
|
+
// Clear all safety timers.
|
|
75
|
+
for (const timer of safetyTimers.values()) {
|
|
76
|
+
clearTimeout(timer);
|
|
77
|
+
}
|
|
78
|
+
safetyTimers.clear();
|
|
79
|
+
}
|
|
80
|
+
// Internal Functions.
|
|
81
|
+
/**
|
|
82
|
+
* Polls the Channels DVR API for upcoming scheduled recordings and schedules pretune timers for eligible jobs.
|
|
83
|
+
*/
|
|
84
|
+
async function pollForUpcomingJobs() {
|
|
85
|
+
const host = getDvrHost();
|
|
86
|
+
if (!host) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const jobs = await fetchFromDvr(host, "/api/v1/jobs");
|
|
90
|
+
if (jobs.length === 0) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const now = Date.now();
|
|
94
|
+
const horizon = now + SCHEDULING_HORIZON_MS;
|
|
95
|
+
// Track which job IDs we see this poll cycle so we can clear timers for removed jobs.
|
|
96
|
+
const seenJobIds = new Set();
|
|
97
|
+
// Get the device mappings once for resolving guide numbers. The cache ensures this is fast on repeated calls.
|
|
98
|
+
const mappings = await getDeviceMappings(host);
|
|
99
|
+
if (mappings.size === 0) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
for (const job of jobs) {
|
|
103
|
+
// Skip cancelled or skipped jobs.
|
|
104
|
+
if (job.item.cancelled || job.skipped) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const startMs = job.start_time * 1000;
|
|
108
|
+
// Skip jobs outside our scheduling horizon or already started.
|
|
109
|
+
if ((startMs > horizon) || (startMs <= now)) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
// Only consider jobs where the first (preferred) channel is a PrismCast channel.
|
|
113
|
+
if (job.channels.length === 0) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const guideNumber = job.channels[0];
|
|
117
|
+
const channelId = resolveGuideNumber(mappings, guideNumber);
|
|
118
|
+
if (!channelId) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
seenJobIds.add(job.id);
|
|
122
|
+
// Skip if a timer is already scheduled for this job.
|
|
123
|
+
if (activeTimers.has(job.id)) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
// Calculate when to pretune. If the pretune time has already passed but the start time hasn't, pretune immediately.
|
|
127
|
+
const pretuneTime = startMs - PRETUNE_LEAD_MS;
|
|
128
|
+
const effectiveDelay = Math.max(0, pretuneTime - now);
|
|
129
|
+
LOG.debug("streaming:pretune", "Scheduling pretune for '%s' (%s) in %ds.", job.name, channelId, Math.round(effectiveDelay / 1000));
|
|
130
|
+
const timer = setTimeout(() => {
|
|
131
|
+
activeTimers.delete(job.id);
|
|
132
|
+
void pretuneChannel(channelId, job.name, startMs);
|
|
133
|
+
}, effectiveDelay);
|
|
134
|
+
activeTimers.set(job.id, timer);
|
|
135
|
+
}
|
|
136
|
+
// Clear timers for jobs that disappeared from the API (cancelled, rescheduled, or moved outside the horizon).
|
|
137
|
+
for (const [jobId, timer] of activeTimers) {
|
|
138
|
+
if (!seenJobIds.has(jobId)) {
|
|
139
|
+
clearTimeout(timer);
|
|
140
|
+
activeTimers.delete(jobId);
|
|
141
|
+
LOG.debug("streaming:pretune", "Cleared timer for removed job %s.", jobId);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Resolves a DVR guide number to a PrismCast channel ID by checking all device mappings.
|
|
147
|
+
* @param mappings - Map of DeviceID to (Map of GuideNumber to channel ID).
|
|
148
|
+
* @param guideNumber - The guide number to resolve (e.g., "7220").
|
|
149
|
+
* @returns The PrismCast channel ID if the guide number maps to a PrismCast channel, undefined otherwise.
|
|
150
|
+
*/
|
|
151
|
+
function resolveGuideNumber(mappings, guideNumber) {
|
|
152
|
+
for (const deviceMappings of mappings.values()) {
|
|
153
|
+
const channelId = deviceMappings.get(guideNumber);
|
|
154
|
+
if (channelId) {
|
|
155
|
+
return channelId;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Pretunes a channel ahead of a scheduled recording. Validates the channel, checks for conflicts, initializes the stream with the preTuned flag, and sets up a
|
|
162
|
+
* safety timeout for unclaimed streams. Retries on failure up to MAX_RETRIES times within the pretune window.
|
|
163
|
+
* @param channelId - The PrismCast channel key (e.g., "cnn").
|
|
164
|
+
* @param jobName - The program title for logging (e.g., "Anderson Cooper 360").
|
|
165
|
+
* @param startTimeMs - The recording start time in milliseconds.
|
|
166
|
+
*/
|
|
167
|
+
async function pretuneChannel(channelId, jobName, startTimeMs) {
|
|
168
|
+
// Check if the channel is already streaming.
|
|
169
|
+
const existingStreamId = getChannelStreamId(channelId);
|
|
170
|
+
if (existingStreamId !== undefined) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
// Validate the channel.
|
|
174
|
+
const validation = validateChannel(channelId);
|
|
175
|
+
if (!validation.valid) {
|
|
176
|
+
LOG.debug("streaming:pretune", "Pretune validation failed for %s: %s.", channelId, typeof validation.body === "string" ? validation.body : "validation error");
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const displayName = validation.channel.name ?? channelId;
|
|
180
|
+
let attempts = 0;
|
|
181
|
+
while (attempts < MAX_RETRIES) {
|
|
182
|
+
attempts++;
|
|
183
|
+
try {
|
|
184
|
+
// eslint-disable-next-line no-await-in-loop
|
|
185
|
+
const streamId = await initializeStream({
|
|
186
|
+
channel: validation.channel,
|
|
187
|
+
channelName: channelId,
|
|
188
|
+
clientAddress: null,
|
|
189
|
+
preTuned: true,
|
|
190
|
+
url: validation.channel.url
|
|
191
|
+
});
|
|
192
|
+
if (streamId !== null) {
|
|
193
|
+
const leadSeconds = Math.round((startTimeMs - Date.now()) / 1000);
|
|
194
|
+
LOG.info("Pretuned %s for %s (starts in %ds).", displayName, jobName, leadSeconds);
|
|
195
|
+
// Set a safety timeout to tear down the stream if no real client connects within 90 seconds of the scheduled start.
|
|
196
|
+
const safetyDelay = Math.max(0, (startTimeMs + SAFETY_TIMEOUT_MS) - Date.now());
|
|
197
|
+
const safetyTimer = setTimeout(() => {
|
|
198
|
+
safetyTimers.delete(streamId);
|
|
199
|
+
const stream = getStream(streamId);
|
|
200
|
+
if (stream?.preTuned) {
|
|
201
|
+
LOG.info("No client connected for pretuned %s. Ending stream.", displayName);
|
|
202
|
+
terminateStream(streamId, channelId, "pretune safety timeout");
|
|
203
|
+
void emitCurrentSystemStatus();
|
|
204
|
+
}
|
|
205
|
+
}, safetyDelay);
|
|
206
|
+
safetyTimers.set(streamId, safetyTimer);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
LOG.warn("Pretune attempt %d/%d failed for %s: %s.", attempts, MAX_RETRIES, displayName, formatError(error));
|
|
212
|
+
}
|
|
213
|
+
// Stop retrying if we're past the scheduled start time.
|
|
214
|
+
if (Date.now() >= startTimeMs) {
|
|
215
|
+
LOG.debug("streaming:pretune", "Past start time for %s. Stopping pretune attempts.", channelId);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
// Brief delay before retry.
|
|
219
|
+
if (attempts < MAX_RETRIES) {
|
|
220
|
+
// eslint-disable-next-line no-await-in-loop
|
|
221
|
+
await delay(RETRY_DELAY_MS);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
LOG.warn("All %d pretune attempts failed for %s.", MAX_RETRIES, displayName);
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=pretune.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pretune.js","sourceRoot":"","sources":["../../src/streaming/pretune.ts"],"names":[],"mappings":"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"}
|
|
@@ -70,12 +70,6 @@ export declare function recordRecoveryAttempt(metrics: RecoveryMetrics, method:
|
|
|
70
70
|
* @param method - The recovery method that succeeded.
|
|
71
71
|
*/
|
|
72
72
|
export declare function recordRecoverySuccess(metrics: RecoveryMetrics, method: string): void;
|
|
73
|
-
/**
|
|
74
|
-
* Capitalizes the first letter of a string.
|
|
75
|
-
* @param str - The string to capitalize.
|
|
76
|
-
* @returns The string with the first letter capitalized.
|
|
77
|
-
*/
|
|
78
|
-
export declare function capitalize(str: string): string;
|
|
79
73
|
/**
|
|
80
74
|
* Formats the recovery metrics summary for the termination log. Uses the SUCCESS_FIELDS mapping to iterate over all recovery methods, eliminating hardcoded
|
|
81
75
|
* checks for each method type. This ensures new recovery methods are automatically included in the summary.
|
|
@@ -151,14 +151,6 @@ export function recordRecoverySuccess(metrics, method) {
|
|
|
151
151
|
metrics.currentRecoveryStartTime = null;
|
|
152
152
|
metrics.currentRecoveryMethod = null;
|
|
153
153
|
}
|
|
154
|
-
/**
|
|
155
|
-
* Capitalizes the first letter of a string.
|
|
156
|
-
* @param str - The string to capitalize.
|
|
157
|
-
* @returns The string with the first letter capitalized.
|
|
158
|
-
*/
|
|
159
|
-
export function capitalize(str) {
|
|
160
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
161
|
-
}
|
|
162
154
|
/**
|
|
163
155
|
* Formats the recovery metrics summary for the termination log. Uses the SUCCESS_FIELDS mapping to iterate over all recovery methods, eliminating hardcoded
|
|
164
156
|
* checks for each method type. This ensures new recovery methods are automatically included in the summary.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recovery.js","sourceRoot":"","sources":["../../src/streaming/recovery.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"recovery.js","sourceRoot":"","sources":["../../src/streaming/recovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,30 @@
|
|
|
1
|
-
import type { Nullable, ResolvedSiteProfile } from "../types/index.js";
|
|
1
|
+
import type { Nullable, ResolvedSiteProfile, StreamingMode } from "../types/index.js";
|
|
2
2
|
import { EventEmitter } from "node:events";
|
|
3
3
|
import type { FFmpegProcess } from "../utils/index.js";
|
|
4
4
|
import type { FMP4SegmenterResult } from "./fmp4Segmenter.js";
|
|
5
|
+
import type { NativeProxy } from "../native/proxy.js";
|
|
5
6
|
import type { Page } from "puppeteer-core";
|
|
7
|
+
import type { PrerollCodec } from "./preroll.js";
|
|
6
8
|
import type { Readable } from "node:stream";
|
|
7
9
|
import type { RecoveryMetrics } from "./recovery.js";
|
|
10
|
+
/**
|
|
11
|
+
* Typed event map for segment notifications. MPEG-TS consumers and lifecycle handlers subscribe to these events. Using a typed interface ensures event names and
|
|
12
|
+
* argument types are checked at compile time, preventing misspelled event names or incorrect payloads.
|
|
13
|
+
*/
|
|
14
|
+
interface SegmentEmitterEventMap {
|
|
15
|
+
audioSegment: [filename: string, data: Buffer];
|
|
16
|
+
initSegment: [data: Buffer];
|
|
17
|
+
segment: [filename: string, data: Buffer];
|
|
18
|
+
terminated: [];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Typed EventEmitter for segment notifications. Narrows Node's untyped EventEmitter to only accept the events defined in SegmentEmitterEventMap.
|
|
22
|
+
*/
|
|
23
|
+
export interface SegmentEmitter extends EventEmitter {
|
|
24
|
+
emit<K extends keyof SegmentEmitterEventMap>(event: K, ...args: SegmentEmitterEventMap[K]): boolean;
|
|
25
|
+
off<K extends keyof SegmentEmitterEventMap>(event: K, listener: (...args: SegmentEmitterEventMap[K]) => void): this;
|
|
26
|
+
on<K extends keyof SegmentEmitterEventMap>(event: K, listener: (...args: SegmentEmitterEventMap[K]) => void): this;
|
|
27
|
+
}
|
|
8
28
|
/**
|
|
9
29
|
* HLS segment and playlist storage for a stream. This includes the fMP4 initialization segment (codec configuration), media segments (.m4s files), and the current
|
|
10
30
|
* playlist content. The playlistReady promise allows callers to wait for the first playlist to be generated.
|
|
@@ -13,14 +33,27 @@ import type { RecoveryMetrics } from "./recovery.js";
|
|
|
13
33
|
* hlsSegments.ts imports getStream from registry.ts.
|
|
14
34
|
*/
|
|
15
35
|
export interface HLSState {
|
|
16
|
-
initSegment: Nullable<Buffer>;
|
|
17
|
-
initSegmentReady: Promise<void>;
|
|
18
36
|
playlist: string;
|
|
19
|
-
|
|
20
|
-
|
|
37
|
+
videoPlaylist: string;
|
|
38
|
+
audioPlaylist: string;
|
|
39
|
+
hasAudio: boolean;
|
|
40
|
+
hasRealPlaylist: boolean;
|
|
41
|
+
initSegment: Nullable<Buffer>;
|
|
21
42
|
segments: Map<string, Buffer>;
|
|
43
|
+
segmentBytes: number;
|
|
44
|
+
audioSegments: Map<string, Buffer>;
|
|
45
|
+
audioSegmentBytes: number;
|
|
46
|
+
segmentEmitter: SegmentEmitter;
|
|
47
|
+
initSegmentReady: Promise<void>;
|
|
22
48
|
signalInitSegmentReady: () => void;
|
|
49
|
+
playlistReady: Promise<void>;
|
|
23
50
|
signalPlaylistReady: () => void;
|
|
51
|
+
prerollBaseUrl: Nullable<string>;
|
|
52
|
+
prerollCodec: Nullable<PrerollCodec>;
|
|
53
|
+
prerollSegmentCount: number;
|
|
54
|
+
prerollStartTime: Nullable<Date>;
|
|
55
|
+
prerollTimer: Nullable<ReturnType<typeof setTimeout>>;
|
|
56
|
+
resumeSegmentIndex: number;
|
|
24
57
|
}
|
|
25
58
|
/**
|
|
26
59
|
* Stream-specific information for idle detection.
|
|
@@ -33,20 +66,27 @@ export interface StreamInfo {
|
|
|
33
66
|
* Registry entry for an active stream. This is the single source of truth for all stream data, including browser state, HLS segments, and the segmenter reference.
|
|
34
67
|
*/
|
|
35
68
|
export interface StreamRegistryEntry {
|
|
69
|
+
captureCodec: Nullable<string>;
|
|
36
70
|
channelName: Nullable<string>;
|
|
37
71
|
clientAddress: Nullable<string>;
|
|
38
72
|
ffmpegProcess: Nullable<FFmpegProcess>;
|
|
73
|
+
hardwareAccelerated: boolean;
|
|
39
74
|
hls: HLSState;
|
|
40
75
|
id: number;
|
|
41
76
|
mpegTsClientCount: number;
|
|
77
|
+
nativeBandwidth: number;
|
|
78
|
+
nativeProxy: Nullable<NativeProxy>;
|
|
79
|
+
nativeResolution: Nullable<string>;
|
|
42
80
|
info: StreamInfo;
|
|
43
|
-
page: Page
|
|
44
|
-
|
|
81
|
+
page: Nullable<Page>;
|
|
82
|
+
preTuned: boolean;
|
|
83
|
+
profile: Nullable<ResolvedSiteProfile>;
|
|
45
84
|
rawCaptureStream: Nullable<Readable>;
|
|
46
85
|
segmenter: Nullable<FMP4SegmenterResult>;
|
|
47
86
|
startTime: Date;
|
|
48
87
|
stopMonitor: Nullable<() => RecoveryMetrics>;
|
|
49
88
|
streamIdStr: string;
|
|
89
|
+
streamingMode: StreamingMode;
|
|
50
90
|
url: string;
|
|
51
91
|
}
|
|
52
92
|
/**
|
|
@@ -99,8 +139,8 @@ export interface StreamMemoryUsage {
|
|
|
99
139
|
total: number;
|
|
100
140
|
}
|
|
101
141
|
/**
|
|
102
|
-
*
|
|
103
|
-
*
|
|
142
|
+
* Returns the memory usage for a single stream's HLS segment storage. Uses the running byte counters maintained by storeSegmentToMap() for O(1) reads instead of
|
|
143
|
+
* iterating all segment buffers. The init segment is a single buffer read (also O(1)).
|
|
104
144
|
* @param entry - The stream registry entry to measure.
|
|
105
145
|
* @returns Memory usage breakdown in bytes.
|
|
106
146
|
*/
|
|
@@ -110,6 +150,13 @@ export declare function getStreamMemoryUsage(entry: StreamRegistryEntry): Stream
|
|
|
110
150
|
* @returns Total memory usage in bytes across all streams.
|
|
111
151
|
*/
|
|
112
152
|
export declare function getTotalSegmentMemory(): number;
|
|
153
|
+
/**
|
|
154
|
+
* Returns whether the last segment contained video traf boxes. Used by the monitor to distinguish dead capture pipelines (audio-only, no video trafs) from legitimate
|
|
155
|
+
* small segments produced by static content (video trafs present but low-bitrate). Returns null if the segmenter does not exist or the video trackId is unknown.
|
|
156
|
+
* @param entry - The stream registry entry to query.
|
|
157
|
+
* @returns True if video trafs were present, false if absent, null if unknown.
|
|
158
|
+
*/
|
|
159
|
+
export declare function getLastSegmentHasVideo(entry: StreamRegistryEntry): Nullable<boolean>;
|
|
113
160
|
/**
|
|
114
161
|
* Gets the size in bytes of the last segment stored for a stream. Used by the monitor to detect dead capture pipelines that produce empty segments (18 bytes observed)
|
|
115
162
|
* while the video element appears healthy.
|
|
@@ -117,3 +164,4 @@ export declare function getTotalSegmentMemory(): number;
|
|
|
117
164
|
* @returns Segment size in bytes, or null if no segmenter exists.
|
|
118
165
|
*/
|
|
119
166
|
export declare function getLastSegmentSize(entry: StreamRegistryEntry): Nullable<number>;
|
|
167
|
+
export {};
|
|
@@ -75,28 +75,38 @@ export function createHLSState() {
|
|
|
75
75
|
// Allow up to 20 listeners per event to support multiple concurrent MPEG-TS clients consuming the same stream.
|
|
76
76
|
segmentEmitter.setMaxListeners(20);
|
|
77
77
|
return {
|
|
78
|
+
audioPlaylist: "",
|
|
79
|
+
audioSegmentBytes: 0,
|
|
80
|
+
audioSegments: new Map(),
|
|
81
|
+
hasAudio: false,
|
|
82
|
+
hasRealPlaylist: false,
|
|
78
83
|
initSegment: null,
|
|
79
84
|
initSegmentReady,
|
|
80
85
|
playlist: "",
|
|
81
86
|
playlistReady,
|
|
87
|
+
prerollBaseUrl: null,
|
|
88
|
+
prerollCodec: null,
|
|
89
|
+
prerollSegmentCount: 0,
|
|
90
|
+
prerollStartTime: null,
|
|
91
|
+
prerollTimer: null,
|
|
92
|
+
resumeSegmentIndex: 0,
|
|
93
|
+
segmentBytes: 0,
|
|
82
94
|
segmentEmitter,
|
|
83
95
|
segments: new Map(),
|
|
84
96
|
signalInitSegmentReady,
|
|
85
|
-
signalPlaylistReady
|
|
97
|
+
signalPlaylistReady,
|
|
98
|
+
videoPlaylist: ""
|
|
86
99
|
};
|
|
87
100
|
}
|
|
88
101
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
102
|
+
* Returns the memory usage for a single stream's HLS segment storage. Uses the running byte counters maintained by storeSegmentToMap() for O(1) reads instead of
|
|
103
|
+
* iterating all segment buffers. The init segment is a single buffer read (also O(1)).
|
|
91
104
|
* @param entry - The stream registry entry to measure.
|
|
92
105
|
* @returns Memory usage breakdown in bytes.
|
|
93
106
|
*/
|
|
94
107
|
export function getStreamMemoryUsage(entry) {
|
|
95
108
|
const initSegmentSize = entry.hls.initSegment?.length ?? 0;
|
|
96
|
-
|
|
97
|
-
for (const segment of entry.hls.segments.values()) {
|
|
98
|
-
segmentsSize += segment.length;
|
|
99
|
-
}
|
|
109
|
+
const segmentsSize = entry.hls.segmentBytes + entry.hls.audioSegmentBytes;
|
|
100
110
|
return {
|
|
101
111
|
initSegment: initSegmentSize,
|
|
102
112
|
segments: segmentsSize,
|
|
@@ -115,6 +125,15 @@ export function getTotalSegmentMemory() {
|
|
|
115
125
|
return total;
|
|
116
126
|
}
|
|
117
127
|
// Segment Health.
|
|
128
|
+
/**
|
|
129
|
+
* Returns whether the last segment contained video traf boxes. Used by the monitor to distinguish dead capture pipelines (audio-only, no video trafs) from legitimate
|
|
130
|
+
* small segments produced by static content (video trafs present but low-bitrate). Returns null if the segmenter does not exist or the video trackId is unknown.
|
|
131
|
+
* @param entry - The stream registry entry to query.
|
|
132
|
+
* @returns True if video trafs were present, false if absent, null if unknown.
|
|
133
|
+
*/
|
|
134
|
+
export function getLastSegmentHasVideo(entry) {
|
|
135
|
+
return entry.segmenter?.getLastSegmentHasVideo() ?? null;
|
|
136
|
+
}
|
|
118
137
|
/**
|
|
119
138
|
* Gets the size in bytes of the last segment stored for a stream. Used by the monitor to detect dead capture pipelines that produce empty segments (18 bytes observed)
|
|
120
139
|
* while the video element appears healthy.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/streaming/registry.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/streaming/registry.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAgO3C,SAAS;AAET,0EAA0E;AAC1E,MAAM,cAAc,GAAG,IAAI,GAAG,EAA+B,CAAC;AAE9D,6EAA6E;AAC7E,IAAI,eAAe,GAAG,CAAC,CAAC;AAExB,cAAc;AAEd;;;GAGG;AACH,MAAM,UAAU,eAAe;IAE7B,OAAO,EAAE,eAAe,CAAC;AAC3B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,KAA0B;IAEvD,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;AACtC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAU;IAEzC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAC5B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,EAAU;IAElC,OAAO,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAChC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa;IAE3B,OAAO,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc;IAE5B,OAAO,cAAc,CAAC,IAAI,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAU;IAEzC,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAErC,IAAG,KAAK,EAAE,CAAC;QAET,KAAK,CAAC,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC9C,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc;IAE5B,IAAI,sBAAsB,GAAe,GAAG,EAAE,GAAwD,CAAC,CAAC;IACxG,IAAI,mBAAmB,GAAe,GAAG,EAAE,GAAwD,CAAC,CAAC;IAErG,MAAM,gBAAgB,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAErD,sBAAsB,GAAG,OAAO,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAElD,mBAAmB,GAAG,OAAO,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,MAAM,cAAc,GAAG,IAAI,YAAY,EAAoB,CAAC;IAE5D,+GAA+G;IAC/G,cAAc,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;IAEnC,OAAO;QAEL,aAAa,EAAE,EAAE;QACjB,iBAAiB,EAAE,CAAC;QACpB,aAAa,EAAE,IAAI,GAAG,EAAE;QACxB,QAAQ,EAAE,KAAK;QACf,eAAe,EAAE,KAAK;QACtB,WAAW,EAAE,IAAI;QACjB,gBAAgB;QAChB,QAAQ,EAAE,EAAE;QACZ,aAAa;QACb,cAAc,EAAE,IAAI;QACpB,YAAY,EAAE,IAAI;QAClB,mBAAmB,EAAE,CAAC;QACtB,gBAAgB,EAAE,IAAI;QACtB,YAAY,EAAE,IAAI;QAClB,kBAAkB,EAAE,CAAC;QACrB,YAAY,EAAE,CAAC;QACf,cAAc;QACd,QAAQ,EAAE,IAAI,GAAG,EAAE;QACnB,sBAAsB;QACtB,mBAAmB;QACnB,aAAa,EAAE,EAAE;KAClB,CAAC;AACJ,CAAC;AAmBD;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAA0B;IAE7D,MAAM,eAAe,GAAG,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,IAAI,CAAC,CAAC;IAC3D,MAAM,YAAY,GAAG,KAAK,CAAC,GAAG,CAAC,YAAY,GAAG,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAE1E,OAAO;QAEL,WAAW,EAAE,eAAe;QAC5B,QAAQ,EAAE,YAAY;QACtB,KAAK,EAAE,eAAe,GAAG,YAAY;KACtC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB;IAEnC,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,KAAI,MAAM,KAAK,IAAI,cAAc,CAAC,MAAM,EAAE,EAAE,CAAC;QAE3C,KAAK,IAAI,oBAAoB,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC;IAC7C,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,kBAAkB;AAElB;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAA0B;IAE/D,OAAO,KAAK,CAAC,SAAS,EAAE,sBAAsB,EAAE,IAAI,IAAI,CAAC;AAC3D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAA0B;IAE3D,OAAO,KAAK,CAAC,SAAS,EAAE,kBAAkB,EAAE,IAAI,IAAI,CAAC;AACvD,CAAC"}
|
|
@@ -2,6 +2,7 @@ import type { Channel, Nullable, ResolvedSiteProfile, UrlValidation } from "../t
|
|
|
2
2
|
import type { Frame, Page } from "puppeteer-core";
|
|
3
3
|
import type { RecoveryMetrics, TabReplacementResult } from "./recovery.js";
|
|
4
4
|
import type { FFmpegProcess } from "../utils/index.js";
|
|
5
|
+
import type { ManifestInterceptorHandle } from "../native/intercept.js";
|
|
5
6
|
import type { Readable } from "node:stream";
|
|
6
7
|
/**
|
|
7
8
|
* Factory function type for creating tab replacement handlers. Called by setupStream after generating stream IDs and resolving the profile, allowing the caller to
|
|
@@ -18,8 +19,10 @@ export interface StreamSetupOptions {
|
|
|
18
19
|
clickSelector?: string;
|
|
19
20
|
clickToPlay?: boolean;
|
|
20
21
|
noVideo?: boolean;
|
|
22
|
+
numericStreamId?: number;
|
|
21
23
|
onTabReplacementFactory?: TabReplacementHandlerFactory;
|
|
22
24
|
profileOverride?: string;
|
|
25
|
+
streamId?: string;
|
|
23
26
|
url: string;
|
|
24
27
|
}
|
|
25
28
|
/**
|
|
@@ -31,6 +34,7 @@ export interface StreamSetupResult {
|
|
|
31
34
|
directTune: boolean;
|
|
32
35
|
cleanup: () => Promise<void>;
|
|
33
36
|
ffmpegProcess: Nullable<FFmpegProcess>;
|
|
37
|
+
manifestInterception: Nullable<ManifestInterceptorHandle>;
|
|
34
38
|
numericStreamId: number;
|
|
35
39
|
page: Page;
|
|
36
40
|
profile: ResolvedSiteProfile;
|
|
@@ -57,7 +61,9 @@ export interface CreatePageWithCaptureOptions {
|
|
|
57
61
|
comment?: string;
|
|
58
62
|
onFFmpegError?: (error: Error) => void;
|
|
59
63
|
profile: ResolvedSiteProfile;
|
|
64
|
+
skipManifestInterception?: boolean;
|
|
60
65
|
streamId: string;
|
|
66
|
+
tabReplacement?: boolean;
|
|
61
67
|
url: string;
|
|
62
68
|
_pageClosedRetries?: number;
|
|
63
69
|
}
|
|
@@ -69,6 +75,7 @@ export interface CreatePageWithCaptureResult {
|
|
|
69
75
|
context: Frame | Page;
|
|
70
76
|
directTune: boolean;
|
|
71
77
|
ffmpegProcess: Nullable<FFmpegProcess>;
|
|
78
|
+
manifestInterception: Nullable<ManifestInterceptorHandle>;
|
|
72
79
|
page: Page;
|
|
73
80
|
rawCaptureStream: Readable;
|
|
74
81
|
}
|
|
@@ -91,7 +98,7 @@ export declare function validateStreamUrl(url: string | undefined): UrlValidatio
|
|
|
91
98
|
* Creates a browser page with media capture and navigates to the URL. This is the reusable core function used by both initial stream setup and tab replacement
|
|
92
99
|
* recovery. It handles:
|
|
93
100
|
* - Creating a new browser page with CSP bypass
|
|
94
|
-
* - Initializing media capture (native fMP4 or
|
|
101
|
+
* - Initializing media capture (native fMP4 or Matroska+FFmpeg)
|
|
95
102
|
* - Navigating to the URL with retry
|
|
96
103
|
* - Setting up video playback via navigateToPage() + initializePlayback()
|
|
97
104
|
*
|