prismcast 1.10.2 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (693) hide show
  1. package/dist/app.d.ts +6 -3
  2. package/dist/app.d.ts.map +1 -0
  3. package/dist/app.js +140 -86
  4. package/dist/app.js.map +1 -1
  5. package/dist/browser/blockedPage.d.ts +80 -0
  6. package/dist/browser/blockedPage.d.ts.map +1 -0
  7. package/dist/browser/blockedPage.js +209 -0
  8. package/dist/browser/blockedPage.js.map +1 -0
  9. package/dist/browser/browserSupervisor.d.ts +80 -0
  10. package/dist/browser/browserSupervisor.d.ts.map +1 -0
  11. package/dist/browser/browserSupervisor.js +186 -0
  12. package/dist/browser/browserSupervisor.js.map +1 -0
  13. package/dist/browser/cdp.d.ts +1 -0
  14. package/dist/browser/cdp.d.ts.map +1 -0
  15. package/dist/browser/cdp.js +6 -5
  16. package/dist/browser/cdp.js.map +1 -1
  17. package/dist/browser/channelSelection.d.ts +45 -14
  18. package/dist/browser/channelSelection.d.ts.map +1 -0
  19. package/dist/browser/channelSelection.js +81 -18
  20. package/dist/browser/channelSelection.js.map +1 -1
  21. package/dist/browser/consent.d.ts +152 -0
  22. package/dist/browser/consent.d.ts.map +1 -0
  23. package/dist/browser/consent.js +418 -0
  24. package/dist/browser/consent.js.map +1 -0
  25. package/dist/browser/display.d.ts +1 -0
  26. package/dist/browser/display.d.ts.map +1 -0
  27. package/dist/browser/hlsPlaylistObserver.d.ts +55 -0
  28. package/dist/browser/hlsPlaylistObserver.d.ts.map +1 -0
  29. package/dist/browser/hlsPlaylistObserver.js +132 -0
  30. package/dist/browser/hlsPlaylistObserver.js.map +1 -0
  31. package/dist/browser/index.d.ts +67 -40
  32. package/dist/browser/index.d.ts.map +1 -0
  33. package/dist/browser/index.js +508 -300
  34. package/dist/browser/index.js.map +1 -1
  35. package/dist/browser/launchGovernor.d.ts +89 -0
  36. package/dist/browser/launchGovernor.d.ts.map +1 -0
  37. package/dist/browser/launchGovernor.js +96 -0
  38. package/dist/browser/launchGovernor.js.map +1 -0
  39. package/dist/browser/login.d.ts +10 -2
  40. package/dist/browser/login.d.ts.map +1 -0
  41. package/dist/browser/login.js +36 -5
  42. package/dist/browser/login.js.map +1 -1
  43. package/dist/browser/manifestInterceptor.d.ts +102 -49
  44. package/dist/browser/manifestInterceptor.d.ts.map +1 -0
  45. package/dist/browser/manifestInterceptor.js +219 -237
  46. package/dist/browser/manifestInterceptor.js.map +1 -1
  47. package/dist/browser/pageStaleness.d.ts +30 -0
  48. package/dist/browser/pageStaleness.d.ts.map +1 -0
  49. package/dist/browser/pageStaleness.js +76 -0
  50. package/dist/browser/pageStaleness.js.map +1 -0
  51. package/dist/browser/precaching.d.ts +95 -3
  52. package/dist/browser/precaching.d.ts.map +1 -0
  53. package/dist/browser/precaching.js +499 -65
  54. package/dist/browser/precaching.js.map +1 -1
  55. package/dist/browser/tabNetworkObserver.d.ts +49 -0
  56. package/dist/browser/tabNetworkObserver.d.ts.map +1 -0
  57. package/dist/browser/tabNetworkObserver.js +144 -0
  58. package/dist/browser/tabNetworkObserver.js.map +1 -0
  59. package/dist/browser/tuning/cache.d.ts +61 -0
  60. package/dist/browser/tuning/cache.d.ts.map +1 -0
  61. package/dist/browser/tuning/cache.js +62 -0
  62. package/dist/browser/tuning/cache.js.map +1 -0
  63. package/dist/browser/tuning/comcastPolymer.d.ts +3 -2
  64. package/dist/browser/tuning/comcastPolymer.d.ts.map +1 -0
  65. package/dist/browser/tuning/comcastPolymer.js +79 -102
  66. package/dist/browser/tuning/comcastPolymer.js.map +1 -1
  67. package/dist/browser/tuning/cox.d.ts +1 -0
  68. package/dist/browser/tuning/cox.d.ts.map +1 -0
  69. package/dist/browser/tuning/directv.d.ts +1 -0
  70. package/dist/browser/tuning/directv.d.ts.map +1 -0
  71. package/dist/browser/tuning/directv.js +52 -46
  72. package/dist/browser/tuning/directv.js.map +1 -1
  73. package/dist/browser/tuning/fox.d.ts +1 -0
  74. package/dist/browser/tuning/fox.d.ts.map +1 -0
  75. package/dist/browser/tuning/fox.js +13 -12
  76. package/dist/browser/tuning/fox.js.map +1 -1
  77. package/dist/browser/tuning/gridSearch.d.ts +68 -0
  78. package/dist/browser/tuning/gridSearch.d.ts.map +1 -0
  79. package/dist/browser/tuning/gridSearch.js +85 -0
  80. package/dist/browser/tuning/gridSearch.js.map +1 -0
  81. package/dist/browser/tuning/hbo.d.ts +1 -0
  82. package/dist/browser/tuning/hbo.d.ts.map +1 -0
  83. package/dist/browser/tuning/hbo.js +73 -45
  84. package/dist/browser/tuning/hbo.js.map +1 -1
  85. package/dist/browser/tuning/hulu.d.ts +1 -0
  86. package/dist/browser/tuning/hulu.d.ts.map +1 -0
  87. package/dist/browser/tuning/hulu.js +128 -173
  88. package/dist/browser/tuning/hulu.js.map +1 -1
  89. package/dist/browser/tuning/shared.d.ts +75 -5
  90. package/dist/browser/tuning/shared.d.ts.map +1 -0
  91. package/dist/browser/tuning/shared.js +216 -6
  92. package/dist/browser/tuning/shared.js.map +1 -1
  93. package/dist/browser/tuning/sling.d.ts +1 -0
  94. package/dist/browser/tuning/sling.d.ts.map +1 -0
  95. package/dist/browser/tuning/sling.js +105 -126
  96. package/dist/browser/tuning/sling.js.map +1 -1
  97. package/dist/browser/tuning/spectrum.d.ts +1 -0
  98. package/dist/browser/tuning/spectrum.d.ts.map +1 -0
  99. package/dist/browser/tuning/spectrum.js +80 -125
  100. package/dist/browser/tuning/spectrum.js.map +1 -1
  101. package/dist/browser/tuning/thumbnailRow.d.ts +1 -0
  102. package/dist/browser/tuning/thumbnailRow.d.ts.map +1 -0
  103. package/dist/browser/tuning/thumbnailRow.js +8 -2
  104. package/dist/browser/tuning/thumbnailRow.js.map +1 -1
  105. package/dist/browser/tuning/tileClick.d.ts +1 -0
  106. package/dist/browser/tuning/tileClick.d.ts.map +1 -0
  107. package/dist/browser/tuning/tileClick.js +3 -2
  108. package/dist/browser/tuning/tileClick.js.map +1 -1
  109. package/dist/browser/tuning/xfinity.d.ts +1 -0
  110. package/dist/browser/tuning/xfinity.d.ts.map +1 -0
  111. package/dist/browser/tuning/youtubeTv.d.ts +1 -0
  112. package/dist/browser/tuning/youtubeTv.d.ts.map +1 -0
  113. package/dist/browser/tuning/youtubeTv.js +74 -127
  114. package/dist/browser/tuning/youtubeTv.js.map +1 -1
  115. package/dist/browser/video.d.ts +54 -14
  116. package/dist/browser/video.d.ts.map +1 -0
  117. package/dist/browser/video.js +296 -138
  118. package/dist/browser/video.js.map +1 -1
  119. package/dist/channels/index.d.ts +1 -0
  120. package/dist/channels/index.d.ts.map +1 -0
  121. package/dist/channels/index.js +12 -9
  122. package/dist/channels/index.js.map +1 -1
  123. package/dist/config/channelForm.d.ts +15 -1
  124. package/dist/config/channelForm.d.ts.map +1 -0
  125. package/dist/config/channelForm.js +28 -12
  126. package/dist/config/channelForm.js.map +1 -1
  127. package/dist/config/consistencyProbe.d.ts +1 -0
  128. package/dist/config/consistencyProbe.d.ts.map +1 -0
  129. package/dist/config/consistencyProbe.js +27 -21
  130. package/dist/config/consistencyProbe.js.map +1 -1
  131. package/dist/config/health.d.ts +41 -8
  132. package/dist/config/health.d.ts.map +1 -0
  133. package/dist/config/health.js +189 -38
  134. package/dist/config/health.js.map +1 -1
  135. package/dist/config/index.d.ts +55 -3
  136. package/dist/config/index.d.ts.map +1 -0
  137. package/dist/config/index.js +320 -69
  138. package/dist/config/index.js.map +1 -1
  139. package/dist/config/paths.d.ts +10 -9
  140. package/dist/config/paths.d.ts.map +1 -0
  141. package/dist/config/paths.js +11 -11
  142. package/dist/config/paths.js.map +1 -1
  143. package/dist/config/persistence.context.d.ts +1 -0
  144. package/dist/config/persistence.context.d.ts.map +1 -0
  145. package/dist/config/persistence.d.ts +13 -10
  146. package/dist/config/persistence.d.ts.map +1 -0
  147. package/dist/config/persistence.js +40 -21
  148. package/dist/config/persistence.js.map +1 -1
  149. package/dist/config/presets.d.ts +1 -0
  150. package/dist/config/presets.d.ts.map +1 -0
  151. package/dist/config/profiles.d.ts +22 -15
  152. package/dist/config/profiles.d.ts.map +1 -0
  153. package/dist/config/profiles.js +81 -47
  154. package/dist/config/profiles.js.map +1 -1
  155. package/dist/config/providerLineups.d.ts +47 -0
  156. package/dist/config/providerLineups.d.ts.map +1 -0
  157. package/dist/config/providerLineups.js +137 -0
  158. package/dist/config/providerLineups.js.map +1 -0
  159. package/dist/config/reactivity.d.ts +84 -0
  160. package/dist/config/reactivity.d.ts.map +1 -0
  161. package/dist/config/reactivity.js +226 -0
  162. package/dist/config/reactivity.js.map +1 -0
  163. package/dist/config/servicePacks.d.ts +18 -2
  164. package/dist/config/servicePacks.d.ts.map +1 -0
  165. package/dist/config/servicePacks.js +53 -11
  166. package/dist/config/servicePacks.js.map +1 -1
  167. package/dist/config/services.d.ts +21 -15
  168. package/dist/config/services.d.ts.map +1 -0
  169. package/dist/config/services.js +35 -24
  170. package/dist/config/services.js.map +1 -1
  171. package/dist/config/sites.d.ts +6 -4
  172. package/dist/config/sites.d.ts.map +1 -0
  173. package/dist/config/sites.js +15 -11
  174. package/dist/config/sites.js.map +1 -1
  175. package/dist/config/userChannels.d.ts +129 -30
  176. package/dist/config/userChannels.d.ts.map +1 -0
  177. package/dist/config/userChannels.js +333 -68
  178. package/dist/config/userChannels.js.map +1 -1
  179. package/dist/config/userConfig.d.ts +14 -9
  180. package/dist/config/userConfig.d.ts.map +1 -0
  181. package/dist/config/userConfig.js +87 -25
  182. package/dist/config/userConfig.js.map +1 -1
  183. package/dist/config/userProfiles.d.ts +10 -6
  184. package/dist/config/userProfiles.d.ts.map +1 -0
  185. package/dist/config/userProfiles.js +64 -39
  186. package/dist/config/userProfiles.js.map +1 -1
  187. package/dist/hdhr/channelMap.d.ts +1 -0
  188. package/dist/hdhr/channelMap.d.ts.map +1 -0
  189. package/dist/hdhr/deviceId.d.ts +1 -0
  190. package/dist/hdhr/deviceId.d.ts.map +1 -0
  191. package/dist/hdhr/discover.d.ts +1 -0
  192. package/dist/hdhr/discover.d.ts.map +1 -0
  193. package/dist/hdhr/discover.js +48 -47
  194. package/dist/hdhr/discover.js.map +1 -1
  195. package/dist/hdhr/getHandlers.d.ts +29 -0
  196. package/dist/hdhr/getHandlers.d.ts.map +1 -0
  197. package/dist/hdhr/getHandlers.js +196 -0
  198. package/dist/hdhr/getHandlers.js.map +1 -0
  199. package/dist/hdhr/identity.d.ts +36 -0
  200. package/dist/hdhr/identity.d.ts.map +1 -0
  201. package/dist/hdhr/identity.js +51 -0
  202. package/dist/hdhr/identity.js.map +1 -0
  203. package/dist/hdhr/index.d.ts +22 -4
  204. package/dist/hdhr/index.d.ts.map +1 -0
  205. package/dist/hdhr/index.js +229 -54
  206. package/dist/hdhr/index.js.map +1 -1
  207. package/dist/hdhr/protocol.d.ts +75 -0
  208. package/dist/hdhr/protocol.d.ts.map +1 -0
  209. package/dist/hdhr/protocol.js +264 -0
  210. package/dist/hdhr/protocol.js.map +1 -0
  211. package/dist/hdhr/tunerState.d.ts +24 -0
  212. package/dist/hdhr/tunerState.d.ts.map +1 -0
  213. package/dist/hdhr/tunerState.js +67 -0
  214. package/dist/hdhr/tunerState.js.map +1 -0
  215. package/dist/hdhr/udp.d.ts +43 -0
  216. package/dist/hdhr/udp.d.ts.map +1 -0
  217. package/dist/hdhr/udp.js +277 -0
  218. package/dist/hdhr/udp.js.map +1 -0
  219. package/dist/identity.d.ts +1 -0
  220. package/dist/identity.d.ts.map +1 -0
  221. package/dist/identity.js +13 -11
  222. package/dist/identity.js.map +1 -1
  223. package/dist/index.d.ts +1 -0
  224. package/dist/index.d.ts.map +1 -0
  225. package/dist/index.js +11 -12
  226. package/dist/index.js.map +1 -1
  227. package/dist/native/codecInference.d.ts +1 -0
  228. package/dist/native/codecInference.d.ts.map +1 -0
  229. package/dist/native/codecInference.js +50 -5
  230. package/dist/native/codecInference.js.map +1 -1
  231. package/dist/native/decrypt.d.ts +6 -2
  232. package/dist/native/decrypt.d.ts.map +1 -0
  233. package/dist/native/decrypt.js +15 -8
  234. package/dist/native/decrypt.js.map +1 -1
  235. package/dist/native/index.d.ts +40 -17
  236. package/dist/native/index.d.ts.map +1 -0
  237. package/dist/native/index.js +281 -120
  238. package/dist/native/index.js.map +1 -1
  239. package/dist/native/probe.d.ts +114 -18
  240. package/dist/native/probe.d.ts.map +1 -0
  241. package/dist/native/probe.js +452 -115
  242. package/dist/native/probe.js.map +1 -1
  243. package/dist/native/proxy.d.ts +57 -5
  244. package/dist/native/proxy.d.ts.map +1 -0
  245. package/dist/native/proxy.js +604 -170
  246. package/dist/native/proxy.js.map +1 -1
  247. package/dist/native/tokenExpiry.d.ts +1 -0
  248. package/dist/native/tokenExpiry.d.ts.map +1 -0
  249. package/dist/native/tokenExpiry.js +1 -1
  250. package/dist/routes/assets.d.ts +1 -0
  251. package/dist/routes/assets.d.ts.map +1 -0
  252. package/dist/routes/assets.js +4 -2
  253. package/dist/routes/assets.js.map +1 -1
  254. package/dist/routes/auth.d.ts +1 -0
  255. package/dist/routes/auth.d.ts.map +1 -0
  256. package/dist/routes/auth.js +0 -1
  257. package/dist/routes/auth.js.map +1 -1
  258. package/dist/routes/cdp.d.ts +5 -5
  259. package/dist/routes/cdp.d.ts.map +1 -0
  260. package/dist/routes/cdp.js +59 -20
  261. package/dist/routes/cdp.js.map +1 -1
  262. package/dist/routes/channels.d.ts +1 -0
  263. package/dist/routes/channels.d.ts.map +1 -0
  264. package/dist/routes/channels.js +2 -1
  265. package/dist/routes/channels.js.map +1 -1
  266. package/dist/routes/clientActions.d.ts +98 -0
  267. package/dist/routes/clientActions.d.ts.map +1 -0
  268. package/dist/routes/clientActions.js +113 -0
  269. package/dist/routes/clientActions.js.map +1 -0
  270. package/dist/routes/components.d.ts +5 -3
  271. package/dist/routes/components.d.ts.map +1 -0
  272. package/dist/routes/components.js +36 -39
  273. package/dist/routes/components.js.map +1 -1
  274. package/dist/routes/config/channels/endpoints/browse.d.ts +1 -0
  275. package/dist/routes/config/channels/endpoints/browse.d.ts.map +1 -0
  276. package/dist/routes/config/channels/endpoints/browse.js +23 -10
  277. package/dist/routes/config/channels/endpoints/browse.js.map +1 -1
  278. package/dist/routes/config/channels/endpoints/bulk.d.ts +1 -0
  279. package/dist/routes/config/channels/endpoints/bulk.d.ts.map +1 -0
  280. package/dist/routes/config/channels/endpoints/bulk.js +5 -3
  281. package/dist/routes/config/channels/endpoints/bulk.js.map +1 -1
  282. package/dist/routes/config/channels/endpoints/crud.d.ts +1 -0
  283. package/dist/routes/config/channels/endpoints/crud.d.ts.map +1 -0
  284. package/dist/routes/config/channels/endpoints/crud.js +85 -93
  285. package/dist/routes/config/channels/endpoints/crud.js.map +1 -1
  286. package/dist/routes/config/channels/endpoints/importExport.d.ts +1 -0
  287. package/dist/routes/config/channels/endpoints/importExport.d.ts.map +1 -0
  288. package/dist/routes/config/channels/endpoints/importExport.js +6 -3
  289. package/dist/routes/config/channels/endpoints/importExport.js.map +1 -1
  290. package/dist/routes/config/channels/endpoints/predefined.d.ts +1 -0
  291. package/dist/routes/config/channels/endpoints/predefined.d.ts.map +1 -0
  292. package/dist/routes/config/channels/endpoints/prefs.d.ts +1 -0
  293. package/dist/routes/config/channels/endpoints/prefs.d.ts.map +1 -0
  294. package/dist/routes/config/channels/endpoints/prefs.js +2 -1
  295. package/dist/routes/config/channels/endpoints/prefs.js.map +1 -1
  296. package/dist/routes/config/channels/endpoints/service.d.ts +2 -1
  297. package/dist/routes/config/channels/endpoints/service.d.ts.map +1 -0
  298. package/dist/routes/config/channels/endpoints/service.js +3 -1
  299. package/dist/routes/config/channels/endpoints/service.js.map +1 -1
  300. package/dist/routes/config/channels/endpoints/tags.d.ts +1 -0
  301. package/dist/routes/config/channels/endpoints/tags.d.ts.map +1 -0
  302. package/dist/routes/config/channels/endpoints/tags.js +13 -7
  303. package/dist/routes/config/channels/endpoints/tags.js.map +1 -1
  304. package/dist/routes/config/channels/healthBridge.d.ts +24 -0
  305. package/dist/routes/config/channels/healthBridge.d.ts.map +1 -0
  306. package/dist/routes/config/channels/healthBridge.js +71 -0
  307. package/dist/routes/config/channels/healthBridge.js.map +1 -0
  308. package/dist/routes/config/channels/http/handler.d.ts +1 -0
  309. package/dist/routes/config/channels/http/handler.d.ts.map +1 -0
  310. package/dist/routes/config/channels/http/playlistHint.d.ts +2 -1
  311. package/dist/routes/config/channels/http/playlistHint.d.ts.map +1 -0
  312. package/dist/routes/config/channels/http/playlistHint.js +16 -2
  313. package/dist/routes/config/channels/http/playlistHint.js.map +1 -1
  314. package/dist/routes/config/channels/http/serviceWarning.d.ts +4 -2
  315. package/dist/routes/config/channels/http/serviceWarning.d.ts.map +1 -0
  316. package/dist/routes/config/channels/http/serviceWarning.js +3 -2
  317. package/dist/routes/config/channels/http/serviceWarning.js.map +1 -1
  318. package/dist/routes/config/channels/index.d.ts +1 -0
  319. package/dist/routes/config/channels/index.d.ts.map +1 -0
  320. package/dist/routes/config/channels/setup.d.ts +1 -0
  321. package/dist/routes/config/channels/setup.d.ts.map +1 -0
  322. package/dist/routes/config/channels/table.d.ts +3 -3
  323. package/dist/routes/config/channels/table.d.ts.map +1 -0
  324. package/dist/routes/config/channels/table.js +148 -107
  325. package/dist/routes/config/channels/table.js.map +1 -1
  326. package/dist/routes/config/http/envelope.d.ts +1 -0
  327. package/dist/routes/config/http/envelope.d.ts.map +1 -0
  328. package/dist/routes/config/index.d.ts +35 -3
  329. package/dist/routes/config/index.d.ts.map +1 -0
  330. package/dist/routes/config/index.js +51 -2
  331. package/dist/routes/config/index.js.map +1 -1
  332. package/dist/routes/config/services.d.ts +5 -4
  333. package/dist/routes/config/services.d.ts.map +1 -0
  334. package/dist/routes/config/services.js +57 -43
  335. package/dist/routes/config/services.js.map +1 -1
  336. package/dist/routes/config/settings.d.ts +1 -0
  337. package/dist/routes/config/settings.d.ts.map +1 -0
  338. package/dist/routes/config/settings.js +98 -49
  339. package/dist/routes/config/settings.js.map +1 -1
  340. package/dist/routes/debug.d.ts +1 -0
  341. package/dist/routes/debug.d.ts.map +1 -0
  342. package/dist/routes/debug.js +277 -139
  343. package/dist/routes/debug.js.map +1 -1
  344. package/dist/routes/health.d.ts +39 -1
  345. package/dist/routes/health.d.ts.map +1 -0
  346. package/dist/routes/health.js +33 -27
  347. package/dist/routes/health.js.map +1 -1
  348. package/dist/routes/hls.d.ts +1 -0
  349. package/dist/routes/hls.d.ts.map +1 -0
  350. package/dist/routes/icons.d.ts +1 -0
  351. package/dist/routes/icons.d.ts.map +1 -0
  352. package/dist/routes/icons.js +1 -1
  353. package/dist/routes/index.d.ts +1 -0
  354. package/dist/routes/index.d.ts.map +1 -0
  355. package/dist/routes/logs.d.ts +1 -0
  356. package/dist/routes/logs.d.ts.map +1 -0
  357. package/dist/routes/logs.js +14 -4
  358. package/dist/routes/logs.js.map +1 -1
  359. package/dist/routes/mpegts.d.ts +1 -0
  360. package/dist/routes/mpegts.d.ts.map +1 -0
  361. package/dist/routes/play.d.ts +1 -0
  362. package/dist/routes/play.d.ts.map +1 -0
  363. package/dist/routes/playlist.d.ts +1 -0
  364. package/dist/routes/playlist.d.ts.map +1 -0
  365. package/dist/routes/playlist.js +7 -2
  366. package/dist/routes/playlist.js.map +1 -1
  367. package/dist/routes/root/content.d.ts +8 -5
  368. package/dist/routes/root/content.d.ts.map +1 -0
  369. package/dist/routes/root/content.js +29 -26
  370. package/dist/routes/root/content.js.map +1 -1
  371. package/dist/routes/root/index.d.ts +2 -1
  372. package/dist/routes/root/index.d.ts.map +1 -0
  373. package/dist/routes/root/index.js +26 -21
  374. package/dist/routes/root/index.js.map +1 -1
  375. package/dist/routes/root/scripts/channels.d.ts +6 -0
  376. package/dist/routes/root/scripts/channels.d.ts.map +1 -0
  377. package/dist/routes/root/scripts/channels.js +197 -118
  378. package/dist/routes/root/scripts/channels.js.map +1 -1
  379. package/dist/routes/root/scripts/clientEscape.d.ts +22 -0
  380. package/dist/routes/root/scripts/clientEscape.d.ts.map +1 -0
  381. package/dist/routes/root/scripts/clientEscape.js +41 -0
  382. package/dist/routes/root/scripts/clientEscape.js.map +1 -0
  383. package/dist/routes/root/scripts/clientUrl.d.ts +23 -0
  384. package/dist/routes/root/scripts/clientUrl.d.ts.map +1 -0
  385. package/dist/routes/root/scripts/clientUrl.js +44 -0
  386. package/dist/routes/root/scripts/clientUrl.js.map +1 -0
  387. package/dist/routes/root/scripts/config.d.ts +6 -0
  388. package/dist/routes/root/scripts/config.d.ts.map +1 -0
  389. package/dist/routes/root/scripts/config.js +110 -24
  390. package/dist/routes/root/scripts/config.js.map +1 -1
  391. package/dist/routes/root/scripts/index.d.ts +1 -0
  392. package/dist/routes/root/scripts/index.d.ts.map +1 -0
  393. package/dist/routes/root/scripts/shared.d.ts +1 -0
  394. package/dist/routes/root/scripts/shared.d.ts.map +1 -0
  395. package/dist/routes/root/scripts/shared.js +112 -26
  396. package/dist/routes/root/scripts/shared.js.map +1 -1
  397. package/dist/routes/root/scripts/status.d.ts +1 -0
  398. package/dist/routes/root/scripts/status.d.ts.map +1 -0
  399. package/dist/routes/root/scripts/status.handlers.d.ts +23 -33
  400. package/dist/routes/root/scripts/status.handlers.d.ts.map +1 -0
  401. package/dist/routes/root/scripts/status.handlers.js +46 -92
  402. package/dist/routes/root/scripts/status.handlers.js.map +1 -1
  403. package/dist/routes/root/scripts/status.js +21 -8
  404. package/dist/routes/root/scripts/status.js.map +1 -1
  405. package/dist/routes/root/styles.d.ts +1 -0
  406. package/dist/routes/root/styles.d.ts.map +1 -0
  407. package/dist/routes/root/styles.js +7 -4
  408. package/dist/routes/root/styles.js.map +1 -1
  409. package/dist/routes/services.d.ts +13 -1
  410. package/dist/routes/services.d.ts.map +1 -0
  411. package/dist/routes/services.js +81 -68
  412. package/dist/routes/services.js.map +1 -1
  413. package/dist/routes/sse.d.ts +1 -0
  414. package/dist/routes/sse.d.ts.map +1 -0
  415. package/dist/routes/sse.js +1 -1
  416. package/dist/routes/sse.js.map +1 -1
  417. package/dist/routes/streams.d.ts +2 -1
  418. package/dist/routes/streams.d.ts.map +1 -0
  419. package/dist/routes/streams.js +6 -4
  420. package/dist/routes/streams.js.map +1 -1
  421. package/dist/routes/theme.d.ts +3 -2
  422. package/dist/routes/theme.d.ts.map +1 -0
  423. package/dist/routes/theme.js +6 -5
  424. package/dist/routes/theme.js.map +1 -1
  425. package/dist/routes/ui.d.ts +3 -1
  426. package/dist/routes/ui.d.ts.map +1 -0
  427. package/dist/routes/ui.js +16 -4
  428. package/dist/routes/ui.js.map +1 -1
  429. package/dist/routes/upgrade.d.ts +1 -0
  430. package/dist/routes/upgrade.d.ts.map +1 -0
  431. package/dist/routes/upgrade.js +6 -2
  432. package/dist/routes/upgrade.js.map +1 -1
  433. package/dist/service/commands.context.d.ts +1 -0
  434. package/dist/service/commands.context.d.ts.map +1 -0
  435. package/dist/service/commands.context.js +4 -3
  436. package/dist/service/commands.context.js.map +1 -1
  437. package/dist/service/commands.d.ts +1 -0
  438. package/dist/service/commands.d.ts.map +1 -0
  439. package/dist/service/commands.js +10 -13
  440. package/dist/service/commands.js.map +1 -1
  441. package/dist/service/generators.context.d.ts +1 -0
  442. package/dist/service/generators.context.d.ts.map +1 -0
  443. package/dist/service/generators.context.js +1 -1
  444. package/dist/service/generators.context.js.map +1 -1
  445. package/dist/service/generators.d.ts +4 -3
  446. package/dist/service/generators.d.ts.map +1 -0
  447. package/dist/service/generators.js +111 -31
  448. package/dist/service/generators.js.map +1 -1
  449. package/dist/service/index.d.ts +1 -0
  450. package/dist/service/index.d.ts.map +1 -0
  451. package/dist/streaming/captureLock.d.ts +44 -0
  452. package/dist/streaming/captureLock.d.ts.map +1 -0
  453. package/dist/streaming/captureLock.js +95 -0
  454. package/dist/streaming/captureLock.js.map +1 -0
  455. package/dist/streaming/captureSession.d.ts +31 -0
  456. package/dist/streaming/captureSession.d.ts.map +1 -0
  457. package/dist/streaming/captureSession.js +54 -0
  458. package/dist/streaming/captureSession.js.map +1 -0
  459. package/dist/streaming/clients.d.ts +1 -0
  460. package/dist/streaming/clients.d.ts.map +1 -0
  461. package/dist/streaming/codec.d.ts +1 -0
  462. package/dist/streaming/codec.d.ts.map +1 -0
  463. package/dist/streaming/fmp4Segmenter.d.ts +29 -2
  464. package/dist/streaming/fmp4Segmenter.d.ts.map +1 -0
  465. package/dist/streaming/fmp4Segmenter.js +79 -26
  466. package/dist/streaming/fmp4Segmenter.js.map +1 -1
  467. package/dist/streaming/hls.d.ts +18 -7
  468. package/dist/streaming/hls.d.ts.map +1 -0
  469. package/dist/streaming/hls.js +220 -120
  470. package/dist/streaming/hls.js.map +1 -1
  471. package/dist/streaming/hlsResume.d.ts +1 -0
  472. package/dist/streaming/hlsResume.d.ts.map +1 -0
  473. package/dist/streaming/hlsResume.js +1 -1
  474. package/dist/streaming/hlsResume.js.map +1 -1
  475. package/dist/streaming/hlsSegments.d.ts +59 -2
  476. package/dist/streaming/hlsSegments.d.ts.map +1 -0
  477. package/dist/streaming/hlsSegments.js +147 -17
  478. package/dist/streaming/hlsSegments.js.map +1 -1
  479. package/dist/streaming/lifecycle.d.ts +4 -1
  480. package/dist/streaming/lifecycle.d.ts.map +1 -0
  481. package/dist/streaming/lifecycle.js +65 -85
  482. package/dist/streaming/lifecycle.js.map +1 -1
  483. package/dist/streaming/monitor.d.ts +6 -4
  484. package/dist/streaming/monitor.d.ts.map +1 -0
  485. package/dist/streaming/monitor.js +343 -145
  486. package/dist/streaming/monitor.js.map +1 -1
  487. package/dist/streaming/mp4Parser.d.ts +5 -4
  488. package/dist/streaming/mp4Parser.d.ts.map +1 -0
  489. package/dist/streaming/mp4Parser.js +36 -13
  490. package/dist/streaming/mp4Parser.js.map +1 -1
  491. package/dist/streaming/mpegts.d.ts +16 -0
  492. package/dist/streaming/mpegts.d.ts.map +1 -0
  493. package/dist/streaming/mpegts.js +61 -17
  494. package/dist/streaming/mpegts.js.map +1 -1
  495. package/dist/streaming/playlistBuilder.d.ts +1 -0
  496. package/dist/streaming/playlistBuilder.d.ts.map +1 -0
  497. package/dist/streaming/preroll.d.ts +32 -5
  498. package/dist/streaming/preroll.d.ts.map +1 -0
  499. package/dist/streaming/preroll.js +70 -32
  500. package/dist/streaming/preroll.js.map +1 -1
  501. package/dist/streaming/pretune.d.ts +26 -1
  502. package/dist/streaming/pretune.d.ts.map +1 -0
  503. package/dist/streaming/pretune.js +22 -24
  504. package/dist/streaming/pretune.js.map +1 -1
  505. package/dist/streaming/pretuneTimers.d.ts +24 -0
  506. package/dist/streaming/pretuneTimers.d.ts.map +1 -0
  507. package/dist/streaming/pretuneTimers.js +56 -0
  508. package/dist/streaming/pretuneTimers.js.map +1 -0
  509. package/dist/streaming/recovery.d.ts +139 -5
  510. package/dist/streaming/recovery.d.ts.map +1 -0
  511. package/dist/streaming/recovery.js +160 -16
  512. package/dist/streaming/recovery.js.map +1 -1
  513. package/dist/streaming/registry.d.ts +27 -9
  514. package/dist/streaming/registry.d.ts.map +1 -0
  515. package/dist/streaming/registry.js +21 -3
  516. package/dist/streaming/registry.js.map +1 -1
  517. package/dist/streaming/setup.d.ts +157 -29
  518. package/dist/streaming/setup.d.ts.map +1 -0
  519. package/dist/streaming/setup.js +1034 -458
  520. package/dist/streaming/setup.js.map +1 -1
  521. package/dist/streaming/showInfo.d.ts +21 -1
  522. package/dist/streaming/showInfo.d.ts.map +1 -0
  523. package/dist/streaming/showInfo.js +31 -16
  524. package/dist/streaming/showInfo.js.map +1 -1
  525. package/dist/streaming/statusEmitter.d.ts +10 -10
  526. package/dist/streaming/statusEmitter.d.ts.map +1 -0
  527. package/dist/streaming/statusEmitter.js +5 -8
  528. package/dist/streaming/statusEmitter.js.map +1 -1
  529. package/dist/types/channels.d.ts +16 -4
  530. package/dist/types/channels.d.ts.map +1 -0
  531. package/dist/types/channels.js +4 -2
  532. package/dist/types/channels.js.map +1 -1
  533. package/dist/types/config.d.ts +16 -5
  534. package/dist/types/config.d.ts.map +1 -0
  535. package/dist/types/index.d.ts +3 -2
  536. package/dist/types/index.d.ts.map +1 -0
  537. package/dist/types/profiles.d.ts +6 -4
  538. package/dist/types/profiles.d.ts.map +1 -0
  539. package/dist/types/selection.d.ts +31 -3
  540. package/dist/types/selection.d.ts.map +1 -0
  541. package/dist/types/selection.js +4 -2
  542. package/dist/types/selection.js.map +1 -1
  543. package/dist/types/shared.d.ts +1 -0
  544. package/dist/types/shared.d.ts.map +1 -0
  545. package/dist/types/streaming.d.ts +8 -0
  546. package/dist/types/streaming.d.ts.map +1 -0
  547. package/dist/upgrade/commands.context.d.ts +1 -0
  548. package/dist/upgrade/commands.context.d.ts.map +1 -0
  549. package/dist/upgrade/commands.context.js +14 -10
  550. package/dist/upgrade/commands.context.js.map +1 -1
  551. package/dist/upgrade/commands.d.ts +8 -15
  552. package/dist/upgrade/commands.d.ts.map +1 -0
  553. package/dist/upgrade/commands.js +27 -28
  554. package/dist/upgrade/commands.js.map +1 -1
  555. package/dist/upgrade/detection.context.d.ts +1 -0
  556. package/dist/upgrade/detection.context.d.ts.map +1 -0
  557. package/dist/upgrade/detection.context.js +4 -1
  558. package/dist/upgrade/detection.context.js.map +1 -1
  559. package/dist/upgrade/detection.d.ts +9 -7
  560. package/dist/upgrade/detection.d.ts.map +1 -0
  561. package/dist/upgrade/detection.js +23 -54
  562. package/dist/upgrade/detection.js.map +1 -1
  563. package/dist/upgrade/index.d.ts +2 -0
  564. package/dist/upgrade/index.d.ts.map +1 -0
  565. package/dist/upgrade/index.js +1 -0
  566. package/dist/upgrade/index.js.map +1 -1
  567. package/dist/upgrade/lifecycle.context.d.ts +20 -0
  568. package/dist/upgrade/lifecycle.context.d.ts.map +1 -0
  569. package/dist/upgrade/lifecycle.context.js +47 -0
  570. package/dist/upgrade/lifecycle.context.js.map +1 -0
  571. package/dist/upgrade/lifecycle.d.ts +72 -0
  572. package/dist/upgrade/lifecycle.d.ts.map +1 -0
  573. package/dist/upgrade/lifecycle.js +175 -0
  574. package/dist/upgrade/lifecycle.js.map +1 -0
  575. package/dist/upgrade/pathHandle.d.ts +30 -0
  576. package/dist/upgrade/pathHandle.d.ts.map +1 -0
  577. package/dist/upgrade/pathHandle.js +102 -0
  578. package/dist/upgrade/pathHandle.js.map +1 -0
  579. package/dist/utils/bootSession.context.d.ts +16 -0
  580. package/dist/utils/bootSession.context.d.ts.map +1 -0
  581. package/dist/utils/bootSession.context.js +60 -0
  582. package/dist/utils/bootSession.context.js.map +1 -0
  583. package/dist/utils/bootSession.d.ts +17 -0
  584. package/dist/utils/bootSession.d.ts.map +1 -0
  585. package/dist/utils/bootSession.js +20 -0
  586. package/dist/utils/bootSession.js.map +1 -0
  587. package/dist/utils/chromeFetch.d.ts +1 -0
  588. package/dist/utils/chromeFetch.d.ts.map +1 -0
  589. package/dist/utils/cliOutput.d.ts +1 -0
  590. package/dist/utils/cliOutput.d.ts.map +1 -0
  591. package/dist/utils/clock.d.ts +5 -4
  592. package/dist/utils/clock.d.ts.map +1 -0
  593. package/dist/utils/clock.js +19 -16
  594. package/dist/utils/clock.js.map +1 -1
  595. package/dist/utils/debugFilter.d.ts +9 -1
  596. package/dist/utils/debugFilter.d.ts.map +1 -0
  597. package/dist/utils/debugFilter.js +60 -33
  598. package/dist/utils/debugFilter.js.map +1 -1
  599. package/dist/utils/delay.d.ts +48 -15
  600. package/dist/utils/delay.d.ts.map +1 -0
  601. package/dist/utils/delay.js +73 -20
  602. package/dist/utils/delay.js.map +1 -1
  603. package/dist/utils/errors.d.ts +32 -0
  604. package/dist/utils/errors.d.ts.map +1 -0
  605. package/dist/utils/errors.js +45 -4
  606. package/dist/utils/errors.js.map +1 -1
  607. package/dist/utils/evaluate.d.ts +1 -0
  608. package/dist/utils/evaluate.d.ts.map +1 -0
  609. package/dist/utils/evaluate.js +29 -23
  610. package/dist/utils/evaluate.js.map +1 -1
  611. package/dist/utils/ffmpeg.context.d.ts +1 -0
  612. package/dist/utils/ffmpeg.context.d.ts.map +1 -0
  613. package/dist/utils/ffmpeg.context.js.map +1 -1
  614. package/dist/utils/ffmpeg.d.ts +29 -7
  615. package/dist/utils/ffmpeg.d.ts.map +1 -0
  616. package/dist/utils/ffmpeg.js +32 -14
  617. package/dist/utils/ffmpeg.js.map +1 -1
  618. package/dist/utils/fileLogger.d.ts +3 -1
  619. package/dist/utils/fileLogger.d.ts.map +1 -0
  620. package/dist/utils/fileLogger.js +64 -29
  621. package/dist/utils/fileLogger.js.map +1 -1
  622. package/dist/utils/format.d.ts +14 -1
  623. package/dist/utils/format.d.ts.map +1 -0
  624. package/dist/utils/format.js +19 -5
  625. package/dist/utils/format.js.map +1 -1
  626. package/dist/utils/index.d.ts +4 -0
  627. package/dist/utils/index.d.ts.map +1 -0
  628. package/dist/utils/index.js +3 -0
  629. package/dist/utils/index.js.map +1 -1
  630. package/dist/utils/logEmitter.d.ts +1 -0
  631. package/dist/utils/logEmitter.d.ts.map +1 -0
  632. package/dist/utils/logger.d.ts +1 -0
  633. package/dist/utils/logger.d.ts.map +1 -0
  634. package/dist/utils/logger.js +10 -6
  635. package/dist/utils/logger.js.map +1 -1
  636. package/dist/utils/m3u.d.ts +3 -2
  637. package/dist/utils/m3u.d.ts.map +1 -0
  638. package/dist/utils/m3u.js +4 -3
  639. package/dist/utils/m3u.js.map +1 -1
  640. package/dist/utils/markup.d.ts +13 -1
  641. package/dist/utils/markup.d.ts.map +1 -0
  642. package/dist/utils/markup.js +28 -4
  643. package/dist/utils/markup.js.map +1 -1
  644. package/dist/utils/memo.d.ts +3 -2
  645. package/dist/utils/memo.d.ts.map +1 -0
  646. package/dist/utils/memo.js +3 -3
  647. package/dist/utils/morganStream.d.ts +1 -0
  648. package/dist/utils/morganStream.d.ts.map +1 -0
  649. package/dist/utils/network.d.ts +1 -0
  650. package/dist/utils/network.d.ts.map +1 -0
  651. package/dist/utils/network.js +2 -2
  652. package/dist/utils/pid.d.ts +3 -25
  653. package/dist/utils/pid.d.ts.map +1 -0
  654. package/dist/utils/pid.js +10 -49
  655. package/dist/utils/pid.js.map +1 -1
  656. package/dist/utils/platform.d.ts +3 -1
  657. package/dist/utils/platform.d.ts.map +1 -0
  658. package/dist/utils/platform.js +2 -1
  659. package/dist/utils/platform.js.map +1 -1
  660. package/dist/utils/processInspector.context.d.ts +8 -0
  661. package/dist/utils/processInspector.context.d.ts.map +1 -0
  662. package/dist/utils/processInspector.context.js +106 -0
  663. package/dist/utils/processInspector.context.js.map +1 -0
  664. package/dist/utils/processInspector.d.ts +55 -0
  665. package/dist/utils/processInspector.d.ts.map +1 -0
  666. package/dist/utils/processInspector.js +127 -0
  667. package/dist/utils/processInspector.js.map +1 -0
  668. package/dist/utils/retry.d.ts +25 -2
  669. package/dist/utils/retry.d.ts.map +1 -0
  670. package/dist/utils/retry.js +33 -6
  671. package/dist/utils/retry.js.map +1 -1
  672. package/dist/utils/runtimeIdentity.context.d.ts +7 -0
  673. package/dist/utils/runtimeIdentity.context.d.ts.map +1 -0
  674. package/dist/utils/runtimeIdentity.context.js +43 -0
  675. package/dist/utils/runtimeIdentity.context.js.map +1 -0
  676. package/dist/utils/runtimeIdentity.d.ts +98 -0
  677. package/dist/utils/runtimeIdentity.d.ts.map +1 -0
  678. package/dist/utils/runtimeIdentity.js +181 -0
  679. package/dist/utils/runtimeIdentity.js.map +1 -0
  680. package/dist/utils/sanitize.d.ts +1 -0
  681. package/dist/utils/sanitize.d.ts.map +1 -0
  682. package/dist/utils/streamContext.d.ts +2 -1
  683. package/dist/utils/streamContext.d.ts.map +1 -0
  684. package/dist/utils/streamContext.js +1 -1
  685. package/dist/utils/timing.d.ts +2 -1
  686. package/dist/utils/timing.d.ts.map +1 -0
  687. package/dist/utils/timing.js +0 -5
  688. package/dist/utils/timing.js.map +1 -1
  689. package/dist/utils/version.d.ts +1 -0
  690. package/dist/utils/version.d.ts.map +1 -0
  691. package/dist/utils/version.js +47 -13
  692. package/dist/utils/version.js.map +1 -1
  693. package/package.json +26 -20
