prismcast 1.10.1 → 1.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/dist/app.d.ts +5 -3
  2. package/dist/app.js +48 -43
  3. package/dist/app.js.map +1 -1
  4. package/dist/browser/hlsPlaylistObserver.d.ts +38 -0
  5. package/dist/browser/hlsPlaylistObserver.js +113 -0
  6. package/dist/browser/hlsPlaylistObserver.js.map +1 -0
  7. package/dist/browser/index.d.ts +26 -16
  8. package/dist/browser/index.js +97 -72
  9. package/dist/browser/index.js.map +1 -1
  10. package/dist/browser/manifestInterceptor.d.ts +29 -29
  11. package/dist/browser/manifestInterceptor.js +106 -212
  12. package/dist/browser/manifestInterceptor.js.map +1 -1
  13. package/dist/browser/tabNetworkObserver.d.ts +48 -0
  14. package/dist/browser/tabNetworkObserver.js +140 -0
  15. package/dist/browser/tabNetworkObserver.js.map +1 -0
  16. package/dist/browser/tuning/hbo.js +31 -123
  17. package/dist/browser/tuning/hbo.js.map +1 -1
  18. package/dist/browser/tuning/hulu.js +70 -7
  19. package/dist/browser/tuning/hulu.js.map +1 -1
  20. package/dist/browser/video.js +1 -1
  21. package/dist/browser/video.js.map +1 -1
  22. package/dist/config/consistencyProbe.js +13 -11
  23. package/dist/config/consistencyProbe.js.map +1 -1
  24. package/dist/config/index.d.ts +6 -2
  25. package/dist/config/index.js +35 -21
  26. package/dist/config/index.js.map +1 -1
  27. package/dist/config/paths.d.ts +29 -8
  28. package/dist/config/paths.js +54 -10
  29. package/dist/config/paths.js.map +1 -1
  30. package/dist/config/presets.js +2 -2
  31. package/dist/config/presets.js.map +1 -1
  32. package/dist/config/profiles.d.ts +1 -1
  33. package/dist/config/profiles.js +12 -12
  34. package/dist/config/profiles.js.map +1 -1
  35. package/dist/config/services.d.ts +5 -5
  36. package/dist/config/services.js +8 -8
  37. package/dist/config/services.js.map +1 -1
  38. package/dist/config/sites.d.ts +2 -2
  39. package/dist/config/sites.js +6 -6
  40. package/dist/config/sites.js.map +1 -1
  41. package/dist/config/userChannels.d.ts +108 -14
  42. package/dist/config/userChannels.js +292 -38
  43. package/dist/config/userChannels.js.map +1 -1
  44. package/dist/config/userConfig.js +2 -2
  45. package/dist/config/userConfig.js.map +1 -1
  46. package/dist/config/userProfiles.d.ts +3 -3
  47. package/dist/config/userProfiles.js +19 -19
  48. package/dist/config/userProfiles.js.map +1 -1
  49. package/dist/hdhr/discover.js +3 -4
  50. package/dist/hdhr/discover.js.map +1 -1
  51. package/dist/identity.d.ts +2 -0
  52. package/dist/identity.js +19 -0
  53. package/dist/identity.js.map +1 -0
  54. package/dist/index.js +7 -9
  55. package/dist/index.js.map +1 -1
  56. package/dist/native/index.d.ts +1 -1
  57. package/dist/native/index.js +9 -18
  58. package/dist/native/index.js.map +1 -1
  59. package/dist/native/proxy.d.ts +1 -4
  60. package/dist/native/proxy.js +0 -9
  61. package/dist/native/proxy.js.map +1 -1
  62. package/dist/routes/cdp.d.ts +171 -0
  63. package/dist/routes/cdp.js +702 -0
  64. package/dist/routes/cdp.js.map +1 -0
  65. package/dist/routes/clientActions.d.ts +97 -0
  66. package/dist/routes/clientActions.js +113 -0
  67. package/dist/routes/clientActions.js.map +1 -0
  68. package/dist/routes/components.d.ts +4 -3
  69. package/dist/routes/components.js +36 -39
  70. package/dist/routes/components.js.map +1 -1
  71. package/dist/routes/config/channels/endpoints/browse.js.map +1 -1
  72. package/dist/routes/config/channels/endpoints/crud.js +37 -72
  73. package/dist/routes/config/channels/endpoints/crud.js.map +1 -1
  74. package/dist/routes/config/channels/endpoints/importExport.js.map +1 -1
  75. package/dist/routes/config/channels/healthBridge.d.ts +23 -0
  76. package/dist/routes/config/channels/healthBridge.js +71 -0
  77. package/dist/routes/config/channels/healthBridge.js.map +1 -0
  78. package/dist/routes/config/channels/table.js +97 -84
  79. package/dist/routes/config/channels/table.js.map +1 -1
  80. package/dist/routes/config/services.js +26 -23
  81. package/dist/routes/config/services.js.map +1 -1
  82. package/dist/routes/config/settings.js +9 -6
  83. package/dist/routes/config/settings.js.map +1 -1
  84. package/dist/routes/debug.js +277 -139
  85. package/dist/routes/debug.js.map +1 -1
  86. package/dist/routes/index.js +2 -0
  87. package/dist/routes/index.js.map +1 -1
  88. package/dist/routes/logs.js +1 -1
  89. package/dist/routes/logs.js.map +1 -1
  90. package/dist/routes/root/content.js +18 -14
  91. package/dist/routes/root/content.js.map +1 -1
  92. package/dist/routes/root/index.js +25 -28
  93. package/dist/routes/root/index.js.map +1 -1
  94. package/dist/routes/root/scripts/channels.js +50 -21
  95. package/dist/routes/root/scripts/channels.js.map +1 -1
  96. package/dist/routes/root/scripts/config.js +72 -3
  97. package/dist/routes/root/scripts/config.js.map +1 -1
  98. package/dist/routes/root/scripts/shared.js +60 -0
  99. package/dist/routes/root/scripts/shared.js.map +1 -1
  100. package/dist/routes/root/scripts/status.handlers.d.ts +5 -27
  101. package/dist/routes/root/scripts/status.handlers.js +8 -60
  102. package/dist/routes/root/scripts/status.handlers.js.map +1 -1
  103. package/dist/routes/root/scripts/status.js +6 -1
  104. package/dist/routes/root/scripts/status.js.map +1 -1
  105. package/dist/routes/streams.js +5 -3
  106. package/dist/routes/streams.js.map +1 -1
  107. package/dist/service/commands.context.js +2 -2
  108. package/dist/service/commands.context.js.map +1 -1
  109. package/dist/service/commands.d.ts +1 -1
  110. package/dist/service/commands.js +1 -10
  111. package/dist/service/commands.js.map +1 -1
  112. package/dist/service/generators.context.js +2 -1
  113. package/dist/service/generators.context.js.map +1 -1
  114. package/dist/service/generators.d.ts +1 -1
  115. package/dist/service/generators.js +6 -19
  116. package/dist/service/generators.js.map +1 -1
  117. package/dist/streaming/clients.js +3 -11
  118. package/dist/streaming/clients.js.map +1 -1
  119. package/dist/streaming/fmp4Segmenter.js +1 -1
  120. package/dist/streaming/fmp4Segmenter.js.map +1 -1
  121. package/dist/streaming/hls.js +2 -6
  122. package/dist/streaming/hls.js.map +1 -1
  123. package/dist/streaming/hlsSegments.d.ts +1 -1
  124. package/dist/streaming/hlsSegments.js.map +1 -1
  125. package/dist/streaming/mpegts.js +1 -1
  126. package/dist/streaming/mpegts.js.map +1 -1
  127. package/dist/streaming/preroll.d.ts +1 -1
  128. package/dist/streaming/preroll.js +1 -5
  129. package/dist/streaming/preroll.js.map +1 -1
  130. package/dist/streaming/setup.js +6 -6
  131. package/dist/streaming/setup.js.map +1 -1
  132. package/dist/streaming/showInfo.js +2 -4
  133. package/dist/streaming/showInfo.js.map +1 -1
  134. package/dist/streaming/statusEmitter.d.ts +5 -8
  135. package/dist/streaming/statusEmitter.js +0 -5
  136. package/dist/streaming/statusEmitter.js.map +1 -1
  137. package/dist/types/channels.d.ts +9 -0
  138. package/dist/types/channels.js.map +1 -1
  139. package/dist/types/profiles.d.ts +2 -2
  140. package/dist/upgrade/commands.context.js +14 -10
  141. package/dist/upgrade/commands.context.js.map +1 -1
  142. package/dist/upgrade/commands.d.ts +7 -15
  143. package/dist/upgrade/commands.js +27 -28
  144. package/dist/upgrade/commands.js.map +1 -1
  145. package/dist/upgrade/detection.context.js +2 -1
  146. package/dist/upgrade/detection.context.js.map +1 -1
  147. package/dist/upgrade/detection.d.ts +2 -1
  148. package/dist/upgrade/detection.js +20 -51
  149. package/dist/upgrade/detection.js.map +1 -1
  150. package/dist/upgrade/index.d.ts +1 -0
  151. package/dist/upgrade/index.js +1 -0
  152. package/dist/upgrade/index.js.map +1 -1
  153. package/dist/upgrade/lifecycle.context.d.ts +19 -0
  154. package/dist/upgrade/lifecycle.context.js +47 -0
  155. package/dist/upgrade/lifecycle.context.js.map +1 -0
  156. package/dist/upgrade/lifecycle.d.ts +72 -0
  157. package/dist/upgrade/lifecycle.js +176 -0
  158. package/dist/upgrade/lifecycle.js.map +1 -0
  159. package/dist/upgrade/pathHandle.d.ts +28 -0
  160. package/dist/upgrade/pathHandle.js +101 -0
  161. package/dist/upgrade/pathHandle.js.map +1 -0
  162. package/dist/utils/bootSession.context.d.ts +15 -0
  163. package/dist/utils/bootSession.context.js +59 -0
  164. package/dist/utils/bootSession.context.js.map +1 -0
  165. package/dist/utils/bootSession.d.ts +16 -0
  166. package/dist/utils/bootSession.js +20 -0
  167. package/dist/utils/bootSession.js.map +1 -0
  168. package/dist/utils/clock.js +1 -1
  169. package/dist/utils/debugFilter.js +1 -0
  170. package/dist/utils/debugFilter.js.map +1 -1
  171. package/dist/utils/ffmpeg.js +1 -1
  172. package/dist/utils/ffmpeg.js.map +1 -1
  173. package/dist/utils/fileLogger.js +3 -3
  174. package/dist/utils/fileLogger.js.map +1 -1
  175. package/dist/utils/format.js +3 -3
  176. package/dist/utils/format.js.map +1 -1
  177. package/dist/utils/index.d.ts +5 -1
  178. package/dist/utils/index.js +5 -1
  179. package/dist/utils/index.js.map +1 -1
  180. package/dist/utils/logger.d.ts +9 -0
  181. package/dist/utils/logger.js +53 -1
  182. package/dist/utils/logger.js.map +1 -1
  183. package/dist/utils/markup.d.ts +22 -0
  184. package/dist/utils/markup.js +77 -0
  185. package/dist/utils/markup.js.map +1 -0
  186. package/dist/utils/morganStream.js +1 -1
  187. package/dist/utils/morganStream.js.map +1 -1
  188. package/dist/utils/network.d.ts +8 -0
  189. package/dist/utils/network.js +22 -0
  190. package/dist/utils/network.js.map +1 -0
  191. package/dist/utils/pid.d.ts +2 -25
  192. package/dist/utils/pid.js +10 -49
  193. package/dist/utils/pid.js.map +1 -1
  194. package/dist/utils/platform.d.ts +0 -27
  195. package/dist/utils/platform.js +0 -57
  196. package/dist/utils/platform.js.map +1 -1
  197. package/dist/utils/processInspector.context.d.ts +7 -0
  198. package/dist/utils/processInspector.context.js +104 -0
  199. package/dist/utils/processInspector.context.js.map +1 -0
  200. package/dist/utils/processInspector.d.ts +54 -0
  201. package/dist/utils/processInspector.js +125 -0
  202. package/dist/utils/processInspector.js.map +1 -0
  203. package/dist/utils/retry.d.ts +1 -1
  204. package/dist/utils/retry.js +1 -1
  205. package/dist/utils/retry.js.map +1 -1
  206. package/dist/utils/runtimeIdentity.context.d.ts +6 -0
  207. package/dist/utils/runtimeIdentity.context.js +13 -0
  208. package/dist/utils/runtimeIdentity.context.js.map +1 -0
  209. package/dist/utils/runtimeIdentity.d.ts +96 -0
  210. package/dist/utils/runtimeIdentity.js +171 -0
  211. package/dist/utils/runtimeIdentity.js.map +1 -0
  212. package/dist/utils/timing.d.ts +1 -1
  213. package/dist/utils/timing.js +0 -5
  214. package/dist/utils/timing.js.map +1 -1
  215. package/dist/utils/version.js +40 -10
  216. package/dist/utils/version.js.map +1 -1
  217. package/package.json +7 -5
  218. package/dist/utils/html.d.ts +0 -6
  219. package/dist/utils/html.js +0 -22
  220. package/dist/utils/html.js.map +0 -1
