prismcast 1.8.0 → 1.10.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 (392) hide show
  1. package/README.md +6 -6
  2. package/dist/app.d.ts +1 -1
  3. package/dist/app.js +41 -13
  4. package/dist/app.js.map +1 -1
  5. package/dist/browser/cdp.d.ts +4 -6
  6. package/dist/browser/cdp.js +18 -22
  7. package/dist/browser/cdp.js.map +1 -1
  8. package/dist/browser/channelSelection.d.ts +41 -11
  9. package/dist/browser/channelSelection.js +75 -24
  10. package/dist/browser/channelSelection.js.map +1 -1
  11. package/dist/browser/display.d.ts +1 -1
  12. package/dist/browser/index.d.ts +8 -8
  13. package/dist/browser/index.js +44 -45
  14. package/dist/browser/index.js.map +1 -1
  15. package/dist/browser/login.d.ts +2 -2
  16. package/dist/browser/login.js +1 -1
  17. package/dist/browser/manifestInterceptor.d.ts +77 -0
  18. package/dist/browser/manifestInterceptor.js +323 -0
  19. package/dist/browser/manifestInterceptor.js.map +1 -0
  20. package/dist/browser/precaching.d.ts +1 -1
  21. package/dist/browser/precaching.js +23 -23
  22. package/dist/browser/precaching.js.map +1 -1
  23. package/dist/browser/tuning/comcastPolymer.d.ts +1 -1
  24. package/dist/browser/tuning/cox.d.ts +1 -1
  25. package/dist/browser/tuning/directv.d.ts +1 -1
  26. package/dist/browser/tuning/directv.js +65 -69
  27. package/dist/browser/tuning/directv.js.map +1 -1
  28. package/dist/browser/tuning/fox.d.ts +1 -1
  29. package/dist/browser/tuning/fox.js +172 -15
  30. package/dist/browser/tuning/fox.js.map +1 -1
  31. package/dist/browser/tuning/hbo.d.ts +1 -1
  32. package/dist/browser/tuning/hbo.js +7 -7
  33. package/dist/browser/tuning/hbo.js.map +1 -1
  34. package/dist/browser/tuning/hulu.d.ts +1 -1
  35. package/dist/browser/tuning/hulu.js +120 -113
  36. package/dist/browser/tuning/hulu.js.map +1 -1
  37. package/dist/browser/tuning/shared.d.ts +3 -3
  38. package/dist/browser/tuning/shared.js +5 -5
  39. package/dist/browser/tuning/sling.d.ts +1 -1
  40. package/dist/browser/tuning/sling.js +30 -30
  41. package/dist/browser/tuning/sling.js.map +1 -1
  42. package/dist/browser/tuning/spectrum.d.ts +1 -1
  43. package/dist/browser/tuning/spectrum.js +21 -22
  44. package/dist/browser/tuning/spectrum.js.map +1 -1
  45. package/dist/browser/tuning/thumbnailRow.d.ts +1 -1
  46. package/dist/browser/tuning/thumbnailRow.js +1 -1
  47. package/dist/browser/tuning/tileClick.d.ts +1 -1
  48. package/dist/browser/tuning/tileClick.js +5 -5
  49. package/dist/browser/tuning/xfinity.d.ts +1 -1
  50. package/dist/browser/tuning/youtubeTv.d.ts +1 -1
  51. package/dist/browser/tuning/youtubeTv.js +17 -18
  52. package/dist/browser/tuning/youtubeTv.js.map +1 -1
  53. package/dist/browser/video.d.ts +20 -6
  54. package/dist/browser/video.js +30 -38
  55. package/dist/browser/video.js.map +1 -1
  56. package/dist/channels/index.d.ts +1 -2
  57. package/dist/channels/index.js +475 -442
  58. package/dist/channels/index.js.map +1 -1
  59. package/dist/config/channelForm.d.ts +58 -0
  60. package/dist/config/channelForm.js +129 -0
  61. package/dist/config/channelForm.js.map +1 -0
  62. package/dist/config/consistencyProbe.d.ts +7 -0
  63. package/dist/config/consistencyProbe.js +143 -0
  64. package/dist/config/consistencyProbe.js.map +1 -0
  65. package/dist/config/health.d.ts +7 -6
  66. package/dist/config/health.js +78 -65
  67. package/dist/config/health.js.map +1 -1
  68. package/dist/config/index.d.ts +1 -1
  69. package/dist/config/index.js +21 -14
  70. package/dist/config/index.js.map +1 -1
  71. package/dist/config/paths.d.ts +9 -3
  72. package/dist/config/paths.js +12 -4
  73. package/dist/config/paths.js.map +1 -1
  74. package/dist/config/persistence.context.d.ts +8 -0
  75. package/dist/config/persistence.context.js +30 -0
  76. package/dist/config/persistence.context.js.map +1 -0
  77. package/dist/config/persistence.d.ts +157 -0
  78. package/dist/config/persistence.js +446 -0
  79. package/dist/config/persistence.js.map +1 -0
  80. package/dist/config/presets.d.ts +14 -4
  81. package/dist/config/presets.js +84 -75
  82. package/dist/config/presets.js.map +1 -1
  83. package/dist/config/profiles.d.ts +2 -2
  84. package/dist/config/profiles.js +7 -9
  85. package/dist/config/profiles.js.map +1 -1
  86. package/dist/config/servicePacks.d.ts +46 -0
  87. package/dist/config/{providerPacks.js → servicePacks.js} +50 -53
  88. package/dist/config/servicePacks.js.map +1 -0
  89. package/dist/config/services.d.ts +228 -0
  90. package/dist/config/services.js +772 -0
  91. package/dist/config/services.js.map +1 -0
  92. package/dist/config/sites.d.ts +3 -3
  93. package/dist/config/sites.js +75 -75
  94. package/dist/config/sites.js.map +1 -1
  95. package/dist/config/userChannels.d.ts +522 -54
  96. package/dist/config/userChannels.js +1343 -366
  97. package/dist/config/userChannels.js.map +1 -1
  98. package/dist/config/userConfig.d.ts +108 -13
  99. package/dist/config/userConfig.js +296 -143
  100. package/dist/config/userConfig.js.map +1 -1
  101. package/dist/config/userProfiles.d.ts +34 -16
  102. package/dist/config/userProfiles.js +189 -100
  103. package/dist/config/userProfiles.js.map +1 -1
  104. package/dist/hdhr/channelMap.d.ts +1 -1
  105. package/dist/hdhr/channelMap.js +10 -16
  106. package/dist/hdhr/channelMap.js.map +1 -1
  107. package/dist/hdhr/deviceId.js +10 -9
  108. package/dist/hdhr/deviceId.js.map +1 -1
  109. package/dist/hdhr/discover.js +4 -5
  110. package/dist/hdhr/discover.js.map +1 -1
  111. package/dist/hdhr/index.js +12 -19
  112. package/dist/hdhr/index.js.map +1 -1
  113. package/dist/index.js +12 -12
  114. package/dist/index.js.map +1 -1
  115. package/dist/native/codecInference.d.ts +44 -0
  116. package/dist/native/codecInference.js +327 -0
  117. package/dist/native/codecInference.js.map +1 -0
  118. package/dist/native/decrypt.d.ts +1 -1
  119. package/dist/native/index.d.ts +5 -5
  120. package/dist/native/index.js +45 -67
  121. package/dist/native/index.js.map +1 -1
  122. package/dist/native/probe.d.ts +27 -9
  123. package/dist/native/probe.js +159 -68
  124. package/dist/native/probe.js.map +1 -1
  125. package/dist/native/proxy.d.ts +4 -2
  126. package/dist/native/proxy.js +56 -53
  127. package/dist/native/proxy.js.map +1 -1
  128. package/dist/native/tokenExpiry.d.ts +22 -0
  129. package/dist/native/tokenExpiry.js +153 -0
  130. package/dist/native/tokenExpiry.js.map +1 -0
  131. package/dist/routes/assets.js +3 -3
  132. package/dist/routes/assets.js.map +1 -1
  133. package/dist/routes/auth.js +9 -13
  134. package/dist/routes/auth.js.map +1 -1
  135. package/dist/routes/components.js +2 -2
  136. package/dist/routes/components.js.map +1 -1
  137. package/dist/routes/config/channels/endpoints/browse.d.ts +6 -0
  138. package/dist/routes/config/channels/endpoints/browse.js +195 -0
  139. package/dist/routes/config/channels/endpoints/browse.js.map +1 -0
  140. package/dist/routes/config/channels/endpoints/bulk.d.ts +6 -0
  141. package/dist/routes/config/channels/endpoints/bulk.js +128 -0
  142. package/dist/routes/config/channels/endpoints/bulk.js.map +1 -0
  143. package/dist/routes/config/channels/endpoints/crud.d.ts +6 -0
  144. package/dist/routes/config/channels/endpoints/crud.js +504 -0
  145. package/dist/routes/config/channels/endpoints/crud.js.map +1 -0
  146. package/dist/routes/config/channels/endpoints/importExport.d.ts +6 -0
  147. package/dist/routes/config/channels/endpoints/importExport.js +131 -0
  148. package/dist/routes/config/channels/endpoints/importExport.js.map +1 -0
  149. package/dist/routes/config/channels/endpoints/predefined.d.ts +6 -0
  150. package/dist/routes/config/channels/endpoints/predefined.js +78 -0
  151. package/dist/routes/config/channels/endpoints/predefined.js.map +1 -0
  152. package/dist/routes/config/channels/endpoints/prefs.d.ts +6 -0
  153. package/dist/routes/config/channels/endpoints/prefs.js +58 -0
  154. package/dist/routes/config/channels/endpoints/prefs.js.map +1 -0
  155. package/dist/routes/config/channels/endpoints/service.d.ts +6 -0
  156. package/dist/routes/config/channels/endpoints/service.js +149 -0
  157. package/dist/routes/config/channels/endpoints/service.js.map +1 -0
  158. package/dist/routes/config/channels/endpoints/tags.d.ts +6 -0
  159. package/dist/routes/config/channels/endpoints/tags.js +185 -0
  160. package/dist/routes/config/channels/endpoints/tags.js.map +1 -0
  161. package/dist/routes/config/channels/http/handler.d.ts +10 -0
  162. package/dist/routes/config/channels/http/handler.js +20 -0
  163. package/dist/routes/config/channels/http/handler.js.map +1 -0
  164. package/dist/routes/config/channels/http/playlistHint.d.ts +25 -0
  165. package/dist/routes/config/channels/http/playlistHint.js +40 -0
  166. package/dist/routes/config/channels/http/playlistHint.js.map +1 -0
  167. package/dist/routes/config/channels/http/serviceWarning.d.ts +10 -0
  168. package/dist/routes/config/channels/http/serviceWarning.js +28 -0
  169. package/dist/routes/config/channels/http/serviceWarning.js.map +1 -0
  170. package/dist/routes/config/channels/index.d.ts +3 -3
  171. package/dist/routes/config/channels/index.js +2 -2
  172. package/dist/routes/config/channels/index.js.map +1 -1
  173. package/dist/routes/config/channels/{routes.d.ts → setup.d.ts} +1 -1
  174. package/dist/routes/config/channels/setup.js +23 -0
  175. package/dist/routes/config/channels/setup.js.map +1 -0
  176. package/dist/routes/config/channels/table.d.ts +13 -13
  177. package/dist/routes/config/channels/table.js +291 -184
  178. package/dist/routes/config/channels/table.js.map +1 -1
  179. package/dist/routes/config/http/envelope.d.ts +111 -0
  180. package/dist/routes/config/http/envelope.js +103 -0
  181. package/dist/routes/config/http/envelope.js.map +1 -0
  182. package/dist/routes/config/index.d.ts +9 -9
  183. package/dist/routes/config/index.js +5 -5
  184. package/dist/routes/config/index.js.map +1 -1
  185. package/dist/routes/config/{providers.js → services.js} +105 -99
  186. package/dist/routes/config/services.js.map +1 -0
  187. package/dist/routes/config/settings.d.ts +1 -1
  188. package/dist/routes/config/settings.js +85 -60
  189. package/dist/routes/config/settings.js.map +1 -1
  190. package/dist/routes/debug.js +17 -18
  191. package/dist/routes/debug.js.map +1 -1
  192. package/dist/routes/health.js +1 -1
  193. package/dist/routes/health.js.map +1 -1
  194. package/dist/routes/icons.js +2 -2
  195. package/dist/routes/icons.js.map +1 -1
  196. package/dist/routes/index.js +2 -2
  197. package/dist/routes/index.js.map +1 -1
  198. package/dist/routes/logs.js +23 -34
  199. package/dist/routes/logs.js.map +1 -1
  200. package/dist/routes/mpegts.js +1 -1
  201. package/dist/routes/playlist.d.ts +5 -5
  202. package/dist/routes/playlist.js +84 -61
  203. package/dist/routes/playlist.js.map +1 -1
  204. package/dist/routes/root/content.js +82 -61
  205. package/dist/routes/root/content.js.map +1 -1
  206. package/dist/routes/root/index.js +2 -5
  207. package/dist/routes/root/index.js.map +1 -1
  208. package/dist/routes/root/scripts/channels.js +538 -581
  209. package/dist/routes/root/scripts/channels.js.map +1 -1
  210. package/dist/routes/root/scripts/config.js +868 -1022
  211. package/dist/routes/root/scripts/config.js.map +1 -1
  212. package/dist/routes/root/scripts/index.d.ts +4 -4
  213. package/dist/routes/root/scripts/shared.js +445 -197
  214. package/dist/routes/root/scripts/shared.js.map +1 -1
  215. package/dist/routes/root/scripts/status.d.ts +4 -0
  216. package/dist/routes/root/scripts/status.handlers.d.ts +182 -0
  217. package/dist/routes/root/scripts/status.handlers.js +713 -0
  218. package/dist/routes/root/scripts/status.handlers.js.map +1 -0
  219. package/dist/routes/root/scripts/status.js +85 -436
  220. package/dist/routes/root/scripts/status.js.map +1 -1
  221. package/dist/routes/root/styles.js +16 -8
  222. package/dist/routes/root/styles.js.map +1 -1
  223. package/dist/routes/services.d.ts +6 -0
  224. package/dist/routes/{providers.js → services.js} +58 -55
  225. package/dist/routes/services.js.map +1 -0
  226. package/dist/routes/sse.d.ts +16 -0
  227. package/dist/routes/sse.js +29 -0
  228. package/dist/routes/sse.js.map +1 -0
  229. package/dist/routes/streams.js +10 -23
  230. package/dist/routes/streams.js.map +1 -1
  231. package/dist/routes/ui.d.ts +2 -2
  232. package/dist/routes/ui.js +2 -0
  233. package/dist/routes/ui.js.map +1 -1
  234. package/dist/routes/upgrade.js +6 -7
  235. package/dist/routes/upgrade.js.map +1 -1
  236. package/dist/service/commands.context.d.ts +6 -0
  237. package/dist/service/commands.context.js +47 -0
  238. package/dist/service/commands.context.js.map +1 -0
  239. package/dist/service/commands.d.ts +53 -8
  240. package/dist/service/commands.js +172 -192
  241. package/dist/service/commands.js.map +1 -1
  242. package/dist/service/generators.context.d.ts +6 -0
  243. package/dist/service/generators.context.js +45 -0
  244. package/dist/service/generators.context.js.map +1 -0
  245. package/dist/service/generators.d.ts +56 -16
  246. package/dist/service/generators.js +509 -291
  247. package/dist/service/generators.js.map +1 -1
  248. package/dist/service/index.d.ts +2 -2
  249. package/dist/streaming/clients.d.ts +2 -2
  250. package/dist/streaming/clients.js +2 -2
  251. package/dist/streaming/codec.d.ts +2 -2
  252. package/dist/streaming/codec.js +0 -1
  253. package/dist/streaming/codec.js.map +1 -1
  254. package/dist/streaming/fmp4Segmenter.d.ts +2 -2
  255. package/dist/streaming/fmp4Segmenter.js +12 -12
  256. package/dist/streaming/hls.d.ts +7 -7
  257. package/dist/streaming/hls.js +104 -92
  258. package/dist/streaming/hls.js.map +1 -1
  259. package/dist/streaming/hlsResume.d.ts +3 -3
  260. package/dist/streaming/hlsResume.js +6 -6
  261. package/dist/streaming/hlsResume.js.map +1 -1
  262. package/dist/streaming/hlsSegments.d.ts +5 -2
  263. package/dist/streaming/hlsSegments.js +14 -9
  264. package/dist/streaming/hlsSegments.js.map +1 -1
  265. package/dist/streaming/monitor.d.ts +12 -12
  266. package/dist/streaming/monitor.js +98 -66
  267. package/dist/streaming/monitor.js.map +1 -1
  268. package/dist/streaming/mp4Parser.d.ts +34 -4
  269. package/dist/streaming/mp4Parser.js +111 -29
  270. package/dist/streaming/mp4Parser.js.map +1 -1
  271. package/dist/streaming/mpegts.d.ts +1 -1
  272. package/dist/streaming/mpegts.js +13 -10
  273. package/dist/streaming/mpegts.js.map +1 -1
  274. package/dist/streaming/playlistBuilder.d.ts +1 -1
  275. package/dist/streaming/playlistBuilder.js +2 -2
  276. package/dist/streaming/preroll.d.ts +9 -9
  277. package/dist/streaming/preroll.js +40 -42
  278. package/dist/streaming/preroll.js.map +1 -1
  279. package/dist/streaming/pretune.js +4 -4
  280. package/dist/streaming/pretune.js.map +1 -1
  281. package/dist/streaming/recovery.d.ts +3 -3
  282. package/dist/streaming/recovery.js +4 -4
  283. package/dist/streaming/registry.d.ts +6 -6
  284. package/dist/streaming/registry.js +4 -8
  285. package/dist/streaming/registry.js.map +1 -1
  286. package/dist/streaming/setup.d.ts +11 -10
  287. package/dist/streaming/setup.js +120 -71
  288. package/dist/streaming/setup.js.map +1 -1
  289. package/dist/streaming/showInfo.d.ts +9 -3
  290. package/dist/streaming/showInfo.js +33 -27
  291. package/dist/streaming/showInfo.js.map +1 -1
  292. package/dist/streaming/statusEmitter.d.ts +8 -7
  293. package/dist/streaming/statusEmitter.js +5 -5
  294. package/dist/streaming/statusEmitter.js.map +1 -1
  295. package/dist/types/channels.d.ts +139 -36
  296. package/dist/types/channels.js +39 -1
  297. package/dist/types/channels.js.map +1 -1
  298. package/dist/types/config.d.ts +22 -3
  299. package/dist/types/index.d.ts +9 -8
  300. package/dist/types/index.js +1 -0
  301. package/dist/types/index.js.map +1 -1
  302. package/dist/types/profiles.d.ts +14 -13
  303. package/dist/types/selection.d.ts +49 -5
  304. package/dist/types/shared.d.ts +1 -1
  305. package/dist/types/streaming.d.ts +2 -2
  306. package/dist/upgrade/commands.context.d.ts +6 -0
  307. package/dist/upgrade/commands.context.js +29 -0
  308. package/dist/upgrade/commands.context.js.map +1 -0
  309. package/dist/upgrade/commands.d.ts +29 -2
  310. package/dist/upgrade/commands.js +87 -134
  311. package/dist/upgrade/commands.js.map +1 -1
  312. package/dist/upgrade/detection.context.d.ts +6 -0
  313. package/dist/upgrade/detection.context.js +24 -0
  314. package/dist/upgrade/detection.context.js.map +1 -0
  315. package/dist/upgrade/detection.d.ts +140 -10
  316. package/dist/upgrade/detection.js +153 -66
  317. package/dist/upgrade/detection.js.map +1 -1
  318. package/dist/upgrade/index.d.ts +2 -2
  319. package/dist/utils/chromeFetch.d.ts +1 -1
  320. package/dist/utils/chromeFetch.js +2 -2
  321. package/dist/utils/clock.d.ts +14 -0
  322. package/dist/utils/clock.js +28 -0
  323. package/dist/utils/clock.js.map +1 -0
  324. package/dist/utils/debugFilter.d.ts +1 -1
  325. package/dist/utils/debugFilter.js +8 -6
  326. package/dist/utils/debugFilter.js.map +1 -1
  327. package/dist/utils/delay.d.ts +2 -3
  328. package/dist/utils/delay.js +10 -16
  329. package/dist/utils/delay.js.map +1 -1
  330. package/dist/utils/evaluate.js +12 -14
  331. package/dist/utils/evaluate.js.map +1 -1
  332. package/dist/utils/ffmpeg.context.d.ts +6 -0
  333. package/dist/utils/ffmpeg.context.js +31 -0
  334. package/dist/utils/ffmpeg.context.js.map +1 -0
  335. package/dist/utils/ffmpeg.d.ts +85 -29
  336. package/dist/utils/ffmpeg.js +126 -129
  337. package/dist/utils/ffmpeg.js.map +1 -1
  338. package/dist/utils/fileLogger.d.ts +18 -2
  339. package/dist/utils/fileLogger.js +60 -31
  340. package/dist/utils/fileLogger.js.map +1 -1
  341. package/dist/utils/format.d.ts +7 -1
  342. package/dist/utils/format.js +22 -2
  343. package/dist/utils/format.js.map +1 -1
  344. package/dist/utils/html.js +1 -3
  345. package/dist/utils/html.js.map +1 -1
  346. package/dist/utils/index.d.ts +21 -20
  347. package/dist/utils/index.js +1 -0
  348. package/dist/utils/index.js.map +1 -1
  349. package/dist/utils/logger.js +31 -45
  350. package/dist/utils/logger.js.map +1 -1
  351. package/dist/utils/m3u.d.ts +18 -3
  352. package/dist/utils/m3u.js +28 -14
  353. package/dist/utils/m3u.js.map +1 -1
  354. package/dist/utils/memo.d.ts +19 -0
  355. package/dist/utils/memo.js +53 -0
  356. package/dist/utils/memo.js.map +1 -0
  357. package/dist/utils/morganStream.js +4 -4
  358. package/dist/utils/morganStream.js.map +1 -1
  359. package/dist/utils/pid.d.ts +2 -2
  360. package/dist/utils/pid.js +2 -2
  361. package/dist/utils/platform.d.ts +1 -1
  362. package/dist/utils/platform.js +3 -2
  363. package/dist/utils/platform.js.map +1 -1
  364. package/dist/utils/retry.d.ts +2 -0
  365. package/dist/utils/retry.js +9 -7
  366. package/dist/utils/retry.js.map +1 -1
  367. package/dist/utils/sanitize.d.ts +2 -2
  368. package/dist/utils/sanitize.js +4 -4
  369. package/dist/utils/streamContext.d.ts +7 -0
  370. package/dist/utils/streamContext.js +10 -1
  371. package/dist/utils/streamContext.js.map +1 -1
  372. package/dist/utils/timing.d.ts +6 -3
  373. package/dist/utils/timing.js +10 -6
  374. package/dist/utils/timing.js.map +1 -1
  375. package/dist/utils/version.d.ts +3 -2
  376. package/dist/utils/version.js +15 -28
  377. package/dist/utils/version.js.map +1 -1
  378. package/package.json +14 -10
  379. package/dist/config/providerPacks.d.ts +0 -46
  380. package/dist/config/providerPacks.js.map +0 -1
  381. package/dist/config/providers.d.ts +0 -175
  382. package/dist/config/providers.js +0 -691
  383. package/dist/config/providers.js.map +0 -1
  384. package/dist/native/intercept.d.ts +0 -35
  385. package/dist/native/intercept.js +0 -184
  386. package/dist/native/intercept.js.map +0 -1
  387. package/dist/routes/config/channels/routes.js +0 -1477
  388. package/dist/routes/config/channels/routes.js.map +0 -1
  389. package/dist/routes/config/providers.js.map +0 -1
  390. package/dist/routes/providers.d.ts +0 -6
  391. package/dist/routes/providers.js.map +0 -1
  392. /package/dist/routes/config/{providers.d.ts → services.d.ts} +0 -0