@@ -1,295 +1,293 @@
1
- import { LOG, chromeFetch, startTimer } from "../utils/index.js";
2
- import { classifyHlsPlaylist } from "../native/probe.js";
3
- /* This module installs a Chrome DevTools Protocol (CDP) listener on the Network domain to capture HLS manifest URLs as the browser's video player fetches them. The
4
- * listener is shared by two consumers:
1
+ import { LOG, extractPathname, startTimer } from "../utils/index.js";
2
+ import { observeHlsPlaylists } from "./hlsPlaylistObserver.js";
3
+ /* This module hosts two consumer-facing state machines built on top of the HLS playlist observer (hlsPlaylistObserver.ts), which in turn rides on the tab-wide
4
+ * network observer (tabNetworkObserver.ts). The layering:
5
5
  *
6
- * - Native HLS streaming: captures the playlist URL during stream startup and hands the CDP session off to the proxy for subsequent token-refresh tunes. See
7
- * installManifestInterceptor() and the consumers in src/native/. This consumer accepts both master and media playlists; the probe normalizes either kind into a
8
- * MediaFeed so downstream code does not branch on which kind arrived.
9
- * - Tune verification: confirms that the channel a strategy clicked actually loaded by matching the resulting manifest URL against an expected predicate (e.g.,
10
- * call sign in the path). See awaitMatchingManifest() and the consumer in src/browser/tuning/fox.ts. This consumer expects master playlists only because tune
11
- * verification is a multi-channel concept that does not apply to direct-tune media-only sources.
6
+ * tabNetworkObserver -> tab-wide CDP transport (OOPIF-aware via auto-attach + flatten)
7
+ * hlsPlaylistObserver -> HLS filter + body fetch + classify + per-URL dedup + wire-arrival sequencing
8
+ * manifestInterceptor -> selection: the channel-selection epoch, playlist membership, the liveness override, finalize semantics, predicate match
12
9
  *
13
- * Both APIs share the underlying CDP observer that filters .m3u8 URLs, fetches the body, and asks classifyHlsPlaylist() for the playlist kind; the kind is
14
- * forwarded to consumers so each can apply its own kind-specific filter without re-reading the body. Classification lives in exactly one place
15
- * (src/native/probe.ts) so the master/media decision cannot drift between the interceptor's transport-layer gate and the probe's resolution-layer dispatch.
16
- * We use a direct fetch rather than CDP's Network.getResponseBody because Chrome's network cache can evict response bodies before we read them, causing
17
- * spurious "No data found for resource" failures.
10
+ * This module hosts the following consumer-facing state machines built on top of the HLS playlist observer:
18
11
  *
19
- * For multi-channel sites, the video player may load manifests for channels other than the one requested. installManifestInterceptor tracks both the first and
20
- * latest master manifest URLs to handle two distinct tuning patterns. The pattern is selected by the directTune flag passed to finalize():
12
+ * - installManifestInterceptor(): the long-lived capture used by native HLS setup. Accepts both master and media playlists, tracks the first and latest URL per
13
+ * kind, and resolves with the playlist appropriate for the tune type when finalize() is called. A direct tune (single-channel site navigated by URL) takes the
14
+ * first qualifying capture, master preferred. A guide tune (multi-channel site) judges the captures through three signals - a channel-selection epoch stamped
15
+ * when selection begins, playlist membership of a media playlist within the latest master's declared children, and media liveness - so the tuned channel's
16
+ * playlist is chosen even when a page-load default channel was captured first. See selectInterceptedManifest for the full rule.
17
+ * - awaitMatchingManifest(): the short-lived verification used after a guide-click tune. Accepts master playlists only and resolves with the first URL whose
18
+ * path satisfies a caller-supplied predicate. Media playlists are skipped at the consumer level because tune verification is a multi-channel concept that
19
+ * does not apply to direct-tune media-only sources.
21
20
  *
22
- * - Direct tunes (directTune=true): the navigated URL itself selects the channel, so the first manifest captured is the correct one - the player loaded it for
23
- * the navigated URL. Background prefetches for other channels may arrive later but should be ignored. The first manifest wins.
24
- * - Guide tunes (directTune=false): page navigation loads a default channel's manifest, then a tuning strategy (guide-grid click, JS bridge call, webpack
25
- * injection, etc.) triggers a new manifest fetch for the requested channel. The latest manifest wins.
26
- *
27
- * awaitMatchingManifest takes a different approach: it resolves with the first master manifest whose URL satisfies the caller-supplied predicate. The CDP session
28
- * is torn down automatically on resolution. This is the verification path - the caller has already triggered the tune and now wants confirmation that the
29
- * resulting manifest belongs to the expected channel.
21
+ * Race safety, URL filtering, body fetching, classification, and wire-arrival sequencing all live below this module - we receive a stream of recognized HLS
22
+ * playlists with their kind, per-kind facts, and arrival ordinal, and apply selection policy. Public callers do not hold CDP session references; the underlying
23
+ * HLS observer disposes its tab network observer deterministically when finalize, timeout, predicate match, or explicit dispose fires.
30
24
  */