@@ -0,0 +1,140 @@
1
+ import { LOG } from "../utils/index.js";
2
+ // Default target filter: attach to every target except the browser-level one. The browser target emits no Network events relevant to a tab consumer, and
3
+ // attaching to it pollutes the session set. Defining the default at module scope makes it shareable across calls and trivially testable.
4
+ const defaultTargetFilter = (info) => info.type !== "browser";
5
+ /**
6
+ * Installs a tab-wide network observer on the given page. The returned handle delivers every response observed across every target accepted by the targetFilter
7
+ * predicate (top page, iframes including OOPIFs, workers, service workers by default) to the supplied callback. Disposal is the caller's responsibility; the
8
+ * returned handle supports both an explicit dispose() call and the "using" keyword via Symbol.dispose.
9
+ *
10
+ * Returns null when the page is already closed, the root CDP session could not be created, or the puppeteer Connection backing the session is not available
11
+ * (e.g., the session detached during construction).
12
+ *
13
+ * @param page - The puppeteer page to observe.
14
+ * @param options - Observer options including the response callback and optional target-type predicate.
15
+ * @returns The observer handle, or null if installation failed.
16
+ */
17
+ export async function observeTabResponses(page, options) {
18
+ if (page.isClosed()) {
19
+ return null;
20
+ }
21
+ let rootSession;
22
+ try {
23
+ rootSession = await page.createCDPSession();
24
+ }
25
+ catch (error) {
26
+ LOG.debug("browser:tabObserver", "Failed to create root CDP session: %s.", String(error));
27
+ return null;
28
+ }
29
+ // Connection lookup. With flatten=true, child sessions multiplex onto the parent connection and are reachable by sessionId via connection.session(). Puppeteer
30
+ // types CDPSession.connection() as `Connection | undefined`; the undefined case indicates the session has been detached or is otherwise unusable. We bail out
31
+ // cleanly rather than risking a TypeError on first child attach.
32
+ const connection = rootSession.connection();
33
+ if (!connection) {
34
+ LOG.debug("browser:tabObserver", "Root CDP session has no connection - aborting observer install.");
35
+ try {
36
+ await rootSession.detach();
37
+ }
38
+ catch (_error) {
39
+ // Cleanup best-effort.
40
+ }
41
+ return null;
42
+ }
43
+ const targetFilter = options.targetFilter ?? defaultTargetFilter;
44
+ // Track every session we attach to so disposal can tear them all down deterministically. The root session is included so it is cleaned up alongside children
45
+ // by one code path. A Set is used because the same session is never registered twice (sessionIds are unique per connection) but membership tests are needed
46
+ // during detach handling.
47
+ const sessions = new Set([rootSession]);
48
+ let disposed = false;
49
+ // Installs Network domain + response listener + recursive auto-attach on a freshly-attached session. Called for the root session synchronously below and for
50
+ // every child target as it attaches. Order matters: the Target.attachedToTarget listener has to be registered before setAutoAttach is sent, because
51
+ // setAutoAttach retroactively fires attachedToTarget for currently-attached children and we cannot miss those events. The Network.responseReceived and
52
+ // Target.detachedFromTarget listeners are insensitive to ordering relative to setAutoAttach; only the attach listener carries the retroactive-fire constraint.
53
+ const attachToSession = async (session, sessionId) => {
54
+ if (disposed) {
55
+ return;
56
+ }
57
+ sessions.add(session);
58
+ // Listen for responses on this session. The handler captures sessionId by closure so the consumer can identify the originating target.
59
+ session.on("Network.responseReceived", (params) => {
60
+ if (disposed) {
61
+ return;
62
+ }
63
+ options.onResponse({ headers: params.response.headers ?? {}, sessionId, url: params.response.url });
64
+ });
65
+ // Propagate the attach policy down this subtree. Without this, grandchildren of the root (iframes inside iframes, workers spawned by an OOPIF) would attach
66
+ // but their Target.attachedToTarget events would fire on the intermediate session, not the root - and we would not be listening there. Recursive setAutoAttach
67
+ // ensures every descendant session reaches our handler.
68
+ session.on("Target.attachedToTarget", (params) => { void onChildAttached(params); });
69
+ session.on("Target.detachedFromTarget", (params) => { onChildDetached(params); });
70
+ try {
71
+ await session.send("Target.setAutoAttach", { autoAttach: true, flatten: true, waitForDebuggerOnStart: false });
72
+ await session.send("Network.enable");
73
+ LOG.debug("browser:tabObserver", "Attached to session %s.", sessionId || "<root>");
74
+ }
75
+ catch (error) {
76
+ // Session may have detached between attach and our init - benign during page navigations and shutdown. Logging at debug rather than warn because the
77
+ // observer's job is to be resilient to lifecycle churn, not to alert on every churn event.
78
+ LOG.debug("browser:tabObserver", "Setup failed for session %s: %s.", sessionId || "<root>", String(error));
79
+ }
80
+ };
81
+ // Handler for Target.attachedToTarget. Resolves the freshly-attached sessionId to a CDPSession via the parent connection, applies the target-type predicate,
82
+ // and forwards to attachToSession. The filter is the consumer's policy lever; the default skips only the browser-level target.
83
+ const onChildAttached = async (params) => {
84
+ if (disposed) {
85
+ return;
86
+ }
87
+ if (!targetFilter({ type: params.targetInfo.type })) {
88
+ return;
89
+ }
90
+ const childSession = connection.session(params.sessionId);
91
+ if (!childSession) {
92
+ // Race: the child detached between the attach event and our lookup. Not actionable.
93
+ return;
94
+ }
95
+ await attachToSession(childSession, params.sessionId);
96
+ };
97
+ // Handler for Target.detachedFromTarget. Removes the detached session from tracking and clears its listeners. We do not call detach() on the session here -
98
+ // Chrome has already torn down the underlying target, and an explicit detach would race with the framework's internal cleanup. removeAllListeners is sufficient
99
+ // because we no longer want events from this session and the session object will be garbage-collected once the framework releases it.
100
+ const onChildDetached = (params) => {
101
+ if (disposed) {
102
+ return;
103
+ }
104
+ const childSession = connection.session(params.sessionId);
105
+ if (!childSession) {
106
+ return;
107
+ }
108
+ sessions.delete(childSession);
109
+ childSession.removeAllListeners("Network.responseReceived");
110
+ childSession.removeAllListeners("Target.attachedToTarget");
111
+ childSession.removeAllListeners("Target.detachedFromTarget");
112
+ LOG.debug("browser:tabObserver", "Detached from session %s.", params.sessionId);
113
+ };
114
+ // Install on the root session. The empty sessionId is the CDP convention for "root" - flattened child sessions get real sessionIds. Doing this synchronously
115
+ // before returning the handle means by the time the caller awaits the returned promise, all currently-attached children have already fired their retroactive
116
+ // Target.attachedToTarget events and been onboarded.
117
+ await attachToSession(rootSession, "");
118
+ const dispose = () => {
119
+ if (disposed) {
120
+ return;
121
+ }
122
+ disposed = true;
123
+ for (const session of sessions) {
124
+ try {
125
+ session.removeAllListeners("Network.responseReceived");
126
+ session.removeAllListeners("Target.attachedToTarget");
127
+ session.removeAllListeners("Target.detachedFromTarget");
128
+ void session.send("Network.disable").catch(() => { });
129
+ void session.detach().catch(() => { });
130
+ }
131
+ catch (_error) {
132
+ // Disposal is best-effort; a failed session teardown does not block teardown of the others. The Set iteration continues regardless.
133
+ }
134
+ }
135
+ sessions.clear();
136
+ LOG.debug("browser:tabObserver", "Tab network observer disposed.");
137
+ };
138
+ return { dispose, [Symbol.dispose]: dispose };
139
+ }
140
+ //# sourceMappingURL=tabNetworkObserver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabNetworkObserver.js","sourceRoot":"","sources":["../../src/browser/tabNetworkObserver.ts"],"names":[],"mappings":"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"}
@@ -1,16 +1,17 @@
1
1
  import { LOG, evaluateWithAbort, formatError } from "../../utils/index.js";