@@ -1,30 +1,30 @@
1
1
  /* Copyright(C) 2024-2026, HJD (https://github.com/hjdhjd). All rights reserved.
2
2
  *
3
- * precaching.ts: Provider channel lineup precaching for PrismCast.
3
+ * precaching.ts: Service channel lineup precaching for PrismCast.
4
4
  */
5
5
  import { LOG, extractDomain, formatError, startTimer } from "../utils/index.js";
6
6
  import { getCurrentBrowser, minimizeBrowserWindow, registerManagedPage, unregisterManagedPage } from "./index.js";
7
7
  import { CONFIG } from "../config/index.js";
8
8
  import { getProviderBySlug } from "./channelSelection.js";
9
9
  import { markDomainAuth } from "../config/health.js";
10
- /* Precaching discovers channel lineups for selected providers at startup so that even the first tune benefits from cached lineup data. Each provider is precached
11
- * sequentially — discovery opens a browser page and navigates to a heavy SPA, so running all providers concurrently would stress CPU and GPU on resource-constrained
10
+ /* Precaching discovers channel lineups for selected services at startup so that even the first tune benefits from cached lineup data. Each service is precached
11
+ * sequentially - discovery opens a browser page and navigates to a heavy SPA, so running all services concurrently would stress CPU and GPU on resource-constrained
12
12
  * systems. The HTTP server starts immediately; precaching begins in the background after a brief delay.
13
13
  *
14
14
  * Precaching is triggered from launchBrowser() in browser/index.ts. This covers both initial server startup and browser crash recovery (where all caches are cleared).
15
- * Each provider has its own try/catch — one failure does not stop the rest. The browser reference is obtained per-provider via getCurrentBrowser() so that a browser
16
- * crash between providers is handled transparently (the next provider gets the relaunched browser).
15
+ * Each service has its own try/catch - one failure does not stop the rest. The browser reference is obtained per-service via getCurrentBrowser() so that a browser
16
+ * crash between services is handled transparently (the next service gets the relaunched browser).
17
17
  */
