@bnhf/prismcast 1.3.4-2026.2.19

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 (234) hide show
  1. package/LICENSE.md +7 -0
  2. package/README.md +347 -0
  3. package/bin/prismcast +6 -0
  4. package/dist/app.d.ts +6 -0
  5. package/dist/app.js +315 -0
  6. package/dist/app.js.map +1 -0
  7. package/dist/browser/cdp.d.ts +38 -0
  8. package/dist/browser/cdp.js +155 -0
  9. package/dist/browser/cdp.js.map +1 -0
  10. package/dist/browser/channelSelection.d.ts +65 -0
  11. package/dist/browser/channelSelection.js +202 -0
  12. package/dist/browser/channelSelection.js.map +1 -0
  13. package/dist/browser/display.d.ts +34 -0
  14. package/dist/browser/display.js +54 -0
  15. package/dist/browser/display.js.map +1 -0
  16. package/dist/browser/index.d.ts +205 -0
  17. package/dist/browser/index.js +1205 -0
  18. package/dist/browser/index.js.map +1 -0
  19. package/dist/browser/tuning/fox.d.ts +2 -0
  20. package/dist/browser/tuning/fox.js +83 -0
  21. package/dist/browser/tuning/fox.js.map +1 -0
  22. package/dist/browser/tuning/hbo.d.ts +2 -0
  23. package/dist/browser/tuning/hbo.js +237 -0
  24. package/dist/browser/tuning/hbo.js.map +1 -0
  25. package/dist/browser/tuning/hulu.d.ts +2 -0
  26. package/dist/browser/tuning/hulu.js +550 -0
  27. package/dist/browser/tuning/hulu.js.map +1 -0
  28. package/dist/browser/tuning/sling.d.ts +2 -0
  29. package/dist/browser/tuning/sling.js +518 -0
  30. package/dist/browser/tuning/sling.js.map +1 -0
  31. package/dist/browser/tuning/thumbnailRow.d.ts +2 -0
  32. package/dist/browser/tuning/thumbnailRow.js +108 -0
  33. package/dist/browser/tuning/thumbnailRow.js.map +1 -0
  34. package/dist/browser/tuning/tileClick.d.ts +2 -0
  35. package/dist/browser/tuning/tileClick.js +103 -0
  36. package/dist/browser/tuning/tileClick.js.map +1 -0
  37. package/dist/browser/tuning/youtubeTv.d.ts +2 -0
  38. package/dist/browser/tuning/youtubeTv.js +182 -0
  39. package/dist/browser/tuning/youtubeTv.js.map +1 -0
  40. package/dist/browser/video.d.ts +289 -0
  41. package/dist/browser/video.js +996 -0
  42. package/dist/browser/video.js.map +1 -0
  43. package/dist/channels/index.d.ts +3 -0
  44. package/dist/channels/index.js +392 -0
  45. package/dist/channels/index.js.map +1 -0
  46. package/dist/config/index.d.ts +53 -0
  47. package/dist/config/index.js +233 -0
  48. package/dist/config/index.js.map +1 -0
  49. package/dist/config/presets.d.ts +98 -0
  50. package/dist/config/presets.js +241 -0
  51. package/dist/config/presets.js.map +1 -0
  52. package/dist/config/profiles.d.ts +79 -0
  53. package/dist/config/profiles.js +245 -0
  54. package/dist/config/profiles.js.map +1 -0
  55. package/dist/config/providers.d.ts +120 -0
  56. package/dist/config/providers.js +450 -0
  57. package/dist/config/providers.js.map +1 -0
  58. package/dist/config/sites.d.ts +22 -0
  59. package/dist/config/sites.js +377 -0
  60. package/dist/config/sites.js.map +1 -0
  61. package/dist/config/userChannels.d.ts +178 -0
  62. package/dist/config/userChannels.js +543 -0
  63. package/dist/config/userChannels.js.map +1 -0
  64. package/dist/config/userConfig.d.ts +235 -0
  65. package/dist/config/userConfig.js +913 -0
  66. package/dist/config/userConfig.js.map +1 -0
  67. package/dist/hdhr/channelMap.d.ts +21 -0
  68. package/dist/hdhr/channelMap.js +82 -0
  69. package/dist/hdhr/channelMap.js.map +1 -0
  70. package/dist/hdhr/deviceId.d.ts +11 -0
  71. package/dist/hdhr/deviceId.js +84 -0
  72. package/dist/hdhr/deviceId.js.map +1 -0
  73. package/dist/hdhr/discover.d.ts +6 -0
  74. package/dist/hdhr/discover.js +155 -0
  75. package/dist/hdhr/discover.js.map +1 -0
  76. package/dist/hdhr/index.d.ts +9 -0
  77. package/dist/hdhr/index.js +87 -0
  78. package/dist/hdhr/index.js.map +1 -0
  79. package/dist/index.d.ts +1 -0
  80. package/dist/index.js +144 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/routes/assets.d.ts +6 -0
  83. package/dist/routes/assets.js +79 -0
  84. package/dist/routes/assets.js.map +1 -0
  85. package/dist/routes/auth.d.ts +6 -0
  86. package/dist/routes/auth.js +77 -0
  87. package/dist/routes/auth.js.map +1 -0
  88. package/dist/routes/channels.d.ts +6 -0
  89. package/dist/routes/channels.js +40 -0
  90. package/dist/routes/channels.js.map +1 -0
  91. package/dist/routes/components.d.ts +138 -0
  92. package/dist/routes/components.js +210 -0
  93. package/dist/routes/components.js.map +1 -0
  94. package/dist/routes/config.d.ts +72 -0
  95. package/dist/routes/config.js +1977 -0
  96. package/dist/routes/config.js.map +1 -0
  97. package/dist/routes/debug.d.ts +6 -0
  98. package/dist/routes/debug.js +274 -0
  99. package/dist/routes/debug.js.map +1 -0
  100. package/dist/routes/health.d.ts +6 -0
  101. package/dist/routes/health.js +85 -0
  102. package/dist/routes/health.js.map +1 -0
  103. package/dist/routes/hls.d.ts +6 -0
  104. package/dist/routes/hls.js +25 -0
  105. package/dist/routes/hls.js.map +1 -0
  106. package/dist/routes/index.d.ts +19 -0
  107. package/dist/routes/index.js +49 -0
  108. package/dist/routes/index.js.map +1 -0
  109. package/dist/routes/logs.d.ts +6 -0
  110. package/dist/routes/logs.js +164 -0
  111. package/dist/routes/logs.js.map +1 -0
  112. package/dist/routes/mpegts.d.ts +6 -0
  113. package/dist/routes/mpegts.js +19 -0
  114. package/dist/routes/mpegts.js.map +1 -0
  115. package/dist/routes/play.d.ts +6 -0
  116. package/dist/routes/play.js +18 -0
  117. package/dist/routes/play.js.map +1 -0
  118. package/dist/routes/playlist.d.ts +36 -0
  119. package/dist/routes/playlist.js +134 -0
  120. package/dist/routes/playlist.js.map +1 -0
  121. package/dist/routes/root.d.ts +6 -0
  122. package/dist/routes/root.js +2920 -0
  123. package/dist/routes/root.js.map +1 -0
  124. package/dist/routes/streams.d.ts +6 -0
  125. package/dist/routes/streams.js +88 -0
  126. package/dist/routes/streams.js.map +1 -0
  127. package/dist/routes/theme.d.ts +15 -0
  128. package/dist/routes/theme.js +275 -0
  129. package/dist/routes/theme.js.map +1 -0
  130. package/dist/routes/ui.d.ts +56 -0
  131. package/dist/routes/ui.js +354 -0
  132. package/dist/routes/ui.js.map +1 -0
  133. package/dist/service/commands.d.ts +41 -0
  134. package/dist/service/commands.js +391 -0
  135. package/dist/service/commands.js.map +1 -0
  136. package/dist/service/generators.d.ts +33 -0
  137. package/dist/service/generators.js +432 -0
  138. package/dist/service/generators.js.map +1 -0
  139. package/dist/service/index.d.ts +2 -0
  140. package/dist/service/index.js +7 -0
  141. package/dist/service/index.js.map +1 -0
  142. package/dist/streaming/clients.d.ts +48 -0
  143. package/dist/streaming/clients.js +114 -0
  144. package/dist/streaming/clients.js.map +1 -0
  145. package/dist/streaming/fmp4Segmenter.d.ts +61 -0
  146. package/dist/streaming/fmp4Segmenter.js +461 -0
  147. package/dist/streaming/fmp4Segmenter.js.map +1 -0
  148. package/dist/streaming/hls.d.ts +120 -0
  149. package/dist/streaming/hls.js +722 -0
  150. package/dist/streaming/hls.js.map +1 -0
  151. package/dist/streaming/hlsSegments.d.ts +54 -0
  152. package/dist/streaming/hlsSegments.js +162 -0
  153. package/dist/streaming/hlsSegments.js.map +1 -0
  154. package/dist/streaming/lifecycle.d.ts +33 -0
  155. package/dist/streaming/lifecycle.js +185 -0
  156. package/dist/streaming/lifecycle.js.map +1 -0
  157. package/dist/streaming/monitor.d.ts +74 -0
  158. package/dist/streaming/monitor.js +1310 -0
  159. package/dist/streaming/monitor.js.map +1 -0
  160. package/dist/streaming/mp4Parser.d.ts +74 -0
  161. package/dist/streaming/mp4Parser.js +566 -0
  162. package/dist/streaming/mp4Parser.js.map +1 -0
  163. package/dist/streaming/mpegts.d.ts +14 -0
  164. package/dist/streaming/mpegts.js +248 -0
  165. package/dist/streaming/mpegts.js.map +1 -0
  166. package/dist/streaming/registry.d.ts +119 -0
  167. package/dist/streaming/registry.js +127 -0
  168. package/dist/streaming/registry.js.map +1 -0
  169. package/dist/streaming/setup.d.ts +135 -0
  170. package/dist/streaming/setup.js +670 -0
  171. package/dist/streaming/setup.js.map +1 -0
  172. package/dist/streaming/showInfo.d.ts +30 -0
  173. package/dist/streaming/showInfo.js +362 -0
  174. package/dist/streaming/showInfo.js.map +1 -0
  175. package/dist/streaming/statusEmitter.d.ts +125 -0
  176. package/dist/streaming/statusEmitter.js +139 -0
  177. package/dist/streaming/statusEmitter.js.map +1 -0
  178. package/dist/types/index.d.ts +403 -0
  179. package/dist/types/index.js +6 -0
  180. package/dist/types/index.js.map +1 -0
  181. package/dist/utils/debugFilter.d.ts +38 -0
  182. package/dist/utils/debugFilter.js +157 -0
  183. package/dist/utils/debugFilter.js.map +1 -0
  184. package/dist/utils/delay.d.ts +6 -0
  185. package/dist/utils/delay.js +15 -0
  186. package/dist/utils/delay.js.map +1 -0
  187. package/dist/utils/errors.d.ts +15 -0
  188. package/dist/utils/errors.js +40 -0
  189. package/dist/utils/errors.js.map +1 -0
  190. package/dist/utils/evaluate.d.ts +51 -0
  191. package/dist/utils/evaluate.js +124 -0
  192. package/dist/utils/evaluate.js.map +1 -0
  193. package/dist/utils/ffmpeg.d.ts +65 -0
  194. package/dist/utils/ffmpeg.js +317 -0
  195. package/dist/utils/ffmpeg.js.map +1 -0
  196. package/dist/utils/fileLogger.d.ts +25 -0
  197. package/dist/utils/fileLogger.js +248 -0
  198. package/dist/utils/fileLogger.js.map +1 -0
  199. package/dist/utils/format.d.ts +16 -0
  200. package/dist/utils/format.js +46 -0
  201. package/dist/utils/format.js.map +1 -0
  202. package/dist/utils/html.d.ts +6 -0
  203. package/dist/utils/html.js +24 -0
  204. package/dist/utils/html.js.map +1 -0
  205. package/dist/utils/index.d.ts +15 -0
  206. package/dist/utils/index.js +20 -0
  207. package/dist/utils/index.js.map +1 -0
  208. package/dist/utils/logEmitter.d.ts +17 -0
  209. package/dist/utils/logEmitter.js +30 -0
  210. package/dist/utils/logEmitter.js.map +1 -0
  211. package/dist/utils/logger.d.ts +82 -0
  212. package/dist/utils/logger.js +219 -0
  213. package/dist/utils/logger.js.map +1 -0
  214. package/dist/utils/m3u.d.ts +32 -0
  215. package/dist/utils/m3u.js +148 -0
  216. package/dist/utils/m3u.js.map +1 -0
  217. package/dist/utils/morganStream.d.ts +7 -0
  218. package/dist/utils/morganStream.js +33 -0
  219. package/dist/utils/morganStream.js.map +1 -0
  220. package/dist/utils/platform.d.ts +64 -0
  221. package/dist/utils/platform.js +157 -0
  222. package/dist/utils/platform.js.map +1 -0
  223. package/dist/utils/retry.d.ts +15 -0
  224. package/dist/utils/retry.js +82 -0
  225. package/dist/utils/retry.js.map +1 -0
  226. package/dist/utils/streamContext.d.ts +28 -0
  227. package/dist/utils/streamContext.js +33 -0
  228. package/dist/utils/streamContext.js.map +1 -0
  229. package/dist/utils/version.d.ts +37 -0
  230. package/dist/utils/version.js +228 -0
  231. package/dist/utils/version.js.map +1 -0
  232. package/package.json +92 -0
  233. package/prismcast.png +0 -0
  234. package/prismcast.svg +74 -0
