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,4 +1,4 @@
1
- import { LOG, delay, evaluateWithAbort } from "../utils/index.js";
1
+ import { LOG, delay, evaluateWithAbort, formatError } from "../utils/index.js";
2
2
  import { getDomainConfig, registerProviderModuleProfile } from "../config/sites.js";
3
3
  import { CONFIG } from "../config/index.js";
4
4
  import { coxProvider } from "./tuning/cox.js";
@@ -19,7 +19,7 @@ import { yttvProvider } from "./tuning/youtubeTv.js";
19
19
  *
20
20
  * Each provider tuning file exports a single ProviderModule object that bundles identity metadata (slug, label, guideUrl), the tuning strategy, and a
21
21
  * discoverChannels implementation. The coordinator builds its strategy dispatch lookup from provider modules at evaluation time. Generic strategies
22
- * (thumbnailRow, tileClick) remain bare ChannelStrategyEntry objects — they are site-specific interaction patterns, not provider-level registrations.
22
+ * (thumbnailRow, tileClick) remain bare ChannelStrategyEntry objects - they are site-specific interaction patterns, not provider-level registrations.
23
23
  *
24
24
  * Shared utilities (scrollAndClick, normalizeChannelName, resolveMatchSelector, logAvailableChannels) live in tuning/shared.ts so that tuning strategy files can
25
25
  * import them without creating a circular dependency with this coordinator. This module re-exports them for backward compatibility.
@@ -42,7 +42,7 @@ import { yttvProvider } from "./tuning/youtubeTv.js";
42
42
  * utilities.
43
43
  */
44
44
  // Provider module registry. The primary registry for all provider-level operations. Each entry bundles identity metadata, tuning strategy, and channel discovery.
45
- // Future capabilities become additional methods on ProviderModule — no new registries needed.
45
+ // Future capabilities become additional methods on ProviderModule - no new registries needed.
46
46
  const providerModules = [
47
47
  coxProvider, directvProvider, foxProvider, hboProvider, huluProvider, slingProvider, spectrumProvider, xfinityProvider, yttvProvider
48
48
  ];
@@ -71,7 +71,6 @@ export async function resolveDirectUrl(profile, page) {
71
71
  if (!channelSelector) {
72
72
  return null;
73
73
  }
74
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
75
74
  return await strategies[channelSelection.strategy]?.resolveDirectUrl?.(channelSelector, page) ?? null;
76
75
  }
77
76
  /**
@@ -84,7 +83,6 @@ export function invalidateDirectUrl(profile) {
84
83
  if (!channelSelector) {
85
84
  return;
86
85
  }
87
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
88
86
  strategies[channelSelection.strategy]?.invalidateDirectUrl?.(channelSelector);
89
87
  }
90
88
  /**
@@ -104,6 +102,16 @@ export function clearChannelSelectionCaches() {
104
102
  export function getProviderBySlug(slug) {
105
103
  return providerModules.find((p) => p.slug === slug);
106
104
  }
105
+ /**
106
+ * Looks up a provider module by its channel-selection strategy name. Used by code paths that have a resolved profile (and therefore the strategy name) but not
107
+ * a slug. Returns undefined when no provider registers the given strategy or when the strategy is one of the generic non-provider strategies (thumbnailRow,
108
+ * tileClick, etc.).
109
+ * @param strategyName - The ChannelSelectionStrategy value (e.g., "foxGrid", "huluLive", "slingLive").
110
+ * @returns The matching provider module or undefined.
111
+ */
112
+ export function getProviderByStrategy(strategyName) {
113
+ return providerModules.find((p) => p.strategyName === strategyName);
114
+ }
107
115
  /**
108
116
  * Returns all registered provider module slugs. Used for validation in the checkboxList setting for precache providers.
109
117
  * @returns Array of provider slugs.
@@ -114,7 +122,7 @@ export function getProviderSlugs() {
114
122
  /**
115
123
  * Returns slug, label, domain, and optional icon URL for all registered provider modules. Used by the checkboxList setting for precache labels and by the
116
124
  * browse modal for provider picker cards with icons. The domain is extracted from the guide URL. The icon URL is derived from the provider's DOMAIN_CONFIG
117
- * entry — the single source of truth for provider icon URLs.
125
+ * entry - the single source of truth for provider icon URLs.
118
126
  * @returns Array of objects with domain, iconUrl, label, noDirectTuneOptimization, and slug properties.
119
127
  */