18
18
  // Delay in milliseconds before precaching begins after browser launch. This gives the browser time to settle after initialization.
19
19
  const PRECACHE_DELAY = 5000;
20
20
  // Guard flag preventing overlapping precache cycles. Set to true before the cycle starts, cleared in a finally block.
21
21
  let precacheInProgress = false;
22
22
  /**
23
- * Starts the precaching cycle if providers are configured. Called from launchBrowser() after the browser is ready. If no providers are selected, or a precache cycle
23
+ * Starts the precaching cycle if services are configured. Called from launchBrowser() after the browser is ready. If no services are selected, or a precache cycle
24
24
  * is already in progress, returns immediately. The actual work is scheduled via setTimeout to avoid blocking browser launch.
25
25
  */
26
26
  export function startPrecaching() {
27
- if (CONFIG.channels.precacheProviders.length === 0) {
27
+ if (CONFIG.channels.precacheServices.length === 0) {
28
28
  return;
29
29
  }
30
30
  if (precacheInProgress) {
@@ -37,39 +37,39 @@ export function startPrecaching() {
37
37
  setTimeout(() => void runPrecacheCycle(), PRECACHE_DELAY);
38
38
  }
39
39
  /**
40
- * Executes the sequential precaching cycle. Discovers channel lineups for each configured provider, clearing the provider's cache first to ensure a complete walk.
41
- * Providers not in the active provider filter are silently skipped when the filter is non-empty.
40
+ * Executes the sequential precaching cycle. Discovers channel lineups for each configured service, clearing the service's cache first to ensure a complete walk.
41
+ * Services not in the active service filter are silently skipped when the filter is non-empty.
42
42
  */
43
43
  async function runPrecacheCycle() {
44
- const slugs = CONFIG.channels.precacheProviders;
45
- const enabledFilter = CONFIG.channels.enabledProviders;
44
+ const slugs = CONFIG.channels.precacheServices;
45
+ const enabledFilter = CONFIG.channels.enabledServices;
46
46
  const hasFilter = enabledFilter.length > 0;
47
47
  const cycleElapsed = startTimer();
48
48
  let skipped = 0;
49
49
  let succeeded = 0;
50
- LOG.info("Starting channel lineup precaching for %d provider%s.", slugs.length, (slugs.length === 1) ? "" : "s");
50
+ LOG.info("Starting channel lineup precaching for %d service%s.", slugs.length, (slugs.length === 1) ? "" : "s");
51
51
  try {
52
- // Providers are precached sequentially — each opens a browser page and navigates to a heavy SPA, so concurrent execution would stress system resources.
52
+ // Services are precached sequentially - each opens a browser page and navigates to a heavy SPA, so concurrent execution would stress system resources.
53
53
  for (const slug of slugs) {
54
54
  const provider = getProviderBySlug(slug);
55
55
  if (!provider) {
56
56
  continue;
57
57
  }
58
- // Skip providers not in the active provider filter. Their stored config is preserved for when the filter changes back.
58
+ // Skip services not in the active service filter. Their stored config is preserved for when the filter changes back.
59
59
  if (hasFilter && !enabledFilter.includes(slug)) {
60
- LOG.debug("precache", "Skipping precache for %s: not in active provider filter.", provider.label);
60
+ LOG.debug("precache", "Skipping precache for %s: not in active service filter.", provider.label);
61
61
  skipped++;
62
62
  continue;
63
63
  }
64
- const providerElapsed = startTimer();
64
+ const serviceElapsed = startTimer();
65
65
  try {
66
- // Clear the provider's cache before discovery to ensure a complete walk, even if a tune partially warmed the cache during the startup delay.
66
+ // Clear the service's cache before discovery to ensure a complete walk, even if a tune partially warmed the cache during the startup delay.
67
67
  provider.strategy.clearCache?.();
68
68
  // eslint-disable-next-line no-await-in-loop
69
69
  const browser = await getCurrentBrowser();
70
70
  // eslint-disable-next-line no-await-in-loop
71
71
  const page = await browser.newPage();
72
- // Suppress audio on precache pages. Providers like Hulu auto-play a default livestream when their guide loads. Since Chrome's --mute-audio is deliberately
72
+ // Suppress audio on precache pages. Services like Hulu auto-play a default livestream when their guide loads. Since Chrome's --mute-audio is deliberately
73
73
  // disabled (puppeteer-stream needs audio capture for active streams), we intercept play() at the prototype level to mute before any media element can produce
74
74
  // audio. evaluateOnNewDocument runs before site JavaScript, so nothing slips through.
75
75
  // eslint-disable-next-line no-await-in-loop
@@ -83,16 +83,16 @@ async function runPrecacheCycle() {
83
83
  });
84
84
  registerManagedPage(page);
85
85
  try {
86
- // Navigate to the provider's guide URL unless the provider handles its own navigation (e.g., sets up response interception before navigating).
86
+ // Navigate to the service's guide URL unless the provider module handles its own navigation (e.g., sets up response interception before navigating).
87
87
  if (!provider.handlesOwnNavigation) {
88
88
  // eslint-disable-next-line no-await-in-loop
89
89
  await page.goto(provider.guideUrl, { timeout: CONFIG.streaming.navigationTimeout, waitUntil: "networkidle2" });
90
90
  }
91
91
  // eslint-disable-next-line no-await-in-loop
92
92
  const channels = await provider.discoverChannels(page);
93
- LOG.info("Precached %s: %d channels (%ss).", provider.label, channels.length, (providerElapsed() / 1000).toFixed(1).replace(/\.0$/, ""));
94
- // A successful discovery with results proves the provider is accessible and authenticated. Mark it so the UI shows the green indicator immediately
95
- // rather than waiting for the first manual tune. When a provider defines validatePrecache, defer to it — some providers (e.g., Sling) return guide data
93
+ LOG.info("Precached %s: %d channels (%ss).", provider.label, channels.length, (serviceElapsed() / 1000).toFixed(1).replace(/\.0$/, ""));
94
+ // A successful discovery with results proves the service is accessible and authenticated. Mark it so the UI shows the green indicator immediately
95
+ // rather than waiting for the first manual tune. When a provider module defines validatePrecache, defer to it - some services (e.g., Sling) return guide data
96
96
  // even without authentication, so a non-empty result alone does not prove paid access.
97
97
  if ((channels.length > 0) && (!provider.validatePrecache || provider.validatePrecache(channels))) {
98
98
  markDomainAuth(extractDomain(provider.guideUrl));
@@ -119,7 +119,7 @@ async function runPrecacheCycle() {
119
119
  }
120
120
  const elapsed = (cycleElapsed() / 1000).toFixed(1).replace(/\.0$/, "");
121
121
  const skippedSuffix = skipped > 0 ? ", " + String(skipped) + " skipped (filtered)" : "";
122
- LOG.info("Channel lineup precaching complete: %d provider%s cached%s in %ss.", succeeded, (succeeded === 1) ? "" : "s", skippedSuffix, elapsed);
122
+ LOG.info("Channel lineup precaching complete: %d service%s cached%s in %ss.", succeeded, (succeeded === 1) ? "" : "s", skippedSuffix, elapsed);
123
123
  }
124
124
  finally {
125
125
  precacheInProgress = false;
@@ -1 +1 @@
1
- {"version":3,"file":"precaching.js","sourceRoot":"","sources":["../../src/browser/precaching.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"precaching.js","sourceRoot":"","sources":["../../src/browser/precaching.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { ChannelSelectionStrategy, ProviderModule } from "../../types/index.js";
1
+ import type { ChannelSelectionStrategy, ProviderModule } from "../../types/index.ts";
2
2
  /**
3
3
  * Configuration for creating a Comcast Polymer SPA provider instance. Each field parameterizes a provider-specific value that differs between Xfinity Stream
4
4
  * and Cox Contour TV while sharing all underlying Polymer SPA logic.
@@ -1,2 +1,2 @@
1
- import type { ProviderModule } from "../../types/index.js";
1
+ import type { ProviderModule } from "../../types/index.ts";
2
2
  export declare const coxProvider: ProviderModule;
@@ -1,2 +1,2 @@
1
- import type { ProviderModule } from "../../types/index.js";
1
+ import type { ProviderModule } from "../../types/index.ts";
2
2
  export declare const directvProvider: ProviderModule;
@@ -11,7 +11,7 @@ let directvFullyDiscovered = false;
11
11
  // Tracks which pages have console listeners registered to avoid duplicate registrations.
12
12
  const directvPagesWithListeners = new WeakSet();
13
13
  const pendingTunes = new WeakMap();
14
- // DirecTV guide URL. All tunes navigate here — the webpack interceptor handles channel selection during page load.
14
+ // DirecTV guide URL. All tunes navigate here - the webpack interceptor handles channel selection during page load.
15
15
  const DIRECTV_GUIDE_URL = "https://stream.directv.com/guide";
16
16
  // Maximum time to wait for the webpack interceptor to emit a tune result. This covers the time from page navigation start through webpack chunk interception,
17
17
  // React fiber tree traversal, Redux store discovery, channel matching, and playConsumable dispatch. Validated tunes complete in 2-5 seconds; eight seconds
@@ -26,7 +26,7 @@ const DISCOVERY_TIMEOUT = 15000;
26
26
  const DIRECTV_LOCAL_NETWORKS = new Set(["abc", "cbs", "cw", "fox", "nbc", "pbs"]);
27
27
  /**
28
28
  * Clears all DirecTV state: the unified channel cache and the fully-discovered flag. Called by clearChannelSelectionCaches() in the coordinator when the browser
29
- * restarts, since cached state may be stale in a new browser session. The pendingTunes WeakMap is self-cleaning — entries are GC'd when their Page references are
29
+ * restarts, since cached state may be stale in a new browser session. The pendingTunes WeakMap is self-cleaning - entries are GC'd when their Page references are
30
30
  * released after page close.
31
31
  */
32
32
  function clearDirectvCache() {
@@ -71,8 +71,8 @@ function processChannelLineup(json) {
71
71
  if (count > 0) {
72
72
  directvFullyDiscovered = true;
73
73
  LOG.debug("tuning:directv", "Channel lineup populated: %s channels.", count);
74
- // Cross-reference local affiliates so the short network name (e.g., "abc") resolves to the affiliate's cache entry (e.g., "abc-wabc"). This mirrors Hulu's
75
- // affiliate cache aliasing (hulu.ts:1089-1098) and ensures warm-cache diagnostic logging works for local channels.
74
+ // Cross-reference local affiliates so the short network name (e.g., "abc") resolves to the affiliate's cache entry (e.g., "abc-wabc"). This mirrors the
75
+ // affiliate-aliasing pattern used in the Hulu provider's channel cache and ensures warm-cache diagnostic logging works for local channels.
76
76
  for (const network of DIRECTV_LOCAL_NETWORKS) {
77
77
  // Skip if the network name itself is already an exact cache key (unlikely on DirecTV, but defensive).
78
78
  if (directvChannelCache.has(network)) {
@@ -86,10 +86,11 @@ function processChannelLineup(json) {
86
86
  affiliates.push({ entry, key });
87
87
  }
88
88
  }
89
- if (affiliates.length > 0) {
90
- affiliates.sort((a, b) => a.key.localeCompare(b.key));
91
- directvChannelCache.set(network, affiliates[0].entry);
92
- LOG.debug("tuning:directv", "Cross-referenced cache: %s -> %s.", network, affiliates[0].entry.displayName);
89
+ affiliates.sort((a, b) => a.key.localeCompare(b.key));
90
+ const primaryAffiliate = affiliates[0];
91
+ if (primaryAffiliate) {
92
+ directvChannelCache.set(network, primaryAffiliate.entry);
93
+ LOG.debug("tuning:directv", "Cross-referenced cache: %s -> %s.", network, primaryAffiliate.entry.displayName);
93
94
  }
94
95
  }
95
96
  }
@@ -112,12 +113,12 @@ function setupConsoleListeners(page) {
112
113
  processChannelLineup(text.slice("[DIRECTV-CHANNELS] ".length));
113
114
  return;
114
115
  }
115
- // Tune success signal — the playConsumable dispatch settled successfully (synchronous action or async thunk resolution).
116
+ // Tune success signal - the playConsumable dispatch settled successfully (synchronous action or async thunk resolution).
116
117
  if (text.startsWith("[DIRECTV-TUNE-OK]")) {
117
118
  pendingTunes.get(page)?.resolve(true);
118
119
  return;
119
120
  }
120
- // Tune failure signal — the playConsumable dispatch failed or the target channel was not found in the Redux store.
121
+ // Tune failure signal - the playConsumable dispatch failed or the target channel was not found in the Redux store.
121
122
  if (text.startsWith("[DIRECTV-TUNE-FAIL]")) {
122
123
  LOG.debug("tuning:directv", "Tune failure signal: %s.", text);
123
124
  pendingTunes.get(page)?.resolve(false);
@@ -161,7 +162,7 @@ async function installDirectTuneInterceptor(page, channelName, discoverOnly) {
161
162
  let wreq = null;
162
163
  // Phase 2: Wrap webpackChunk push to capture __webpack_require__. This wrapper intercepts natural chunk pushes and captures __webpack_require__ from any chunk
163
164
  // that includes an entry point callback (the optional third element). DirecTV's current webpack build uses 2-element chunks without callbacks, so this wrapper
164
- // serves as defense-in-depth — the primary capture mechanism is the synthetic chunk push in Phase 4. If a future DirecTV deploy changes the chunk format, this
165
+ // serves as defense-in-depth - the primary capture mechanism is the synthetic chunk push in Phase 4. If a future DirecTV deploy changes the chunk format, this
165
166
  // wrapper captures wreq early without needing a code change.
166
167
  const chunkArrayName = "webpackChunk_directv_web";
167
168
  // Ensure the chunk array exists before wrapping.
@@ -203,7 +204,7 @@ async function installDirectTuneInterceptor(page, channelName, discoverOnly) {
203
204
  // One-shot diagnostic flags for the poll loop. Prevent flooding the console when the poll retries repeatedly.
204
205
  let mountPointLogged = false;
205
206
  let bfsExhaustedLogged = false;
206
- // Tracks when the page body first has children (DOM content loaded). Used for milestone-based early exit — if the page has rendered content but no React
207
+ // Tracks when the page body first has children (DOM content loaded). Used for milestone-based early exit - if the page has rendered content but no React
207
208
  // mount point exists after MOUNT_SEARCH_TIMEOUT, the SPA structure is different from what we expect and continuing to poll is pointless.
208
209
  let pageContentTime = 0;
209
210
  const MOUNT_SEARCH_TIMEOUT = 5000;
@@ -215,11 +216,11 @@ async function installDirectTuneInterceptor(page, channelName, discoverOnly) {
215
216
  if ((Date.now() - pollStart) > MAX_POLL_TIME) {
216
217
  clearInterval(pollTimer);
217
218
  // eslint-disable-next-line no-console
218
- console.log("[DIRECTV-DIAG] Overall poll timeout (" + String(MAX_POLL_TIME) + "ms) — " +
219
+ console.log("[DIRECTV-DIAG] Overall poll timeout (" + String(MAX_POLL_TIME) + "ms) - " +
219
220
  (cachedStore ? "Redux store found but channel lineup never populated." : "Redux store not found."));
220
221
  if (!discoverOnlyFlag) {
221
222
  // eslint-disable-next-line no-console
222
- console.log("[DIRECTV-TUNE-FAIL] Timed out — " +
223
+ console.log("[DIRECTV-TUNE-FAIL] Timed out - " +
223
224
  (cachedStore ? "channel lineup never populated in Redux store." : "React mount point or Redux store not found."));
224
225
  }
225
226
  return;
@@ -228,7 +229,7 @@ async function installDirectTuneInterceptor(page, channelName, discoverOnly) {
228
229
  // asynchronously by the SPA's API calls. We cache the store reference and continue polling for channel data in Phase 3b below.
229
230
  if (!cachedStore) {
230
231
  // Find the React mount point. Try the known DirecTV ID first (fast path), then scan body's direct children for any element with React fiber
231
- // properties. This resilient approach survives element ID changes — if DirecTV renames their root container, the body scan finds it automatically.
232
+ // properties. This resilient approach survives element ID changes - if DirecTV renames their root container, the body scan finds it automatically.
232
233
  let mountEl = document.getElementById("app-root");
233
234
  let reactKey = mountEl ? findReactKey(mountEl) : undefined;
234
235
  if (!reactKey) {
@@ -263,12 +264,12 @@ async function installDirectTuneInterceptor(page, channelName, discoverOnly) {
263
264
  mountPointLogged = true;
264
265
  // eslint-disable-next-line no-console
265
266
  console.log("[DIRECTV-DIAG] React mount: <" + mountEl.tagName.toLowerCase() + " id=\"" + (mountEl.id || "") + "\"> via " +
266
- reactKey.split("$")[0] + "$.");
267
+ (reactKey.split("$")[0] ?? "") + "$.");
267
268
  }
268
269
  // Get the fiber root from the React property. __reactContainer$ returns an internal root object whose .current property is the actual fiber node.
269
270
  // __reactFiber$ returns the fiber directly.
270
271
  const value = mountEl[reactKey];
271
- const fiberRoot = (value && (typeof value.current === "object")) ? value.current : value ?? null;
272
+ const fiberRoot = (value && (typeof value["current"] === "object")) ? value["current"] : value ?? null;
272
273
  if (!fiberRoot) {
273
274
  return;
274
275
  }
@@ -285,18 +286,18 @@ async function installDirectTuneInterceptor(page, channelName, discoverOnly) {
285
286
  visited++;
286
287
  // Check for Redux store on pendingProps. React-Redux v7 attaches directly as pendingProps.store. React-Redux v8 uses React context, placing the
287
288
  // store at pendingProps.value.store on the context Provider fiber node.
288
- const props = node.pendingProps;
289
- const candidate = (props?.store ?? props?.value?.store);
290
- if (candidate && (typeof candidate.dispatch === "function") && (typeof candidate.getState === "function")) {
289
+ const props = node["pendingProps"];
290
+ const candidate = (props?.["store"] ?? props?.["value"]?.["store"]);
291
+ if (candidate && (typeof candidate["dispatch"] === "function") && (typeof candidate["getState"] === "function")) {
291
292
  store = candidate;
292
293
  break;
293
294
  }
294
295
  // Traverse fiber tree: child and sibling links.
295
- if (node.child) {
296
- queue.push(node.child);
296
+ if (node["child"]) {
297
+ queue.push(node["child"]);
297
298
  }
298
- if (node.sibling) {
299
- queue.push(node.sibling);
299
+ if (node["sibling"]) {
300
+ queue.push(node["sibling"]);
300
301
  }
301
302
  }
302
303
  if (!store) {
@@ -307,7 +308,7 @@ async function installDirectTuneInterceptor(page, channelName, discoverOnly) {
307
308
  }
308
309
  return;
309
310
  }
310
- // Cache the store reference for subsequent poll iterations. The store object is stable — Redux stores are never recreated.
311
+ // Cache the store reference for subsequent poll iterations. The store object is stable - Redux stores are never recreated.
311
312
  cachedStore = store;
312
313
  // eslint-disable-next-line no-console
313
314
  console.log("[DIRECTV-DIAG] Found Redux store after " + String(Date.now() - pollStart) + "ms (" + String(visited) + " nodes visited).");
@@ -317,34 +318,34 @@ async function installDirectTuneInterceptor(page, channelName, discoverOnly) {
317
318
  // Fallback paths cover plausible restructurings in future app versions.
318
319
  const state = cachedStore.getState();
319
320
  let channels = [];
320
- if (state.channels) {
321
- const channelsState = state.channels;
321
+ if (state["channels"]) {
322
+ const channelsState = state["channels"];
322
323
  // Primary path: channelArrays is the validated location for the channel lineup array.
323
- if (Array.isArray(channelsState.channelArrays)) {
324
- channels = channelsState.channelArrays;
324
+ if (Array.isArray(channelsState["channelArrays"])) {
325
+ channels = channelsState["channelArrays"];
325
326
  }
326
- else if (Array.isArray(channelsState.lineup)) {
327
- channels = channelsState.lineup;
327
+ else if (Array.isArray(channelsState["lineup"])) {
328
+ channels = channelsState["lineup"];
328
329
  }
329
- else if (Array.isArray(channelsState.channels)) {
330
- channels = channelsState.channels;
330
+ else if (Array.isArray(channelsState["channels"])) {
331
+ channels = channelsState["channels"];
331
332
  }
332
- else if (Array.isArray(channelsState.allChannels)) {
333
- channels = channelsState.allChannels;
333
+ else if (Array.isArray(channelsState["allChannels"])) {
334
+ channels = channelsState["allChannels"];
334
335
  }
335
336
  }
336
337
  // Fallback: some app versions may use a dedicated channelLineup reducer.
337
- if ((channels.length === 0) && state.channelLineup) {
338
- const lineup = state.channelLineup;
339
- if (Array.isArray(lineup.channelArrays)) {
340
- channels = lineup.channelArrays;
338
+ if ((channels.length === 0) && state["channelLineup"]) {
339
+ const lineup = state["channelLineup"];
340
+ if (Array.isArray(lineup["channelArrays"])) {
341
+ channels = lineup["channelArrays"];
341
342
  }
342
- else if (Array.isArray(lineup.channels)) {
343
- channels = lineup.channels;
343
+ else if (Array.isArray(lineup["channels"])) {
344
+ channels = lineup["channels"];
344
345
  }
345
346
  }
346
347
  if (channels.length === 0) {
347
- // The channel data hasn't arrived yet — the SPA is still fetching from DirecTV's API. Log once and continue polling.
348
+ // The channel data hasn't arrived yet - the SPA is still fetching from DirecTV's API. Log once and continue polling.
348
349
  if (!channelsEmptyLogged) {
349
350
  channelsEmptyLogged = true;
350
351
  // eslint-disable-next-line no-console
@@ -352,7 +353,7 @@ async function installDirectTuneInterceptor(page, channelName, discoverOnly) {
352
353
  }
353
354
  return;
354
355
  }
355
- // Channel data found — stop polling and proceed with emission and (optionally) tuning.
356
+ // Channel data found - stop polling and proceed with emission and (optionally) tuning.
356
357
  clearInterval(pollTimer);
357
358
  // eslint-disable-next-line no-console
358
359
  console.log("[DIRECTV-DIAG] Channel lineup populated: " + String(channels.length) + " channels (" + String(Date.now() - pollStart) + "ms total).");
@@ -370,10 +371,10 @@ async function installDirectTuneInterceptor(page, channelName, discoverOnly) {
370
371
  return;
371
372
  }
372
373
  // If wreq wasn't captured naturally from chunk callbacks (DirecTV's current webpack build uses 2-element chunks without entry point callbacks), use a
373
- // synthetic chunk push to capture __webpack_require__. By this point webpack has fully initialized — it replaced the chunk array's push method with its own
374
+ // synthetic chunk push to capture __webpack_require__. By this point webpack has fully initialized - it replaced the chunk array's push method with its own
374
375
  // webpackJsonpCallback handler during bootstrap. Pushing a synthetic chunk with an entry point callback (third element) triggers webpack to call it with
375
376
  // __webpack_require__ as the argument, giving us access to all loaded modules. The ref holder pattern isolates the synchronous callback mutation from
376
- // TypeScript's control flow narrowing — wreq is narrowed to null in this scope, so we capture into a holder and only assign after a null check.
377
+ // TypeScript's control flow narrowing - wreq is narrowed to null in this scope, so we capture into a holder and only assign after a null check.
377
378
  if (!wreq) {
378
379
  const wreqHolder = { ref: null };
379
380
  try {
@@ -392,7 +393,7 @@ async function installDirectTuneInterceptor(page, channelName, discoverOnly) {
392
393
  }
393
394
  else {
394
395
  // eslint-disable-next-line no-console
395
- console.log("[DIRECTV-DIAG] Synthetic chunk push did not trigger callback — webpack handler may not be in place.");
396
+ console.log("[DIRECTV-DIAG] Synthetic chunk push did not trigger callback - webpack handler may not be in place.");
396
397
  }
397
398
  }
398
399
  if (!wreq) {
@@ -419,7 +420,7 @@ async function installDirectTuneInterceptor(page, channelName, discoverOnly) {
419
420
  ", resourceId=" + (targetChannel.resourceId ?? "") + ").");
420
421
  // Find the playConsumable module by searching webpack module factories for the characteristic function signature. The factory source must contain both
421
422
  // "playConsumable" and "playAsset" to positively identify the module. Once found, load it via wreq(moduleId) which uses webpack's internal closure-based
422
- // cache and correctly resolves all closure dependencies. Manual factory execution (wreq.m[id](mod, exports, wreq)) does NOT work — it creates a fresh
423
+ // cache and correctly resolves all closure dependencies. Manual factory execution (wreq.m[id](mod, exports, wreq)) does NOT work - it creates a fresh
423
424
  // closure scope where minified dependency variables are uninitialized, causing "a is not a function" at call time.
424
425
  let playConsumableFn = null;
425
426
  for (const moduleId of Object.keys(wreq.m)) {
@@ -438,8 +439,8 @@ async function installDirectTuneInterceptor(page, channelName, discoverOnly) {
438
439
  // Load the module through webpack's own require function. This resolves all internal dependencies correctly via the closure-based module cache.
439
440
  try {
440
441
  const moduleExports = wreq(moduleId);
441
- if (typeof moduleExports.playConsumable === "function") {
442
- playConsumableFn = moduleExports.playConsumable;
442
+ if (typeof moduleExports["playConsumable"] === "function") {
443
+ playConsumableFn = moduleExports["playConsumable"];
443
444
  }
444
445
  else {
445
446
  // Search all exports for a function whose source mentions playConsumable.
@@ -467,9 +468,9 @@ async function installDirectTuneInterceptor(page, channelName, discoverOnly) {
467
468
  }
468
469
  // eslint-disable-next-line no-console
469
470
  console.log("[DIRECTV-DIAG] Dispatching playConsumable for " + (targetChannel.channelName ?? "") + ".");
470
- // Call playConsumable directly with the validated payload. The function is self-dispatching — it uses the dispatch and getState functions passed in the
471
+ // Call playConsumable directly with the validated payload. The function is self-dispatching - it uses the dispatch and getState functions passed in the
471
472
  // payload to internally dispatch Redux actions for authorization, CDN token generation, manifest construction, and player initialization. This is NOT a
472
- // Redux action creator — do not wrap the call in store.dispatch().
473
+ // Redux action creator - do not wrap the call in store.dispatch().
473
474
  try {
474
475
  playConsumableFn({
475
476
  consumable: {
@@ -498,22 +499,19 @@ async function installDirectTuneInterceptor(page, channelName, discoverOnly) {
498
499
  /**
499
500
  * Installs the DirecTV webpack interceptor before navigation. Called by the coordinator's resolveDirectUrl before page.goto fires. Always returns null so
500
501
  * channel selection runs directvGridStrategy, which awaits the interceptor result and falls back to logo click if it fails. The interceptor is installed
501
- * regardless of cache state — warm tunes dispatch playConsumable immediately, cold tunes discover the channel via Redux store extraction during page load.
502
+ * regardless of cache state - warm tunes dispatch playConsumable immediately, cold tunes discover the channel via Redux store extraction during page load.
502
503
  *
503
504
  * @param channelSelector - The channel selector string (e.g., "CNN", "ESPN").
504
505
  * @param page - The Puppeteer page for evaluateOnNewDocument installation and console listener setup.
505
- * @returns Always null — channel selection is never skipped.
506
+ * @returns Always null - channel selection is never skipped.
506
507
  */
507
508
  async function resolveDirectvDirectUrl(channelSelector, page) {
508
509
  const normalizedName = normalizeChannelName(channelSelector);
509
510
  // Set up console listeners for all tunes. Must be registered before navigation so we capture signals emitted during page load.
510
511
  setupConsoleListeners(page);
511
- // Create a per-page tune state. The console listener resolves this page's entry when [DIRECTV-TUNE-OK] or [DIRECTV-TUNE-FAIL] is emitted. We build the
512
- // TuneState object first so the resolve function is captured before any await yields control.
513
- const tuneState = {};
514
- tuneState.promise = new Promise((resolve) => {
515
- tuneState.resolve = resolve;
516
- });
512
+ // Create a per-page tune state. The console listener resolves this page's entry when [DIRECTV-TUNE-OK] or [DIRECTV-TUNE-FAIL] is emitted.
513
+ const { promise, resolve } = Promise.withResolvers();
514
+ const tuneState = { promise, resolve };
517
515
  pendingTunes.set(page, tuneState);
518
516
  // Install the webpack interceptor. It runs before any page JavaScript and captures __webpack_require__ from the first webpack chunk push.
519
517
  try {
@@ -549,7 +547,7 @@ async function directvGridStrategy(page, profile) {
549
547
  const tuneState = pendingTunes.get(page);
550
548
  if (tuneState) {
551
549
  // Race the tune promise against a timeout. The promise maps to discriminated string results so we can distinguish "interceptor reported failure" from
552
- // "no signal arrived" — critical for debugging whether the interceptor ran at all or stalled silently.
550
+ // "no signal arrived" - critical for debugging whether the interceptor ran at all or stalled silently.
553
551
  const result = await Promise.race([
554
552
  tuneState.promise.then((v) => v ? "success" : "failure"),
555
553
  delay(TUNE_TIMEOUT).then(() => "timeout")
@@ -574,10 +572,10 @@ async function directvGridStrategy(page, profile) {
574
572
  return await directvLogoClickFallback(page, channelName);
575
573
  }
576
574
  /**
577
- * Logo click fallback for DirecTV Stream. The guide's logo column is NOT virtualized — all ~152 channel logos are always in the DOM with
575
+ * Logo click fallback for DirecTV Stream. The guide's logo column is NOT virtualized - all ~152 channel logos are always in the DOM with
578
576
  * aria-label="view {channelName}" attributes. This function:
579
577
  * 1. Finds the logo element by aria-label
580
- * 2. Scrolls it into view and clicks it (DOM click, not coordinate-based — an invisible overlay blocks coordinate clicks)
578
+ * 2. Scrolls it into view and clicks it (DOM click, not coordinate-based - an invisible overlay blocks coordinate clicks)
581
579
  * 3. Waits for the mini-guide play button to appear
582
580
  * 4. Clicks the inner Pressable to start playback
583
581
  *
@@ -617,10 +615,8 @@ async function directvLogoClickFallback(page, channelName) {
617
615
  // the first alphabetically for deterministic selection consistent with the webpack interceptor and the Node-side cache alias in processChannelLineup.
618
616
  if (!logo) {
619
617
  const prefixMatches = logos.filter(({ label }) => label.startsWith(lowerName + "-"));
620
- if (prefixMatches.length > 0) {
621
- prefixMatches.sort((a, b) => a.label.localeCompare(b.label));
622
- logo = prefixMatches[0].el;
623
- }
618
+ prefixMatches.sort((a, b) => a.label.localeCompare(b.label));
619
+ logo = prefixMatches[0]?.el ?? null;
624
620
  }
625
621
  }
626
622
  if (!logo) {
@@ -636,7 +632,7 @@ async function directvLogoClickFallback(page, channelName) {
636
632
  // Log available channels from the logo column to help users identify the correct channelSelector value.
637
633
  const availableChannels = await page.evaluate(() => {
638
634
  const logos = document.querySelectorAll("[aria-label^=\"view \"]");
639
- return Array.from(logos).map((el) => (el.getAttribute("aria-label") ?? "").slice("view ".length)).filter((name) => name.length > 0).sort();
635
+ return Array.from(logos).map((el) => (el.getAttribute("aria-label") ?? "").slice("view ".length)).filter((name) => name.length > 0).toSorted();
640
636
  });
641
637
  logAvailableChannels({
642
638
  availableChannels,
@@ -723,7 +719,7 @@ async function discoverDirectvChannels(page) {
723
719
  }
724
720
  // Set up console listeners before navigation so we capture the [DIRECTV-CHANNELS] signal.
725
721
  setupConsoleListeners(page);
726
- // Install the interceptor in discover-only mode — skip the playConsumable tune phase.
722
+ // Install the interceptor in discover-only mode - skip the playConsumable tune phase.
727
723
  try {
728
724
  await installDirectTuneInterceptor(page, "", true);
729
725
  }
@@ -763,7 +759,7 @@ export const directvProvider = {
763
759
  handlesOwnNavigation: true,
764
760
  label: "DirecTV Stream",
765
761
  // Profile for DirecTV Stream (stream.directv.com) live guide. The guide page is a React Native for Web SPA where all ~152 channel logos are always in the DOM
766
- // (not virtualized). The primary tuning mechanism bypasses DOM interaction entirely by injecting into webpack internals — capturing __webpack_require__ via the
762
+ // (not virtualized). The primary tuning mechanism bypasses DOM interaction entirely by injecting into webpack internals - capturing __webpack_require__ via the
767
763
  // chunk push array, extracting the Redux store from the React fiber tree, and dispatching the playConsumable action to switch channels. The fallback uses logo
768
764
  // aria-label matching with DOM click (coordinate clicks are blocked by an invisible overlay). Uses selectReadyVideo because the page has multiple video elements.
769
765
  profile: {