2
2
  import { CONFIG } from "../../config/index.js";
3
3
  import { logAvailableChannels } from "./shared.js";
4
- // Base URL for HBO Max watch page navigation and tab URL construction.
4
+ // Base URL for HBO Max watch page navigation. Used to build full watch URLs by concatenating with the relative /channel/watch/<uuid>/<uuid> path read from the
5
+ // rail. Kept as a separate constant from HBO_CHANNELS_URL so the two roles (path-concatenation base vs. landing-URL constant) stay distinct.
5
6
  const HBO_MAX_BASE_URL = "https://play.hbomax.com";
7
+ // The /channels hub URL where HBO Max surfaces all live linear channels. PrismCast lands here directly via provider.guideUrl rather than chasing brand-page links
8
+ // out of the homepage menu - HBO's site restructure (late 2025/early 2026) consolidated brand tabs into the /channels hub, deprecating the previous menu-bar
9
+ // driven navigation entirely. This is now the single landing URL for both tuning and discovery.
10
+ const HBO_CHANNELS_URL = "https://play.hbomax.com/channels";
6
11
  // Unified channel cache for HBO Max. Maps lowercased channel names (e.g., "hbo", "hbo hits") to their combined discovery and tuning data. Populated during the
7
12
  // first tune (when the strategy reads all channels from the channel rail) or the first discovery call. Both tuning (via resolveHboDirectUrl) and discovery (via
