@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,518 @@
1
+ import { LOG, delay, formatError } from "../../utils/index.js";
2
+ import { logAvailableChannels, normalizeChannelName } from "../channelSelection.js";
3
+ import { CONFIG } from "../../config/index.js";
4
+ // Sling TV guide grid row index cache. Maps normalized channel names (from data-testid="channel-{NAME}" attributes) to their row indices extracted from the
5
+ // parent .guide-row-container CSS class (gridGuideRow-{N}). Separate from the Hulu guideRowCache because Sling uses a different row index system (CSS class-based)
6
+ // and different scroll mechanics (.guide-cell scrollTop vs document.documentElement.scrollTop).
7
+ const slingRowCache = new Map();
8
+ // Channel GUID cache: maps normalized channel names to stable Sling channel GUIDs. Populated by intercepting guide grid API responses during guide page load.
9
+ // Channel GUIDs are permanent infrastructure identifiers that never change — only the per-program asset_id rotates.
10
+ const slingChannelGuidCache = new Map();
11
+ // Playback info URL template captured from the grid API response. The CDN hostname (e.g., cbd46b77.cdn.cms.movetv.com) may change, so we derive it at runtime
12
+ // from the first PLAY_CONTENT tile's playback_info.url rather than hardcoding it. The template stores everything up to and including "/channels" — the caller
13
+ // appends "/{channel_guid}/schedule/now/playback_info.qvt".
14
+ let slingPlaybackInfoBase = null;
15
+ // Tracks which pages have response interception listeners registered to avoid duplicate registrations.
16
+ const pagesWithListeners = new WeakSet();
17
+ // Base URL for constructing direct player URLs. This is the user-facing Sling domain, not a CDN edge — stable.
18
+ const SLING_PLAYER_BASE = "https://watch.sling.com/1/asset";
19
+ // Polling interval for the frontier-based cache wait. 300ms balances responsiveness (detecting newly arrived API pages quickly) against CPU overhead from cache
20
+ // scans. The API delivers pages in bursts, so shorter intervals provide diminishing returns.
21
+ const FRONTIER_POLL_INTERVAL = 300;
22
+ // Maximum time to wait for the target channel's GUID to appear in the cache. The full Sling channel alphabet takes 3-5 seconds to deliver via the paginated
23
+ // grid API. Five seconds ensures even the latest-alphabet channels are captured while bounding worst-case wait time for channels that genuinely aren't in the
24
+ // lineup (where the frontier never passes the target because it IS the last channel alphabetically).
25
+ const FRONTIER_MAX_WAIT = 5000;
26
+ /**
27
+ * Clears all Sling TV caches: row index cache, channel GUID cache, and playback info base URL. Called by clearChannelSelectionCaches() in the coordinator when
28
+ * the browser restarts, since cached state may be stale in a new browser session.
29
+ */
30
+ function clearSlingCache() {
31
+ slingRowCache.clear();
32
+ slingChannelGuidCache.clear();
33
+ slingPlaybackInfoBase = null;
34
+ }
35
+ /**
36
+ * Reads all rendered channel entries from the Sling TV guide grid in a single browser evaluate call. Extracts names from data-testid="channel-{NAME}" attributes
37
+ * and row indices from the parent .guide-row-container CSS class containing gridGuideRow-{N}. When the target channel is found, also locates the on-now program
38
+ * cell, scrolls it into view, and returns its center coordinates — eliminating a second evaluate round-trip. Populates the slingRowCache as a side effect. For
39
+ * local affiliates (ABC, FOX, NBC), also matches channels whose name starts with the target followed by " (" to handle the "network (callsign)" format.
40
+ * @param page - The Puppeteer page object.
41
+ * @param targetName - The normalized (lowercased, trimmed) channel name to match, or null to skip click target resolution.
42
+ * @returns Object with all rendered channels, optional click coordinates for the target's on-now cell, and the actual matched name (which may differ from
43
+ * targetName for local affiliates).
44
+ */
45
+ async function readSlingChannelsAndLocate(page, targetName) {
46
+ const raw = await page.evaluate((target) => {
47
+ const containers = document.querySelectorAll("[data-testid^=\"channel-\"]");
48
+ if (containers.length === 0) {
49
+ return null;
50
+ }
51
+ const prefix = "channel-";
52
+ const channels = [];
53
+ let clickTarget = null;
54
+ let matchedName = null;
55
+ for (const el of Array.from(containers)) {
56
+ const testid = el.getAttribute("data-testid") ?? "";
57
+ const name = testid.slice(prefix.length).trim().replace(/\s+/g, " ").toLowerCase();
58
+ // Extract row index from the parent .guide-row-container CSS class. The class name follows the pattern "gridGuideRow-{N}".
59
+ let rowIndex = -1;
60
+ const rowContainer = el.closest(".guide-row-container");
61
+ if (rowContainer) {
62
+ const classMatch = /gridGuideRow-(\d+)/.exec(rowContainer.className);
63
+ if (classMatch) {
64
+ rowIndex = parseInt(classMatch[1], 10);
65
+ }
66
+ }
67
+ channels.push({ name, rowIndex });
68
+ // When the target is found, locate the on-now program cell in the same pass. This avoids a second querySelectorAll + normalize loop in a separate evaluate.
69
+ // Sling local affiliates use the format "network (callsign)" so we also check for a prefix match where the channel name starts with the target followed by
70
+ // " (". This handles market-specific call signs without hardcoding them.
71
+ if (target && !clickTarget && ((name === target) || name.startsWith(target + " ("))) {
72
+ matchedName = name;
73
+ if (rowContainer) {
74
+ const onNow = rowContainer.querySelector(".grid-program-cell-container.active");
75
+ if (onNow) {
76
+ onNow.scrollIntoView({ behavior: "instant", block: "center", inline: "center" });
77
+ const rect = onNow.getBoundingClientRect();
78
+ if ((rect.width > 0) && (rect.height > 0)) {
79
+ clickTarget = { x: rect.x + (rect.width / 2), y: rect.y + (rect.height / 2) };
80
+ }
81
+ }
82
+ }
83
+ }
84
+ }
85
+ return { channels, clickTarget, matchedName };
86
+ }, targetName);
87
+ if (!raw) {
88
+ return { channels: null, clickTarget: null, matchedName: null };
89
+ }
90
+ // Populate the row index cache with discovered mappings.
91
+ const rendered = [];
92
+ for (const ch of raw.channels) {
93
+ rendered.push({ name: ch.name, rowIndex: ch.rowIndex });
94
+ if (ch.rowIndex >= 0) {
95
+ slingRowCache.set(ch.name, ch.rowIndex);
96
+ }
97
+ }
98
+ return { channels: rendered, clickTarget: raw.clickTarget, matchedName: raw.matchedName };
99
+ }
100
+ // Shorter timeout for non-final click attempts. Successful navigations typically complete in 1-2 seconds, so 5 seconds is generous while still failing fast enough
101
+ // to allow meaningful retries within the overall time budget.
102
+ const CLICK_RETRY_TIMEOUT = 5000;
103
+ // The guide page URL contains this path segment. Used to detect whether the page has navigated away from the guide after a click attempt.
104
+ const GUIDE_URL_MARKER = "grid_guide";
105
+ // Maximum number of click attempts before giving up. Three attempts with 5-second timeouts on the first two gives a worst-case wall-clock time of ~15 seconds —
106
+ // comparable to a single 10-second timeout plus the overhead of the full retry cycle, but with a much higher success rate for transient failures.
107
+ const MAX_CLICK_ATTEMPTS = 3;
108
+ /**
109
+ * Clicks the on-now program cell and waits for Sling to navigate to the player page. The click triggers a full page navigation to a /1/asset/{assetId}/watch URL,
110
+ * so we use Promise.all with page.waitForNavigation() to ensure the player page's DOM is ready before returning. Without this wait, initializePlayback() could run
111
+ * against a page that is mid-transition — either finding nothing or grabbing a stale element from the guide page. Uses domcontentloaded rather than load because
112
+ * the player page only needs to render a <video> element — waiting for all subresources (images, fonts) would add unnecessary latency since startVideoPlayback()
113
+ * independently waits for the video element. No settle delay before the click because readSlingChannelsAndLocate() already called scrollIntoView and read
114
+ * getBoundingClientRect, confirming the element is positioned, and any mispositioned click is caught by the navigation timeout.
115
+ * @param page - The Puppeteer page object.
116
+ * @param target - The x/y coordinates of the on-now cell to click.
117
+ * @param timeout - Navigation timeout in milliseconds. Defaults to CONFIG.streaming.navigationTimeout.
118
+ * @returns Result object with success status and optional failure reason.
119
+ */
120
+ async function clickSlingOnNowAndWaitForNavigation(page, target, timeout = CONFIG.streaming.navigationTimeout) {
121
+ try {
122
+ // Register the navigation wait before the click fires to avoid a race where the navigation completes before waitForNavigation starts listening.
123
+ await Promise.all([
124
+ page.waitForNavigation({ timeout, waitUntil: "domcontentloaded" }),
125
+ page.mouse.click(target.x, target.y)
126
+ ]);
127
+ return { success: true };
128
+ }
129
+ catch (error) {
130
+ return { reason: "Navigation to Sling TV player page failed: " + formatError(error) + ".", success: false };
131
+ }
132
+ }
133
+ /**
134
+ * Attempts to click the on-now program cell up to MAX_CLICK_ATTEMPTS times with in-place retry. On each non-final attempt, uses a shorter timeout (CLICK_RETRY_TIMEOUT)
135
+ * to fail fast and allow a retry within the same time budget as a single full-timeout attempt plus the expensive full-retry cycle. Between retries, checks whether the
136
+ * page has already navigated away from the guide (indicating the click triggered navigation but the player page was slow to load) and re-reads on-now cell coordinates
137
+ * from the guide page to handle any virtualizer layout shifts that occurred during the timeout.
138
+ * @param page - The Puppeteer page object.
139
+ * @param initialTarget - The x/y coordinates from the initial readSlingChannelsAndLocate() call.
140
+ * @param normalizedName - The normalized channel name for re-reading coordinates on retry.
141
+ * @param channelName - The original channel name for log messages.
142
+ * @returns Result object with success status and optional failure reason from the last attempt.
143
+ */
144
+ async function clickWithRetry(page, initialTarget, normalizedName, channelName) {
145
+ let target = initialTarget;
146
+ let lastResult = { reason: "No click attempts made.", success: false };
147
+ for (let attempt = 0; attempt < MAX_CLICK_ATTEMPTS; attempt++) {
148
+ // On retry attempts, re-read on-now cell coordinates. The virtualizer may have shifted layout during the timeout, making the original coordinates stale.
149
+ if (attempt > 0) {
150
+ // If the page has navigated away from the guide, the click did trigger navigation — it was just slow. Return success and let initializePlayback's
151
+ // waitForVideoReady handle the rest rather than re-clicking a page that is already mid-navigation.
152
+ if (!page.url().includes(GUIDE_URL_MARKER)) {
153
+ LOG.debug("tuning:sling", "Sling page navigated away from guide after click attempt %s for %s. Treating as success.", attempt, channelName);
154
+ return { success: true };
155
+ }
156
+ LOG.debug("tuning:sling", "Sling click attempt %s of %s for %s.", attempt + 1, MAX_CLICK_ATTEMPTS, channelName);
157
+ // Re-read coordinates from the still-loaded guide page. Wrapped in try/catch because the page might commit a pending navigation between the URL check above
158
+ // and this evaluate call, destroying the execution context. In that case, fall through with the previous coordinates — the next URL check will detect the
159
+ // navigation and return success.
160
+ try {
161
+ // eslint-disable-next-line no-await-in-loop
162
+ const retryResult = await readSlingChannelsAndLocate(page, normalizedName);
163
+ if (retryResult.clickTarget) {
164
+ target = retryResult.clickTarget;
165
+ }
166
+ }
167
+ catch {
168
+ // Page navigated away during re-read. The next iteration's URL check will catch this.
169
+ }
170
+ }
171
+ // Use a shorter timeout on non-final attempts to fail fast. The final attempt gets the full navigationTimeout as a last-ditch effort.
172
+ const timeout = (attempt < (MAX_CLICK_ATTEMPTS - 1)) ? CLICK_RETRY_TIMEOUT : CONFIG.streaming.navigationTimeout;
173
+ // eslint-disable-next-line no-await-in-loop
174
+ lastResult = await clickSlingOnNowAndWaitForNavigation(page, target, timeout);
175
+ if (lastResult.success) {
176
+ return lastResult;
177
+ }
178
+ }
179
+ return lastResult;
180
+ }
181
+ /**
182
+ * Looks up a channel GUID from the cache by normalized name. Falls back to local affiliate prefix matching where the cache key starts with the target name
183
+ * followed by " (" (e.g., "abc" matches "abc (wls)"). On a prefix match, caches the GUID under the primary name for O(1) on subsequent lookups.
184
+ * @param normalizedName - The normalized (lowercased, trimmed) channel name.
185
+ * @returns The channel GUID or null if not cached.
186
+ */
187
+ function findChannelGuid(normalizedName) {
188
+ let channelGuid = slingChannelGuidCache.get(normalizedName) ?? null;
189
+ if (!channelGuid) {
190
+ for (const [key, guid] of slingChannelGuidCache) {
191
+ if (key.startsWith(normalizedName + " (")) {
192
+ channelGuid = guid;
193
+ // Cache under the primary name for O(1) on next lookup.
194
+ slingChannelGuidCache.set(normalizedName, guid);
195
+ break;
196
+ }
197
+ }
198
+ }
199
+ return channelGuid;
200
+ }
201
+ /**
202
+ * Returns the alphabetically latest key in the GUID cache. Used by the polling loop to detect when the API has delivered pages past the target's position —
203
+ * if the frontier sorts after the target name, the API page covering the target's range has already been processed and the channel is not in the lineup.
204
+ * @returns The alphabetically latest cache key, or null if the cache is empty.
205
+ */
206
+ function getCacheFrontier() {
207
+ let max = null;
208
+ for (const key of slingChannelGuidCache.keys()) {
209
+ if (!max || (key > max)) {
210
+ max = key;
211
+ }
212
+ }
213
+ return max;
214
+ }
215
+ /**
216
+ * Fetches the current asset_id for a Sling channel from the public playback info endpoint. Returns null if the playback info base URL has not been captured yet
217
+ * (cold cache), the endpoint returns a non-200 response, or the response does not contain an asset GUID. Callers should catch network and JSON parse errors.
218
+ * @param channelGuid - The stable Sling channel GUID.
219
+ * @returns The current asset_id string or null.
220
+ */
221
+ async function fetchSlingAssetId(channelGuid) {
222
+ if (!slingPlaybackInfoBase) {
223
+ return null;
224
+ }
225
+ const response = await fetch(slingPlaybackInfoBase + "/" + channelGuid + "/schedule/now/playback_info.qvt");
226
+ if (!response.ok) {
227
+ return null;
228
+ }
229
+ const data = await response.json();
230
+ return data.playback_info?.asset?.guid ?? null;
231
+ }
232
+ /**
233
+ * Resolves a direct player URL from the channel GUID cache. Looks up the channel GUID by normalized name (with local affiliate prefix fallback), fetches the
234
+ * current asset_id from the public playback info endpoint, and returns the full player URL. Returns null if the channel is not in the GUID cache, the playback
235
+ * info base URL is unknown, or the asset_id fetch fails. Shared by both resolveSlingDirectUrl (pre-navigation) and the slingGridStrategy fast path
236
+ * (post-guide-load).
237
+ * @param normalizedName - The normalized (lowercased, trimmed) channel name.
238
+ * @returns The direct player URL (e.g., "https://watch.sling.com/1/asset/{id}/watch") or null.
239
+ */
240
+ async function resolvePlayerUrl(normalizedName) {
241
+ const channelGuid = findChannelGuid(normalizedName);
242
+ if (!channelGuid) {
243
+ return null;
244
+ }
245
+ const assetId = await fetchSlingAssetId(channelGuid);
246
+ if (!assetId) {
247
+ return null;
248
+ }
249
+ return SLING_PLAYER_BASE + "/" + assetId + "/watch";
250
+ }
251
+ /**
252
+ * Processes a single Sling grid guide API response, extracting channel GUIDs and the playback info base URL. Called by the response interception listener for
253
+ * each paginated grid_guide_a_z response. Populates the slingChannelGuidCache and, on the first tile with a PLAY_CONTENT action, captures slingPlaybackInfoBase.
254
+ * @param data - The parsed JSON response from the grid guide API.
255
+ */
256
+ function processGridApiResponse(data) {
257
+ if (!data.ribbons) {
258
+ return;
259
+ }
260
+ for (const ribbon of data.ribbons) {
261
+ const tile = ribbon.tiles?.[0];
262
+ const channelName = tile?.actions?.DETAIL_VIEW?.adobe?.ChannelName;
263
+ const channelGuid = ribbon.stitch_id;
264
+ const isLive = !!tile?.actions?.PLAY_CONTENT;
265
+ // Only cache ribbons that have a PLAY_CONTENT action. AVOD/VOD variant channels (e.g., "truTV Sneak Peak", Freestream browse entries) share the same
266
+ // ChannelName as the live channel but have a different GUID that doesn't resolve via playback_info. Live channels always have PLAY_CONTENT; variants
267
+ // only have DETAIL_VIEW.
268
+ if (channelName && channelGuid && isLive) {
269
+ slingChannelGuidCache.set(normalizeChannelName(channelName), channelGuid);
270
+ }
271
+ // Extract the playback info base URL from the first tile that has one. The CDN hostname varies (e.g., cbd46b77.cdn.cms.movetv.com), so we derive it
272
+ // from the actual API response rather than hardcoding. We take everything up to and including "/channels" from the tile's playback_info.url.
273
+ if (!slingPlaybackInfoBase) {
274
+ const playbackUrl = tile?.actions?.PLAY_CONTENT?.playback_info?.url;
275
+ if ((typeof playbackUrl === "string") && playbackUrl.includes("/channels/")) {
276
+ slingPlaybackInfoBase = playbackUrl.substring(0, playbackUrl.indexOf("/channels/") + "/channels".length);
277
+ }
278
+ }
279
+ }
280
+ }
281
+ /**
282
+ * Sets up response interception on the page to capture Sling's grid guide API responses. As the guide page loads, Sling fetches paginated channel data from the
283
+ * grid_guide_a_z API. Each response contains ribbons with channel GUIDs (stitch_id) and channel names (DETAIL_VIEW.adobe.ChannelName). We intercept these to
284
+ * populate the slingChannelGuidCache, enabling the API fast path on subsequent tunes. Also extracts the playback info base URL from the first PLAY_CONTENT tile
285
+ * to avoid hardcoding the CDN hostname. Uses a WeakSet to prevent duplicate listener registration on the same page.
286
+ * @param page - The Puppeteer page object.
287
+ */
288
+ function setupGridResponseInterception(page) {
289
+ if (pagesWithListeners.has(page)) {
290
+ return;
291
+ }
292
+ pagesWithListeners.add(page);
293
+ page.on("response", (response) => {
294
+ const url = response.url();
295
+ if (!url.includes("pres/grid_guide_a_z") || (response.status() !== 200)) {
296
+ return;
297
+ }
298
+ void response.json().then((data) => {
299
+ processGridApiResponse(data);
300
+ }).catch(() => {
301
+ // Response parsing failed — binary search fallback handles it.
302
+ });
303
+ });
304
+ }
305
+ /**
306
+ * Resolves a direct player URL for a Sling channel. When the channel GUID cache is warm (populated by grid API response interception), fetches the current
307
+ * asset_id from the public playback_info endpoint and returns a direct player URL. When the cache is cold, sets up response interception on the page so that
308
+ * the next guide page navigation will populate the cache for future tunes.
309
+ * @param channelSelector - The channel selector string (e.g., "ESPN", "ABC").
310
+ * @param page - The Puppeteer page for setting up response interception on cold cache.
311
+ * @returns The direct player URL or null.
312
+ */
313
+ async function resolveSlingDirectUrl(channelSelector, page) {
314
+ const normalizedName = normalizeChannelName(channelSelector);
315
+ // Warm cache: resolve the player URL from the channel GUID cache and the public playback info endpoint.
316
+ try {
317
+ const playerUrl = await resolvePlayerUrl(normalizedName);
318
+ if (playerUrl) {
319
+ LOG.debug("tuning:sling", "Sling direct URL resolved for %s: %s.", channelSelector, playerUrl);
320
+ return playerUrl;
321
+ }
322
+ }
323
+ catch (error) {
324
+ LOG.debug("tuning:sling", "Sling playback info fetch failed for %s: %s.", channelSelector, formatError(error));
325
+ return null;
326
+ }
327
+ // Cold cache: set up response interception for the upcoming guide page navigation. The listener will populate slingChannelGuidCache as the guide loads,
328
+ // making the channel_guid available for the execute fast path and subsequent resolveDirectUrl calls.
329
+ setupGridResponseInterception(page);
330
+ return null;
331
+ }
332
+ /**
333
+ * Invalidates the cached channel GUID for the given channel selector. Called when a cached direct URL fails to produce a working stream.
334
+ * @param channelSelector - The channel selector string to invalidate.
335
+ */
336
+ function invalidateSlingDirectUrl(channelSelector) {
337
+ slingChannelGuidCache.delete(normalizeChannelName(channelSelector));
338
+ }
339
+ /**
340
+ * Sling TV grid strategy: finds a channel in the virtualized, alphabetically sorted guide grid at watch.sling.com/dashboard/grid_guide/grid_guide_a_z using binary
341
+ * search on the .guide-cell scroll container, then clicks the on-now program cell to navigate to the player page. The guide renders ~8-10 of ~638 rows at a time,
342
+ * each 120px tall, sorted A-Z by channel name. Channel identification uses data-testid="channel-{NAME}" attributes.
343
+ *
344
+ * The selection process:
345
+ * 1. Wait for channel entries to appear in the DOM (confirms guide grid has loaded and API data is flowing)
346
+ * 2. API fast path with frontier polling: poll the GUID cache until the target channel appears, the cache frontier passes the target's position, or 5 seconds
347
+ * elapse — then fetch the current asset_id and navigate directly, skipping all steps below
348
+ * 3. Read grid metadata: locate the .guide-cell scroll host and compute total rows from scrollHeight / 120
349
+ * 4. Check the slingRowCache for a direct-scroll shortcut from a previous tune
350
+ * 5. Binary search: scroll .guide-cell to the midpoint row, read rendered channels, compare alphabetically to adjust bounds
351
+ * 6. Click the on-now program cell via clickWithRetry() — retries up to 3 times on navigation timeout before giving up
352
+ * @param page - The Puppeteer page object.
353
+ * @param profile - The resolved site profile with a non-null channelSelector (channel name).
354
+ * @returns Result object with success status and optional failure reason.
355
+ */
356
+ async function slingGridStrategy(page, profile) {
357
+ const channelName = profile.channelSelector;
358
+ const normalizedName = normalizeChannelName(channelName);
359
+ // Phase 1: Wait for the guide grid to render. Channel entries appear as data-testid="channel-{NAME}" elements within the virtualized list.
360
+ try {
361
+ await page.waitForSelector("[data-testid^=\"channel-\"]", { timeout: 5000, visible: true });
362
+ }
363
+ catch {
364
+ return { reason: "Sling TV guide grid did not load.", success: false };
365
+ }
366
+ // API fast path with frontier-based polling. The GUID cache populates progressively as paginated grid API responses arrive in alphabetical order during page
367
+ // load. Rather than checking the cache once (missing late-alphabet channels whose API page hasn't arrived yet), poll until one of three conditions is met:
368
+ // (1) the target channel's GUID appears in the cache, (2) the cache frontier (alphabetically latest entry) passes the target's position (confirming the
369
+ // channel is not in the lineup), or (3) the maximum wait time is exceeded. This ensures the cache is fully populated for all subsequent tunes — a one-time
370
+ // cost that eliminates binary search for the rest of the session.
371
+ const pollStart = Date.now();
372
+ while ((Date.now() - pollStart) < FRONTIER_MAX_WAIT) {
373
+ if (findChannelGuid(normalizedName)) {
374
+ break;
375
+ }
376
+ // Check if the cache frontier has passed the target's alphabetical position. If the latest cache key sorts after the target, the API page covering
377
+ // the target's range has already been processed — the channel is not in the lineup and further polling won't help.
378
+ const frontier = getCacheFrontier();
379
+ if (frontier && (frontier > normalizedName)) {
380
+ LOG.debug("tuning:sling", "Cache frontier \"%s\" passed target \"%s\". Channel not in Sling lineup.", frontier, normalizedName);
381
+ break;
382
+ }
383
+ // eslint-disable-next-line no-await-in-loop
384
+ await delay(FRONTIER_POLL_INTERVAL);
385
+ }
386
+ // After polling, attempt the full resolve pipeline: GUID lookup → asset_id fetch → player URL construction.
387
+ const playerUrl = await resolvePlayerUrl(normalizedName).catch(() => null);
388
+ if (playerUrl) {
389
+ LOG.debug("tuning:sling", "Sling API fast path for %s (%sms): %s.", channelName, Date.now() - pollStart, playerUrl);
390
+ try {
391
+ await page.goto(playerUrl, { timeout: CONFIG.streaming.navigationTimeout, waitUntil: "domcontentloaded" });
392
+ return { success: true };
393
+ }
394
+ catch (error) {
395
+ return { reason: "Sling TV API fast path navigation failed: " + formatError(error) + ".", success: false };
396
+ }
397
+ }
398
+ LOG.debug("tuning:sling", "Sling binary search fallback for %s after %sms polling (GUID cache: %s, base URL: %s).", channelName, Date.now() - pollStart, slingChannelGuidCache.size, slingPlaybackInfoBase ? "yes" : "no");
399
+ // Phase 2: Read grid metadata. The .guide-cell element is the scroll host for the virtualized channel list. Each row is 120px tall with a 30px offset for the
400
+ // time header row at the top of the grid.
401
+ const ROW_HEIGHT = 120;
402
+ const ROW_OFFSET = 30;
403
+ const gridMeta = await page.evaluate((rowHeight) => {
404
+ const guideCell = document.querySelector(".guide-cell");
405
+ if (!guideCell) {
406
+ return null;
407
+ }
408
+ const totalRows = Math.round(guideCell.scrollHeight / rowHeight);
409
+ if (totalRows <= 0) {
410
+ return null;
411
+ }
412
+ return { totalRows };
413
+ }, ROW_HEIGHT);
414
+ if (!gridMeta) {
415
+ return { reason: "Could not locate Sling TV guide grid scroll container.", success: false };
416
+ }
417
+ const { totalRows } = gridMeta;
418
+ // Helper: scroll the .guide-cell container to a specific row index and wait for the virtualizer to render.
419
+ const scrollToRow = async (rowIndex) => {
420
+ await page.evaluate((scrollTo) => {
421
+ const guideCell = document.querySelector(".guide-cell");
422
+ if (guideCell) {
423
+ guideCell.scrollTop = scrollTo;
424
+ }
425
+ }, ROW_OFFSET + (rowIndex * ROW_HEIGHT));
426
+ await delay(200);
427
+ };
428
+ // Phase 3: Check the cache for a direct-scroll shortcut. If we've tuned to this channel before, skip binary search and scroll directly.
429
+ const cachedRow = slingRowCache.get(normalizedName);
430
+ if (cachedRow !== undefined) {
431
+ LOG.debug("tuning:sling", "Sling row cache hit for %s at row %s.", channelName, cachedRow);
432
+ await scrollToRow(cachedRow);
433
+ const { channels, clickTarget } = await readSlingChannelsAndLocate(page, normalizedName);
434
+ if (channels && clickTarget) {
435
+ return await clickWithRetry(page, clickTarget, normalizedName, channelName);
436
+ }
437
+ // Cache hit but channel not found at expected position. Clear this entry and fall through to binary search.
438
+ LOG.debug("tuning:sling", "Sling cache miss for %s. Falling back to binary search.", channelName);
439
+ slingRowCache.delete(normalizedName);
440
+ }
441
+ // Phase 4: Binary search through the virtualized channel list. On each iteration we scroll to the midpoint, read rendered channels (with click target resolution
442
+ // for the target name), and either click immediately on match or compare alphabetically to adjust bounds. The combined readSlingChannelsAndLocate call returns
443
+ // both the channel list (for direction) and the on-now cell coordinates (for clicking) in a single browser round-trip.
444
+ let low = 0;
445
+ let high = totalRows - 1;
446
+ const maxIterations = 12;
447
+ let foundClickTarget = null;
448
+ let foundMatchedName = null;
449
+ for (let iteration = 0; iteration < maxIterations; iteration++) {
450
+ if (low > high) {
451
+ break;
452
+ }
453
+ const mid = Math.floor((low + high) / 2);
454
+ // eslint-disable-next-line no-await-in-loop
455
+ await scrollToRow(mid);
456
+ // eslint-disable-next-line no-await-in-loop
457
+ const { channels, clickTarget, matchedName } = await readSlingChannelsAndLocate(page, normalizedName);
458
+ if (!channels || (channels.length === 0)) {
459
+ continue;
460
+ }
461
+ // If the target was found, the click coordinates are already resolved. No second evaluate needed.
462
+ if (clickTarget) {
463
+ foundClickTarget = clickTarget;
464
+ foundMatchedName = matchedName;
465
+ break;
466
+ }
467
+ // Sort by name so first/last reflect alphabetical extremes. querySelectorAll returns DOM insertion order, which may not match visual order in a virtualizer
468
+ // that recycles elements by appending new rows rather than inserting in visual position.
469
+ channels.sort((a, b) => a.name.localeCompare(b.name));
470
+ // Determine binary search direction by comparing against the first and last rendered channel names.
471
+ const first = channels[0].name;
472
+ const last = channels[channels.length - 1].name;
473
+ if (normalizedName.localeCompare(first) < 0) {
474
+ // Target sorts before the first rendered channel. Scroll up.
475
+ high = mid - 1;
476
+ continue;
477
+ }
478
+ if (normalizedName.localeCompare(last) > 0) {
479
+ // Target sorts after the last rendered channel. Scroll down.
480
+ low = mid + 1;
481
+ continue;
482
+ }
483
+ // Target is between the first and last rendered channels but no exact match. The channel may not exist in the guide.
484
+ break;
485
+ }
486
+ if (!foundClickTarget) {
487
+ // Log available channels from the row cache to help users identify the correct channelSelector value. The cache contains channels seen during binary search
488
+ // iterations and any prior tune attempts in this session — a partial but often actionable subset of Sling's ~636 channel catalog.
489
+ const availableChannels = Array.from(slingRowCache.keys()).sort();
490
+ if (availableChannels.length > 0) {
491
+ logAvailableChannels({
492
+ availableChannels,
493
+ channelName,
494
+ guideUrl: "https://watch.sling.com/dashboard/grid_guide/grid_guide_a_z",
495
+ providerName: "Sling TV"
496
+ });
497
+ }
498
+ return { reason: "Could not find channel " + channelName + " in Sling TV guide grid.", success: false };
499
+ }
500
+ // When a local affiliate was matched via prefix, cache the network name as an alias so subsequent tunes skip binary search and scroll directly to the
501
+ // affiliate's row.
502
+ if (foundMatchedName && (foundMatchedName !== normalizedName)) {
503
+ const affiliateRow = slingRowCache.get(foundMatchedName);
504
+ if (affiliateRow !== undefined) {
505
+ slingRowCache.set(normalizedName, affiliateRow);
506
+ }
507
+ }
508
+ // Phase 5: Click the on-now program cell and wait for Sling to navigate to the player page. Uses the retry loop to handle transient click or navigation failures
509
+ // without tearing down the entire attempt and reloading the guide page.
510
+ return await clickWithRetry(page, foundClickTarget, normalizedName, channelName);
511
+ }
512
+ export const slingStrategy = {
513
+ clearCache: clearSlingCache,
514
+ execute: slingGridStrategy,
515
+ invalidateDirectUrl: invalidateSlingDirectUrl,
516
+ resolveDirectUrl: resolveSlingDirectUrl
517
+ };
518
+ //# sourceMappingURL=sling.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sling.js","sourceRoot":"","sources":["../../../src/browser/tuning/sling.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { ChannelStrategyEntry } from "../../types/index.js";
2
+ export declare const thumbnailRowStrategy: ChannelStrategyEntry;