@@ -0,0 +1,550 @@
1
+ import { LOG, delay, evaluateWithAbort, formatError } from "../../utils/index.js";
2
+ import { logAvailableChannels, normalizeChannelName, scrollAndClick } from "../channelSelection.js";
3
+ import { CONFIG } from "../../config/index.js";
4
+ // Guide grid row number cache. Maps lowercased, trimmed channel names from data-testid attributes to their row numbers (from sr-only text). Populated passively
5
+ // during binary search iterations and used for direct-scroll optimization on subsequent tunes. Session-scoped — cleared when the browser restarts.
6
+ const guideRowCache = new Map();
7
+ /**
8
+ * Clears the Hulu guide row cache. Called by clearChannelSelectionCaches() in the coordinator when the browser restarts.
9
+ */
10
+ function clearHuluCache() {
11
+ guideRowCache.clear();
12
+ }
13
+ /**
14
+ * Reads all rendered channel containers from the guide grid, extracting their names from data-testid attributes and row numbers from sr-only text. Populates the
15
+ * row number cache as a side effect.
16
+ * @param page - The Puppeteer page object.
17
+ * @returns Array of rendered channels in DOM order, or null if no channels are rendered.
18
+ */
19
+ async function readRenderedChannels(page) {
20
+ const channels = await page.evaluate(() => {
21
+ const containers = document.querySelectorAll("[data-testid^=\"live-guide-channel-kyber-\"]");
22
+ if (containers.length === 0) {
23
+ return null;
24
+ }
25
+ const prefix = "live-guide-channel-kyber-";
26
+ const results = [];
27
+ for (const el of Array.from(containers)) {
28
+ const testid = el.getAttribute("data-testid") ?? "";
29
+ const name = testid.slice(prefix.length).trim().replace(/\s+/g, " ").toLowerCase();
30
+ // Extract row number from sr-only text. Format: "{Name} Details, row {N} of {Total}. ..."
31
+ let rowNumber = -1;
32
+ const btn = el.querySelector("[data-testid=\"live-guide-channel-button\"]");
33
+ if (btn) {
34
+ const srOnly = btn.querySelector(".sr-only, [class*=\"sr-only\"]");
35
+ if (srOnly) {
36
+ const match = /row (\d+) of/.exec(srOnly.textContent);
37
+ if (match) {
38
+ // Row numbers in sr-only text are 1-based. Convert to 0-based for scroll offset calculation.
39
+ rowNumber = parseInt(match[1], 10) - 1;
40
+ }
41
+ }
42
+ }
43
+ results.push({ name, rowNumber });
44
+ }
45
+ return results;
46
+ });
47
+ if (!channels) {
48
+ return null;
49
+ }
50
+ // Assign DOM indices and populate the row number cache.
51
+ const rendered = [];
52
+ for (let i = 0; i < channels.length; i++) {
53
+ const ch = channels[i];
54
+ rendered.push({ domIndex: i, name: ch.name, rowNumber: ch.rowNumber });
55
+ // Cache the row number for future direct-scroll lookups.
56
+ if (ch.rowNumber >= 0) {
57
+ guideRowCache.set(ch.name, ch.rowNumber);
58
+ }
59
+ }
60
+ return rendered;
61
+ }
62
+ /**
63
+ * Locates the on-now program cell for the channel at the given data-testid name (lowercased, trimmed), scrolls it into view, and returns its center coordinates
64
+ * for a subsequent page.mouse.click(). We return coordinates rather than clicking inside the evaluate because page.mouse.click() generates the full pointer event
65
+ * chain (pointerdown → mousedown → pointerup → mouseup → click) that React's event delegation requires, whereas a bare DOM .click() dispatches only a synthetic
66
+ * click event that may not be processed reliably in a Puppeteer automation context.
67
+ * @param page - The Puppeteer page object.
68
+ * @param targetName - The lowercased, trimmed channel name to match against data-testid.
69
+ * @returns Center coordinates of the on-now cell, or null if not found.
70
+ */
71
+ async function locateOnNowCell(page, targetName) {
72
+ return evaluateWithAbort(page, (target) => {
73
+ const prefix = "live-guide-channel-kyber-";
74
+ const containers = document.querySelectorAll("[data-testid^=\"" + prefix + "\"]");
75
+ for (const el of Array.from(containers)) {
76
+ const testid = el.getAttribute("data-testid") ?? "";
77
+ const name = testid.slice(prefix.length).trim().replace(/\s+/g, " ").toLowerCase();
78
+ if (name === target) {
79
+ const row = el.closest("[data-testid=\"live-guide-row\"]");
80
+ if (!row) {
81
+ return null;
82
+ }
83
+ const onNow = row.querySelector(".LiveGuideProgram--first");
84
+ if (!onNow) {
85
+ return null;
86
+ }
87
+ onNow.scrollIntoView({ behavior: "instant", block: "center", inline: "center" });
88
+ const rect = onNow.getBoundingClientRect();
89
+ if ((rect.width > 0) && (rect.height > 0)) {
90
+ return { x: rect.x + (rect.width / 2), y: rect.y + (rect.height / 2) };
91
+ }
92
+ return null;
93
+ }
94
+ }
95
+ return null;
96
+ }, [targetName]);
97
+ }
98
+ // US broadcast call sign pattern. Local affiliate stations have 3-4 uppercase letter call signs starting with W (east of the Mississippi) or K (west). This
99
+ // pattern is used to identify call signs in the guide grid so position-based inference can find local affiliates that sort by their hidden network name.
100
+ const CALL_SIGN_PATTERN = /^[WK][A-Z]{2,3}$/i;
101
+ /**
102
+ * Position-based inference for local affiliates. When binary search returns "missing" (target name sorts between rendered channels but no exact match), this
103
+ * function identifies the local affiliate at the target's alphabetical insertion point.
104
+ *
105
+ * The guide sorts local affiliates by their network name (ABC, CBS, NBC, etc.), but displays call signs (WLS, WBBM, WMAQ) in data-testid. The binary search
106
+ * converges to the correct scroll position because the target network name sorts correctly, but the name match fails because the data-testid contains the call
107
+ * sign. The affiliate occupies the DOM position where the network name would be if it existed.
108
+ *
109
+ * Algorithm:
110
+ * 1. Filter rendered channels to non-call-sign names (these sort correctly by their displayed name)
111
+ * 2. Find where the target would insert alphabetically among the non-call-sign neighbors
112
+ * 3. The channel at the DOM position between those two neighbors is the local affiliate
113
+ * @param rendered - The rendered channels in DOM order.
114
+ * @param targetName - The lowercased target channel name.
115
+ * @returns The name of the inferred local affiliate channel, or null if inference fails.
116
+ */
117
+ function inferLocalAffiliate(rendered, targetName) {
118
+ // Build a list of non-call-sign channels with their DOM indices. These channels sort alphabetically by their displayed name and serve as position anchors.
119
+ const anchors = [];
120
+ for (const ch of rendered) {
121
+ if (!CALL_SIGN_PATTERN.test(ch.name)) {
122
+ anchors.push(ch);
123
+ }
124
+ }
125
+ // If no non-call-sign channels are rendered, we have no position anchors and cannot infer the affiliate.
126
+ if (anchors.length === 0) {
127
+ return null;
128
+ }
129
+ // Find the insertion point: the first anchor whose name sorts after the target.
130
+ let insertBeforeIndex = -1;
131
+ for (let i = 0; i < anchors.length; i++) {
132
+ if (targetName.localeCompare(anchors[i].name) < 0) {
133
+ insertBeforeIndex = i;
134
+ break;
135
+ }
136
+ }
137
+ // Determine the DOM index range between the two surrounding anchor channels.
138
+ let lowerDomIndex;
139
+ let upperDomIndex;
140
+ if (insertBeforeIndex === 0) {
141
+ // Target sorts before all anchors. Look for call signs before the first anchor.
142
+ lowerDomIndex = -1;
143
+ upperDomIndex = anchors[0].domIndex;
144
+ }
145
+ else if (insertBeforeIndex === -1) {
146
+ // Target sorts after all anchors. Look for call signs after the last anchor.
147
+ lowerDomIndex = anchors[anchors.length - 1].domIndex;
148
+ upperDomIndex = rendered.length;
149
+ }
150
+ else {
151
+ // Target sorts between two anchors.
152
+ lowerDomIndex = anchors[insertBeforeIndex - 1].domIndex;
153
+ upperDomIndex = anchors[insertBeforeIndex].domIndex;
154
+ }
155
+ // Find call sign channels in the DOM range between the two anchors.
156
+ for (const ch of rendered) {
157
+ if ((ch.domIndex > lowerDomIndex) && (ch.domIndex < upperDomIndex) && CALL_SIGN_PATTERN.test(ch.name)) {
158
+ return ch.name;
159
+ }
160
+ }
161
+ return null;
162
+ }
163
+ /**
164
+ * Waits for the play button to appear and clicks it using coordinate-based mouse events. Called by clickOnNowCellAndPlay after each on-now cell click attempt.
165
+ * @param page - The Puppeteer page object.
166
+ * @param playSelector - The CSS selector for the play button, or undefined if no play button is needed.
167
+ * @param timeout - Optional timeout in milliseconds for the play button to appear. Defaults to CONFIG.streaming.videoTimeout.
168
+ * @returns Result object with success status and optional failure reason.
169
+ */
170
+ async function waitForPlayButton(page, playSelector, timeout) {
171
+ if (!playSelector) {
172
+ return { success: true };
173
+ }
174
+ try {
175
+ await page.waitForSelector(playSelector, { timeout: timeout ?? CONFIG.streaming.videoTimeout, visible: true });
176
+ // Wait two animation frames for React to flush pending state updates. The play button may be visible in the DOM before React's concurrent mode has committed
177
+ // the channel selection state to the component's event handlers. Without this, clicking immediately can trigger playback of the previously-selected channel
178
+ // rather than the one we just chose. The double-rAF pattern synchronizes with the browser's rendering pipeline rather than using a fixed delay.
179
+ await page.evaluate(async () => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(() => { resolve(); }))));
180
+ // Get the play button's coordinates for a real mouse click. Like the on-now cell click, we use page.mouse.click() to generate the full pointer event chain
181
+ // rather than a bare DOM .click().
182
+ const playTarget = await evaluateWithAbort(page, (selector) => {
183
+ const el = document.querySelector(selector);
184
+ if (!el) {
185
+ return null;
186
+ }
187
+ el.scrollIntoView({ behavior: "instant", block: "center", inline: "center" });
188
+ const rect = el.getBoundingClientRect();
189
+ if ((rect.width > 0) && (rect.height > 0)) {
190
+ return { x: rect.x + (rect.width / 2), y: rect.y + (rect.height / 2) };
191
+ }
192
+ return null;
193
+ }, [playSelector]);
194
+ if (!playTarget) {
195
+ return { reason: "Play selector " + playSelector + " found but has no dimensions.", success: false };
196
+ }
197
+ await scrollAndClick(page, playTarget);
198
+ return { success: true };
199
+ }
200
+ catch (error) {
201
+ return { reason: "Could not click play selector " + playSelector + ": " + formatError(error) + ".", success: false };
202
+ }
203
+ }
204
+ /**
205
+ * Clicks the on-now program cell and waits for the play button with navigation verification, retrying on failure. Handles two distinct failure modes: (1) the
206
+ * on-now cell click doesn't register because React hasn't fully hydrated event handlers — the play button never appears; (2) the play button appears and is
207
+ * clicked but the click is silently swallowed — navigation to the player page doesn't occur. Both failures are detected by waitForPlayButton and trigger a
208
+ * retry of the full on-now cell and play button sequence after a brief delay.
209
+ * @param page - The Puppeteer page object.
210
+ * @param clickTarget - The lowercased, trimmed channel name to locate in the guide grid.
211
+ * @param playSelector - The CSS selector for the play button, or undefined if no play button is needed.
212
+ * @param channelName - The original channel name for logging.
213
+ * @returns Result object with success status and optional failure reason.
214
+ */
215
+ async function clickOnNowCellAndPlay(page, clickTarget, playSelector, channelName) {
216
+ // Maximum number of on-now cell click attempts. The first click may not register if React hasn't finished hydrating the guide's event handlers.
217
+ const MAX_CLICK_ATTEMPTS = 3;
218
+ // Shorter timeout for the play button on non-final attempts. When a click registers, the play button appears in under 10ms — this timeout only determines how
219
+ // quickly we detect a missed click and retry. Keeping it low saves ~2s per failed attempt compared to the previous 3000ms value.
220
+ const RETRY_PLAY_TIMEOUT = 1000;
221
+ // Delay between click retries. Gives React additional time to finish hydrating event handlers.
222
+ const CLICK_RETRY_DELAY = 1500;
223
+ // Track the last click coordinates for diagnostic logging on failure.
224
+ let lastClickCoords = null;
225
+ for (let attempt = 0; attempt < MAX_CLICK_ATTEMPTS; attempt++) {
226
+ // eslint-disable-next-line no-await-in-loop
227
+ const onNowTarget = await locateOnNowCell(page, clickTarget);
228
+ if (!onNowTarget) {
229
+ return { reason: "Found channel " + channelName + " but could not locate on-now program cell.", success: false };
230
+ }
231
+ lastClickCoords = onNowTarget;
232
+ // eslint-disable-next-line no-await-in-loop
233
+ await scrollAndClick(page, onNowTarget);
234
+ // Use a shorter timeout on non-final attempts to enable quick retries. The final attempt uses the full default timeout as a last chance.
235
+ const playTimeout = (attempt < MAX_CLICK_ATTEMPTS - 1) ? RETRY_PLAY_TIMEOUT : undefined;
236
+ // eslint-disable-next-line no-await-in-loop
237
+ const result = await waitForPlayButton(page, playSelector, playTimeout);
238
+ if (result.success) {
239
+ return result;
240
+ }
241
+ // The play button either didn't appear (on-now cell click missed) or appeared and was clicked but navigation didn't occur (play button click swallowed).
242
+ // Retry the full sequence after a brief delay to allow further React hydration.
243
+ if (attempt < MAX_CLICK_ATTEMPTS - 1) {
244
+ LOG.debug("tuning:hulu", "Channel selection attempt failed for %s: %s. Retrying (attempt %s of %s).", channelName, result.reason, attempt + 2, MAX_CLICK_ATTEMPTS);
245
+ // eslint-disable-next-line no-await-in-loop
246
+ await delay(CLICK_RETRY_DELAY);
247
+ }
248
+ }
249
+ // All click attempts exhausted. Run diagnostics to capture the page state at the moment of failure. This information is critical for debugging intermittent
250
+ // guide interaction failures where the on-now cell is found but clicking it never produces the play button modal.
251
+ if (lastClickCoords) {
252
+ try {
253
+ const diagnostics = await evaluateWithAbort(page, (x, y, target) => {
254
+ // What elements are at the click coordinates, from topmost to bottommost? If something other than the on-now cell is intercepting clicks, it will
255
+ // appear first in this list.
256
+ const elements = document.elementsFromPoint(x, y);
257
+ const elementStack = elements.slice(0, 5).map((el) => {
258
+ const tag = el.tagName.toLowerCase();
259
+ const id = el.id ? ("#" + el.id) : "";
260
+ const cls = el.className && (typeof el.className === "string") ? ("." + el.className.trim().split(/\s+/).slice(0, 2).join(".")) : "";
261
+ const testId = el.getAttribute("data-testid") ?? "";
262
+ const testIdStr = testId ? ("[data-testid=\"" + testId + "\"]") : "";
263
+ return tag + id + cls + testIdStr;
264
+ });
265
+ // Check whether the on-now cell has React internal properties, which indicates React has hydrated the element and attached event handlers. React
266
+ // attaches __reactFiber$ and __reactProps$ properties to hydrated elements. If these are absent, the element is rendered but not interactive.
267
+ let hydrated = false;
268
+ let onNowFound = false;
269
+ const prefix = "live-guide-channel-kyber-";
270
+ const containers = document.querySelectorAll("[data-testid^=\"" + prefix + "\"]");
271
+ for (const el of Array.from(containers)) {
272
+ const testid = el.getAttribute("data-testid") ?? "";
273
+ const name = testid.slice(prefix.length).trim().replace(/\s+/g, " ").toLowerCase();
274
+ if (name === target) {
275
+ const row = el.closest("[data-testid=\"live-guide-row\"]");
276
+ if (row) {
277
+ const onNow = row.querySelector(".LiveGuideProgram--first");
278
+ if (onNow) {
279
+ onNowFound = true;
280
+ hydrated = Object.keys(onNow).some((k) => k.startsWith("__reactFiber") || k.startsWith("__reactProps"));
281
+ }
282
+ }
283
+ break;
284
+ }
285
+ }
286
+ return { elementStack, hydrated, onNowFound, pageAge: Math.round(performance.now()) };
287
+ }, [lastClickCoords.x, lastClickCoords.y, clickTarget]);
288
+ LOG.warn("Guide click diagnostics for %s: pageAge=%sms, onNowCell=%s, reactHydrated=%s, elementsAtClick=[%s].", channelName, diagnostics.pageAge, diagnostics.onNowFound ? "present" : "missing", diagnostics.hydrated ? "yes" : "no", diagnostics.elementStack.join(" > "));
289
+ }
290
+ catch (error) {
291
+ LOG.warn("Could not collect guide click diagnostics for %s: %s.", channelName, formatError(error));
292
+ }
293
+ }
294
+ return { reason: "Play button did not appear after " + String(MAX_CLICK_ATTEMPTS) + " on-now cell click attempts for " + channelName + ".", success: false };
295
+ }
296
+ /**
297
+ * Guide grid strategy: finds a channel in a virtualized, alphabetically sorted channel grid by scrolling the page to the target row using binary search, then
298
+ * clicking the on-now program cell to open the playback overlay. This strategy works for sites like Hulu Live TV where the channel guide is rendered as a
299
+ * virtualized list — only ~13 of ~124 rows exist in the DOM at any time, positioned absolutely within a tall spacer div. The virtualizer renders rows based on
300
+ * the page scroll position (`document.documentElement.scrollTop`), so we scroll to bring the target channel into the DOM, then interact with it directly.
301
+ *
302
+ * Three mechanisms handle different channel types:
303
+ * 1. Binary search with passive row number caching — primary mechanism for most channels (~800ms first time, ~200ms on cache hit)
304
+ * 2. Position-based inference — handles local affiliates when searching by network name (e.g., "ABC" finds WLS at the right sort position)
305
+ * 3. Linear scan fallback — safety net for raw call sign searches or any channel the binary search cannot find (~2.4 seconds)
306
+ *
307
+ * The selection process:
308
+ * 1. If listSelector is provided, click the tab/button to reveal the channel list (e.g., a "Channels" tab)
309
+ * 2. Wait for the channel grid rows to render in the DOM
310
+ * 3. Check the row number cache for a direct-scroll shortcut
311
+ * 4. Binary search: scroll to the midpoint row, read rendered channels (caching row numbers), check for exact match or infer local affiliate
312
+ * 5. If binary search fails, linear scan from top to bottom as a universal fallback
313
+ * 6. Click the on-now program cell (`.LiveGuideProgram--first`) in the target channel's row to open the playback overlay
314
+ * 7. If playSelector is provided, wait for and click the play button to start live playback
315
+ * @param page - The Puppeteer page object.
316
+ * @param profile - The resolved site profile with a non-null channelSelector (channel name) and channelSelection config.
317
+ * @returns Result object with success status and optional failure reason.
318
+ */
319
+ async function guideGridStrategy(page, profile) {
320
+ const { channelSelection, channelSelector: channelName } = profile;
321
+ const { listSelector, playSelector } = channelSelection;
322
+ // Ensure the guide is open and on the correct tab. We wait for the tab button to become VISIBLE (not just present in the DOM) because the guide overlay may exist
323
+ // in the DOM structure while still hidden during page initialization or animation. Clicking a hidden button dispatches a DOM event but has no visual effect — the
324
+ // guide remains hidden and the virtualizer never populates rows. We use $eval for the click because overlapping elements (spinners, overlays) can intercept
325
+ // Puppeteer's coordinate-based mouse events.
326
+ if (listSelector) {
327
+ try {
328
+ await page.waitForSelector(listSelector, { timeout: CONFIG.streaming.videoTimeout, visible: true });
329
+ await page.$eval(listSelector, (el) => { el.click(); });
330
+ // Brief delay for the tab switch animation and virtualizer initialization.
331
+ await delay(300);
332
+ }
333
+ catch (error) {
334
+ LOG.warn("Could not click channel list selector %s: %s.", listSelector, formatError(error));
335
+ }
336
+ }
337
+ // Wait for channel grid rows to become visible. If rows don't appear within a short initial window, retry the tab click once — the first click may have fired
338
+ // during a transitional state before the guide was fully interactive, or the guide may have been animating open.
339
+ let rowsVisible = false;
340
+ for (let guideAttempt = 0; guideAttempt < 2; guideAttempt++) {
341
+ try {
342
+ const rowTimeout = (guideAttempt === 0) ? 5000 : CONFIG.streaming.videoTimeout;
343
+ // eslint-disable-next-line no-await-in-loop
344
+ await page.waitForSelector("[data-testid=\"live-guide-row\"]", { timeout: rowTimeout, visible: true });
345
+ rowsVisible = true;
346
+ break;
347
+ }
348
+ catch {
349
+ // Rows not visible yet. On first failure, retry the tab click in case the guide wasn't fully interactive.
350
+ if ((guideAttempt === 0) && listSelector) {
351
+ LOG.debug("tuning:hulu", "Guide rows not visible after initial wait. Retrying tab click for %s.", listSelector);
352
+ try {
353
+ // eslint-disable-next-line no-await-in-loop
354
+ await page.$eval(listSelector, (el) => { el.click(); });
355
+ // eslint-disable-next-line no-await-in-loop
356
+ await delay(500);
357
+ }
358
+ catch {
359
+ // Retry click failed. Fall through to final wait attempt.
360
+ }
361
+ }
362
+ }
363
+ }
364
+ if (!rowsVisible) {
365
+ return { reason: "Channel grid rows did not render.", success: false };
366
+ }
367
+ // Each row in the virtualized grid is exactly 112px tall. The total number of channels is derived from the spacer div's height.
368
+ const ROW_HEIGHT = 112;
369
+ // Normalize the channel name to lowercase for case-insensitive matching against data-testid suffixes.
370
+ const normalizedName = normalizeChannelName(channelName);
371
+ // Read grid metadata by walking up from a rendered row to find the spacer and viewport divs. The spacer div is the direct parent of all absolutely-positioned
372
+ // rows, and its height equals totalRows * ROW_HEIGHT. The viewport div is the spacer's parent (overflow: hidden). We calculate gridDocTop as the viewport's
373
+ // document-level offset, so that scrolling to gridDocTop + (rowIndex * ROW_HEIGHT) places that row at the top of the browser viewport.
374
+ const gridMeta = await page.evaluate((rowHeight) => {
375
+ const row = document.querySelector("[data-testid=\"live-guide-row\"]");
376
+ if (!row) {
377
+ return null;
378
+ }
379
+ // The spacer div is the parent of all row elements.
380
+ const spacer = row.parentElement;
381
+ if (!spacer) {
382
+ return null;
383
+ }
384
+ const spacerHeight = spacer.offsetHeight;
385
+ if (spacerHeight < rowHeight) {
386
+ return null;
387
+ }
388
+ // The viewport div is the spacer's parent. Its position relative to the document determines our scroll offset.
389
+ const viewport = spacer.parentElement;
390
+ if (!viewport) {
391
+ return null;
392
+ }
393
+ const gridDocTop = viewport.getBoundingClientRect().top + document.documentElement.scrollTop;
394
+ return { gridDocTop, totalRows: Math.round(spacerHeight / rowHeight) };
395
+ }, ROW_HEIGHT);
396
+ if (!gridMeta) {
397
+ return { reason: "Could not locate channel grid spacer element.", success: false };
398
+ }
399
+ const { gridDocTop, totalRows } = gridMeta;
400
+ // Helper: scroll to a specific row index and wait for the virtualizer to render.
401
+ const scrollToRow = async (rowIndex) => {
402
+ await page.evaluate((scrollTo) => {
403
+ document.documentElement.scrollTop = scrollTo;
404
+ }, gridDocTop + (rowIndex * ROW_HEIGHT));
405
+ await delay(200);
406
+ };
407
+ // The name of the channel to click. This starts as the normalized target name but may be replaced by a local affiliate call sign via position inference.
408
+ let clickTarget = normalizedName;
409
+ // Check the row number cache for a direct-scroll shortcut. If we've seen this channel before, we can skip binary search entirely and scroll directly to it.
410
+ const cachedRow = guideRowCache.get(normalizedName);
411
+ if (cachedRow !== undefined) {
412
+ LOG.debug("tuning:hulu", "Guide cache hit for %s at row %s.", channelName, cachedRow);
413
+ await scrollToRow(cachedRow);
414
+ // Read rendered channels to update the cache and confirm the channel is present.
415
+ const rendered = await readRenderedChannels(page);
416
+ if (rendered) {
417
+ const match = rendered.find((ch) => ch.name === normalizedName);
418
+ if (match) {
419
+ return await clickOnNowCellAndPlay(page, normalizedName, playSelector, channelName);
420
+ }
421
+ }
422
+ // Cache hit but channel not found at expected position. The guide may have changed. Clear this entry and fall through to binary search.
423
+ LOG.debug("tuning:hulu", "Guide cache miss for %s. Falling back to binary search.", channelName);
424
+ guideRowCache.delete(normalizedName);
425
+ }
426
+ // Binary search through the virtualized channel list. On each iteration we scroll to the midpoint of the current range, wait for the virtualizer to render,
427
+ // then check if the target channel is among the ~13 rendered rows. If not, we compare the target name alphabetically against the first and last rendered
428
+ // channel names to narrow the range. The search converges in ~3-4 iterations because the 13-row render window covers a large fraction of the remaining range.
429
+ let low = 0;
430
+ let high = totalRows - 1;
431
+ const maxIterations = 10;
432
+ let found = false;
433
+ for (let iteration = 0; iteration < maxIterations; iteration++) {
434
+ if (low > high) {
435
+ break;
436
+ }
437
+ const mid = Math.floor((low + high) / 2);
438
+ // eslint-disable-next-line no-await-in-loop
439
+ await scrollToRow(mid);
440
+ // Read all rendered channels, populating the row number cache as a side effect.
441
+ // eslint-disable-next-line no-await-in-loop
442
+ const rendered = await readRenderedChannels(page);
443
+ if (!rendered || (rendered.length === 0)) {
444
+ continue;
445
+ }
446
+ // Check for an exact match first.
447
+ const exactMatch = rendered.find((ch) => ch.name === normalizedName);
448
+ if (exactMatch) {
449
+ found = true;
450
+ break;
451
+ }
452
+ // Determine binary search direction by comparing the target against the first and last rendered non-call-sign channel names. Call sign channels (W*/K*
453
+ // local affiliates) are excluded from direction comparison because they sort by hidden network name, not by their displayed call sign — using them for
454
+ // localeCompare would send the search the wrong way.
455
+ const nonCallSigns = rendered.filter((ch) => !CALL_SIGN_PATTERN.test(ch.name));
456
+ if (nonCallSigns.length === 0) {
457
+ // All rendered channels are call signs. Cannot determine direction. Move down and hope for better data.
458
+ low = mid + 1;
459
+ continue;
460
+ }
461
+ const first = nonCallSigns[0].name;
462
+ const last = nonCallSigns[nonCallSigns.length - 1].name;
463
+ if (normalizedName.localeCompare(first) < 0) {
464
+ // Target sorts before the first visible non-call-sign channel. Scroll up (toward lower row indices).
465
+ high = mid - 1;
466
+ continue;
467
+ }
468
+ if (normalizedName.localeCompare(last) > 0) {
469
+ // Target sorts after the last visible non-call-sign channel. Scroll down (toward higher row indices).
470
+ low = mid + 1;
471
+ continue;
472
+ }
473
+ // The target is alphabetically between the first and last rendered channels but was not found by exact data-testid match. This is the "missing" case — the
474
+ // channel may be a local affiliate whose call sign doesn't match the network name we're searching for. Try position-based inference.
475
+ const inferred = inferLocalAffiliate(rendered, normalizedName);
476
+ if (inferred) {
477
+ LOG.debug("tuning:hulu", "Inferred local affiliate %s for network name %s.", inferred, channelName);
478
+ clickTarget = inferred;
479
+ found = true;
480
+ // Cache the network name → affiliate's row number so subsequent tunes for the same network name become direct scrolls.
481
+ const inferredRow = guideRowCache.get(inferred);
482
+ if (inferredRow !== undefined) {
483
+ guideRowCache.set(normalizedName, inferredRow);
484
+ }
485
+ }
486
+ break;
487
+ }
488
+ // If binary search did not find the channel (and position inference didn't identify a local affiliate), fall back to a linear scan through all channels. This
489
+ // handles edge cases like raw call sign searches (e.g., "WLS") where localeCompare gives the wrong direction, or channels like "Lakeshore PBS" that sort by
490
+ // hidden network name but don't match the W/K call sign pattern.
491
+ if (!found) {
492
+ LOG.debug("tuning:hulu", "Binary search did not find %s. Starting linear scan fallback.", channelName);
493
+ for (let row = 0; row < totalRows; row += 10) {
494
+ // eslint-disable-next-line no-await-in-loop
495
+ await scrollToRow(row);
496
+ // eslint-disable-next-line no-await-in-loop
497
+ const rendered = await readRenderedChannels(page);
498
+ if (!rendered) {
499
+ continue;
500
+ }
501
+ const match = rendered.find((ch) => ch.name === normalizedName);
502
+ if (match) {
503
+ found = true;
504
+ break;
505
+ }
506
+ }
507
+ }
508
+ if (!found) {
509
+ // Log available channels from the guide row cache to help users identify the correct channelSelector value. The cache accumulates all channel names encountered
510
+ // during binary search and linear scan, so it contains most or all channels even though the virtualized grid only renders ~13 at a time.
511
+ const availableChannels = Array.from(guideRowCache.keys()).sort();
512
+ if (availableChannels.length > 0) {
513
+ logAvailableChannels({
514
+ availableChannels,
515
+ channelName,
516
+ guideUrl: "https://www.hulu.com/live-tv",
517
+ presetSuffix: "-hulu",
518
+ providerName: "Hulu"
519
+ });
520
+ }
521
+ return { reason: "Could not find channel " + channelName + " in guide grid.", success: false };
522
+ }
523
+ // Click the on-now program cell and wait for the play button, with click retries to handle React hydration timing.
524
+ return await clickOnNowCellAndPlay(page, clickTarget, playSelector, channelName);
525
+ }
526
+ /**
527
+ * Wraps guideGridStrategy with a single retry after dismissing any stale overlay that may be covering the guide grid. After a failed click attempt on the on-now
528
+ * cell, the playback overlay or entity modal can remain open, obscuring the guide and preventing subsequent channel selection attempts from locating guide rows.
529
+ * Pressing Escape closes most modal overlays in React-based SPAs.
530
+ * @param page - The Puppeteer page object.
531
+ * @param profile - The resolved site profile with a non-null channelSelector (channel name) and channelSelection config.
532
+ * @returns Result object with success status and optional failure reason.
533
+ */
534
+ async function guideGridWithRetry(page, profile) {
535
+ let result = await guideGridStrategy(page, profile);
536
+ if (!result.success) {
537
+ LOG.warn("Guide grid channel selection failed: %s. Dismissing overlay and retrying.", result.reason ?? "Unknown reason");
538
+ try {
539
+ await page.keyboard.press("Escape");
540
+ await delay(500);
541
+ }
542
+ catch (error) {
543
+ LOG.debug("tuning:hulu", "Could not dismiss guide overlay: %s.", formatError(error));
544
+ }
545
+ result = await guideGridStrategy(page, profile);
546
+ }
547
+ return result;
548
+ }
549
+ export const huluStrategy = { clearCache: clearHuluCache, execute: guideGridWithRetry };
550
+ //# sourceMappingURL=hulu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hulu.js","sourceRoot":"","sources":["../../../src/browser/tuning/hulu.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { ChannelStrategyEntry } from "../../types/index.js";
2
+ export declare const slingStrategy: ChannelStrategyEntry;