120
128
  export function getProviderModuleInfo() {
@@ -171,35 +179,78 @@ export function getCachedProviderChannels() {
171
179
  export { logAvailableChannels, normalizeChannelName, resolveMatchSelector, scrollAndClick } from "./tuning/shared.js";
172
180
  /**
173
181
  * Selects a channel from a multi-channel player UI using the strategy specified in the profile. This is the main entry point for channel selection, called by
174
- * tuneToChannel() after page navigation.
182
+ * initializePlayback() after page navigation.
175
183
  *
176
- * The function handles:
177
- * - Pre-selection scroll phase to force lazy-loaded content into the DOM (when scrollToBottom or scrollSelector+scrollTarget is set)
178
- * - Polling for channel element readiness before strategy dispatch (when profile.channelSelection.matchSelector is set)
179
- * - Strategy dispatch based on profile.channelSelection.strategy
180
- * - No-op for single-channel sites (strategy "none" or no channelSelector)
181
- * - Logging of selection attempts and results
184
+ * The function handles, in order:
185
+ * - No-op short-circuit for single-channel sites (strategy "none" or no channelSelector).
186
+ * - Category resolution: when the active provider declares categorySelectors and the profile's selector matches, resolveCategorySelector() is invoked to convert
187
+ * the category to a concrete per-user identifier. The resolved value replaces profile.channelSelector for the rest of the call and is persisted via the
188
+ * options.persistResolution callback if supplied. Resolution failures fall through with the original category selector - the strategy attempts a best-effort
189
+ * match and the verifier fails open.
190
+ * - Pre-selection scroll phase to force lazy-loaded content into the DOM (when scrollToBottom or scrollSelector+scrollTarget is set).
191
+ * - Polling for channel element readiness before strategy dispatch (when profile.channelSelection.matchSelector is set).
192
+ * - Strategy dispatch based on profile.channelSelection.strategy.
182
193
  * @param page - The Puppeteer page object.
183
194
  * @param profile - The resolved site profile containing channelSelection config and channelSelector slug.
195
+ * @param options - Optional callbacks for the resolution layer. Currently the only field is persistResolution.
184
196
  * @returns Result object with success status and optional failure reason.
185
197
  */
186
- export async function selectChannel(page, profile) {
198
+ export async function selectChannel(page, profile, options = {}) {
187
199
  const { channelSelection } = profile;
188
200
  // No channel selection needed if strategy is "none" or no channelSelector is specified.
189
201
  if ((channelSelection.strategy === "none") || !isChannelSelectionProfile(profile)) {
190
202
  return { success: true };
191
203
  }
204
+ // The narrowed profile is used for the rest of this function. Resolution may produce a refined profile with a concrete channelSelector replacing the original
205
+ // category value; the strategy receives the refined version. Reassigning the parameter would lose the narrowing TypeScript inferred from isChannelSelectionProfile
206
+ // above, so we keep a separate local instead.
207
+ let activeProfile = profile;
208
+ // Category resolution. When the profile's selector is a category value declared by the active provider, we delegate to the provider's category resolver to
209
+ // convert the category to a concrete per-user identifier (e.g., "FOXD2C" -> "WFLD" for a Chicago-market user). The resolved value replaces the profile's selector
210
+ // for the remainder of this function so the strategy and verifier act on a concrete identifier. Persistence is fire-and-forget on the caller's persistResolution
211
+ // callback - failures there do not abort the tune; the resolved selector still flows through this attempt, only the disk write is lost. When resolution returns
212
+ // a CategoryResolutionFailure, the framework's response is governed by categories.requireResolution: strict providers abort the tune with the resolver-authored
213
+ // reason; permissive providers log the reason and let the strategy attempt a best-effort match while the verifier fails open for the wildcard case.
214
+ //
215
+ // The whole resolution feature is gated by a single optional sub-object on the provider, so one optional-chain access decides whether to engage the layer at
216
+ // all. The type system guarantees that if `categories` is present, every field needed for resolution is also present.
217
+ const provider = getProviderByStrategy(channelSelection.strategy);
218
+ const categoryResolution = provider?.categoryResolution;
219
+ if (categoryResolution?.selectors.includes(activeProfile.channelSelector)) {
220
+ const resolution = await categoryResolution.resolve(activeProfile.channelSelector, page);
221
+ if ("callSign" in resolution) {
222
+ LOG.debug("tuning", "Resolved category selector \"%s\" to \"%s\" via %s.", activeProfile.channelSelector, resolution.callSign, provider?.label ?? "unknown");
223
+ activeProfile = { ...activeProfile, channelSelector: resolution.callSign };
224
+ if (options.persistResolution) {
225
+ const resolved = resolution.callSign;
226
+ options.persistResolution(resolved).catch((persistError) => {
227
+ LOG.debug("tuning", "Failed to persist resolved category selector to \"%s\": %s.", resolved, formatError(persistError));
228
+ });
229
+ }
230
+ }
231
+ else if (categoryResolution.requireResolution) {
232
+ // Strict provider: a category selector that cannot be resolved is an unrecoverable error. The resolver authored the reason - it has the knowledge to write
233
+ // a complete, domain-shaped sentence including any selector- or provider-specific remediation guidance - so the framework relays it verbatim into the
234
+ // setup-failure path that marks channel health, terminates the pending stream, and surfaces the message to the user.
235
+ return { reason: resolution.reason, success: false };
236
+ }
237
+ else {
238
+ // Permissive provider: log the resolver-authored reason at debug level and fall through. The strategy will attempt a best-effort match against the original
239
+ // category selector, and the verifier will fail open for the wildcard case if the captured manifest URL cannot be cross-checked against discovered category
240
+ // members.
241
+ LOG.debug("tuning", "%s", resolution.reason);
242
+ }
243
+ }
192
244
  const entry = strategies[channelSelection.strategy];
193
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
194
245
  if (!entry) {
195
246
  LOG.warn("Unknown channel selection strategy: %s.", channelSelection.strategy);
196
247
  return { reason: "Unknown channel selection strategy.", success: false };
197
248
  }
198
- // Pre-selection scroll phase. Some sites (e.g., Disney+) lazy-load entire page sections — headings, tiles, and images only appear in the DOM after scrolling
249
+ // Pre-selection scroll phase. Some sites (e.g., Disney+) lazy-load entire page sections - headings, tiles, and images only appear in the DOM after scrolling
199
250
  // them into the viewport. Two scroll modes are supported: scrollToBottom scrolls the page to the bottom to force all lazy content into the DOM, and
200
251
  // scrollSelector+scrollTarget progressively scrolls until a specific element with matching text content is found and scrolled into view. Both modes gate on a
201
- // readiness signal before scrolling — scrollToBottom waits for the page to become scrollable (scrollHeight > innerHeight), while scrollSelector waits for the
202
- // first matching DOM element — since SPAs typically fire the load event before React/framework rendering completes.
252
+ // readiness signal before scrolling - scrollToBottom waits for the page to become scrollable (scrollHeight > innerHeight), while scrollSelector waits for the
253
+ // first matching DOM element - since SPAs typically fire the load event before React/framework rendering completes.
203
254
  if (channelSelection.scrollToBottom) {
204
255
  // Wait for the SPA to render enough content to make the page scrollable. SPAs fire the load event before the framework renders page sections, so scrollHeight
205
256
  // equals innerHeight immediately after navigation. We poll until scrollHeight exceeds innerHeight, indicating content has been rendered and there is somewhere
@@ -217,7 +268,7 @@ export async function selectChannel(page, profile) {
217
268
  else if (channelSelection.scrollSelector && channelSelection.scrollTarget) {
218
269
  // Targeted scroll: find a specific element matching scrollSelector whose text content equals scrollTarget, then scroll it into view. This is used when only a
219
270
  // particular section needs to be visible rather than the entire page. Progressively scrolls in viewport-sized increments, checking after each step whether the
220
- // target element has appeared — necessary because sites with IntersectionObserver-based lazy loading only add sections to the DOM as they enter the viewport.
271
+ // target element has appeared - necessary because sites with IntersectionObserver-based lazy loading only add sections to the DOM as they enter the viewport.
221
272
  let found = false;
222
273
  // Wait for at least one element matching the selector to appear so the SPA has started rendering content.
223
274
  try {
@@ -262,12 +313,12 @@ export async function selectChannel(page, profile) {
262
313
  LOG.debug("tuning:tileClick", "Scroll target \"%s\" via \"%s\": not found after %sms. Found headings: %s.", channelSelection.scrollTarget, channelSelection.scrollSelector, CONFIG.streaming.videoTimeout, JSON.stringify(headings));
263
314
  }
264
315
  }
265
- // Poll for the channel element to appear and become visible. Only run when matchSelector is explicitly configured — the default fallback in
316
+ // Poll for the channel element to appear and become visible. Only run when matchSelector is explicitly configured - the default fallback in
266
317
  // resolveMatchSelector() is for strategy-internal use, and guide-based strategies that don't set matchSelector skip this wait entirely. For <img> elements, we
267
318
  // also verify load completion (img.complete + naturalWidth) to prevent race conditions where the element exists with the correct src but hasn't finished
268
319
  // rendering, which can cause layout instability and click failures.
269
320
  if (channelSelection.matchSelector) {
270
- const selector = resolveMatchSelector(profile);
321
+ const selector = resolveMatchSelector(activeProfile);
271
322
  LOG.debug("tuning:tileClick", "Polling for matchSelector: %s (timeout: %sms).", selector, CONFIG.playback.channelSelectorDelay);
272
323
  try {
273
324
  await page.waitForFunction((sel) => {
@@ -288,12 +339,12 @@ export async function selectChannel(page, profile) {
288
339
  LOG.debug("tuning:tileClick", "matchSelector poll succeeded: element found and visible.");
289
340
  }
290
341
  catch {
291
- // Timeout — the element hasn't appeared or loaded yet. Proceed anyway and let the strategy evaluate and report not-found naturally.
342
+ // Timeout - the element hasn't appeared or loaded yet. Proceed anyway and let the strategy evaluate and report not-found naturally.
292
343
  LOG.debug("tuning:tileClick", "matchSelector poll timed out after %sms. Element not found or not visible.", CONFIG.playback.channelSelectorDelay);
293
344
  }
294
345
  }
295
- // Dispatch to the appropriate strategy via the registry.
296
- const result = await entry.execute(page, profile);
346
+ // Dispatch to the appropriate strategy via the registry. The strategy receives activeProfile so it sees the resolved category selector when applicable.
347
+ const result = await entry.execute(page, activeProfile);
297
348
  return result;
298
349
  }
299
350
  //# sourceMappingURL=channelSelection.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"channelSelection.js","sourceRoot":"","sources":["../../src/browser/channelSelection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"channelSelection.js","sourceRoot":"","sources":["../../src/browser/channelSelection.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { Nullable } from "../types/index.js";
1
+ import type { Nullable } from "../types/index.ts";
2
2
  /**
3
3
  * GPU capabilities detected from the browser at startup via CDP SystemInfo, WebGL renderer identification, and MediaRecorder probing.
4
4
  */
@@ -1,7 +1,7 @@
1
1
  import type { Browser, LaunchOptions, Page } from "puppeteer-core";
2
- import { clearLoginState, isLoginModeActive } from "./login.js";
2
+ import { clearLoginState, isLoginModeActive } from "./login.ts";
3
3
  import { getStream } from "puppeteer-stream";
4
- import type { Nullable } from "../types/index.js";
4
+ import type { Nullable } from "../types/index.ts";
5
5
  /**
6
6
  * Returns true if graceful shutdown is in progress.
7
7
  */
@@ -11,7 +11,7 @@ export declare function isGracefulShutdown(): boolean;
11
11
  */
12
12
  export declare function setGracefulShutdown(value: boolean): void;
13
13
  export { clearLoginState, isLoginModeActive };
14
- export { type LoginStatus, endLoginMode, getLoginPage, getLoginStatus, startLoginMode } from "./login.js";
14
+ export { type LoginStatus, endLoginMode, getLoginPage, getLoginStatus, startLoginMode } from "./login.ts";
15
15
  /**
16
16
  * Computes the current system status and emits it to SSE subscribers. Called when browser state changes significantly or when streams are added/removed.
17
17
  */
@@ -43,16 +43,16 @@ export declare function ensureDataDirectory(): Promise<void>;
43
43
  /**
44
44
  * Ensures a clean slate for browser launch by terminating any stale Chrome processes and removing orphaned profile lock files. Chrome locks its profile directory
45
45
  * while running, and if a previous instance crashed without releasing the lock, we cannot launch a new browser with the same profile. This function uses the
46
- * saved Chrome PID to find and terminate the process via process.kill(), then polls for exit using signal 0. This approach is fully cross-platform — it does not
46
+ * saved Chrome PID to find and terminate the process via process.kill(), then polls for exit using signal 0. This approach is fully cross-platform - it does not
47
47
  * rely on Unix-only tools like pkill or pgrep.
48
48
  *
49
49
  * The termination strategy escalates from SIGTERM to SIGKILL. SIGTERM is sent first, giving Chrome up to 5 seconds to flush its profile databases (LevelDB,
50
50
  * extension state, session storage) and exit cleanly. If Chrome does not exit, SIGKILL is sent as a fallback. This escalation is critical when called from the
51
- * process exit handler — Chrome may be running normally (e.g., after a capture probe timeout), and an immediate SIGKILL would corrupt its profile databases,
51
+ * process exit handler - Chrome may be running normally (e.g., after a capture probe timeout), and an immediate SIGKILL would corrupt its profile databases,
52
52
  * poisoning the Docker volume for subsequent container restarts.
53
53
  *
54
54
  * If no PID is available (first run or clean shutdown where the PID file was already removed), process killing is skipped entirely and only lock file cleanup
55
- * runs. When a PID file does exist but the process is gone (Docker restart with a mounted volume — the PID belongs to the previous container's PID namespace),
55
+ * runs. When a PID file does exist but the process is gone (Docker restart with a mounted volume - the PID belongs to the previous container's PID namespace),
56
56
  * process.kill() throws ESRCH, which is caught gracefully.
57
57
  *
58
58
  * This is called at startup before launching the browser and from the process exit handler as a crash recovery fallback. It's safe to call even when no stale
@@ -133,11 +133,11 @@ export declare function getBrowserPages(): Promise<Page[]>;
133
133
  *
134
134
  * - browserRef.close() sends CDP Browser.close and waits for WebSocket teardown, which hangs 3-5 seconds even after Chrome exits.
135
135
  * - browserRef.disconnect() drops the WebSocket instantly but orphans Chrome as a Node child process, creating a zombie that process.kill(pid, 0) cannot detect.
136
- * - Synchronous polling (Atomics.wait) blocks the event loop, preventing Node from processing SIGCHLD to reap the child — Chrome becomes a zombie regardless
136
+ * - Synchronous polling (Atomics.wait) blocks the event loop, preventing Node from processing SIGCHLD to reap the child - Chrome becomes a zombie regardless
137
137
  * of how SIGTERM was sent.
138
138
  *
139
139
  * Instead, we send SIGTERM through the ChildProcess handle and listen for the `exit` event. This keeps the event loop running so Node can process SIGCHLD and
140
- * reap Chrome properly. The exit event fires only after the process is fully reaped — no zombies, no polling, no event loop blocking.
140
+ * reap Chrome properly. The exit event fires only after the process is fully reaped - no zombies, no polling, no event loop blocking.
141
141
  */
142
142
  export declare function closeBrowser(): Promise<void>;
143
143
  /**
@@ -1,5 +1,5 @@
1
1
  import { getGpuCapabilities, setBrowserChrome, setGpuCapabilities, setMaxSupportedViewport } from "./display.js";
2
- import { LOG, cancellableTimeout, clearPidFile, evaluateWithAbort, formatError, isProcessRunning, readPidFile, startTimer, writePidFile } from "../utils/index.js";
2
+ import { LOG, cancellableTimeout, clearPidFile, delay, evaluateWithAbort, formatError, isProcessRunning, readPidFile, startTimer, writePidFile } from "../utils/index.js";
3
3
  import { clearLoginState, isLoginModeActive, setBrowserAccessors } from "./login.js";
4
4
  import { getAllStreams, getStreamCount } from "../streaming/registry.js";
5
5
  import { getChromeDataDir, getChromePidFilePath, getDataDir, getExtensionDir } from "../config/paths.js";
@@ -25,7 +25,7 @@ const { promises: fsPromises } = fs;
25
25
  * - dataDir: The filesystem location for persistent data (Chrome profile, extension files). Resolved via config/paths.ts, which is created on startup if it doesn't
26
26
  * exist.
27
27
  *
28
- * Stream tracking and ID generation have been moved to streaming/registry.ts for unified stream management across all output types (HLS, MPEG-TS, etc.).
28
+ * Stream tracking and ID generation live in streaming/registry.ts for unified stream management across all output types (HLS, MPEG-TS, etc.).
29
29
  */
30
30
  // The shared browser instance used by all streaming sessions. Created on first stream request or during warmup. Set to null when the browser is not running or
31
31
  // has disconnected.
@@ -34,7 +34,7 @@ let currentBrowser = null;
34
34
  // can be cleaned up after a crash or container restart without relying on Unix-only tools like pkill/pgrep.
35
35
  let chromePid = null;
36
36
  // Tracks whether this process has taken ownership of Chrome cleanup by running killStaleChrome() during startup. The exit handler checks this flag to avoid
37
- // killing Chrome that belongs to another running PrismCast instance — e.g., when a duplicate instance is rejected by the instance guard and exits before
37
+ // killing Chrome that belongs to another running PrismCast instance - e.g., when a duplicate instance is rejected by the instance guard and exits before
38
38
  // killStaleChrome() runs in the startup sequence.
39
39
  let ownsChromeCleanup = false;
40
40
  // The Chrome version string (e.g., "Chrome/144.0.7559.110") captured when the browser launches. Cleared when the browser disconnects. Used by the
@@ -217,7 +217,7 @@ function saveChromePid(pid) {
217
217
  }
218
218
  /**
219
219
  * Loads the Chrome PID from the module-level variable (fast path) or falls back to reading the PID file on disk (crash recovery path). Returns null if no PID
220
- * is available — either first run or the PID file was already cleaned up.
220
+ * is available - either first run or the PID file was already cleaned up.
221
221
  * @returns The Chrome process ID, or null if unavailable.
222
222
  */
223
223
  function loadChromePid() {
@@ -241,16 +241,16 @@ function syncSleep(ms) {
241
241
  /**
242
242
  * Ensures a clean slate for browser launch by terminating any stale Chrome processes and removing orphaned profile lock files. Chrome locks its profile directory
243
243
  * while running, and if a previous instance crashed without releasing the lock, we cannot launch a new browser with the same profile. This function uses the
244
- * saved Chrome PID to find and terminate the process via process.kill(), then polls for exit using signal 0. This approach is fully cross-platform — it does not
244
+ * saved Chrome PID to find and terminate the process via process.kill(), then polls for exit using signal 0. This approach is fully cross-platform - it does not
245
245
  * rely on Unix-only tools like pkill or pgrep.
246
246
  *
247
247
  * The termination strategy escalates from SIGTERM to SIGKILL. SIGTERM is sent first, giving Chrome up to 5 seconds to flush its profile databases (LevelDB,
248
248
  * extension state, session storage) and exit cleanly. If Chrome does not exit, SIGKILL is sent as a fallback. This escalation is critical when called from the
249
- * process exit handler — Chrome may be running normally (e.g., after a capture probe timeout), and an immediate SIGKILL would corrupt its profile databases,
249
+ * process exit handler - Chrome may be running normally (e.g., after a capture probe timeout), and an immediate SIGKILL would corrupt its profile databases,
250
250
  * poisoning the Docker volume for subsequent container restarts.
251
251
  *
252
252
  * If no PID is available (first run or clean shutdown where the PID file was already removed), process killing is skipped entirely and only lock file cleanup
253
- * runs. When a PID file does exist but the process is gone (Docker restart with a mounted volume — the PID belongs to the previous container's PID namespace),
253
+ * runs. When a PID file does exist but the process is gone (Docker restart with a mounted volume - the PID belongs to the previous container's PID namespace),
254
254
  * process.kill() throws ESRCH, which is caught gracefully.
255
255
  *
256
256
  * This is called at startup before launching the browser and from the process exit handler as a crash recovery fallback. It's safe to call even when no stale
@@ -263,7 +263,7 @@ export function killStaleChrome() {
263
263
  if (pid !== null) {
264
264
  try {
265
265
  // Send SIGTERM first to give Chrome a chance to flush its profile databases (LevelDB, extension state, session storage) before exiting. This is critical
266
- // when called from the process exit handler — Chrome may be running normally (e.g., after a capture probe timeout) and SIGKILL would corrupt its profile
266
+ // when called from the process exit handler - Chrome may be running normally (e.g., after a capture probe timeout) and SIGKILL would corrupt its profile
267
267
  // databases, poisoning the Docker volume for subsequent restarts.
268
268
  process.kill(pid, "SIGTERM");
269
269
  LOG.debug("browser:lifecycle", "Sent SIGTERM to Chrome process %d.", pid);
@@ -277,7 +277,7 @@ export function killStaleChrome() {
277
277
  process.kill(pid, "SIGKILL");
278
278
  }
279
279
  catch (_error) {
280
- // ESRCH — Chrome exited between the poll check and the kill call.
280
+ // ESRCH - Chrome exited between the poll check and the kill call.
281
281
  }
282
282
  const KILL_WAIT_MS = 2000;
283
283
  if (!waitForChromeExit(pid, KILL_WAIT_MS, POLL_INTERVAL_MS)) {
@@ -286,7 +286,7 @@ export function killStaleChrome() {
286
286
  }
287
287
  }
288
288
  catch (error) {
289
- // ESRCH means the process does not exist — expected when there are no stale processes from a clean shutdown, or in Docker where the PID belongs to a
289
+ // ESRCH means the process does not exist - expected when there are no stale processes from a clean shutdown, or in Docker where the PID belongs to a
290
290
  // previous container's PID namespace.
291
291
  if (error.code !== "ESRCH") {
292
292
  LOG.warn("Failed to signal Chrome process %d: %s.", pid, formatError(error));
@@ -308,7 +308,7 @@ export function canCleanupChrome() {
308
308
  return ownsChromeCleanup;
309
309
  }
310
310
  /**
311
- * Polls until the Chrome process with the given PID has exited, or the timeout expires. Uses process.kill(pid, 0) to check process existence — throws ESRCH
311
+ * Polls until the Chrome process with the given PID has exited, or the timeout expires. Uses process.kill(pid, 0) to check process existence - throws ESRCH
312
312
  * when the process is gone. Between polls, sleeps synchronously using Atomics.wait() for cross-platform compatibility.
313
313
  * @param pid - The Chrome process ID to wait for.
314
314
  * @param timeoutMs - Maximum time to wait in milliseconds.
@@ -501,14 +501,6 @@ async function launchWithCustomArgs(opts) {
501
501
  }
502
502
  return puppeteerLaunch(opts);
503
503
  }
504
- /**
505
- * Detects the maximum supported viewport dimensions based on the user's display. This function measures the available screen space and subtracts browser chrome to
506
- * determine the largest viewport we can use for video capture.
507
- *
508
- * The detection uses a temporary page (or existing page if available) to evaluate screen dimensions via JavaScript. The result is cached in the display module for
509
- * use by the preset system when determining effective viewport.
510
- * @param browser - The browser instance to use for detection.
511
- */
512
504
  /**
513
505
  * Formats the GPU capabilities into a human-readable suffix for the "Chrome ready" log line. The renderer string is already cleaned (ANGLE wrapper and Metal
514
506
  * prefix stripped) at detection time, so this function uses it directly and appends hardware-accelerated codec names in brackets when available.
@@ -524,13 +516,21 @@ function formatGpuSuffix(gpu) {
524
516
  if (codecs.length > 0) {
525
517
  return " (GPU: " + gpu.renderer + " [" + codecs.join(", ") + "])";
526
518
  }
527
- // No hardware encoding available. A GPU may be present for rendering but lack hardware encoding — show the GPU name without codecs if we have a non-trivial
519
+ // No hardware encoding available. A GPU may be present for rendering but lack hardware encoding - show the GPU name without codecs if we have a non-trivial
528
520
  // renderer string, otherwise label as software rendering.
529
521
  if (gpu.renderer && (gpu.renderer !== "unknown")) {
530
522
  return " (GPU: " + gpu.renderer + ")";
531
523
  }
532
524
  return " (software rendering)";
533
525
  }
526
+ /**
527
+ * Detects the maximum supported viewport dimensions based on the user's display. This function measures the available screen space and subtracts browser chrome to
528
+ * determine the largest viewport we can use for video capture.
529
+ *
530
+ * The detection uses a temporary page (or existing page if available) to evaluate screen dimensions via JavaScript. The result is cached in the display module for
531
+ * use by the preset system when determining effective viewport.
532
+ * @param browser - The browser instance to use for detection.
533
+ */
534
534
  async function detectDisplayDimensions(browser) {
535
535
  let tempPage = null;
536
536
  let usingTempPage = false;
@@ -565,11 +565,11 @@ async function detectDisplayDimensions(browser) {
565
565
  if ((dimensions.chromeWidth >= 0) && (dimensions.chromeHeight >= 0)) {
566
566
  break;
567
567
  }
568
- // Chrome dimensions are negative — the window manager is still transitioning. Wait briefly and remeasure.
568
+ // Chrome dimensions are negative - the window manager is still transitioning. Wait briefly and remeasure.
569
569
  if (attempt < 2) {
570
570
  LOG.debug("browser:lifecycle", "Display detection measured negative chrome dimensions (%s\u00d7%s, attempt %s). Retrying after window state settles.", dimensions.chromeWidth, dimensions.chromeHeight, attempt + 1);
571
571
  // eslint-disable-next-line no-await-in-loop
572
- await new Promise((resolve) => setTimeout(resolve, 100));
572
+ await delay(100);
573
573
  }
574
574
  }
575
575
  // If all attempts produced negative chrome dimensions, skip caching to avoid poisoning window sizing. The preset system will use the configured preset without
@@ -588,7 +588,7 @@ async function detectDisplayDimensions(browser) {
588
588
  setBrowserChrome(dimensions.chromeWidth, dimensions.chromeHeight);
589
589
  setMaxSupportedViewport(maxWidth, maxHeight);
590
590
  LOG.debug("browser:lifecycle", "Display detection complete: screen %s\u00d7%s, chrome %s\u00d7%s, max viewport %s\u00d7%s.", dimensions.availWidth, dimensions.availHeight, dimensions.chromeWidth, dimensions.chromeHeight, maxWidth, maxHeight);
591
- // Detect GPU capabilities via CDP SystemInfo.getInfo. This is the authoritative source for GPU identity and hardware encoding capabilities — it runs at the
591
+ // Detect GPU capabilities via CDP SystemInfo.getInfo. This is the authoritative source for GPU identity and hardware encoding capabilities - it runs at the
592
592
  // browser level (no page context or secure context required) and returns the actual list of hardware-accelerated video encoding profiles.
593
593
  try {
594
594
  const cdpSession = await browser.target().createCDPSession();
@@ -596,7 +596,7 @@ async function detectDisplayDimensions(browser) {
596
596
  const sysInfo = await cdpSession.send("SystemInfo.getInfo");
597
597
  // Extract the GPU renderer from the primary device. The WebGL unmasked renderer provides a richer string (includes ANGLE backend info), so we query
598
598
  // that as well and prefer it when available.
599
- const deviceName = (sysInfo.gpu.devices.length > 0) ? sysInfo.gpu.devices[0].deviceString : "unknown";
599
+ const deviceName = sysInfo.gpu.devices[0]?.deviceString ?? "unknown";
600
600
  // Get the unmasked WebGL renderer for a more descriptive GPU identity string.
601
601
  const webglRenderer = await evaluateWithAbort(targetPage, () => {
602
602
  const canvas = document.createElement("canvas");
@@ -610,9 +610,9 @@ async function detectDisplayDimensions(browser) {
610
610
  // Extract the meaningful GPU name. ANGLE wraps the actual GPU identity: "ANGLE (Vendor, GPU Name, API Version)". The GPU name is the second
611
611
  // comma-separated field. For non-ANGLE renderers, use the device string from CDP.
612
612
  let renderer = webglRenderer;
613
- const angleMatch = /^ANGLE \([^,]+, ([^,]+)/.exec(webglRenderer);
614
- if (angleMatch) {
615
- renderer = angleMatch[1].trim();
613
+ const anglePart = /^ANGLE \([^,]+, ([^,]+)/.exec(webglRenderer)?.[1];
614
+ if (anglePart) {
615
+ renderer = anglePart.trim();
616
616
  // Strip the "ANGLE Metal Renderer: " prefix that macOS adds.
617
617
  const metalPrefix = "ANGLE Metal Renderer: ";
618
618
  if (renderer.startsWith(metalPrefix)) {
@@ -623,18 +623,18 @@ async function detectDisplayDimensions(browser) {
623
623
  renderer = deviceName;
624
624
  }
625
625
  // Determine hardware encoding capability. Two paths:
626
- // 1. featureStatus.video_encode === "enabled" — authoritative Chrome-level flag indicating the platform's hardware encoding framework is active
626
+ // 1. featureStatus.video_encode === "enabled" - authoritative Chrome-level flag indicating the platform's hardware encoding framework is active
627
627
  // (VideoToolbox on macOS, VA-API on Linux, DXVA on Windows). When enabled, H.264 hardware encoding is always available.
628
- // 2. videoEncoding profile array — lists specific hardware-accelerated codec profiles (e.g., "H264 Main", "HEVC Main"). Populated on Linux/Windows
628
+ // 2. videoEncoding profile array - lists specific hardware-accelerated codec profiles (e.g., "H264 Main", "HEVC Main"). Populated on Linux/Windows
629
629
  // via VA-API/DXVA but empty on macOS where VideoToolbox doesn't enumerate through this interface.
630
- const videoEncodeEnabled = sysInfo.gpu.featureStatus?.video_encode === "enabled";
630
+ const videoEncodeEnabled = sysInfo.gpu.featureStatus?.["video_encode"] === "enabled";
631
631
  const h264FromProfiles = sysInfo.gpu.videoEncoding.some((e) => e.profile.startsWith("H264"));
632
632
  const hevcFromProfiles = sysInfo.gpu.videoEncoding.some((e) => e.profile.startsWith("HEVC"));
633
633
  const av1FromProfiles = sysInfo.gpu.videoEncoding.some((e) => e.profile.startsWith("AV1"));
634
634
  // H.264 hardware encoding is available when either the feature flag or the profile list confirms it.
635
635
  const h264Hardware = videoEncodeEnabled || h264FromProfiles;
636
636
  // HEVC and AV1 hardware encoding: check the profile list first (authoritative on Linux/Windows). On macOS (empty profile list), probe via MediaRecorder
637
- // in the page context — MediaRecorder.isTypeSupported works in non-secure contexts unlike VideoEncoder.
637
+ // in the page context - MediaRecorder.isTypeSupported works in non-secure contexts unlike VideoEncoder.
638
638
  let hevcHardware = hevcFromProfiles;
639
639
  let av1Hardware = av1FromProfiles;
640
640
  if (videoEncodeEnabled && (!hevcHardware || !av1Hardware)) {
@@ -651,7 +651,7 @@ async function detectDisplayDimensions(browser) {
651
651
  av1Hardware ||= av1Supported;
652
652
  }
653
653
  setGpuCapabilities({ av1HardwareEncoding: av1Hardware, h264HardwareEncoding: h264Hardware, hevcHardwareEncoding: hevcHardware, renderer });
654
- LOG.debug("browser:lifecycle", "GPU detection: device=%s, renderer=%s, H.264=%s, HEVC=%s, AV1=%s, video_encode=%s, encoding profiles=%s.", deviceName, renderer, h264Hardware, hevcHardware, av1Hardware, sysInfo.gpu.featureStatus?.video_encode ?? "unknown", sysInfo.gpu.videoEncoding.map((e) => e.profile).join(", ") || "none");
654
+ LOG.debug("browser:lifecycle", "GPU detection: device=%s, renderer=%s, H.264=%s, HEVC=%s, AV1=%s, video_encode=%s, encoding profiles=%s.", deviceName, renderer, h264Hardware, hevcHardware, av1Hardware, sysInfo.gpu.featureStatus?.["video_encode"] ?? "unknown", sysInfo.gpu.videoEncoding.map((e) => e.profile).join(", ") || "none");
655
655
  }
656
656
  finally {
657
657
  void cdpSession.detach().catch(() => { });
@@ -785,7 +785,7 @@ async function launchBrowser() {
785
785
  currentBrowser.on("disconnected", handleBrowserDisconnect);
786
786
  LOG.debug("timing:browser", "Chrome process spawned. (+%sms)", browserElapsed());
787
787
  // Poll for the puppeteer-stream extension to finish initializing. The extension injects a START_RECORDING function into its options page context. We poll
788
- // for this function's existence rather than using a fixed delay, so the browser is ready as soon as the extension loads — typically 200-500ms rather than the
788
+ // for this function's existence rather than using a fixed delay, so the browser is ready as soon as the extension loads - typically 200-500ms rather than the
789
789
  // full configured timeout. Uses getExtensionPage() from puppeteer-stream to locate the extension's options page.
790
790
  try {
791
791
  const extensionPage = await getExtensionPage(currentBrowser);
@@ -802,7 +802,7 @@ async function launchBrowser() {
802
802
  await detectDisplayDimensions(currentBrowser);
803
803
  LOG.debug("timing:browser", "Display detection complete. (+%sms)", browserElapsed());
804
804
  // Log the Chrome version for diagnostic reference. This helps correlate browser behavior changes (tab unresponsiveness, memory pressure, capture issues)
805
- // with specific Chrome releases. We also capture the User-Agent string so that server-side fetch() calls to provider CDNs can match Chrome's identity.
805
+ // with specific Chrome releases. We also capture the User-Agent string so that server-side fetch() calls to service CDNs can match Chrome's identity.
806
806
  const chromeVersion = await currentBrowser.version();
807
807
  const userAgent = await currentBrowser.userAgent();
808
808
  browserLaunchTime = Date.now();
@@ -814,7 +814,7 @@ async function launchBrowser() {
814
814
  LOG.debug("timing:browser", "Browser ready. Total: %sms.", browserElapsed());
815
815
  // Emit system status update for SSE subscribers.
816
816
  await emitCurrentSystemStatus();
817
- // Start background precaching of selected provider channel lineups. Fire-and-forget — the setTimeout inside startPrecaching() ensures the actual work is fully
817
+ // Start background precaching of selected service channel lineups. Fire-and-forget - the setTimeout inside startPrecaching() ensures the actual work is fully
818
818
  // async and non-blocking.
819
819
  startPrecaching();
820
820
  }
@@ -880,7 +880,7 @@ export async function minimizeBrowserWindow() {
880
880
  }
881
881
  // Delegate to resizeAndMinimizeWindow for the actual CDP operations. This ensures consistent resize+minimize behavior and maintains a single source of
882
882
  // truth for the viewport sizing logic.
883
- await resizeAndMinimizeWindow(targetPage, true);
883
+ await resizeAndMinimizeWindow(targetPage);
884
884
  // Clean up the temporary page if we created one.
885
885
  if (usingTempPage && tempPage) {
886
886
  unregisterManagedPage(tempPage);
@@ -927,11 +927,11 @@ export async function getBrowserPages() {
927
927
  *
928
928
  * - browserRef.close() sends CDP Browser.close and waits for WebSocket teardown, which hangs 3-5 seconds even after Chrome exits.
929
929
  * - browserRef.disconnect() drops the WebSocket instantly but orphans Chrome as a Node child process, creating a zombie that process.kill(pid, 0) cannot detect.
930
- * - Synchronous polling (Atomics.wait) blocks the event loop, preventing Node from processing SIGCHLD to reap the child — Chrome becomes a zombie regardless
930
+ * - Synchronous polling (Atomics.wait) blocks the event loop, preventing Node from processing SIGCHLD to reap the child - Chrome becomes a zombie regardless
931
931
  * of how SIGTERM was sent.
932
932
  *
933
933
  * Instead, we send SIGTERM through the ChildProcess handle and listen for the `exit` event. This keeps the event loop running so Node can process SIGCHLD and
934
- * reap Chrome properly. The exit event fires only after the process is fully reaped — no zombies, no polling, no event loop blocking.
934
+ * reap Chrome properly. The exit event fires only after the process is fully reaped - no zombies, no polling, no event loop blocking.
935
935
  */
936
936
  export async function closeBrowser() {
937
937
  // Ensure the flag is set so the disconnect handler knows this is intentional. Normally set earlier by app.ts shutdown(), but set here as a fallback for direct
@@ -954,9 +954,8 @@ export async function closeBrowser() {
954
954
  const KILL_WAIT_MS = 2000;
955
955
  // Listen for the exit event before sending the signal. The event fires after the OS reaps the process, so there is no zombie window. Resolves to true so
956
956
  // Promise.race can distinguish exit from timeout.
957
- const exitPromise = new Promise((resolve) => {
958
- chromeProcess.on("exit", () => { resolve(true); });
959
- });
957
+ const { promise: exitPromise, resolve: signalExit } = Promise.withResolvers();
958
+ chromeProcess.on("exit", () => { signalExit(true); });
960
959
  chromeProcess.kill("SIGTERM");
961
960
  LOG.debug("browser:lifecycle", "Sent SIGTERM to Chrome process %d.", chromeProcess.pid);
962
961
  // Wait for Chrome to exit after SIGTERM, with a timeout. If Chrome doesn't exit in time, escalate to SIGKILL.
@@ -1138,7 +1137,7 @@ function checkBrowserRestart() {
1138
1137
  // If there are active streams, cancel any pending quiet timer and return. Streams that start during the quiet period reset the countdown.
1139
1138
  if (getStreamCount() > 0) {
1140
1139
  if (restartQuietTimer) {
1141
- LOG.debug("browser:lifecycle", "Browser restart quiet period cancelled — streams are active.");
1140
+ LOG.debug("browser:lifecycle", "Browser restart quiet period cancelled - streams are active.");
1142
1141
  clearTimeout(restartQuietTimer);
1143
1142
  restartQuietTimer = null;
1144
1143
  }
@@ -1146,7 +1145,7 @@ function checkBrowserRestart() {
1146
1145
  }
1147
1146
  // No active streams and the browser is old enough. Start the quiet timer if one is not already running.
1148
1147
  if (!restartQuietTimer) {
1149
- LOG.debug("browser:lifecycle", "Browser uptime exceeds threshold. Quiet period started — restart will proceed if no streams start within %s minutes.", Math.round(BROWSER_RESTART_QUIET_PERIOD / 60000));
1148
+ LOG.debug("browser:lifecycle", "Browser uptime exceeds threshold. Quiet period started - restart will proceed if no streams start within %s minutes.", Math.round(BROWSER_RESTART_QUIET_PERIOD / 60000));
1150
1149
  restartQuietTimer = setTimeout(() => {
1151
1150
  void executeBrowserRestart();
1152
1151
  }, BROWSER_RESTART_QUIET_PERIOD);
@@ -1162,7 +1161,7 @@ async function executeBrowserRestart() {
1162
1161
  // Final guard: re-check all preconditions. Conditions may have changed during the quiet period (e.g., a stream started just before the timer fired, login
1163
1162
  // mode was activated, or the browser disconnected on its own).
1164
1163
  if (gracefulShutdownInProgress || isLoginModeActive() || (getStreamCount() > 0) || !currentBrowser || !currentBrowser.connected || !browserLaunchTime) {
1165
- LOG.debug("browser:lifecycle", "Browser restart aborted — preconditions no longer met.");
1164
+ LOG.debug("browser:lifecycle", "Browser restart aborted - preconditions no longer met.");
1166
1165
  return;
1167
1166
  }
1168
1167
  const age = Date.now() - browserLaunchTime;
@@ -1172,7 +1171,7 @@ async function executeBrowserRestart() {
1172
1171
  try {
1173
1172
  // closeBrowser() sets gracefulShutdownInProgress = true internally and performs SIGTERM-based Chrome termination.
1174
1173
  await closeBrowser();
1175
- // Reset the flag since the server is NOT shutting down — only the browser is restarting.
1174
+ // Reset the flag since the server is NOT shutting down - only the browser is restarting.
1176
1175
  setGracefulShutdown(false);
1177
1176
  // Launch a fresh browser instance so it is ready for the next stream request.
1178
1177
  await getCurrentBrowser();