31
- // Default timeout for the long-lived interception used by native HLS.
32
- const INTERCEPTION_TIMEOUT = 15000;
33
- // Brief delay after finalize() is called to allow any in-flight manifest response to arrive. Applied only for guide-based tunes (directTune=false) where the
34
- // channel switch may trigger a manifest fetch that arrives milliseconds after the click handler returns. Direct tunes skip this delay and resolve immediately.
35
- const FINALIZE_SETTLE_DELAY = 1500;
25
+ // Brief delay after finalize() is called to allow any in-flight manifest response to arrive. Applied for guide-based tunes (directTune=false), where the channel
26
+ // switch may trigger a manifest fetch that arrives milliseconds after the click handler returns, and for direct tunes that have not yet captured a master
27
+ // manifest. A direct tune skips this delay and resolves immediately only once a master manifest has already arrived. Exported so a caller sizing its own
28
+ // interception window (streaming/setup.ts) can include the settle window the window must outlive.
29
+ export const FINALIZE_SETTLE_DELAY = 1500;
36
30
  // Default timeout for awaitMatchingManifest. Tune verification is a short-lived check after a click, so the budget is tighter than the native interception path.
37
31
  const VERIFICATION_TIMEOUT = 8000;
38
- // Timeout for the per-response manifest body fetch. The body is fed to classifyHlsPlaylist() to determine whether it is a master playlist; if Chrome's network
39
- // cache evicts the body or the CDN serves a slow response, we abandon and treat the response as non-master.
40
- const MANIFEST_BODY_FETCH_TIMEOUT = 5000;
41
32
  /**
42
- * Selects the playlist URL appropriate for a given resolution mode from the per-kind first/latest state captured by the manifest observer. Master URLs always
43
- * outrank media URLs when both kinds were observed because masters declare richer metadata (variant bandwidth, resolution, separate audio renditions). Within
44
- * the chosen kind, direct tunes pick the first URL and guide tunes pick the latest URL. Returns null when no qualifying URL is available.
45
- *
46
- * Pure function exported for unit testing; the closure inside installManifestInterceptor() consumes it through the same shape.
33
+ * Reports whether a media playlist is a verified NON-member of a master's declared children, comparing by pathname only. Membership is URL-identity policy, not
34
+ * manifest-format knowledge, which is why it lives here rather than in probe. Pathname-only because post-redirect response URLs diverge from a master's declared
35
+ * child URIs at the host far more often than at the path. The judgment is member-conservative: a media URL that cannot be parsed, an empty declared child list, or
36
+ * an empty effective set after dropping unparseable children all read as MEMBER (the master wins, degrading to the categorical rule, never a new failure). Only a
37
+ * parseable media pathname absent from a non-empty set of parseable child pathnames is a verified non-member.
47
38
  *
48
- * @param state - The captured-URL state at resolution time.
49
- * @returns The selected URL, or null when no URL matches the resolution mode.
39
+ * @param mediaUrl - The candidate media playlist URL.
40
+ * @param childUrls - The master's declared child playlist URLs.
41
+ * @returns True only when the media pathname is proven absent from a non-empty effective set of child pathnames.
50
42
  */