8
13
  // getCachedChannels / discoverHboChannels) read from this single cache. Cleared on browser disconnect via clearHboCache().
9
14
  const hboChannelCache = new Map();
10
- // Module-level cache for the HBO tab page URL discovered from the homepage menu bar. Cleared on browser disconnect (via clearHboCache) and inline when the
11
- // cached URL turns out to be stale (the channel rail is not found at the cached URL). Separate from the channel cache because it's a navigation target, not
12
- // channel data.
13
- let hboTabUrl = null;
14
15
  /**
15
16
  * Returns a cached HBO Max watch URL for the given channel selector, or null if no cached URL exists.
16
17
  * @param channelSelector - The channel selector string (e.g., "HBO", "HBO Hits").
@@ -32,12 +33,11 @@ function invalidateHboDirectUrl(channelSelector) {
32
33
  hboChannelCache.delete(channelSelector.toLowerCase());
33
34
  }
34
35
  /**
35
- * Clears all HBO caches: the unified channel cache and the tab URL. Called by clearChannelSelectionCaches() in the coordinator when the browser restarts, since
36
- * cached state may be stale in a new browser session.
36
+ * Clears the unified HBO channel cache. Called by clearChannelSelectionCaches() in the coordinator when the browser restarts, since cached watch URLs may be
37
+ * stale in a new browser session.
37
38
  */
38
39
  function clearHboCache() {
39
40
  hboChannelCache.clear();
40
- hboTabUrl = null;
41
41
  }
42
42
  /**
43
43
  * Derives a DiscoveredChannel array from the unified channel cache. HBO does not create alias entries (resolveHboDirectUrl does exact-match only, no
@@ -60,43 +60,15 @@ function populateHboChannelCache(rawChannels) {
60
60
  }
61
61
  }
62
62
  /**
63
- * Reads the HBO tab URL from the homepage menu bar. The HBO brand page is linked via an `a[aria-label="H B O"]` element in the top navigation. The href attribute
64
- * contains a relative path like `/channel/c0d1f27a-...` which we combine with the base URL to form the full page URL.
65
- * @param page - The Puppeteer page object, expected to be on the HBO Max homepage.
66
- * @returns The full HBO tab page URL, or null if the tab link was not found.
67
- */
68
- async function readHboTabUrl(page) {
69
- // Wait for the HBO tab link to appear in the menu bar. The homepage is a single-page application that renders the navigation dynamically after the initial HTML
70
- // shell loads. Without this wait, the evaluate call below would run against an incomplete DOM and fail to find the tab link.
71
- const HBO_TAB_SELECTOR = "a[aria-label=\"H B O\"]";
72
- try {
73
- await page.waitForSelector(HBO_TAB_SELECTOR, { timeout: 5000 });
74
- }
75
- catch {
76
- return null;
77
- }
78
- const href = await evaluateWithAbort(page, (selector) => {
79
- const tab = document.querySelector(selector);
80
- if (!tab) {
81
- return null;
82
- }
83
- return tab.getAttribute("href");
84
- }, [HBO_TAB_SELECTOR]);
85
- if (!href) {
86
- return null;
87
- }
88
- return HBO_MAX_BASE_URL + href;
89
- }
90
- /**
91
- * Reads all channels from the HBO Channels tile rail on the HBO tab page. The rail section contains tiles for each live channel, each with a backup text
92
- * `<p aria-hidden="true">` element containing the channel name and an `<a>` with href pointing to the watch page. Returns all discovered channels so the caller
63
+ * Reads all channels from the "Everything You Love From HBO" rail on the /channels hub. The rail section contains tiles for each live linear channel, each with
64
+ * a `<p aria-hidden="true">` element containing the channel name and an `<a>` whose href points to the watch page. Returns all discovered channels so the caller
93
65
  * can populate the cache in bulk.
94
- * @param page - The Puppeteer page object, expected to be on the HBO tab page.
66
+ * @param page - The Puppeteer page object, expected to be on https://play.hbomax.com/channels.
95
67
  * @returns Object with `railFound` indicating whether the rail section was present, and `channels` containing all discovered channel names and watch paths.
96
68
  */