51
- export function selectInterceptedManifest(state) {
52
- if (state.directTune) {
53
- return state.firstMasterUrl ?? state.firstMediaUrl;
43
+ function isVerifiedNonMemberOfMaster(mediaUrl, childUrls) {
44
+ const mediaPathname = extractPathname(mediaUrl);
45
+ // A media URL we cannot parse cannot be proven foreign, so it reads as a member and the master wins.
46
+ if (mediaPathname === null) {
47
+ return false;
48
+ }
49
+ // Normalize the declared children to pathnames, dropping any that cannot be parsed. An empty declared list or a list that parses to nothing yields an empty
50
+ // effective set: with no children to compare against, membership cannot be disproven, so the master wins.
51
+ const childPathnames = childUrls.map((child) => extractPathname(child)).filter((pathname) => pathname !== null);
52
+ if (childPathnames.length === 0) {
53
+ return false;
54
54
  }
55
- return state.latestMasterUrl ?? state.latestMediaUrl;
55
+ return !childPathnames.includes(mediaPathname);
56
56
  }
57
57
  /**
58
- * Installs a CDP Network.responseReceived listener that calls onPlaylist() for every recognized HLS playlist URL observed. The listener filters to .m3u8 URLs,
59
- * fetches the body, and asks classifyHlsPlaylist() for the playlist kind; recognized kinds (master, media) fire the callback with the kind label, while
60
- * unrecognized bodies are skipped. Consumers apply their own kind-specific filtering on top of the callback - installManifestInterceptor accepts both kinds
61
- * with master priority, while awaitMatchingManifest accepts master only.
58
+ * Selects the playlist appropriate for a given resolution mode from the captured state, reporting the URL together with the kind selection decided.
62
59
  *
63
- * Returns null when the page is closed or the CDP session cannot be created. The caller is responsible for calling dispose() to clean up.
64
- * @param page - The Puppeteer page to monitor.
65
- * @param onPlaylist - Callback invoked for each verified HLS playlist URL together with its kind.
66
- * @param logCategory - Debug log category for this observer's lifecycle messages.
67
- * @returns The observer handle, or null if installation failed.
60
+ * Direct tunes take the earliest qualifying capture (firstMasterUrl before firstMediaUrl), master preferred, with the kind tagged by the slot that supplied the
61
+ * URL. The epoch never participates on the direct branch: a direct tune navigates by URL and its first capture answers.
62
+ *
63
+ * Guide tunes apply a three-signal rule over the latest captures:
64
+ *
65
+ * 1. No master captured: the media fallback (latestMedia's URL) or null. Liveness gates only the override of a master, never the sole available feed, so a VOD
66
+ * media playlist still answers when it is all that arrived.
67
+ * 2. Epoch declared and the master post-epoch (latestMaster.ordinal > markOrdinal): the master, categorically. A master observed after channel selection began
68
+ * answers the click. An absent epoch (mark never called) also leaves the master the winner, because without a click there is no fresher truth to prefer.
69
+ * 3. Override to the media only when every clause holds: an epoch exists (rule 2 falling through means the master is pre-epoch), latestMedia is live, the media
70
+ * is post-epoch (latestMedia.ordinal > markOrdinal - which structurally postdates the pre-epoch master, so no separate ordering clause is needed), and the
71
+ * media is a verified non-member of the master's children. Otherwise the master wins.
72
+ *
73
+ * Membership is member-conservative (see isVerifiedNonMemberOfMaster): anything short of a proven foreign pathname keeps the master, so the override engages only
74
+ * where a media playlist is demonstrably not one of the master's own children. Pure selection policy exported so the rule is testable in isolation from the
75
+ * network plumbing; the closure inside installManifestInterceptor() consumes it through the same shape.
76
+ *
77
+ * @param state - The captured state at resolution time.
78
+ * @returns The selected playlist, or null when no URL matches the resolution mode.
68
79
  */
69
- async function startManifestObserver(page, onPlaylist, logCategory) {
70
- if (page.isClosed()) {
80
+ export function selectInterceptedManifest(state) {
81
+ if (state.directTune) {
82
+ if (state.firstMasterUrl !== null) {
83
+ return { kind: "master", url: state.firstMasterUrl };
84
+ }
85
+ if (state.firstMediaUrl !== null) {
86
+ return { kind: "media", url: state.firstMediaUrl };
87
+ }
71
88
  return null;
72
89
  }
73
- let cdpSession;
74
- try {
75
- cdpSession = await page.createCDPSession();
76
- }
77
- catch (error) {
78
- LOG.debug(logCategory, "Failed to create CDP session: %s.", String(error));
79
- return null;
90
+ const { latestMaster, latestMedia, markOrdinal } = state;
91
+ // Rule 1: no master captured. The media fallback answers regardless of liveness.
92
+ if (latestMaster === null) {
93
+ return (latestMedia === null) ? null : { kind: "media", url: latestMedia.url };
80
94
  }
81
- try {
82
- await cdpSession.send("Network.enable");
95
+ // Rule 2: a master observed after channel selection began answers the click categorically; an absent epoch keeps the master too.
96
+ if ((markOrdinal !== null) && (latestMaster.ordinal > markOrdinal)) {
97
+ return { kind: "master", url: latestMaster.url };
83
98
  }
84
- catch (error) {
85
- LOG.debug(logCategory, "Failed to enable Network domain: %s.", String(error));
86
- return null;
99
+ // Rule 3: override to a live, post-epoch, verified non-member media; otherwise the master wins.
100
+ if ((markOrdinal !== null) && (latestMedia !== null) && latestMedia.live && (latestMedia.ordinal > markOrdinal) &&
101
+ isVerifiedNonMemberOfMaster(latestMedia.url, latestMaster.childUrls)) {
102
+ return { kind: "media", url: latestMedia.url };
87
103
  }
88
- LOG.debug(logCategory, "CDP manifest observer installed.");
89
- let disposed = false;
90
- // Listen for completed responses. We use Network.responseReceived to capture .m3u8 URLs, then fetch the manifest body directly from Node.js to verify it is
91
- // a master manifest. This avoids CDP's Network.getResponseBody which is unreliable - Chrome's network cache can evict response bodies before we read them.
92
- const onResponseReceived = async (params) => {
93
- if (disposed) {
94
- return;
95
- }
96
- const url = params.response.url;
97
- // Filter for .m3u8 URLs. Strip query parameters before checking the extension.
98
- const urlPath = url.split("?")[0] ?? "";
99
- if (!urlPath.endsWith(".m3u8")) {
100
- return;
101
- }
102
- LOG.debug(logCategory, "Observed .m3u8 response: %s.", url.slice(0, 120));
103
- // Fetch the manifest body directly to inspect whether this is a master manifest. The URL contains embedded CDN auth tokens, so no cookies or special
104
- // headers are needed beyond the Chrome User-Agent injected by chromeFetch().
105
- try {
106
- const response = await chromeFetch(url, { signal: AbortSignal.timeout(MANIFEST_BODY_FETCH_TIMEOUT) });
107
- if (!response.ok) {
108
- LOG.debug(logCategory, "Manifest fetch returned HTTP %s for %s.", response.status, url.slice(0, 120));
109
- return;
110
- }
111
- const body = await response.text();
112
- // Delegate the master/media decision to the canonical classifier. Recognized kinds (master, media) are forwarded to the callback together with the kind
113
- // label; consumers apply their own filter. Both consumers of onPlaylist maintain their own resolved-flag guard, so a callback that arrives after dispose
114
- // is harmless - the downstream resolve is idempotent.
115
- const kind = classifyHlsPlaylist(body);
116
- if (kind === "unknown") {
117
- LOG.debug(logCategory, "Skipping .m3u8 classified as unknown (no recognizable HLS directives).");
118
- return;
119
- }
120
- onPlaylist(url, kind);
121
- }
122
- catch (error) {
123
- LOG.debug(logCategory, "Could not fetch .m3u8 body: %s.", String(error));
124
- }
125
- };
126
- // Wrap the async handler to avoid no-misused-promises - EventEmitter.on() does not handle returned promises.
127
- const wrappedHandler = (...args) => { void onResponseReceived(...args); };
128
- cdpSession.on("Network.responseReceived", wrappedHandler);
129
- const dispose = (keepSession) => {
130
- if (disposed) {
131
- return;
132
- }
133
- disposed = true;
134
- cdpSession.off("Network.responseReceived", wrappedHandler);
135
- if (!keepSession) {
136
- removeManifestInterceptor(cdpSession);
137
- }
138
- };
139
- return { cdpSession, dispose };
104
+ return { kind: "master", url: latestMaster.url };
140
105
  }
141
106
  /**
142
- * Installs a long-lived CDP listener that tracks observed HLS playlist URLs on the given page. Both master and media playlists are accepted; the listener tracks
143
- * first/latest URLs separately per kind so master-based and media-only sites both work without separate code paths. Master playlists take precedence over media
144
- * playlists when both have arrived during the interception window because masters carry richer metadata (variant bandwidth, resolution, separate audio
145
- * renditions) that improves the resulting MediaFeed. The returned handle provides a finalize(directTune) callback - when called, the interceptor resolves with
146
- * whichever URL is appropriate for the tune type:
107
+ * Installs a long-lived HLS playlist observer that tracks observed playlists on the given page. Both master and media playlists are accepted; the first and latest
108
+ * URL per kind are tracked separately, guarded by wire-arrival order, so master-based and media-only sites both work without separate code paths. The returned
109
+ * handle provides a finalize(directTune) callback and a markChannelSelectionStart() epoch stamp; when finalize is called the observer resolves through
110
+ * selectInterceptedManifest with the playlist appropriate for the tune type:
111
+ *
112
+ * - directTune=true: resolves immediately if a master manifest has already arrived (or after the settle delay when only a media-only manifest, or nothing, has
113
+ * arrived so far) with the first manifest captured. Used by sites where the navigated URL itself selects the channel and the player loads its manifest before
114
+ * the click handler returns. Master-first URL preferred over media-first.
115
+ * - directTune=false: resolves after the settle delay through the guide-tune rule (channel-selection epoch, playlist membership, liveness), so the tuned
116
+ * channel's playlist wins over a page-load default channel captured first. Used by guide-based sites where the channel-switch click triggers a new manifest
117
+ * fetch that may arrive milliseconds after the click handler returns.
147
118
  *
148
- * - directTune=true: resolves immediately (or after the settle delay if no manifest has arrived yet) with the first manifest captured. Used by sites where the
149
- * navigated URL itself selects the channel and the player loads its manifest before the click handler returns. Master-first URL preferred over media-first.
150
- * - directTune=false: resolves after the settle delay with the latest manifest captured. Used by guide-based sites where the channel-switch click triggers a new
151
- * manifest fetch that may arrive milliseconds after the click handler returns. Master-latest URL preferred over media-latest.
119
+ * On timeout (finalize never called), the observer resolves epoch-free through the same guide-tune rule with no epoch declared, so the categorical
120
+ * master-preferred outcome holds and a direct tune the timeout may be guarding is never exposed to the epoch rule. The underlying HLS observer is disposed
121
+ * automatically on resolution; callers do not hold CDP session references.
152
122
  *
153
- * On timeout (finalize never called), the listener resolves with the latest captured URL using the same master-priority rule, or null if none arrived. The CDP
154
- * session is preserved in the result and ownership transfers to the caller; callers must call removeManifestInterceptor() when done.
155
- * @param page - The Puppeteer page to monitor.
156
- * @param timeout - Maximum time in milliseconds to wait for a manifest. Acts as a safety net if finalize() is never called.
157
- * @returns The interceptor handle, or null if the CDP session could not be created.
123
+ * @param page - The puppeteer page to monitor.
124
+ * @param timeout - Maximum time in milliseconds to wait for a manifest. Required: every caller sizes its interception window consciously to outlive the phases it
125
+ * observes, so no production caller relies on a shared default. Acts as a safety net if finalize() is never called.
126
+ * @param observeFactory - The HLS playlist observer factory (default observeHlsPlaylists); injectable so the selection state machine can run without a live browser.
127
+ * @returns The interceptor handle, or null if the observer could not be installed.
158
128
  */
159
- export async function installManifestInterceptor(page, timeout = INTERCEPTION_TIMEOUT) {
129
+ export async function installManifestInterceptor(page, timeout, observeFactory = observeHlsPlaylists) {
160
130
  const elapsed = startTimer();
161
- // Track first/latest URLs separately per playlist kind. Master URLs take precedence at selection time because masters carry richer metadata. For direct
162
- // tunes, the first URL of the higher-priority kind wins; for guide tunes, the latest URL of the higher-priority kind wins. Without separate per-kind
163
- // tracking, a master-based site whose player happens to also load a media playlist for a different channel would risk picking the wrong one.
164
- let firstMasterUrl = null;
165
- let latestMasterUrl = null;
166
- let firstMediaUrl = null;
167
- let latestMediaUrl = null;
131
+ // Track the first and latest observation per kind, plus the epoch fence. Slot updates are guarded by wire-arrival order, not delivery order: the first slot
132
+ // holds the lowest-ordinal arrival and the latest facts hold the highest, so a body fetch that resolves out of order still records arrival truth. Under
133
+ // in-order delivery this is byte-for-byte the earliest/most-recent behavior. manifestCount stays purely diagnostic for the log lines.
134
+ let firstMaster = null;
135
+ let firstMedia = null;
136
+ let latestMaster = null;
137
+ let latestMedia = null;
138
+ let markOrdinal = null;
168
139
  let resolved = false;
169
140
  let manifestCount = 0;
170
141
  const { promise, resolve } = Promise.withResolvers();
171
- const observer = await startManifestObserver(page, (url, kind) => {
172
- manifestCount++;
173
- if (kind === "master") {
174
- firstMasterUrl ??= url;
175
- latestMasterUrl = url;
176
- }
177
- else if (kind === "media") {
178
- firstMediaUrl ??= url;
179
- latestMediaUrl = url;
142
+ const observer = await observeFactory(page, {
143
+ logCategory: "native:intercept",
144
+ onPlaylist: (playlist) => {
145
+ manifestCount++;
146
+ if (playlist.kind === "master") {
147
+ // First slot: the lowest ordinal wins, so a late delivery of an even-earlier arrival corrects it.
148
+ if ((firstMaster === null) || (playlist.sequence < firstMaster.ordinal)) {
149
+ firstMaster = { ordinal: playlist.sequence, url: playlist.url };
150
+ }
151
+ // Latest slot: the highest ordinal wins, so a late delivery of a lower arrival never displaces it.
152
+ if ((latestMaster === null) || (playlist.sequence > latestMaster.ordinal)) {
153
+ latestMaster = { childUrls: playlist.childUrls, ordinal: playlist.sequence, url: playlist.url };
154
+ }
155
+ }
156
+ else {
157
+ if ((firstMedia === null) || (playlist.sequence < firstMedia.ordinal)) {
158
+ firstMedia = { ordinal: playlist.sequence, url: playlist.url };
159
+ }
160
+ if ((latestMedia === null) || (playlist.sequence > latestMedia.ordinal)) {
161
+ latestMedia = { live: playlist.live, ordinal: playlist.sequence, url: playlist.url };
162
+ }
163
+ }
164
+ LOG.debug("native:intercept", "%s playlist captured (#%s, seq %s) in %sms: %s.", playlist.kind, manifestCount, playlist.sequence, elapsed(), playlist.url.slice(0, 120));
180
165
  }
181
- LOG.debug("native:intercept", "%s playlist captured (#%s) in %sms: %s.", kind, manifestCount, elapsed(), url.slice(0, 120));
182
- }, "native:intercept");
166
+ });
183
167
  if (!observer) {
184
168
  return null;
185
169
  }
186
- // Selects the URL appropriate for the resolution mode by delegating to the pure selectInterceptedManifest() helper. Closure variables are passed by value so
187
- // the helper does not depend on shared mutable state.
188
- const selectUrl = (directTune) => selectInterceptedManifest({
170
+ // Builds the selection state from the closure locals. epochFree forces markOrdinal to null so the timeout path resolves without the epoch rule, which must
171
+ // never engage on a path a direct tune can reach.
172
+ const buildState = (directTune, epochFree) => ({
189
173
  directTune,
190
- firstMasterUrl,
191
- firstMediaUrl,
192
- latestMasterUrl,
193
- latestMediaUrl
174
+ firstMasterUrl: firstMaster?.url ?? null,
175
+ firstMediaUrl: firstMedia?.url ?? null,
176
+ latestMaster,
177
+ latestMedia,
178
+ markOrdinal: epochFree ? null : markOrdinal
194
179
  });
195
- // Timeout guard. If finalize() is never called (defensive), resolve with whatever we have after the timeout.
196
- const timer = setTimeout(() => {
180
+ // Centralized resolution helper. Used by the timeout, finalize, and dispose paths to settle the promise exactly once and tear down the observer regardless of
181
+ // which path won. The selection honors the direct-vs-guide semantics and, for guide tunes, the epoch/membership/liveness rule via selectInterceptedManifest.
182
+ const settle = (options) => {
183
+ const { directTune, epochFree = false, reason } = options;
197
184
  if (resolved) {
198
185
  return;
199
186
  }
200
187
  resolved = true;
201
- // The timeout path mirrors the latest-URL semantics of a guide tune; if no finalize() ever arrived we err on the side of the most recent capture.
202
- const selected = selectUrl(false);
203
- if (selected) {
204
- LOG.debug("native:intercept", "Manifest interception timed out after %sms. Resolving with latest URL (%s captured).", elapsed(), manifestCount);
205
- observer.dispose(true);
206
- resolve({ cdpSession: observer.cdpSession, masterManifestUrl: selected });
188
+ clearTimeout(timer);
189
+ const selection = selectInterceptedManifest(buildState(directTune, epochFree));
190
+ if (selection) {
191
+ LOG.debug("native:intercept", "Interception %s in %sms with %s capture(s). Using %s %s: %s.", reason, elapsed(), manifestCount, directTune ? "first" : "latest", selection.kind, selection.url.slice(0, 120));
192
+ observer.dispose();
193
+ resolve({ manifestUrl: selection.url, selectedKind: selection.kind });
207
194
  }
208
195
  else {
209
- LOG.debug("native:intercept", "Manifest interception timed out after %sms. No HLS playlist captured.", elapsed());
210
- observer.dispose(false);
196
+ LOG.debug("native:intercept", "Interception %s in %sms but no HLS playlist was captured.", reason, elapsed());
197
+ observer.dispose();
211
198
  resolve(null);
212
199
  }
213
- }, timeout);
200
+ };
201
+ // Timeout guard. If finalize() is never called (defensive), settle epoch-free after the timeout: the timeout cannot know the tune kind, and the epoch rule must
202
+ // never engage on a path a direct tune can reach, so it resolves with the categorical master-preferred outcome. With the interception budget sized by the
203
+ // caller to outlive the tune (streaming/setup.ts), this path is last-resort defense rather than a routine finish.
204
+ const timer = setTimeout(() => { settle({ directTune: false, epochFree: true, reason: "timed out" }); }, timeout);
214
205
  // Finalize function exposed on the returned handle. Called by the stream setup code after channel selection is complete. The resolution strategy depends on
215
206
  // two factors: whether a qualifying manifest has already been captured, and whether the tune is direct or guide-based.
216
207
  //
217
- // - Manifest captured + direct tune: resolve immediately (A&E, most TVE sites - manifest arrived during page load).
208
+ // - Master manifest captured + direct tune: settle immediately (A&E, most TVE sites - manifest arrived during page load).
218
209
  // - Manifest captured + guide tune: wait FINALIZE_SETTLE_DELAY (Fox guide, Hulu - a newer manifest from the channel switch may still arrive).
219
210
  // - No manifest captured + either: wait FINALIZE_SETTLE_DELAY (Fox Sports - manifest fetch starts after video element appears, hasn't arrived yet).
220
211
  const finalize = (directTune) => {
221
212
  if (resolved) {
222
213
  return;
223
214
  }
224
- // Helper that resolves the promise with the current state. The selectUrl helper applies the master-first priority and the directTune first/latest semantics.
225
- const resolveNow = () => {
226
- if (resolved) {
227
- return;
228
- }
229
- resolved = true;
230
- clearTimeout(timer);
231
- const selectedUrl = selectUrl(directTune);
232
- if (selectedUrl) {
233
- LOG.debug("native:intercept", "Interception finalized in %sms with %s playlist capture(s). Using %s: %s.", elapsed(), manifestCount, directTune ? "first" : "latest", selectedUrl.slice(0, 120));
234
- observer.dispose(true);
235
- resolve({ cdpSession: observer.cdpSession, masterManifestUrl: selectedUrl });
236
- }
237
- else {
238
- LOG.debug("native:intercept", "Interception finalized in %sms but no HLS playlist was captured.", elapsed());
239
- observer.dispose(false);
240
- resolve(null);
241
- }
242
- };
243
- // For direct tunes, resolve immediately if a master URL has already arrived. We do not short-circuit on a media-only first URL because a master may still
244
- // be in flight - waiting the settle delay gives master priority a chance to take effect. Once the settle elapses we resolve with whichever URL ranks
245
- // highest under selectUrl.
246
- if (directTune && firstMasterUrl) {
247
- resolveNow();
215
+ // For direct tunes, settle immediately if a master URL has already arrived. We do not short-circuit on a media-only first URL because a master may still
216
+ // be in flight - waiting the settle delay gives the master-first preference a chance to take effect.
217
+ if (directTune && (firstMaster !== null)) {
218
+ settle({ directTune: true, reason: "finalized" });
248
219
  }
249
220
  else {
250
- setTimeout(resolveNow, FINALIZE_SETTLE_DELAY);
221
+ setTimeout(() => { settle({ directTune, reason: "finalized" }); }, FINALIZE_SETTLE_DELAY);
222
+ }
223
+ };
224
+ // Stamps the observation epoch at which channel selection begins by fencing the observer's current wire sequence. Observations after the stamp answer the
225
+ // click; guide-tune selection prefers a master observed after this point. Callable again - the latest stamp wins - and a no-op once the interception has
226
+ // resolved, matching the finalize and dispose guard idiom.
227
+ const markChannelSelectionStart = () => {
228
+ if (resolved) {
229
+ return;
230
+ }
231
+ markOrdinal = observer.currentSequence();
232
+ LOG.debug("native:intercept", "Channel selection epoch stamped at wire sequence %s.", markOrdinal);
233
+ };
234
+ // Explicit dispose path for "using" syntax or caller-driven cancellation. Safe to call more than once. Resolves the promise with null if it has not already
235
+ // settled, so callers awaiting the promise are never left hanging on a disposed observer.
236
+ const dispose = () => {
237
+ if (resolved) {
238
+ return;
251
239
  }
240
+ resolved = true;
241
+ clearTimeout(timer);
242
+ LOG.debug("native:intercept", "Interception disposed in %sms with %s capture(s) and no resolution selected.", elapsed(), manifestCount);
243
+ observer.dispose();
244
+ resolve(null);
252
245
  };
253
- return { finalize, promise };
246
+ return { dispose, finalize, markChannelSelectionStart, promise, [Symbol.dispose]: dispose };
254
247
  }
255
248
  /**
256
- * Awaits the first master HLS manifest URL whose URL satisfies the supplied predicate. Used by tune-verification paths where the caller has just clicked or
249
+ * Awaits the first master HLS manifest whose URL satisfies the supplied predicate. Used by tune-verification paths where the caller has just clicked or
257
250
  * navigated to a target channel and wants to confirm the resulting stream belongs to that channel rather than to whatever default the page was previously
258
251
  * showing.
259
252
  *
260
- * Resolves with the matching URL on success. Resolves with null if no matching manifest arrives within the timeout. The CDP session is torn down automatically
261
- * in both cases - the verification path does not hand off the session to a downstream consumer.
262
- * @param page - The Puppeteer page to monitor.
253
+ * Resolves with the matching URL on success. Resolves with null if no matching manifest arrives within the timeout. The underlying observer is torn down
254
+ * automatically in both cases - the verification path does not hand off a session to a downstream consumer.
255
+ *
256
+ * @param page - The puppeteer page to monitor.
263
257
  * @param predicate - Test applied to each verified master manifest URL. Return true to accept the URL and resolve.
264
258
  * @param timeout - Maximum time in milliseconds to wait for a matching manifest.
259
+ * @param observeFactory - The HLS playlist observer factory (default observeHlsPlaylists); injectable so the verification state machine can run without a live browser.
265
260
  * @returns The matching URL, or null on timeout.
266
261
  */
267
- export async function awaitMatchingManifest(page, predicate, timeout = VERIFICATION_TIMEOUT) {
262
+ export async function awaitMatchingManifest(page, predicate, timeout = VERIFICATION_TIMEOUT, observeFactory = observeHlsPlaylists) {
268
263
  const elapsed = startTimer();
269
264
  let resolved = false;
270
265
  const { promise, resolve } = Promise.withResolvers();
271
266
  // Tune verification is a multi-channel concept that only applies to master playlists - direct-tune media-only sources do not run a guide-click verification
272
267
  // step because the page navigation itself selects the channel. We reject media playlists at the consumer level rather than asking the observer to filter so
273
268
  // the observer keeps a single canonical contract (forward all recognized HLS playlists with their kind).
274
- const observer = await startManifestObserver(page, (url, kind) => {
275
- if (resolved) {
276
- return;
277
- }
278
- if (kind !== "master") {
279
- LOG.debug("native:intercept", "Tune verification ignoring non-master playlist (%s).", kind);
280
- return;
281
- }
282
- if (predicate(url)) {
283
- resolved = true;
284
- LOG.debug("native:intercept", "Matching manifest found in %sms: %s.", elapsed(), url.slice(0, 120));
285
- observer?.dispose(false);
286
- clearTimeout(timer);
287
- resolve(url);
288
- }
289
- else {
290
- LOG.debug("native:intercept", "Master manifest did not match predicate: %s.", url.slice(0, 120));
269
+ const observer = await observeFactory(page, {
270
+ logCategory: "native:intercept",
271
+ onPlaylist: (playlist) => {
272
+ if (resolved) {
273
+ return;
274
+ }
275
+ if (playlist.kind !== "master") {
276
+ LOG.debug("native:intercept", "Tune verification ignoring non-master playlist (%s).", playlist.kind);
277
+ return;
278
+ }
279
+ if (predicate(playlist.url)) {
280
+ resolved = true;
281
+ LOG.debug("native:intercept", "Matching manifest found in %sms: %s.", elapsed(), playlist.url.slice(0, 120));
282
+ observer?.dispose();
283
+ clearTimeout(timer);
284
+ resolve(playlist.url);
285
+ }
286
+ else {
287
+ LOG.debug("native:intercept", "Master manifest did not match predicate: %s.", playlist.url.slice(0, 120));
288
+ }
291
289
  }
292
- }, "native:intercept");
290
+ });
293
291
  if (!observer) {
294
292
  return null;
295
293
  }
@@ -299,25 +297,9 @@ export async function awaitMatchingManifest(page, predicate, timeout = VERIFICAT
299
297
  }
300
298
  resolved = true;
301
299
  LOG.debug("native:intercept", "Awaiting matching manifest timed out after %sms.", elapsed());
302
- observer.dispose(false);
300
+ observer.dispose();
303
301
  resolve(null);
304
302
  }, timeout);
305
303
  return promise;
306
304
  }
307
- /**
308
- * Removes the Network domain listener and detaches the CDP session. Safe to call multiple times or on an already-detached session. Public so that native HLS
309
- * consumers (which receive the CDP session through ManifestInterceptionResult) can clean up after token-refresh handoffs and proxy shutdown.
310
- * @param cdpSession - The CDP session to clean up.
311
- */
312
- export function removeManifestInterceptor(cdpSession) {
313
- try {
314
- cdpSession.removeAllListeners("Network.responseReceived");
315
- void cdpSession.send("Network.disable").catch(() => { });
316
- void cdpSession.detach().catch(() => { });
317
- }
318
- catch (_error) {
319
- // Ignore errors during cleanup - the session may already be detached.
320
- }
321
- LOG.debug("native:intercept", "CDP manifest interceptor removed.");
322
- }
323
305
  //# sourceMappingURL=manifestInterceptor.js.map