97
69
  async function readHboChannelRail(page) {
98
- const HBO_RAIL_SELECTOR = "section[data-testid=\"hbo-page-rail-distribution-channels-us_rail\"]";
99
- // Wait for the distribution channels rail section to appear. If it doesn't appear, the tab URL may be stale or the page structure changed.
70
+ const HBO_RAIL_SELECTOR = "section[data-testid=\"channels-hub-page-everything-you-love-hbo-rail-us_rail\"]";
71
+ // Wait for the HBO channel rail section to appear. If it doesn't appear, the page structure may have changed or the user's HBO Max subscription may not be active.
100
72
  try {
101
73
  await page.waitForSelector(HBO_RAIL_SELECTOR, { timeout: CONFIG.streaming.videoTimeout });
102
74
  }
@@ -142,74 +114,23 @@ async function readHboChannelRail(page) {
142
114
  return { channels, railFound: true };
143
115
  }
144
116
  /**
145
- * HBO grid strategy: discovers the HBO channels tab URL from the homepage menu bar, navigates to the tab page, reads the live channel rail for all channel
146
- * watch URLs, and navigates to the target channel's URL. All discovered channels are cached so that subsequent tunes to any HBO channel resolve via resolveDirectUrl
147
- * without loading the tab page.
117
+ * HBO grid strategy: reads the live channel rail on the /channels hub for all channel watch URLs, then navigates to the target channel's URL. All discovered
118
+ * channels are cached so subsequent tunes resolve via resolveDirectUrl without re-reading the rail.
148
119
  *
149
- * The strategy handles three navigations per tune:
150
- * 1. Homepage (already loaded by navigateToPage) -> read menu bar for tab URL (or use cache)
151
- * 2. Tab page -> read channel rail for all watch URLs
152
- * 3. Watch page -> video playback begins
120
+ * The strategy handles two phases per tune:
121
+ * 1. Channel rail (page is already on /channels, navigated by the coordinator via provider.guideUrl) -> read all watch URLs.
122
+ * 2. Watch page -> video playback begins.
153
123
  *
154
- * When the cached tab URL is stale (rail section not found), the strategy clears the cache, navigates back to the homepage, rediscovers the tab URL, and retries.
155
- * This fallback triggers at most once per tune attempt.
156
- * @param page - The Puppeteer page object, expected to be on the HBO Max homepage.
124
+ * @param page - The Puppeteer page object, expected to be on https://play.hbomax.com/channels.
157
125
  * @param profile - The resolved site profile with a non-null channelSelector (channel name, e.g., "HBO", "HBO Hits").
158
126
  * @returns Result object with success status and optional failure reason.
159
127
  */
160
128
  async function hboGridStrategy(page, profile) {
161
129
  const channelName = profile.channelSelector;
162
- let usedCache = false;
163
- // Phase 1: Navigate to the HBO tab page. Use cached URL if available, otherwise discover it from the homepage menu bar.
164
- if (hboTabUrl) {
165
- usedCache = true;
166
- LOG.debug("tuning:hbo", "Using cached HBO tab URL: %s.", hboTabUrl);
167
- }
168
- else {
169
- const discovered = await readHboTabUrl(page);
170
- if (!discovered) {
171
- return { reason: "HBO tab not found in homepage menu bar. HBO Max subscription may not be active.", success: false };
172
- }
173
- hboTabUrl = discovered;
174
- LOG.debug("tuning:hbo", "Discovered HBO tab URL: %s.", hboTabUrl);
175
- }
176
- try {
177
- await page.goto(hboTabUrl, { timeout: CONFIG.streaming.navigationTimeout, waitUntil: "load" });
178
- }
179
- catch (error) {
180
- return { reason: "Failed to navigate to HBO tab page: " + formatError(error) + ".", success: false };
181
- }
182
- // Phase 2: Read the channel rail for all channel watch URLs.
183
- let railResult = await readHboChannelRail(page);
184
- // Fallback: if the rail was not found and we used a cached URL, the cache may be stale. Clear it, navigate back to the homepage, rediscover the tab URL, and retry.
185
- if (!railResult.railFound && usedCache) {
186
- LOG.debug("tuning:hbo", "HBO channel rail not found at cached URL. Rediscovering tab URL from homepage.");
187
- hboTabUrl = null;
188
- try {
189
- await page.goto(HBO_MAX_BASE_URL, { timeout: CONFIG.streaming.navigationTimeout, waitUntil: "load" });
190
- }
191
- catch (error) {
192
- return { reason: "Failed to navigate back to HBO Max homepage: " + formatError(error) + ".", success: false };
193
- }
194
- const rediscovered = await readHboTabUrl(page);
195
- if (!rediscovered) {
196
- return { reason: "HBO tab not found in homepage menu bar after cache invalidation.", success: false };
197
- }
198
- hboTabUrl = rediscovered;
199
- LOG.debug("tuning:hbo", "Rediscovered HBO tab URL: %s.", hboTabUrl);
200
- try {
201
- await page.goto(hboTabUrl, { timeout: CONFIG.streaming.navigationTimeout, waitUntil: "load" });
202
- }
203
- catch (error) {
204
- return { reason: "Failed to navigate to rediscovered HBO tab page: " + formatError(error) + ".", success: false };
205
- }
206
- railResult = await readHboChannelRail(page);
207
- if (!railResult.railFound) {
208
- return { reason: "HBO channel rail not found at rediscovered URL. Site structure may have changed.", success: false };
209
- }
210
- }
130
+ // Phase 1: Read the channel rail. The coordinator has already navigated the page to provider.guideUrl (the /channels hub), so we read the rail in place.
131
+ const railResult = await readHboChannelRail(page);
211
132
  if (!railResult.railFound) {
212
- return { reason: "HBO channel rail not found on tab page.", success: false };
133
+ return { reason: "HBO channel rail not found on /channels. HBO Max may have restructured the page, or the subscription may not be active.", success: false };
213
134
  }
214
135
  // Populate the unified channel cache with all discovered channels. Always repopulate rather than skipping when the cache has entries, because invalidated
215
136
  // entries (deleted by invalidateHboDirectUrl) need to be restored with fresh watch URLs from the rail.
@@ -221,12 +142,12 @@ async function hboGridStrategy(page, profile) {
221
142
  logAvailableChannels({
222
143
  availableChannels: railResult.channels.map((ch) => ch.name).sort(),
223
144
  channelName,
224
- guideUrl: "https://play.hbomax.com",
145
+ guideUrl: HBO_CHANNELS_URL,
225
146
  providerName: "HBO Max"
226
147
  });
227
148
  return { reason: "Channel " + channelName + " not found in HBO channel rail.", success: false };
228
149
  }
229
- // Phase 3: Navigate to the watch URL to start playback.
150
+ // Phase 2: Navigate to the watch URL to start playback.
230
151
  LOG.debug("tuning:hbo", "Navigating to HBO Max watch URL for %s.", channelName);
231
152
  try {
232
153
  await page.goto(watchUrl, { timeout: CONFIG.streaming.navigationTimeout, waitUntil: "load" });
@@ -249,9 +170,8 @@ async function resolveHboDirectUrlAsync(channelSelector, _page) {
249
170
  }
250
171
  /**
251
172
  * Discovers all channels from the HBO Max channel rail. Returns cached results if the unified channel cache is populated from a prior tune or discovery call.
252
- * Otherwise, extracts the HBO tab URL from the homepage menu bar, navigates to the tab page, reads all channels from the distribution channels rail, and
253
- * populates the cache (unless empty, to allow retries on transient failures or missing HBO subscription).
254
- * @param page - The Puppeteer page object, already on the HBO Max homepage (navigated by the route handler).
173
+ * Otherwise reads the rail in place - the route handler has already navigated the page to provider.guideUrl (the /channels hub).
174
+ * @param page - The Puppeteer page object, already on https://play.hbomax.com/channels (navigated by the route handler).
255
175
  * @returns Array of discovered channels.
256
176
  */
257
177
  async function discoverHboChannels(page) {
@@ -259,19 +179,7 @@ async function discoverHboChannels(page) {
259
179
  if (hboChannelCache.size > 0) {
260
180
  return buildHboDiscoveredChannels();
261
181
  }
262
- // Discover the HBO tab URL from the homepage menu bar.
263
- const tabUrl = await readHboTabUrl(page);
264
- if (!tabUrl) {
265
- return [];
266
- }
267
- // Navigate to the HBO tab page.
268
- try {
269
- await page.goto(tabUrl, { timeout: CONFIG.streaming.navigationTimeout, waitUntil: "load" });
270
- }
271
- catch {
272
- return [];
273
- }
274
- // Read the channel rail.
182
+ // Read the channel rail directly. The coordinator has already landed the page on /channels.
275
183
  const railResult = await readHboChannelRail(page);
276
184
  if (!railResult.railFound || (railResult.channels.length === 0)) {
277
185
  return [];
@@ -292,11 +200,11 @@ function getHboCachedChannels() {
292
200
  export const hboProvider = {
293
201
  discoverChannels: discoverHboChannels,
294
202
  getCachedChannels: getHboCachedChannels,
295
- guideUrl: "https://play.hbomax.com",
203
+ guideUrl: HBO_CHANNELS_URL,
296
204
  label: "HBO Max",
297
- // Profile for HBO Max live channels (play.hbomax.com). The HBO brand page contains a "Distribution Channels" rail showing all 5 live linear channels (HBO, HBO
298
- // Hits, HBO Drama, HBO Comedy, HBO Movies) as tiles. The hboGrid strategy discovers the HBO tab URL from the homepage menu bar, navigates to it, then reads the
299
- // channel rail for the watch URL matching the channelSelector name. Extends fullscreenApi for requestFullscreen() behavior inherited by the watch page.
205
+ // Profile for HBO Max live channels (play.hbomax.com/channels). The /channels hub contains an "Everything You Love From HBO" rail showing all 5 live linear
206
+ // channels (HBO, HBO Hits, HBO Drama, HBO Comedy, HBO Movies) as tiles. The hboGrid strategy reads the rail for the watch URL matching the channelSelector name
207
+ // and navigates to it. Extends fullscreenApi for requestFullscreen() behavior inherited by the watch page.
300
208
  profile: {
301
209
  category: "multiChannel",
302
210
  channelSelection: { strategy: "hboGrid" },
@@ -1 +1 @@
1
- {"version":3,"file":"hbo.js","sourceRoot":"","sources":["../../../src/browser/tuning/hbo.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,GAAG,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAE/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEnD,uEAAuE;AACvE,MAAM,gBAAgB,GAAG,yBAAyB,CAAC;AAWnD,+JAA+J;AAC/J,gKAAgK;AAChK,2HAA2H;AAC3H,MAAM,eAAe,GAAG,IAAI,GAAG,EAA2B,CAAC;AAE3D,2JAA2J;AAC3J,4JAA4J;AAC5J,gBAAgB;AAChB,IAAI,SAAS,GAAqB,IAAI,CAAC;AAEvC;;;;GAIG;AACH,SAAS,mBAAmB,CAAC,eAAuB;IAElD,MAAM,KAAK,GAAG,eAAe,CAAC,GAAG,CAAC,eAAe,CAAC,WAAW,EAAE,CAAC,CAAC;IAEjE,IAAG,KAAK,EAAE,CAAC;QAET,GAAG,CAAC,KAAK,CAAC,YAAY,EAAE,2BAA2B,EAAE,eAAe,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QAEtF,OAAO,KAAK,CAAC,QAAQ,CAAC;IACxB,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,SAAS,sBAAsB,CAAC,eAAuB;IAErD,eAAe,CAAC,MAAM,CAAC,eAAe,CAAC,WAAW,EAAE,CAAC,CAAC;AACxD,CAAC;AAED;;;GAGG;AACH,SAAS,aAAa;IAEpB,eAAe,CAAC,KAAK,EAAE,CAAC;IACxB,SAAS,GAAG,IAAI,CAAC;AACnB,CAAC;AAED;;;;GAIG;AACH,SAAS,0BAA0B;IAEjC,OAAO,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AAChI,CAAC;AAED;;;;GAIG;AACH,SAAS,uBAAuB,CAAC,WAAkD;IAEjF,KAAI,MAAM,EAAE,IAAI,WAAW,EAAE,CAAC;QAE5B,MAAM,QAAQ,GAAG,gBAAgB,GAAG,EAAE,CAAC,SAAS,CAAC;QAEjD,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,eAAe,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;QAElH,GAAG,CAAC,KAAK,CAAC,YAAY,EAAE,sCAAsC,EAAE,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACrF,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,aAAa,CAAC,IAAU;IAErC,gKAAgK;IAChK,6HAA6H;IAC7H,MAAM,gBAAgB,GAAG,yBAAyB,CAAC;IAEnD,IAAI,CAAC;QAEH,MAAM,IAAI,CAAC,eAAe,CAAC,gBAAgB,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QAEP,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC,IAAI,EAAE,CAAC,QAAgB,EAAoB,EAAE;QAEhF,MAAM,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAE7C,IAAG,CAAC,GAAG,EAAE,CAAC;YAER,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAEvB,IAAG,CAAC,IAAI,EAAE,CAAC;QAET,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,gBAAgB,GAAG,IAAI,CAAC;AACjC,CAAC;AAUD;;;;;;GAMG;AACH,KAAK,UAAU,kBAAkB,CAAC,IAAU;IAE1C,MAAM,iBAAiB,GAAG,sEAAsE,CAAC;IAEjG,2IAA2I;IAC3I,IAAI,CAAC;QAEH,MAAM,IAAI,CAAC,eAAe,CAAC,iBAAiB,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC;IAC5F,CAAC;IAAC,MAAM,CAAC;QAEP,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAC5C,CAAC;IAED,+JAA+J;IAC/J,iKAAiK;IACjK,yHAAyH;IACzH,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,QAAgB,EAAQ,EAAE;QAE7C,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC7F,CAAC,EAAE,iBAAiB,CAAC,CAAC;IAEtB,IAAI,CAAC;QAEH,MAAM,IAAI,CAAC,eAAe,CAAC,iBAAiB,GAAG,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1E,CAAC;IAAC,MAAM,CAAC;QAEP,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAC5C,CAAC;IAED,sIAAsI;IACtI,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,IAAI,EAAE,CAAC,QAAgB,EAAyC,EAAE;QAEzG,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAE9C,IAAG,CAAC,IAAI,EAAE,CAAC;YAET,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,OAAO,GAA0C,EAAE,CAAC;QAE1D,KAAI,MAAM,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YAE3D,MAAM,MAAM,GAAG,MAAM,CAAC,aAAa,CAAC,yBAAyB,CAAC,CAAC;YAE/D,IAAG,CAAC,MAAM,EAAE,CAAC;gBAEX,SAAS;YACX,CAAC;YAED,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YAE/C,IAAG,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAErB,SAAS;YACX,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;YAEzC,wIAAwI;YACxI,IAAG,IAAI,EAAE,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;gBAErC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1C,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC,EAAE,CAAC,iBAAiB,CAAC,CAAC,CAAC;IAExB,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AACvC,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,KAAK,UAAU,eAAe,CAAC,IAAU,EAAE,OAAgC;IAEzE,MAAM,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC;IAC5C,IAAI,SAAS,GAAG,KAAK,CAAC;IAEtB,wHAAwH;IACxH,IAAG,SAAS,EAAE,CAAC;QAEb,SAAS,GAAG,IAAI,CAAC;QAEjB,GAAG,CAAC,KAAK,CAAC,YAAY,EAAE,+BAA+B,EAAE,SAAS,CAAC,CAAC;IACtE,CAAC;SAAM,CAAC;QAEN,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;QAE7C,IAAG,CAAC,UAAU,EAAE,CAAC;YAEf,OAAO,EAAE,MAAM,EAAE,iFAAiF,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QACvH,CAAC;QAED,SAAS,GAAG,UAAU,CAAC;QAEvB,GAAG,CAAC,KAAK,CAAC,YAAY,EAAE,6BAA6B,EAAE,SAAS,CAAC,CAAC;IACpE,CAAC;IAED,IAAI,CAAC;QAEH,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,iBAAiB,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;IACjG,CAAC;IAAC,OAAM,KAAK,EAAE,CAAC;QAEd,OAAO,EAAE,MAAM,EAAE,sCAAsC,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACvG,CAAC;IAED,6DAA6D;IAC7D,IAAI,UAAU,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAEhD,oKAAoK;IACpK,IAAG,CAAC,UAAU,CAAC,SAAS,IAAI,SAAS,EAAE,CAAC;QAEtC,GAAG,CAAC,KAAK,CAAC,YAAY,EAAE,gFAAgF,CAAC,CAAC;QAE1G,SAAS,GAAG,IAAI,CAAC;QAEjB,IAAI,CAAC;YAEH,MAAM,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,iBAAiB,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QACxG,CAAC;QAAC,OAAM,KAAK,EAAE,CAAC;YAEd,OAAO,EAAE,MAAM,EAAE,+CAA+C,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAChH,CAAC;QAED,MAAM,YAAY,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;QAE/C,IAAG,CAAC,YAAY,EAAE,CAAC;YAEjB,OAAO,EAAE,MAAM,EAAE,kEAAkE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QACxG,CAAC;QAED,SAAS,GAAG,YAAY,CAAC;QAEzB,GAAG,CAAC,KAAK,CAAC,YAAY,EAAE,+BAA+B,EAAE,SAAS,CAAC,CAAC;QAEpE,IAAI,CAAC;YAEH,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,iBAAiB,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QACjG,CAAC;QAAC,OAAM,KAAK,EAAE,CAAC;YAEd,OAAO,EAAE,MAAM,EAAE,mDAAmD,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QACpH,CAAC;QAED,UAAU,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,CAAC;QAE5C,IAAG,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;YAEzB,OAAO,EAAE,MAAM,EAAE,kFAAkF,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QACxH,CAAC;IACH,CAAC;IAED,IAAG,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;QAEzB,OAAO,EAAE,MAAM,EAAE,yCAAyC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC/E,CAAC;IAED,0JAA0J;IAC1J,uGAAuG;IACvG,uBAAuB,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAE7C,uDAAuD;IACvD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC;IAElD,IAAG,CAAC,QAAQ,EAAE,CAAC;QAEb,oHAAoH;QACpH,oBAAoB,CAAC;YAEnB,iBAAiB,EAAE,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE;YAClE,WAAW;YACX,QAAQ,EAAE,yBAAyB;YACnC,YAAY,EAAE,SAAS;SACxB,CAAC,CAAC;QAEH,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,iCAAiC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAClG,CAAC;IAED,wDAAwD;IACxD,GAAG,CAAC,KAAK,CAAC,YAAY,EAAE,yCAAyC,EAAE,WAAW,CAAC,CAAC;IAEhF,IAAI,CAAC;QAEH,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,iBAAiB,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;IAChG,CAAC;IAAC,OAAM,KAAK,EAAE,CAAC;QAEd,OAAO,EAAE,MAAM,EAAE,4CAA4C,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC7G,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC3B,CAAC;AAED;;;;;;GAMG;AACH,+FAA+F;AAC/F,KAAK,UAAU,wBAAwB,CAAC,eAAuB,EAAE,KAAW;IAE1E,OAAO,mBAAmB,CAAC,eAAe,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,mBAAmB,CAAC,IAAU;IAE3C,sDAAsD;IACtD,IAAG,eAAe,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;QAE5B,OAAO,0BAA0B,EAAE,CAAC;IACtC,CAAC;IAED,uDAAuD;IACvD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;IAEzC,IAAG,CAAC,MAAM,EAAE,CAAC;QAEX,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,gCAAgC;IAChC,IAAI,CAAC;QAEH,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,iBAAiB,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9F,CAAC;IAAC,MAAM,CAAC;QAEP,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,yBAAyB;IACzB,MAAM,UAAU,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAElD,IAAG,CAAC,UAAU,CAAC,SAAS,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;QAE/D,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,uBAAuB,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAE7C,OAAO,0BAA0B,EAAE,CAAC;AACtC,CAAC;AAED;;;GAGG;AACH,SAAS,oBAAoB;IAE3B,IAAG,eAAe,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QAE9B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,0BAA0B,EAAE,CAAC;AACtC,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAmB;IAEzC,gBAAgB,EAAE,mBAAmB;IACrC,iBAAiB,EAAE,oBAAoB;IACvC,QAAQ,EAAE,yBAAyB;IACnC,KAAK,EAAE,SAAS;IAEhB,+JAA+J;IAC/J,gKAAgK;IAChK,wJAAwJ;IACxJ,OAAO,EAAE;QAEP,QAAQ,EAAE,cAAc;QACxB,gBAAgB,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE;QACzC,WAAW,EAAE,2GAA2G;QACxH,OAAO,EAAE,eAAe;QACxB,OAAO,EAAE,yCAAyC;KACnD;IACD,WAAW,EAAE,QAAQ;IACrB,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE;QAER,UAAU,EAAE,aAAa;QACzB,OAAO,EAAE,eAAe;QACxB,mBAAmB,EAAE,sBAAsB;QAC3C,gBAAgB,EAAE,wBAAwB;KAC3C;IACD,YAAY,EAAE,SAAS;CACxB,CAAC"}
1
+ {"version":3,"file":"hbo.js","sourceRoot":"","sources":["../../../src/browser/tuning/hbo.ts"],"names":[],"mappings":"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"}
@@ -554,6 +554,63 @@ async function releaseHeldPlaylist(page) {
554
554
  }
555
555
  });
556
556
  }
557
+ /**
558
+ * Hulu surfaces a "Who's Watching?" profile-selector modal when an account has more than one profile, and re-prompts intermittently across sessions. The modal
559
+ * blocks all guide and channel UI until a profile is chosen, so PrismCast's tuning logic (#CHANNELS click, guide-grid binary search) never gets a chance to run.
560
+ * This helper detects the modal and clicks the first profile, restoring the normal /live page flow. Both the tuning path (guideGridStrategy) and the discovery
561
+ * path (discoverHuluChannels) call this immediately after navigation so they share one source of truth for the profile-selector workaround.
562
+ *
563
+ * Selection policy: always the first profile. We do not expose a configurable name because the typical PrismCast deployment has a single primary profile, and an
564
+ * operator who wants a different one can reorder profiles in Hulu's account settings. The selected profile name is captured in the audit log so the operator
565
+ * always knows which one was picked.
566
+ *
567
+ * The probe is cheap when no modal is present (one DOM evaluate) and best-effort when it is: any failure between detection and post-click navigation is logged
568
+ * and returns silently. The downstream #CHANNELS-wait timeout becomes the failure signal of last resort, so the existing error path stays intact.
569
+ * @param page - The Puppeteer page object.
570
+ */
571
+ async function handleProfileSelectorIfPresent(page) {
572
+ const probe = await page.evaluate(() => {
573
+ const modal = document.querySelector("[data-testid=\"ProfileSelectorModal\"]");
574
+ if (!modal) {
575
+ return { present: false };
576
+ }
577
+ const tile = modal.querySelector("a.ProfileList__profile-item--modal");
578
+ if (!tile) {
579
+ return { present: true };
580
+ }
581
+ const rect = tile.getBoundingClientRect();
582
+ const ariaLabel = tile.getAttribute("aria-label") ?? "";
583
+ // "Switch profile to <name>" is the format Hulu emits today; we strip the prefix to surface the name in the log. A future relabel falls through to the
584
+ // literal "(unnamed)" rather than failing, so a Hulu UX rename never blocks tuning - it only loses the friendly name in the audit line.
585
+ const match = (/^Switch profile to (.+)$/).exec(ariaLabel);
586
+ return {
587
+ name: match ? match[1] : "(unnamed)",
588
+ present: true,
589
+ tile: { x: rect.left + (rect.width / 2), y: rect.top + (rect.height / 2) }
590
+ };
591
+ });
592
+ if (!probe.present) {
593
+ return;
594
+ }
595
+ if (!probe.tile) {
596
+ LOG.warn("Hulu prompted for profile selection but no profile tile was found. Tuning may fail; sign into Hulu in this browser to investigate.");
597
+ return;
598
+ }
599
+ LOG.info("Hulu prompted for profile selection. Defaulting to the first profile, \"%s\".", probe.name ?? "(unnamed)");
600
+ try {
601
+ // Coordinate-based click. Synthetic element.click() inside page.evaluate() does not reliably reach React's handlers on Hulu's SPA; the project's tuning code
602
+ // standardizes on page.mouse.click() with computed centers for this reason.
603
+ await page.mouse.click(probe.tile.x, probe.tile.y);
604
+ // Wait for the URL to leave /profiles, indicating the navigation triggered by the profile click has begun. The caller's downstream wait for #CHANNELS picks
605
+ // up the rest of the page settling; we only need to confirm the picker is out of the way before yielding back. The 5s timeout is a "post-click settle"
606
+ // budget - longer than a synchronous URL update needs, shorter than CONFIG.streaming.videoTimeout (which is sized for page-render waits); a real failure
607
+ // bubbles up via the catch and the downstream #CHANNELS-wait becomes the final timeout.
608
+ await page.waitForFunction(() => !window.location.pathname.startsWith("/profiles"), { timeout: 5000 });
609
+ }
610
+ catch (error) {
611
+ LOG.warn("Hulu profile selection click did not complete cleanly: %s.", formatError(error));
612
+ }
613
+ }
557
614
  /**
558
615
  * 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
559
616
  * 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
@@ -566,13 +623,14 @@ async function releaseHeldPlaylist(page) {
566
623
  * 3. Linear scan fallback - safety net for raw call sign searches or any channel the binary search cannot find (~2.4 seconds)
567
624
  *
568
625
  * The selection process:
569
- * 1. If listSelector is provided, click the tab/button to reveal the channel list (e.g., a "Channels" tab)
570
- * 2. Wait for the channel grid rows to render in the DOM
571
- * 3. Check the unified cache for a row number direct-scroll shortcut
572
- * 4. Binary search: scroll to the midpoint row, read rendered channels (caching row numbers), check for exact match or infer local affiliate
573
- * 5. If binary search fails, linear scan from top to bottom as a universal fallback
574
- * 6. Click the on-now program cell (`.LiveGuideProgram--first`) in the target channel's row to open the playback overlay
575
- * 7. If playSelector is provided, wait for and click the play button to start live playback
626
+ * 1. Clear any "Who's Watching?" profile-selector modal that intercepts before guide interaction (see handleProfileSelectorIfPresent)
627
+ * 2. If listSelector is provided, click the tab/button to reveal the channel list (e.g., a "Channels" tab)
628
+ * 3. Wait for the channel grid rows to render in the DOM
629
+ * 4. Check the unified cache for a row number direct-scroll shortcut
630
+ * 5. Binary search: scroll to the midpoint row, read rendered channels (caching row numbers), check for exact match or infer local affiliate
631
+ * 6. If binary search fails, linear scan from top to bottom as a universal fallback
632
+ * 7. Click the on-now program cell (`.LiveGuideProgram--first`) in the target channel's row to open the playback overlay
633
+ * 8. If playSelector is provided, wait for and click the play button to start live playback
576
634
  * @param page - The Puppeteer page object.
577
635
  * @param profile - The resolved site profile with a non-null channelSelector (channel name) and channelSelection config.
578
636
  * @returns Result object with success status and optional failure reason.
@@ -580,6 +638,9 @@ async function releaseHeldPlaylist(page) {
580
638
  async function guideGridStrategy(page, profile) {
581
639
  const { channelSelection, channelSelector: channelName } = profile;
582
640
  const { listSelector, playSelector } = channelSelection;
641
+ // Clear the "Who's Watching?" profile-selector modal before any guide interaction. Hulu prompts for profile selection intermittently across sessions; without
642
+ // this step, all downstream selectors (#CHANNELS, live-guide-row) fail because the picker overlay blocks them.
643
+ await handleProfileSelectorIfPresent(page);
583
644
  // 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
584
645
  // 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
585
646
  // guide remains hidden and the virtualizer never populates rows. We use $eval for the click because overlapping elements (spinners, overlays) can intercept
@@ -1357,6 +1418,8 @@ async function discoverHuluChannels(page) {
1357
1418
  catch {
1358
1419
  return [];
1359
1420
  }
1421
+ // Clear the "Who's Watching?" profile-selector modal before any guide interaction (same logic guideGridStrategy uses on the tuning path).
1422
+ await handleProfileSelectorIfPresent(page);
1360
1423
  // Click the Channels tab to reveal the channel list and trigger full API expansion. Matches the tuning path's retry logic - if guide rows don't appear after
1361
1424
  // the first tab click, retry once with a longer delay in case the first click fired during a transitional state before the guide was fully interactive.
1362
1425
  const listSelector = "#CHANNELS";