prismcast 1.10.2 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.d.ts +6 -3
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +140 -86
- package/dist/app.js.map +1 -1
- package/dist/browser/blockedPage.d.ts +80 -0
- package/dist/browser/blockedPage.d.ts.map +1 -0
- package/dist/browser/blockedPage.js +209 -0
- package/dist/browser/blockedPage.js.map +1 -0
- package/dist/browser/browserSupervisor.d.ts +80 -0
- package/dist/browser/browserSupervisor.d.ts.map +1 -0
- package/dist/browser/browserSupervisor.js +186 -0
- package/dist/browser/browserSupervisor.js.map +1 -0
- package/dist/browser/cdp.d.ts +1 -0
- package/dist/browser/cdp.d.ts.map +1 -0
- package/dist/browser/cdp.js +6 -5
- package/dist/browser/cdp.js.map +1 -1
- package/dist/browser/channelSelection.d.ts +45 -14
- package/dist/browser/channelSelection.d.ts.map +1 -0
- package/dist/browser/channelSelection.js +81 -18
- package/dist/browser/channelSelection.js.map +1 -1
- package/dist/browser/consent.d.ts +152 -0
- package/dist/browser/consent.d.ts.map +1 -0
- package/dist/browser/consent.js +418 -0
- package/dist/browser/consent.js.map +1 -0
- package/dist/browser/display.d.ts +1 -0
- package/dist/browser/display.d.ts.map +1 -0
- package/dist/browser/hlsPlaylistObserver.d.ts +55 -0
- package/dist/browser/hlsPlaylistObserver.d.ts.map +1 -0
- package/dist/browser/hlsPlaylistObserver.js +132 -0
- package/dist/browser/hlsPlaylistObserver.js.map +1 -0
- package/dist/browser/index.d.ts +67 -40
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +508 -300
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/launchGovernor.d.ts +89 -0
- package/dist/browser/launchGovernor.d.ts.map +1 -0
- package/dist/browser/launchGovernor.js +96 -0
- package/dist/browser/launchGovernor.js.map +1 -0
- package/dist/browser/login.d.ts +10 -2
- package/dist/browser/login.d.ts.map +1 -0
- package/dist/browser/login.js +36 -5
- package/dist/browser/login.js.map +1 -1
- package/dist/browser/manifestInterceptor.d.ts +102 -49
- package/dist/browser/manifestInterceptor.d.ts.map +1 -0
- package/dist/browser/manifestInterceptor.js +219 -237
- package/dist/browser/manifestInterceptor.js.map +1 -1
- package/dist/browser/pageStaleness.d.ts +30 -0
- package/dist/browser/pageStaleness.d.ts.map +1 -0
- package/dist/browser/pageStaleness.js +76 -0
- package/dist/browser/pageStaleness.js.map +1 -0
- package/dist/browser/precaching.d.ts +95 -3
- package/dist/browser/precaching.d.ts.map +1 -0
- package/dist/browser/precaching.js +499 -65
- package/dist/browser/precaching.js.map +1 -1
- package/dist/browser/tabNetworkObserver.d.ts +49 -0
- package/dist/browser/tabNetworkObserver.d.ts.map +1 -0
- package/dist/browser/tabNetworkObserver.js +144 -0
- package/dist/browser/tabNetworkObserver.js.map +1 -0
- package/dist/browser/tuning/cache.d.ts +61 -0
- package/dist/browser/tuning/cache.d.ts.map +1 -0
- package/dist/browser/tuning/cache.js +62 -0
- package/dist/browser/tuning/cache.js.map +1 -0
- package/dist/browser/tuning/comcastPolymer.d.ts +3 -2
- package/dist/browser/tuning/comcastPolymer.d.ts.map +1 -0
- package/dist/browser/tuning/comcastPolymer.js +79 -102
- package/dist/browser/tuning/comcastPolymer.js.map +1 -1
- package/dist/browser/tuning/cox.d.ts +1 -0
- package/dist/browser/tuning/cox.d.ts.map +1 -0
- package/dist/browser/tuning/directv.d.ts +1 -0
- package/dist/browser/tuning/directv.d.ts.map +1 -0
- package/dist/browser/tuning/directv.js +52 -46
- package/dist/browser/tuning/directv.js.map +1 -1
- package/dist/browser/tuning/fox.d.ts +1 -0
- package/dist/browser/tuning/fox.d.ts.map +1 -0
- package/dist/browser/tuning/fox.js +13 -12
- package/dist/browser/tuning/fox.js.map +1 -1
- package/dist/browser/tuning/gridSearch.d.ts +68 -0
- package/dist/browser/tuning/gridSearch.d.ts.map +1 -0
- package/dist/browser/tuning/gridSearch.js +85 -0
- package/dist/browser/tuning/gridSearch.js.map +1 -0
- package/dist/browser/tuning/hbo.d.ts +1 -0
- package/dist/browser/tuning/hbo.d.ts.map +1 -0
- package/dist/browser/tuning/hbo.js +73 -45
- package/dist/browser/tuning/hbo.js.map +1 -1
- package/dist/browser/tuning/hulu.d.ts +1 -0
- package/dist/browser/tuning/hulu.d.ts.map +1 -0
- package/dist/browser/tuning/hulu.js +128 -173
- package/dist/browser/tuning/hulu.js.map +1 -1
- package/dist/browser/tuning/shared.d.ts +75 -5
- package/dist/browser/tuning/shared.d.ts.map +1 -0
- package/dist/browser/tuning/shared.js +216 -6
- package/dist/browser/tuning/shared.js.map +1 -1
- package/dist/browser/tuning/sling.d.ts +1 -0
- package/dist/browser/tuning/sling.d.ts.map +1 -0
- package/dist/browser/tuning/sling.js +105 -126
- package/dist/browser/tuning/sling.js.map +1 -1
- package/dist/browser/tuning/spectrum.d.ts +1 -0
- package/dist/browser/tuning/spectrum.d.ts.map +1 -0
- package/dist/browser/tuning/spectrum.js +80 -125
- package/dist/browser/tuning/spectrum.js.map +1 -1
- package/dist/browser/tuning/thumbnailRow.d.ts +1 -0
- package/dist/browser/tuning/thumbnailRow.d.ts.map +1 -0
- package/dist/browser/tuning/thumbnailRow.js +8 -2
- package/dist/browser/tuning/thumbnailRow.js.map +1 -1
- package/dist/browser/tuning/tileClick.d.ts +1 -0
- package/dist/browser/tuning/tileClick.d.ts.map +1 -0
- package/dist/browser/tuning/tileClick.js +3 -2
- package/dist/browser/tuning/tileClick.js.map +1 -1
- package/dist/browser/tuning/xfinity.d.ts +1 -0
- package/dist/browser/tuning/xfinity.d.ts.map +1 -0
- package/dist/browser/tuning/youtubeTv.d.ts +1 -0
- package/dist/browser/tuning/youtubeTv.d.ts.map +1 -0
- package/dist/browser/tuning/youtubeTv.js +74 -127
- package/dist/browser/tuning/youtubeTv.js.map +1 -1
- package/dist/browser/video.d.ts +54 -14
- package/dist/browser/video.d.ts.map +1 -0
- package/dist/browser/video.js +296 -138
- package/dist/browser/video.js.map +1 -1
- package/dist/channels/index.d.ts +1 -0
- package/dist/channels/index.d.ts.map +1 -0
- package/dist/channels/index.js +12 -9
- package/dist/channels/index.js.map +1 -1
- package/dist/config/channelForm.d.ts +15 -1
- package/dist/config/channelForm.d.ts.map +1 -0
- package/dist/config/channelForm.js +28 -12
- package/dist/config/channelForm.js.map +1 -1
- package/dist/config/consistencyProbe.d.ts +1 -0
- package/dist/config/consistencyProbe.d.ts.map +1 -0
- package/dist/config/consistencyProbe.js +27 -21
- package/dist/config/consistencyProbe.js.map +1 -1
- package/dist/config/health.d.ts +41 -8
- package/dist/config/health.d.ts.map +1 -0
- package/dist/config/health.js +189 -38
- package/dist/config/health.js.map +1 -1
- package/dist/config/index.d.ts +55 -3
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +320 -69
- package/dist/config/index.js.map +1 -1
- package/dist/config/paths.d.ts +10 -9
- package/dist/config/paths.d.ts.map +1 -0
- package/dist/config/paths.js +11 -11
- package/dist/config/paths.js.map +1 -1
- package/dist/config/persistence.context.d.ts +1 -0
- package/dist/config/persistence.context.d.ts.map +1 -0
- package/dist/config/persistence.d.ts +13 -10
- package/dist/config/persistence.d.ts.map +1 -0
- package/dist/config/persistence.js +40 -21
- package/dist/config/persistence.js.map +1 -1
- package/dist/config/presets.d.ts +1 -0
- package/dist/config/presets.d.ts.map +1 -0
- package/dist/config/profiles.d.ts +22 -15
- package/dist/config/profiles.d.ts.map +1 -0
- package/dist/config/profiles.js +81 -47
- package/dist/config/profiles.js.map +1 -1
- package/dist/config/providerLineups.d.ts +47 -0
- package/dist/config/providerLineups.d.ts.map +1 -0
- package/dist/config/providerLineups.js +137 -0
- package/dist/config/providerLineups.js.map +1 -0
- package/dist/config/reactivity.d.ts +84 -0
- package/dist/config/reactivity.d.ts.map +1 -0
- package/dist/config/reactivity.js +226 -0
- package/dist/config/reactivity.js.map +1 -0
- package/dist/config/servicePacks.d.ts +18 -2
- package/dist/config/servicePacks.d.ts.map +1 -0
- package/dist/config/servicePacks.js +53 -11
- package/dist/config/servicePacks.js.map +1 -1
- package/dist/config/services.d.ts +21 -15
- package/dist/config/services.d.ts.map +1 -0
- package/dist/config/services.js +35 -24
- package/dist/config/services.js.map +1 -1
- package/dist/config/sites.d.ts +6 -4
- package/dist/config/sites.d.ts.map +1 -0
- package/dist/config/sites.js +15 -11
- package/dist/config/sites.js.map +1 -1
- package/dist/config/userChannels.d.ts +129 -30
- package/dist/config/userChannels.d.ts.map +1 -0
- package/dist/config/userChannels.js +333 -68
- package/dist/config/userChannels.js.map +1 -1
- package/dist/config/userConfig.d.ts +14 -9
- package/dist/config/userConfig.d.ts.map +1 -0
- package/dist/config/userConfig.js +87 -25
- package/dist/config/userConfig.js.map +1 -1
- package/dist/config/userProfiles.d.ts +10 -6
- package/dist/config/userProfiles.d.ts.map +1 -0
- package/dist/config/userProfiles.js +64 -39
- package/dist/config/userProfiles.js.map +1 -1
- package/dist/hdhr/channelMap.d.ts +1 -0
- package/dist/hdhr/channelMap.d.ts.map +1 -0
- package/dist/hdhr/deviceId.d.ts +1 -0
- package/dist/hdhr/deviceId.d.ts.map +1 -0
- package/dist/hdhr/discover.d.ts +1 -0
- package/dist/hdhr/discover.d.ts.map +1 -0
- package/dist/hdhr/discover.js +48 -47
- package/dist/hdhr/discover.js.map +1 -1
- package/dist/hdhr/getHandlers.d.ts +29 -0
- package/dist/hdhr/getHandlers.d.ts.map +1 -0
- package/dist/hdhr/getHandlers.js +196 -0
- package/dist/hdhr/getHandlers.js.map +1 -0
- package/dist/hdhr/identity.d.ts +36 -0
- package/dist/hdhr/identity.d.ts.map +1 -0
- package/dist/hdhr/identity.js +51 -0
- package/dist/hdhr/identity.js.map +1 -0
- package/dist/hdhr/index.d.ts +22 -4
- package/dist/hdhr/index.d.ts.map +1 -0
- package/dist/hdhr/index.js +229 -54
- package/dist/hdhr/index.js.map +1 -1
- package/dist/hdhr/protocol.d.ts +75 -0
- package/dist/hdhr/protocol.d.ts.map +1 -0
- package/dist/hdhr/protocol.js +264 -0
- package/dist/hdhr/protocol.js.map +1 -0
- package/dist/hdhr/tunerState.d.ts +24 -0
- package/dist/hdhr/tunerState.d.ts.map +1 -0
- package/dist/hdhr/tunerState.js +67 -0
- package/dist/hdhr/tunerState.js.map +1 -0
- package/dist/hdhr/udp.d.ts +43 -0
- package/dist/hdhr/udp.d.ts.map +1 -0
- package/dist/hdhr/udp.js +277 -0
- package/dist/hdhr/udp.js.map +1 -0
- package/dist/identity.d.ts +1 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +13 -11
- package/dist/identity.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -12
- package/dist/index.js.map +1 -1
- package/dist/native/codecInference.d.ts +1 -0
- package/dist/native/codecInference.d.ts.map +1 -0
- package/dist/native/codecInference.js +50 -5
- package/dist/native/codecInference.js.map +1 -1
- package/dist/native/decrypt.d.ts +6 -2
- package/dist/native/decrypt.d.ts.map +1 -0
- package/dist/native/decrypt.js +15 -8
- package/dist/native/decrypt.js.map +1 -1
- package/dist/native/index.d.ts +40 -17
- package/dist/native/index.d.ts.map +1 -0
- package/dist/native/index.js +281 -120
- package/dist/native/index.js.map +1 -1
- package/dist/native/probe.d.ts +114 -18
- package/dist/native/probe.d.ts.map +1 -0
- package/dist/native/probe.js +452 -115
- package/dist/native/probe.js.map +1 -1
- package/dist/native/proxy.d.ts +57 -5
- package/dist/native/proxy.d.ts.map +1 -0
- package/dist/native/proxy.js +604 -170
- package/dist/native/proxy.js.map +1 -1
- package/dist/native/tokenExpiry.d.ts +1 -0
- package/dist/native/tokenExpiry.d.ts.map +1 -0
- package/dist/native/tokenExpiry.js +1 -1
- package/dist/routes/assets.d.ts +1 -0
- package/dist/routes/assets.d.ts.map +1 -0
- package/dist/routes/assets.js +4 -2
- package/dist/routes/assets.js.map +1 -1
- package/dist/routes/auth.d.ts +1 -0
- package/dist/routes/auth.d.ts.map +1 -0
- package/dist/routes/auth.js +0 -1
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/cdp.d.ts +5 -5
- package/dist/routes/cdp.d.ts.map +1 -0
- package/dist/routes/cdp.js +59 -20
- package/dist/routes/cdp.js.map +1 -1
- package/dist/routes/channels.d.ts +1 -0
- package/dist/routes/channels.d.ts.map +1 -0
- package/dist/routes/channels.js +2 -1
- package/dist/routes/channels.js.map +1 -1
- package/dist/routes/clientActions.d.ts +98 -0
- package/dist/routes/clientActions.d.ts.map +1 -0
- package/dist/routes/clientActions.js +113 -0
- package/dist/routes/clientActions.js.map +1 -0
- package/dist/routes/components.d.ts +5 -3
- package/dist/routes/components.d.ts.map +1 -0
- package/dist/routes/components.js +36 -39
- package/dist/routes/components.js.map +1 -1
- package/dist/routes/config/channels/endpoints/browse.d.ts +1 -0
- package/dist/routes/config/channels/endpoints/browse.d.ts.map +1 -0
- package/dist/routes/config/channels/endpoints/browse.js +23 -10
- package/dist/routes/config/channels/endpoints/browse.js.map +1 -1
- package/dist/routes/config/channels/endpoints/bulk.d.ts +1 -0
- package/dist/routes/config/channels/endpoints/bulk.d.ts.map +1 -0
- package/dist/routes/config/channels/endpoints/bulk.js +5 -3
- package/dist/routes/config/channels/endpoints/bulk.js.map +1 -1
- package/dist/routes/config/channels/endpoints/crud.d.ts +1 -0
- package/dist/routes/config/channels/endpoints/crud.d.ts.map +1 -0
- package/dist/routes/config/channels/endpoints/crud.js +85 -93
- package/dist/routes/config/channels/endpoints/crud.js.map +1 -1
- package/dist/routes/config/channels/endpoints/importExport.d.ts +1 -0
- package/dist/routes/config/channels/endpoints/importExport.d.ts.map +1 -0
- package/dist/routes/config/channels/endpoints/importExport.js +6 -3
- package/dist/routes/config/channels/endpoints/importExport.js.map +1 -1
- package/dist/routes/config/channels/endpoints/predefined.d.ts +1 -0
- package/dist/routes/config/channels/endpoints/predefined.d.ts.map +1 -0
- package/dist/routes/config/channels/endpoints/prefs.d.ts +1 -0
- package/dist/routes/config/channels/endpoints/prefs.d.ts.map +1 -0
- package/dist/routes/config/channels/endpoints/prefs.js +2 -1
- package/dist/routes/config/channels/endpoints/prefs.js.map +1 -1
- package/dist/routes/config/channels/endpoints/service.d.ts +2 -1
- package/dist/routes/config/channels/endpoints/service.d.ts.map +1 -0
- package/dist/routes/config/channels/endpoints/service.js +3 -1
- package/dist/routes/config/channels/endpoints/service.js.map +1 -1
- package/dist/routes/config/channels/endpoints/tags.d.ts +1 -0
- package/dist/routes/config/channels/endpoints/tags.d.ts.map +1 -0
- package/dist/routes/config/channels/endpoints/tags.js +13 -7
- package/dist/routes/config/channels/endpoints/tags.js.map +1 -1
- package/dist/routes/config/channels/healthBridge.d.ts +24 -0
- package/dist/routes/config/channels/healthBridge.d.ts.map +1 -0
- package/dist/routes/config/channels/healthBridge.js +71 -0
- package/dist/routes/config/channels/healthBridge.js.map +1 -0
- package/dist/routes/config/channels/http/handler.d.ts +1 -0
- package/dist/routes/config/channels/http/handler.d.ts.map +1 -0
- package/dist/routes/config/channels/http/playlistHint.d.ts +2 -1
- package/dist/routes/config/channels/http/playlistHint.d.ts.map +1 -0
- package/dist/routes/config/channels/http/playlistHint.js +16 -2
- package/dist/routes/config/channels/http/playlistHint.js.map +1 -1
- package/dist/routes/config/channels/http/serviceWarning.d.ts +4 -2
- package/dist/routes/config/channels/http/serviceWarning.d.ts.map +1 -0
- package/dist/routes/config/channels/http/serviceWarning.js +3 -2
- package/dist/routes/config/channels/http/serviceWarning.js.map +1 -1
- package/dist/routes/config/channels/index.d.ts +1 -0
- package/dist/routes/config/channels/index.d.ts.map +1 -0
- package/dist/routes/config/channels/setup.d.ts +1 -0
- package/dist/routes/config/channels/setup.d.ts.map +1 -0
- package/dist/routes/config/channels/table.d.ts +3 -3
- package/dist/routes/config/channels/table.d.ts.map +1 -0
- package/dist/routes/config/channels/table.js +148 -107
- package/dist/routes/config/channels/table.js.map +1 -1
- package/dist/routes/config/http/envelope.d.ts +1 -0
- package/dist/routes/config/http/envelope.d.ts.map +1 -0
- package/dist/routes/config/index.d.ts +35 -3
- package/dist/routes/config/index.d.ts.map +1 -0
- package/dist/routes/config/index.js +51 -2
- package/dist/routes/config/index.js.map +1 -1
- package/dist/routes/config/services.d.ts +5 -4
- package/dist/routes/config/services.d.ts.map +1 -0
- package/dist/routes/config/services.js +57 -43
- package/dist/routes/config/services.js.map +1 -1
- package/dist/routes/config/settings.d.ts +1 -0
- package/dist/routes/config/settings.d.ts.map +1 -0
- package/dist/routes/config/settings.js +98 -49
- package/dist/routes/config/settings.js.map +1 -1
- package/dist/routes/debug.d.ts +1 -0
- package/dist/routes/debug.d.ts.map +1 -0
- package/dist/routes/debug.js +277 -139
- package/dist/routes/debug.js.map +1 -1
- package/dist/routes/health.d.ts +39 -1
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +33 -27
- package/dist/routes/health.js.map +1 -1
- package/dist/routes/hls.d.ts +1 -0
- package/dist/routes/hls.d.ts.map +1 -0
- package/dist/routes/icons.d.ts +1 -0
- package/dist/routes/icons.d.ts.map +1 -0
- package/dist/routes/icons.js +1 -1
- package/dist/routes/index.d.ts +1 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/logs.d.ts +1 -0
- package/dist/routes/logs.d.ts.map +1 -0
- package/dist/routes/logs.js +14 -4
- package/dist/routes/logs.js.map +1 -1
- package/dist/routes/mpegts.d.ts +1 -0
- package/dist/routes/mpegts.d.ts.map +1 -0
- package/dist/routes/play.d.ts +1 -0
- package/dist/routes/play.d.ts.map +1 -0
- package/dist/routes/playlist.d.ts +1 -0
- package/dist/routes/playlist.d.ts.map +1 -0
- package/dist/routes/playlist.js +7 -2
- package/dist/routes/playlist.js.map +1 -1
- package/dist/routes/root/content.d.ts +8 -5
- package/dist/routes/root/content.d.ts.map +1 -0
- package/dist/routes/root/content.js +29 -26
- package/dist/routes/root/content.js.map +1 -1
- package/dist/routes/root/index.d.ts +2 -1
- package/dist/routes/root/index.d.ts.map +1 -0
- package/dist/routes/root/index.js +26 -21
- package/dist/routes/root/index.js.map +1 -1
- package/dist/routes/root/scripts/channels.d.ts +6 -0
- package/dist/routes/root/scripts/channels.d.ts.map +1 -0
- package/dist/routes/root/scripts/channels.js +197 -118
- package/dist/routes/root/scripts/channels.js.map +1 -1
- package/dist/routes/root/scripts/clientEscape.d.ts +22 -0
- package/dist/routes/root/scripts/clientEscape.d.ts.map +1 -0
- package/dist/routes/root/scripts/clientEscape.js +41 -0
- package/dist/routes/root/scripts/clientEscape.js.map +1 -0
- package/dist/routes/root/scripts/clientUrl.d.ts +23 -0
- package/dist/routes/root/scripts/clientUrl.d.ts.map +1 -0
- package/dist/routes/root/scripts/clientUrl.js +44 -0
- package/dist/routes/root/scripts/clientUrl.js.map +1 -0
- package/dist/routes/root/scripts/config.d.ts +6 -0
- package/dist/routes/root/scripts/config.d.ts.map +1 -0
- package/dist/routes/root/scripts/config.js +110 -24
- package/dist/routes/root/scripts/config.js.map +1 -1
- package/dist/routes/root/scripts/index.d.ts +1 -0
- package/dist/routes/root/scripts/index.d.ts.map +1 -0
- package/dist/routes/root/scripts/shared.d.ts +1 -0
- package/dist/routes/root/scripts/shared.d.ts.map +1 -0
- package/dist/routes/root/scripts/shared.js +112 -26
- package/dist/routes/root/scripts/shared.js.map +1 -1
- package/dist/routes/root/scripts/status.d.ts +1 -0
- package/dist/routes/root/scripts/status.d.ts.map +1 -0
- package/dist/routes/root/scripts/status.handlers.d.ts +23 -33
- package/dist/routes/root/scripts/status.handlers.d.ts.map +1 -0
- package/dist/routes/root/scripts/status.handlers.js +46 -92
- package/dist/routes/root/scripts/status.handlers.js.map +1 -1
- package/dist/routes/root/scripts/status.js +21 -8
- package/dist/routes/root/scripts/status.js.map +1 -1
- package/dist/routes/root/styles.d.ts +1 -0
- package/dist/routes/root/styles.d.ts.map +1 -0
- package/dist/routes/root/styles.js +7 -4
- package/dist/routes/root/styles.js.map +1 -1
- package/dist/routes/services.d.ts +13 -1
- package/dist/routes/services.d.ts.map +1 -0
- package/dist/routes/services.js +81 -68
- package/dist/routes/services.js.map +1 -1
- package/dist/routes/sse.d.ts +1 -0
- package/dist/routes/sse.d.ts.map +1 -0
- package/dist/routes/sse.js +1 -1
- package/dist/routes/sse.js.map +1 -1
- package/dist/routes/streams.d.ts +2 -1
- package/dist/routes/streams.d.ts.map +1 -0
- package/dist/routes/streams.js +6 -4
- package/dist/routes/streams.js.map +1 -1
- package/dist/routes/theme.d.ts +3 -2
- package/dist/routes/theme.d.ts.map +1 -0
- package/dist/routes/theme.js +6 -5
- package/dist/routes/theme.js.map +1 -1
- package/dist/routes/ui.d.ts +3 -1
- package/dist/routes/ui.d.ts.map +1 -0
- package/dist/routes/ui.js +16 -4
- package/dist/routes/ui.js.map +1 -1
- package/dist/routes/upgrade.d.ts +1 -0
- package/dist/routes/upgrade.d.ts.map +1 -0
- package/dist/routes/upgrade.js +6 -2
- package/dist/routes/upgrade.js.map +1 -1
- package/dist/service/commands.context.d.ts +1 -0
- package/dist/service/commands.context.d.ts.map +1 -0
- package/dist/service/commands.context.js +4 -3
- package/dist/service/commands.context.js.map +1 -1
- package/dist/service/commands.d.ts +1 -0
- package/dist/service/commands.d.ts.map +1 -0
- package/dist/service/commands.js +10 -13
- package/dist/service/commands.js.map +1 -1
- package/dist/service/generators.context.d.ts +1 -0
- package/dist/service/generators.context.d.ts.map +1 -0
- package/dist/service/generators.context.js +1 -1
- package/dist/service/generators.context.js.map +1 -1
- package/dist/service/generators.d.ts +4 -3
- package/dist/service/generators.d.ts.map +1 -0
- package/dist/service/generators.js +111 -31
- package/dist/service/generators.js.map +1 -1
- package/dist/service/index.d.ts +1 -0
- package/dist/service/index.d.ts.map +1 -0
- package/dist/streaming/captureLock.d.ts +44 -0
- package/dist/streaming/captureLock.d.ts.map +1 -0
- package/dist/streaming/captureLock.js +95 -0
- package/dist/streaming/captureLock.js.map +1 -0
- package/dist/streaming/captureSession.d.ts +31 -0
- package/dist/streaming/captureSession.d.ts.map +1 -0
- package/dist/streaming/captureSession.js +54 -0
- package/dist/streaming/captureSession.js.map +1 -0
- package/dist/streaming/clients.d.ts +1 -0
- package/dist/streaming/clients.d.ts.map +1 -0
- package/dist/streaming/codec.d.ts +1 -0
- package/dist/streaming/codec.d.ts.map +1 -0
- package/dist/streaming/fmp4Segmenter.d.ts +29 -2
- package/dist/streaming/fmp4Segmenter.d.ts.map +1 -0
- package/dist/streaming/fmp4Segmenter.js +79 -26
- package/dist/streaming/fmp4Segmenter.js.map +1 -1
- package/dist/streaming/hls.d.ts +18 -7
- package/dist/streaming/hls.d.ts.map +1 -0
- package/dist/streaming/hls.js +220 -120
- package/dist/streaming/hls.js.map +1 -1
- package/dist/streaming/hlsResume.d.ts +1 -0
- package/dist/streaming/hlsResume.d.ts.map +1 -0
- package/dist/streaming/hlsResume.js +1 -1
- package/dist/streaming/hlsResume.js.map +1 -1
- package/dist/streaming/hlsSegments.d.ts +59 -2
- package/dist/streaming/hlsSegments.d.ts.map +1 -0
- package/dist/streaming/hlsSegments.js +147 -17
- package/dist/streaming/hlsSegments.js.map +1 -1
- package/dist/streaming/lifecycle.d.ts +4 -1
- package/dist/streaming/lifecycle.d.ts.map +1 -0
- package/dist/streaming/lifecycle.js +65 -85
- package/dist/streaming/lifecycle.js.map +1 -1
- package/dist/streaming/monitor.d.ts +6 -4
- package/dist/streaming/monitor.d.ts.map +1 -0
- package/dist/streaming/monitor.js +343 -145
- package/dist/streaming/monitor.js.map +1 -1
- package/dist/streaming/mp4Parser.d.ts +5 -4
- package/dist/streaming/mp4Parser.d.ts.map +1 -0
- package/dist/streaming/mp4Parser.js +36 -13
- package/dist/streaming/mp4Parser.js.map +1 -1
- package/dist/streaming/mpegts.d.ts +16 -0
- package/dist/streaming/mpegts.d.ts.map +1 -0
- package/dist/streaming/mpegts.js +61 -17
- package/dist/streaming/mpegts.js.map +1 -1
- package/dist/streaming/playlistBuilder.d.ts +1 -0
- package/dist/streaming/playlistBuilder.d.ts.map +1 -0
- package/dist/streaming/preroll.d.ts +32 -5
- package/dist/streaming/preroll.d.ts.map +1 -0
- package/dist/streaming/preroll.js +70 -32
- package/dist/streaming/preroll.js.map +1 -1
- package/dist/streaming/pretune.d.ts +26 -1
- package/dist/streaming/pretune.d.ts.map +1 -0
- package/dist/streaming/pretune.js +22 -24
- package/dist/streaming/pretune.js.map +1 -1
- package/dist/streaming/pretuneTimers.d.ts +24 -0
- package/dist/streaming/pretuneTimers.d.ts.map +1 -0
- package/dist/streaming/pretuneTimers.js +56 -0
- package/dist/streaming/pretuneTimers.js.map +1 -0
- package/dist/streaming/recovery.d.ts +139 -5
- package/dist/streaming/recovery.d.ts.map +1 -0
- package/dist/streaming/recovery.js +160 -16
- package/dist/streaming/recovery.js.map +1 -1
- package/dist/streaming/registry.d.ts +27 -9
- package/dist/streaming/registry.d.ts.map +1 -0
- package/dist/streaming/registry.js +21 -3
- package/dist/streaming/registry.js.map +1 -1
- package/dist/streaming/setup.d.ts +157 -29
- package/dist/streaming/setup.d.ts.map +1 -0
- package/dist/streaming/setup.js +1034 -458
- package/dist/streaming/setup.js.map +1 -1
- package/dist/streaming/showInfo.d.ts +21 -1
- package/dist/streaming/showInfo.d.ts.map +1 -0
- package/dist/streaming/showInfo.js +31 -16
- package/dist/streaming/showInfo.js.map +1 -1
- package/dist/streaming/statusEmitter.d.ts +10 -10
- package/dist/streaming/statusEmitter.d.ts.map +1 -0
- package/dist/streaming/statusEmitter.js +5 -8
- package/dist/streaming/statusEmitter.js.map +1 -1
- package/dist/types/channels.d.ts +16 -4
- package/dist/types/channels.d.ts.map +1 -0
- package/dist/types/channels.js +4 -2
- package/dist/types/channels.js.map +1 -1
- package/dist/types/config.d.ts +16 -5
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/index.d.ts +3 -2
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/profiles.d.ts +6 -4
- package/dist/types/profiles.d.ts.map +1 -0
- package/dist/types/selection.d.ts +31 -3
- package/dist/types/selection.d.ts.map +1 -0
- package/dist/types/selection.js +4 -2
- package/dist/types/selection.js.map +1 -1
- package/dist/types/shared.d.ts +1 -0
- package/dist/types/shared.d.ts.map +1 -0
- package/dist/types/streaming.d.ts +8 -0
- package/dist/types/streaming.d.ts.map +1 -0
- package/dist/upgrade/commands.context.d.ts +1 -0
- package/dist/upgrade/commands.context.d.ts.map +1 -0
- package/dist/upgrade/commands.context.js +14 -10
- package/dist/upgrade/commands.context.js.map +1 -1
- package/dist/upgrade/commands.d.ts +8 -15
- package/dist/upgrade/commands.d.ts.map +1 -0
- package/dist/upgrade/commands.js +27 -28
- package/dist/upgrade/commands.js.map +1 -1
- package/dist/upgrade/detection.context.d.ts +1 -0
- package/dist/upgrade/detection.context.d.ts.map +1 -0
- package/dist/upgrade/detection.context.js +4 -1
- package/dist/upgrade/detection.context.js.map +1 -1
- package/dist/upgrade/detection.d.ts +9 -7
- package/dist/upgrade/detection.d.ts.map +1 -0
- package/dist/upgrade/detection.js +23 -54
- package/dist/upgrade/detection.js.map +1 -1
- package/dist/upgrade/index.d.ts +2 -0
- package/dist/upgrade/index.d.ts.map +1 -0
- package/dist/upgrade/index.js +1 -0
- package/dist/upgrade/index.js.map +1 -1
- package/dist/upgrade/lifecycle.context.d.ts +20 -0
- package/dist/upgrade/lifecycle.context.d.ts.map +1 -0
- package/dist/upgrade/lifecycle.context.js +47 -0
- package/dist/upgrade/lifecycle.context.js.map +1 -0
- package/dist/upgrade/lifecycle.d.ts +72 -0
- package/dist/upgrade/lifecycle.d.ts.map +1 -0
- package/dist/upgrade/lifecycle.js +175 -0
- package/dist/upgrade/lifecycle.js.map +1 -0
- package/dist/upgrade/pathHandle.d.ts +30 -0
- package/dist/upgrade/pathHandle.d.ts.map +1 -0
- package/dist/upgrade/pathHandle.js +102 -0
- package/dist/upgrade/pathHandle.js.map +1 -0
- package/dist/utils/bootSession.context.d.ts +16 -0
- package/dist/utils/bootSession.context.d.ts.map +1 -0
- package/dist/utils/bootSession.context.js +60 -0
- package/dist/utils/bootSession.context.js.map +1 -0
- package/dist/utils/bootSession.d.ts +17 -0
- package/dist/utils/bootSession.d.ts.map +1 -0
- package/dist/utils/bootSession.js +20 -0
- package/dist/utils/bootSession.js.map +1 -0
- package/dist/utils/chromeFetch.d.ts +1 -0
- package/dist/utils/chromeFetch.d.ts.map +1 -0
- package/dist/utils/cliOutput.d.ts +1 -0
- package/dist/utils/cliOutput.d.ts.map +1 -0
- package/dist/utils/clock.d.ts +5 -4
- package/dist/utils/clock.d.ts.map +1 -0
- package/dist/utils/clock.js +19 -16
- package/dist/utils/clock.js.map +1 -1
- package/dist/utils/debugFilter.d.ts +9 -1
- package/dist/utils/debugFilter.d.ts.map +1 -0
- package/dist/utils/debugFilter.js +60 -33
- package/dist/utils/debugFilter.js.map +1 -1
- package/dist/utils/delay.d.ts +48 -15
- package/dist/utils/delay.d.ts.map +1 -0
- package/dist/utils/delay.js +73 -20
- package/dist/utils/delay.js.map +1 -1
- package/dist/utils/errors.d.ts +32 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +45 -4
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/evaluate.d.ts +1 -0
- package/dist/utils/evaluate.d.ts.map +1 -0
- package/dist/utils/evaluate.js +29 -23
- package/dist/utils/evaluate.js.map +1 -1
- package/dist/utils/ffmpeg.context.d.ts +1 -0
- package/dist/utils/ffmpeg.context.d.ts.map +1 -0
- package/dist/utils/ffmpeg.context.js.map +1 -1
- package/dist/utils/ffmpeg.d.ts +29 -7
- package/dist/utils/ffmpeg.d.ts.map +1 -0
- package/dist/utils/ffmpeg.js +32 -14
- package/dist/utils/ffmpeg.js.map +1 -1
- package/dist/utils/fileLogger.d.ts +3 -1
- package/dist/utils/fileLogger.d.ts.map +1 -0
- package/dist/utils/fileLogger.js +64 -29
- package/dist/utils/fileLogger.js.map +1 -1
- package/dist/utils/format.d.ts +14 -1
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +19 -5
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/logEmitter.d.ts +1 -0
- package/dist/utils/logEmitter.d.ts.map +1 -0
- package/dist/utils/logger.d.ts +1 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +10 -6
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/m3u.d.ts +3 -2
- package/dist/utils/m3u.d.ts.map +1 -0
- package/dist/utils/m3u.js +4 -3
- package/dist/utils/m3u.js.map +1 -1
- package/dist/utils/markup.d.ts +13 -1
- package/dist/utils/markup.d.ts.map +1 -0
- package/dist/utils/markup.js +28 -4
- package/dist/utils/markup.js.map +1 -1
- package/dist/utils/memo.d.ts +3 -2
- package/dist/utils/memo.d.ts.map +1 -0
- package/dist/utils/memo.js +3 -3
- package/dist/utils/morganStream.d.ts +1 -0
- package/dist/utils/morganStream.d.ts.map +1 -0
- package/dist/utils/network.d.ts +1 -0
- package/dist/utils/network.d.ts.map +1 -0
- package/dist/utils/network.js +2 -2
- package/dist/utils/pid.d.ts +3 -25
- package/dist/utils/pid.d.ts.map +1 -0
- package/dist/utils/pid.js +10 -49
- package/dist/utils/pid.js.map +1 -1
- package/dist/utils/platform.d.ts +3 -1
- package/dist/utils/platform.d.ts.map +1 -0
- package/dist/utils/platform.js +2 -1
- package/dist/utils/platform.js.map +1 -1
- package/dist/utils/processInspector.context.d.ts +8 -0
- package/dist/utils/processInspector.context.d.ts.map +1 -0
- package/dist/utils/processInspector.context.js +106 -0
- package/dist/utils/processInspector.context.js.map +1 -0
- package/dist/utils/processInspector.d.ts +55 -0
- package/dist/utils/processInspector.d.ts.map +1 -0
- package/dist/utils/processInspector.js +127 -0
- package/dist/utils/processInspector.js.map +1 -0
- package/dist/utils/retry.d.ts +25 -2
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +33 -6
- package/dist/utils/retry.js.map +1 -1
- package/dist/utils/runtimeIdentity.context.d.ts +7 -0
- package/dist/utils/runtimeIdentity.context.d.ts.map +1 -0
- package/dist/utils/runtimeIdentity.context.js +43 -0
- package/dist/utils/runtimeIdentity.context.js.map +1 -0
- package/dist/utils/runtimeIdentity.d.ts +98 -0
- package/dist/utils/runtimeIdentity.d.ts.map +1 -0
- package/dist/utils/runtimeIdentity.js +181 -0
- package/dist/utils/runtimeIdentity.js.map +1 -0
- package/dist/utils/sanitize.d.ts +1 -0
- package/dist/utils/sanitize.d.ts.map +1 -0
- package/dist/utils/streamContext.d.ts +2 -1
- package/dist/utils/streamContext.d.ts.map +1 -0
- package/dist/utils/streamContext.js +1 -1
- package/dist/utils/timing.d.ts +2 -1
- package/dist/utils/timing.d.ts.map +1 -0
- package/dist/utils/timing.js +0 -5
- package/dist/utils/timing.js.map +1 -1
- package/dist/utils/version.d.ts +1 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +47 -13
- package/dist/utils/version.js.map +1 -1
- package/package.json +26 -20
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
/* Copyright(C) 2024-2026, HJD (https://github.com/hjdhjd). All rights reserved.
|
|
2
|
-
*
|
|
3
|
-
* precaching.ts: Service channel lineup precaching for PrismCast.
|
|
4
|
-
*/
|
|
5
1
|
import { LOG, extractDomain, formatError, startTimer } from "../utils/index.js";
|
|
6
|
-
import {
|
|
2
|
+
import { clearDomainAuthRequirement, getDomainAuthState, markDomainAuth, markDomainAuthRequired } from "../config/health.js";
|
|
3
|
+
import { getCurrentBrowser, isGracefulShutdown, minimizeBrowserWindow, registerManagedPage, unregisterManagedPage } from "./index.js";
|
|
4
|
+
import { getProviderBySlug, getProvidersForDomain } from "./channelSelection.js";
|
|
7
5
|
import { CONFIG } from "../config/index.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
6
|
+
import { classifyBlockedPage } from "./blockedPage.js";
|
|
7
|
+
import { getProfileForUrl } from "../config/profiles.js";
|
|
8
|
+
import { isLoginModeActive } from "./login.js";
|
|
9
|
+
import { persistProviderLineup } from "../config/providerLineups.js";
|
|
10
|
+
import { startOverlayHandling } from "./consent.js";
|
|
10
11
|
/* 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
12
|
* 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
13
|
* systems. The HTTP server starts immediately; precaching begins in the background after a brief delay.
|
|
@@ -14,44 +15,516 @@ import { markDomainAuth } from "../config/health.js";
|
|
|
14
15
|
* 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
16
|
* 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
17
|
* crash between services is handled transparently (the next service gets the relaunched browser).
|
|
18
|
+
*
|
|
19
|
+
* This module also owns the discovery-outcome policy (recordDiscoveryOutcome): the single source of truth for how a completed discovery walk translates into domain
|
|
20
|
+
* auth state and a persisted channel lineup, shared by the precache cycle here and the /services/:slug/channels endpoint. The routes layer never calls a health
|
|
21
|
+
* mutator or the lineup store directly - it calls the recorder, which does. The page session itself is owned by withProviderGuidePage: the single guarded-page
|
|
22
|
+
* primitive both the precache cycle and that endpoint walk their guides through, and the one place the empty-walk retry policy lives, so no provider carries a
|
|
23
|
+
* retry of its own.
|
|
17
24
|
*/
|
|
18
25
|
// Delay in milliseconds before precaching begins after browser launch. This gives the browser time to settle after initialization.
|
|
19
26
|
const PRECACHE_DELAY = 5000;
|
|
20
|
-
|
|
27
|
+
/* Delay in milliseconds before the services that discovered nothing are re-attempted. Five minutes puts the second pass well past the contention a boot creates -
|
|
28
|
+
* the browser launch, the first tunes, the DVR's own channel scan - which is the likeliest reason a provider's lazy content never appeared inside its walk. There
|
|
29
|
+
* is exactly one such pass: a service still empty on a settled system has a standing problem that another walk will not solve, and a repeating attempt would keep
|
|
30
|
+
* waking the browser for it indefinitely.
|
|
31
|
+
*/
|
|
32
|
+
const PRECACHE_RETRY_DELAY = 300000;
|
|
33
|
+
// Guard flag preventing overlapping precache cycles. Set to true before the cycle starts, cleared through releasePrecacheGuard in a finally block.
|
|
21
34
|
let precacheInProgress = false;
|
|
35
|
+
// Handle for the scheduled precache cycle, tracked so a graceful shutdown can cancel it before it fires. Null when no cycle is pending.
|
|
36
|
+
let precacheTimer = null;
|
|
37
|
+
/* The pending deferred re-attempt: the services still to re-walk and the timer that will do it. One value rather than two fields, so arming, cancelling, and
|
|
38
|
+
* firing each move the whole thing at once - a slug list with no timer behind it, or a timer whose slugs were cleared, is not a state this can reach.
|
|
39
|
+
*/
|
|
40
|
+
let deferredRetry = null;
|
|
41
|
+
/* Whether a full precache cycle was requested while the single-flight guard was held. The request always comes from a browser relaunch, which cleared every
|
|
42
|
+
* provider cache, so the run holding the guard is walking guides for a browser that no longer exists and its result is worth nothing - dropping the request would
|
|
43
|
+
* leave the new browser with no lineups at all. Whoever releases the guard runs it; releasePrecacheGuard is where that happens, and it is the only place.
|
|
44
|
+
*/
|
|
45
|
+
let fullCycleRequested = false;
|
|
22
46
|
/**
|
|
23
|
-
*
|
|
24
|
-
* is already in progress, returns immediately. The actual work is scheduled via setTimeout to avoid blocking browser launch.
|
|
47
|
+
* Cancels a pending deferred re-attempt and drops its state as one unit. A no-op when nothing is pending.
|
|
25
48
|
*/
|
|
26
|
-
|
|
49
|
+
function clearDeferredRetry() {
|
|
50
|
+
if (deferredRetry) {
|
|
51
|
+
clearTimeout(deferredRetry.timer);
|
|
52
|
+
deferredRetry = null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Releases the single-flight guard and honors any full-cycle request that arrived while it was held.
|
|
57
|
+
*
|
|
58
|
+
* The order is the whole point. The flag is cleared first, so the reentrant startPrecaching below finds a free guard and schedules. Honoring first would have that
|
|
59
|
+
* call see the guard still held and record the very request being honored, which is how a browser relaunch's cycle would go round forever without ever running.
|
|
60
|
+
* Every path that takes the guard - the cycle, the deferred re-attempt, and the post-login revalidation - releases it here, so the hand-off has one home rather
|
|
61
|
+
* than a copy at each site.
|
|
62
|
+
* @param deps - The injected dependencies, handed to the cycle this may start.
|
|
63
|
+
*/
|
|
64
|
+
function releasePrecacheGuard(deps) {
|
|
65
|
+
precacheInProgress = false;
|
|
66
|
+
if (!fullCycleRequested) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
fullCycleRequested = false;
|
|
70
|
+
startPrecaching(deps);
|
|
71
|
+
}
|
|
72
|
+
export const defaultPrecachingDeps = {
|
|
73
|
+
getCurrentBrowser,
|
|
74
|
+
getProviderBySlug,
|
|
75
|
+
getProvidersForDomain,
|
|
76
|
+
isGracefulShutdown,
|
|
77
|
+
minimizeBrowserWindow,
|
|
78
|
+
persistProviderLineup,
|
|
79
|
+
registerManagedPage,
|
|
80
|
+
startOverlayHandling,
|
|
81
|
+
unregisterManagedPage
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Starts the precaching cycle if services are configured. Called from launchBrowser() after the browser is ready. If no services are selected, a shutdown is in
|
|
85
|
+
* progress, or a precache cycle is already in progress, returns immediately. The actual work is scheduled via setTimeout to avoid blocking browser launch.
|
|
86
|
+
* @param deps - The injected browser and provider-registry dependencies; defaults to defaultPrecachingDeps.
|
|
87
|
+
*/
|
|
88
|
+
export function startPrecaching(deps = defaultPrecachingDeps) {
|
|
27
89
|
if (CONFIG.channels.precacheServices.length === 0) {
|
|
28
90
|
return;
|
|
29
91
|
}
|
|
92
|
+
// Never schedule a precache during graceful shutdown. launchBrowser() can be reached during teardown; without this guard the scheduled cycle would fire after the
|
|
93
|
+
// browser is closed and relaunch Chrome.
|
|
94
|
+
if (deps.isGracefulShutdown()) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
30
97
|
if (precacheInProgress) {
|
|
98
|
+
// Record the request rather than dropping it. This call is a browser relaunch's, and the run currently holding the guard is walking guides for a browser whose
|
|
99
|
+
// caches were just cleared out from under it - so the request is the one worth keeping, and whoever releases the guard runs it.
|
|
100
|
+
fullCycleRequested = true;
|
|
31
101
|
LOG.debug("precache", "Precache deferred: already in progress.");
|
|
32
102
|
return;
|
|
33
103
|
}
|
|
34
104
|
// Set the guard before scheduling so that a second call during the delay window (e.g., rapid browser crash + relaunch) sees the flag and defers.
|
|
35
105
|
precacheInProgress = true;
|
|
36
|
-
//
|
|
37
|
-
|
|
106
|
+
// A full cycle supersedes any pending deferred re-attempt: it walks every configured service, the empty ones included, and letting both run would put two passes
|
|
107
|
+
// over the same guides in contention for one browser.
|
|
108
|
+
clearDeferredRetry();
|
|
109
|
+
// Schedule the precache cycle after a brief delay to let the browser settle. The handle is tracked so stopPrecaching() can cancel it if shutdown begins during the
|
|
110
|
+
// delay window; the ref is cleared when the timer fires since it is then spent.
|
|
111
|
+
precacheTimer = setTimeout(() => {
|
|
112
|
+
precacheTimer = null;
|
|
113
|
+
void runPrecacheCycle(deps);
|
|
114
|
+
}, PRECACHE_DELAY);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Cancels every scheduled precache - the startup cycle and the deferred re-attempt alike - and clears the in-progress guard. Called during graceful shutdown so
|
|
118
|
+
* nothing scheduled can fire after the browser has been closed. Safe to call when nothing is pending.
|
|
119
|
+
*
|
|
120
|
+
* The guard is cleared directly rather than through releasePrecacheGuard, and the hand-off flag with it: a shutdown must not honor a pending full-cycle request,
|
|
121
|
+
* which is exactly what releasing through the hand-off would do.
|
|
122
|
+
*/
|
|
123
|
+
export function stopPrecaching() {
|
|
124
|
+
if (precacheTimer) {
|
|
125
|
+
clearTimeout(precacheTimer);
|
|
126
|
+
precacheTimer = null;
|
|
127
|
+
}
|
|
128
|
+
clearDeferredRetry();
|
|
129
|
+
fullCycleRequested = false;
|
|
130
|
+
precacheInProgress = false;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Records the consequences of a completed channel discovery: the domain auth state it proves, and the durable lineup it produced. This is the single source of
|
|
134
|
+
* truth for discovery-outcome policy, consumed by both the precache cycle (via precacheService) and the /services/:slug/channels endpoint - the routes layer never
|
|
135
|
+
* calls a health mutator or the lineup store directly. The two halves are bundled deliberately: a completed walk is one event, and what it proves about the domain
|
|
136
|
+
* and what it found on the guide are that event's transient and durable records.
|
|
137
|
+
*
|
|
138
|
+
* An empty result classifies the still-open page: a confirmed authentication wall marks the provider's domain needs-sign-in; a consent overlay and the unknown
|
|
139
|
+
* classification change no state (an unexplained empty walk is not evidence of anything). A non-empty result that the provider's validatePrecache accepts (or that
|
|
140
|
+
* needs no validation) marks the domain verified and persists the lineup; a non-empty result the validator rejects proves the wall is gone but not that paid access
|
|
141
|
+
* exists, so it clears a standing needs-sign-in entry back to unknown, changes nothing else, and persists nothing - a rejected walk is not a lineup the store can
|
|
142
|
+
* safely replace a slice with.
|
|
143
|
+
* @param provider - The provider whose discovery completed.
|
|
144
|
+
* @param channels - The discovered channels (possibly empty).
|
|
145
|
+
* @param page - The still-open discovery page, inspected only when the result is empty.
|
|
146
|
+
* @param deps - The injected dependencies; the lineup write runs through deps.persistProviderLineup.
|
|
147
|
+
* @param classification - A classification the caller already performed against the page state it wants recorded. When omitted, the still-open page is classified
|
|
148
|
+
* here, which is what every caller that has not already looked does.
|
|
149
|
+
* @returns A promise that resolves once any classification and state recording completes.
|
|
150
|
+
*/
|
|
151
|
+
export async function recordDiscoveryOutcome(provider, channels, page, deps, classification) {
|
|
152
|
+
const domain = extractDomain(provider.guideUrl);
|
|
153
|
+
if (channels.length === 0) {
|
|
154
|
+
const outcome = classification ?? await classifyBlockedPage(page, { indicators: provider.authWallIndicators, requestedUrl: provider.guideUrl });
|
|
155
|
+
switch (outcome.kind) {
|
|
156
|
+
case "authWall": {
|
|
157
|
+
markDomainAuthRequired(domain);
|
|
158
|
+
LOG.warn("%s returned no channels because the provider is presenting an authentication wall (%s). Sign in from the channel table's login icon.", provider.label, outcome.evidence);
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
case "consentOverlay": {
|
|
162
|
+
LOG.warn("%s returned no channels while a consent overlay was present on the page. Open the channel in PrismCast's Chrome from the channel table's " +
|
|
163
|
+
"login icon to dismiss it.", provider.label);
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
case "unknown": {
|
|
167
|
+
LOG.debug("precache", "%s returned no channels and the page did not classify as an auth wall or consent overlay; leaving domain auth unchanged.", provider.label);
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
// A successful discovery with results proves the service is accessible and authenticated. Mark it so the UI shows the green indicator immediately rather than
|
|
174
|
+
// waiting for the first manual tune. When a provider module defines validatePrecache, defer to it - some services (e.g., Sling) return guide data even without
|
|
175
|
+
// authentication, so a non-empty result alone does not prove paid access.
|
|
176
|
+
if (!provider.validatePrecache || provider.validatePrecache(channels)) {
|
|
177
|
+
markDomainAuth(domain);
|
|
178
|
+
/* Persist what the walk found so the lineup outlives this browser session. The provider states its own durable shape through exportDurableLineup - which
|
|
179
|
+
* fields survive a session is provider knowledge, not the recorder's - and a provider with nothing durable to add contributes the channel identities the walk
|
|
180
|
+
* returned.
|
|
181
|
+
*
|
|
182
|
+
* Only a walk the validator accepted is written, and the reason is the store's replace semantics: a slice is replaced wholesale, so a rejected walk - one the
|
|
183
|
+
* provider itself judges not to be a trustworthy statement of its lineup - could shrink a fuller slice written earlier, dropping channels out of the
|
|
184
|
+
* cold-listing fallback and taking their durable watch URLs with them. Verify-on-use covers staleness, not a lineup the provider has already rejected.
|
|
185
|
+
*
|
|
186
|
+
* The write is fire-and-forget by design: it never throws, and making the discovery endpoint's response wait on a file write would charge the user for a
|
|
187
|
+
* durability guarantee they did not ask for.
|
|
188
|
+
*/
|
|
189
|
+
const lineup = provider.exportDurableLineup?.() ??
|
|
190
|
+
channels.map((channel) => ({ channelSelector: channel.channelSelector, name: channel.name }));
|
|
191
|
+
// The real write absorbs its own failures, so the trailing catch is about the port rather than the store: whatever a caller injects here, this call site can
|
|
192
|
+
// never become a rejection source that takes down the walk it belongs to. The health-state flush guards its own fire-and-forget write the same way.
|
|
193
|
+
void deps.persistProviderLineup(provider.slug, lineup).catch((error) => {
|
|
194
|
+
LOG.debug("precache", "The channel lineup write for %s did not complete: %s.", provider.label, formatError(error));
|
|
195
|
+
});
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
// The validator rejected: the wall is gone (channels came back) but paid access is unproven. Clear a standing needs-sign-in entry back to unknown so the red
|
|
199
|
+
// state never outlives the wall it reported - clearDomainAuthRequirement is a no-op unless the domain is currently flagged, so verified state is never touched.
|
|
200
|
+
clearDomainAuthRequirement(domain);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Gives an empty discovery walk one more chance, when the page it left behind says the emptiness is unexplained.
|
|
204
|
+
*
|
|
205
|
+
* The classification comes first and decides everything. A confirmed authentication wall or a standing consent overlay explains the empty result completely, and
|
|
206
|
+
* neither is something a reload can fix - so those skip the retry and hand their classification back, because the evidence that justified it is on the page as it
|
|
207
|
+
* stands and a reload would only put a fresh, undismissed banner in front of the recorder. An unclassifiable page is the case worth retrying: the guide rendered,
|
|
208
|
+
* nothing blocked it, and the lineup simply never populated within the walk's budget, which a reload plausibly cures.
|
|
209
|
+
*
|
|
210
|
+
* The reload is skipped for a provider that navigates inside its own walk, since the retry re-navigates for itself; a reload that throws ends the retry rather than
|
|
211
|
+
* risking a second walk against a page in an unknown state, and the first walk's classification is what gets recorded.
|
|
212
|
+
* @param options - The page, provider, profile, and dependencies. See RetryAfterEmptyWalkOptions.
|
|
213
|
+
* @returns The retry's channels and, when the retry was declined, the classification to record.
|
|
214
|
+
*/
|
|
215
|
+
async function retryAfterEmptyWalk(options) {
|
|
216
|
+
const { deps, page, profile, provider } = options;
|
|
217
|
+
const classification = await classifyBlockedPage(page, { indicators: provider.authWallIndicators, requestedUrl: provider.guideUrl });
|
|
218
|
+
if (classification.kind !== "unknown") {
|
|
219
|
+
LOG.debug("precache", "%s returned no channels and the page classified as %s; recording that rather than retrying.", provider.label, classification.kind);
|
|
220
|
+
return { channels: [], classification };
|
|
221
|
+
}
|
|
222
|
+
if (provider.handlesOwnNavigation) {
|
|
223
|
+
LOG.debug("precache", "Retrying the empty discovery walk for %s without a reload: its walk navigates for itself.", provider.label);
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
LOG.debug("precache", "Reloading the guide and retrying the empty discovery walk for %s.", provider.label);
|
|
227
|
+
try {
|
|
228
|
+
await page.reload({ timeout: CONFIG.streaming.navigationTimeout, waitUntil: "networkidle2" });
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
LOG.debug("precache", "The reload before retrying %s failed: %s. Recording the first walk's outcome instead.", provider.label, formatError(error));
|
|
232
|
+
return { channels: [], classification };
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// A fresh controller for the second walk's poll. The first one is already aborted, and the poll's entry check returns immediately on an aborted signal, so
|
|
236
|
+
// reusing it would silently leave the retry unprotected against a banner that reappears after the reload.
|
|
237
|
+
const retryController = new AbortController();
|
|
238
|
+
try {
|
|
239
|
+
void deps.startOverlayHandling(page, profile, { phase: "discovery", signal: retryController.signal });
|
|
240
|
+
return { channels: await provider.discoverChannels(page) };
|
|
241
|
+
}
|
|
242
|
+
finally {
|
|
243
|
+
retryController.abort();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Opens a guarded browser page for a provider's guide, runs its discovery walk under a consent-overlay poll, and cleans the page up. This is the single owner of the
|
|
248
|
+
* discovery page session shared by the precache cycle and the /services/:slug/channels endpoint: it holds the page lifecycle (creation, managed-page registration,
|
|
249
|
+
* close), the abort mechanics (close-on-abort plus the pre-navigation early-abort), the audio-mute override, and the discovery-phase overlay poll that dismisses
|
|
250
|
+
* cookie banners and per-site modals during the walk. The overlay poll is aborted the instant the walk completes, so the page is quiet by construction before the
|
|
251
|
+
* afterWalk hook inspects it - a poll still clicking could dismiss the very overlay a classification is about to report.
|
|
252
|
+
*
|
|
253
|
+
* A walk that comes back empty gets one reload-and-retry, on the terms retryAfterEmptyWalk sets out. The hook still runs exactly once, against whichever result
|
|
254
|
+
* stands.
|
|
255
|
+
* @param provider - The provider whose guide to walk.
|
|
256
|
+
* @param options - The optional post-walk hook and abort signal. See WithProviderGuidePageOptions.
|
|
257
|
+
* @param deps - The injected browser and overlay-poll dependencies; defaults to defaultPrecachingDeps.
|
|
258
|
+
* @returns The discovered channels (possibly empty).
|
|
259
|
+
*/
|
|
260
|
+
export async function withProviderGuidePage(provider, options = {}, deps = defaultPrecachingDeps) {
|
|
261
|
+
const { afterWalk, signal } = options;
|
|
262
|
+
const browser = await deps.getCurrentBrowser();
|
|
263
|
+
const page = await browser.newPage();
|
|
264
|
+
// Close the page the moment the caller aborts, so any in-progress Puppeteer operation throws and propagates the cancellation through discoverChannels without each
|
|
265
|
+
// provider having to poll the signal. The helper owns this mechanism because it owns page creation - no caller ever holds the page reference, so close-on-abort
|
|
266
|
+
// must live beside the lifecycle it cancels. The listener is registered the instant the page exists and removed in the finally.
|
|
267
|
+
const onAbort = () => {
|
|
268
|
+
void page.close().catch(() => { });
|
|
269
|
+
};
|
|
270
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
271
|
+
// The overlay poll that dismisses cookie banners and per-site modals during the walk. Its controller is aborted the instant the walk completes, so the page is
|
|
272
|
+
// quiet by construction before any classification the afterWalk hook performs - a poll still clicking could dismiss the very overlay a classification is about to
|
|
273
|
+
// report.
|
|
274
|
+
const overlayController = new AbortController();
|
|
275
|
+
try {
|
|
276
|
+
// If the caller aborted between entering this helper and creating the page, the abort listener fired while the page did not yet exist, so it never closed the
|
|
277
|
+
// just-created page. Bail now - the finally closes it - and let the discovery caller map this to its abort sentinel.
|
|
278
|
+
if (signal?.aborted) {
|
|
279
|
+
throw new Error("Discovery aborted before navigation.");
|
|
280
|
+
}
|
|
281
|
+
// Suppress audio on the guide page. Services like Hulu auto-play a default livestream when their guide loads. Since Chrome's --mute-audio is deliberately
|
|
282
|
+
// disabled (puppeteer-stream needs audio capture for active streams), we intercept play() at the prototype level to mute before any media element can produce
|
|
283
|
+
// audio. evaluateOnNewDocument runs before site JavaScript, so nothing slips through.
|
|
284
|
+
await page.evaluateOnNewDocument(() => {
|
|
285
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
286
|
+
const originalPlay = HTMLMediaElement.prototype.play;
|
|
287
|
+
HTMLMediaElement.prototype.play = async function () {
|
|
288
|
+
this.muted = true;
|
|
289
|
+
return originalPlay.call(this);
|
|
290
|
+
};
|
|
291
|
+
});
|
|
292
|
+
deps.registerManagedPage(page);
|
|
293
|
+
// Launch the discovery-phase overlay poll before navigation: a handlesOwnNavigation provider navigates inside discoverChannels, and the tick-error taxonomy lets
|
|
294
|
+
// the poll survive that navigation. The phase's window is the backstop; the abort after the walk is the terminator. The guide page is not a tune, so the phase
|
|
295
|
+
// forbids the embed-gate accept - only cookie rejection and per-site modal dismissal run here.
|
|
296
|
+
const { profile } = getProfileForUrl(provider.guideUrl);
|
|
297
|
+
void deps.startOverlayHandling(page, profile, { phase: "discovery", signal: overlayController.signal });
|
|
298
|
+
// Navigate to the service's guide URL unless the provider module handles its own navigation (e.g., sets up response interception before navigating). We use
|
|
299
|
+
// networkidle2 rather than load because SPA-based services (e.g., Hulu) have heavy async initialization that can prevent the load event from firing reliably.
|
|
300
|
+
if (!provider.handlesOwnNavigation) {
|
|
301
|
+
await page.goto(provider.guideUrl, { timeout: CONFIG.streaming.navigationTimeout, waitUntil: "networkidle2" });
|
|
302
|
+
}
|
|
303
|
+
let channels = await provider.discoverChannels(page);
|
|
304
|
+
// The walk is complete. Abort the overlay poll so the page is quiet by construction before anything classifies it.
|
|
305
|
+
overlayController.abort();
|
|
306
|
+
let classification;
|
|
307
|
+
/* An empty walk gets one more attempt, because the failure it most often represents is transient: a rail or grid whose lazy content never populated inside the
|
|
308
|
+
* walk's budget, on a page that is otherwise fine. The gates are the ones that make a second walk meaningful at all - a closed or cancelled session has nothing
|
|
309
|
+
* to retry against, and a shutdown must not open new work - and the retry's own classification decides whether a reload could help.
|
|
310
|
+
*/
|
|
311
|
+
if ((channels.length === 0) && !page.isClosed() && !signal?.aborted && !deps.isGracefulShutdown()) {
|
|
312
|
+
({ channels, classification } = await retryAfterEmptyWalk({ deps, page, profile, provider }));
|
|
313
|
+
}
|
|
314
|
+
// The hook runs once, on whichever result stands - and receives the first walk's classification only when the retry declined to reload, so the page it is
|
|
315
|
+
// handed and the classification it records always describe the same moment.
|
|
316
|
+
await afterWalk?.(page, channels, classification);
|
|
317
|
+
return channels;
|
|
318
|
+
}
|
|
319
|
+
finally {
|
|
320
|
+
signal?.removeEventListener("abort", onAbort);
|
|
321
|
+
overlayController.abort();
|
|
322
|
+
deps.unregisterManagedPage(page);
|
|
323
|
+
try {
|
|
324
|
+
await page.close();
|
|
325
|
+
}
|
|
326
|
+
catch {
|
|
327
|
+
// Page may already be closed if the browser disconnected during discovery or the abort handler already closed it.
|
|
328
|
+
}
|
|
329
|
+
// Re-minimize the browser window. Opening the temporary discovery page may have restored the window on macOS. Skipped while login mode is active so a
|
|
330
|
+
// discovery finishing mid-login never minimizes the window under the user.
|
|
331
|
+
if (!isLoginModeActive()) {
|
|
332
|
+
await deps.minimizeBrowserWindow();
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Precaches a single service: clears the service's cache, then walks its guide through the shared guarded page session, logging the timing and recording the
|
|
338
|
+
* discovery outcome once the walk completes. The per-service primitive behind the precache cycle. Errors propagate to the caller, and no service filtering happens
|
|
339
|
+
* here - the cycle loop owns both the filter skip and the per-service error containment.
|
|
340
|
+
* @param provider - The provider to precache.
|
|
341
|
+
* @returns The discovered channels (possibly empty).
|
|
342
|
+
*/
|
|
343
|
+
export async function precacheService(provider, deps = defaultPrecachingDeps) {
|
|
344
|
+
const serviceElapsed = startTimer();
|
|
345
|
+
// Clear the service's cache before discovery to ensure a complete walk, even if a tune partially warmed the cache during the startup delay.
|
|
346
|
+
provider.strategy.clearCache?.();
|
|
347
|
+
return withProviderGuidePage(provider, {
|
|
348
|
+
afterWalk: async (page, channels, classification) => {
|
|
349
|
+
LOG.info("Precached %s: %d channels (%ss).", provider.label, channels.length, (serviceElapsed() / 1000).toFixed(1).replace(/\.0$/, ""));
|
|
350
|
+
// Record the outcome while the page is still open - an empty result classifies the page it walked, unless the session already classified it and declined to
|
|
351
|
+
// reload, in which case that verdict travels here rather than being re-derived from a page the reload would have changed.
|
|
352
|
+
await recordDiscoveryOutcome(provider, channels, page, deps, classification);
|
|
353
|
+
}
|
|
354
|
+
}, deps);
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Revalidates a domain's authentication after login mode ends: when the domain is currently marked needs-sign-in, re-runs channel discovery for every provider
|
|
358
|
+
* whose guide lives on that domain so fresh success evidence can clear the flag through the discovery-outcome policy. Wired to the login-end observer by app.ts.
|
|
359
|
+
*
|
|
360
|
+
* Never rejects - the observer wiring voids the returned promise, so every failure is logged and absorbed here. Revalidation deliberately ignores both the
|
|
361
|
+
* precacheServices and enabledServices filters: this is clearing-evidence collection for a domain the user just signed in to, not precaching, and the cycle's
|
|
362
|
+
* filter skip is unchanged. While it runs it holds the same single-flight guard the precache cycle holds, so a cycle scheduled mid-revalidation (a browser crash
|
|
363
|
+
* relaunch) defers instead of overlapping.
|
|
364
|
+
* @param url - The login session's URL; its extracted domain selects the providers to revalidate.
|
|
365
|
+
* @returns A promise that resolves when revalidation completes or is skipped. It never rejects.
|
|
366
|
+
*/
|
|
367
|
+
export async function revalidateDomainAuth(url, deps = defaultPrecachingDeps) {
|
|
368
|
+
try {
|
|
369
|
+
const domain = extractDomain(url);
|
|
370
|
+
// Only a standing needs-sign-in entry warrants an automatic discovery - a verified or unknown domain has nothing to clear.
|
|
371
|
+
if (getDomainAuthState(domain)?.status !== "needsLogin") {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
// The profile-test wizard and sequential multi-provider sign-in flows re-enter login mode immediately after ending it; the final Done fires this observer
|
|
375
|
+
// again with login mode inactive, so deferring here loses nothing.
|
|
376
|
+
if (isLoginModeActive()) {
|
|
377
|
+
LOG.debug("precache", "Skipping post-login revalidation for %s: login mode is active again.", domain);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if (deps.isGracefulShutdown()) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
// Defer to an in-flight precache cycle rather than overlapping it. Honest limitation: if the flagged provider is not in that cycle's configured service set,
|
|
384
|
+
// the flag persists until the next successful discovery or tune for the domain - an in-flight cycle cannot be retargeted.
|
|
385
|
+
if (precacheInProgress) {
|
|
386
|
+
LOG.info("Deferring the post-login revalidation for %s to the precache cycle already in progress.", domain);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
// Match every provider whose guide lives on the domain the user just signed in to. The observer's URL may be a DOMAIN_CONFIG loginUrl override rather than a
|
|
390
|
+
// channel URL, but both extract to the same registrable domain the guide URLs use.
|
|
391
|
+
const providers = deps.getProvidersForDomain(domain);
|
|
392
|
+
if (providers.length === 0) {
|
|
393
|
+
LOG.debug("precache", "No provider guide matches %s; skipping post-login revalidation.", domain);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
// Acquire the single-flight guard for the duration of the revalidation, exactly as the cycle does.
|
|
397
|
+
precacheInProgress = true;
|
|
398
|
+
try {
|
|
399
|
+
LOG.info("Re-running channel discovery for %s to verify authentication after sign-in.", domain);
|
|
400
|
+
for (const provider of providers) {
|
|
401
|
+
try {
|
|
402
|
+
// eslint-disable-next-line no-await-in-loop
|
|
403
|
+
await precacheService(provider, deps);
|
|
404
|
+
}
|
|
405
|
+
catch (error) {
|
|
406
|
+
// A provider still behind its wall commonly times out the guide navigation. Contain the failure per provider and keep going - the flag simply stays set.
|
|
407
|
+
LOG.warn("Post-login revalidation failed for %s: %s.", provider.label, formatError(error));
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
finally {
|
|
412
|
+
releasePrecacheGuard(deps);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
catch (error) {
|
|
416
|
+
LOG.warn("Post-login revalidation for %s failed: %s.", url, formatError(error));
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Schedules the one deferred re-attempt for the services a cycle finished with nothing to show for. Does nothing when every service came back with a lineup, and
|
|
421
|
+
* nothing during a shutdown, where scheduling work against the browser is precisely what teardown is closing down.
|
|
422
|
+
* @param slugs - The services that walked and found no channels.
|
|
423
|
+
* @param deps - The injected dependencies, handed to the pass this schedules.
|
|
424
|
+
*/
|
|
425
|
+
function armDeferredRetry(slugs, deps) {
|
|
426
|
+
if ((slugs.length === 0) || deps.isGracefulShutdown()) {
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
LOG.debug("precache", "Scheduling one deferred discovery re-attempt for %d service%s in %d minutes.", slugs.length, (slugs.length === 1) ? "" : "s", PRECACHE_RETRY_DELAY / 60000);
|
|
430
|
+
deferredRetry = { slugs, timer: setTimeout(() => void runDeferredRetry(deps), PRECACHE_RETRY_DELAY) };
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Runs the single deferred re-attempt for the services a cycle left empty. Never rejects - it is driven by a timer with nobody to hand a rejection to, so every
|
|
434
|
+
* per-service failure is contained the same way the cycle contains its own.
|
|
435
|
+
*
|
|
436
|
+
* A service whose lineup arrived in the interval - from a later full cycle, or from an on-demand discovery a user triggered - is skipped rather than re-walked,
|
|
437
|
+
* because the walk it would run is the expensive part and the answer is already in hand.
|
|
438
|
+
* @param deps - The injected browser and provider-registry dependencies.
|
|
439
|
+
* @returns A promise that resolves once the pass completes or is skipped.
|
|
440
|
+
*/
|
|
441
|
+
async function runDeferredRetry(deps) {
|
|
442
|
+
const pending = deferredRetry;
|
|
443
|
+
// Drop the state before acting on it. This is the only pass there will be, so a handle left standing would tell a later cancellation that something is still
|
|
444
|
+
// scheduled when nothing is.
|
|
445
|
+
deferredRetry = null;
|
|
446
|
+
if (!pending || deps.isGracefulShutdown()) {
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
/* A cycle or a post-login revalidation holding the guard is already walking guides, quite possibly these same ones. This pass exists to try again on a settled
|
|
450
|
+
* system, not to contend with a run in flight - and the run in flight is the better attempt of the two, so this one is dropped wholesale rather than rescheduled.
|
|
451
|
+
*/
|
|
452
|
+
if (precacheInProgress) {
|
|
453
|
+
LOG.debug("precache", "Skipping the deferred discovery re-attempt: a precache cycle is already in progress.");
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
precacheInProgress = true;
|
|
457
|
+
let attempted = 0;
|
|
458
|
+
let succeeded = 0;
|
|
459
|
+
try {
|
|
460
|
+
for (const slug of pending.slugs) {
|
|
461
|
+
// Re-checked every iteration, exactly as the cycle's own loop does: a shutdown that begins mid-pass must stop opening discovery pages, or the next
|
|
462
|
+
// getCurrentBrowser relaunches the Chrome that teardown just closed.
|
|
463
|
+
if (deps.isGracefulShutdown()) {
|
|
464
|
+
break;
|
|
465
|
+
}
|
|
466
|
+
const provider = deps.getProviderBySlug(slug);
|
|
467
|
+
if (!provider) {
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
if (provider.getCachedChannels()) {
|
|
471
|
+
LOG.debug("precache", "Skipping the deferred re-attempt for %s: its lineup was discovered in the meantime.", provider.label);
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
attempted++;
|
|
475
|
+
try {
|
|
476
|
+
// eslint-disable-next-line no-await-in-loop
|
|
477
|
+
const channels = await precacheService(provider, deps);
|
|
478
|
+
if (channels.length > 0) {
|
|
479
|
+
succeeded++;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
catch (error) {
|
|
483
|
+
LOG.warn("The deferred channel discovery re-attempt failed for %s: %s.", provider.label, formatError(error));
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
if (attempted > 0) {
|
|
487
|
+
LOG.info("Deferred channel discovery re-attempt complete: %d of %d service%s now have a lineup.", succeeded, attempted, (attempted === 1) ? "" : "s");
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
finally {
|
|
491
|
+
releasePrecacheGuard(deps);
|
|
492
|
+
}
|
|
38
493
|
}
|
|
39
494
|
/**
|
|
40
495
|
* 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.
|
|
496
|
+
* Services not in the active service filter are silently skipped when the filter is non-empty. Services that walk and find nothing are handed to a single deferred
|
|
497
|
+
* re-attempt, minutes later, once whatever startup contention may have starved them has passed.
|
|
42
498
|
*/
|
|
43
|
-
async function runPrecacheCycle() {
|
|
499
|
+
async function runPrecacheCycle(deps) {
|
|
500
|
+
/* Bail if a graceful shutdown began while this cycle was queued. Discovery opens browser pages via getCurrentBrowser(), which would relaunch Chrome after shutdown
|
|
501
|
+
* closed it; this guard makes the cycle a no-op during teardown regardless of how the timer-cancellation race resolves. Reset the in-progress flag since the
|
|
502
|
+
* early return skips the finally block below - directly rather than through the hand-off, because honoring a full-cycle request is the one thing a teardown path
|
|
503
|
+
* must not do.
|
|
504
|
+
*/
|
|
505
|
+
if (deps.isGracefulShutdown()) {
|
|
506
|
+
precacheInProgress = false;
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
44
509
|
const slugs = CONFIG.channels.precacheServices;
|
|
45
510
|
const enabledFilter = CONFIG.channels.enabledServices;
|
|
46
511
|
const hasFilter = enabledFilter.length > 0;
|
|
47
512
|
const cycleElapsed = startTimer();
|
|
513
|
+
// The services that walked but found nothing, named rather than counted: the deferred re-attempt below needs to know which ones to come back to.
|
|
514
|
+
const emptySlugs = [];
|
|
48
515
|
let skipped = 0;
|
|
49
516
|
let succeeded = 0;
|
|
50
517
|
LOG.info("Starting channel lineup precaching for %d service%s.", slugs.length, (slugs.length === 1) ? "" : "s");
|
|
51
518
|
try {
|
|
52
519
|
// Services are precached sequentially - each opens a browser page and navigates to a heavy SPA, so concurrent execution would stress system resources.
|
|
53
520
|
for (const slug of slugs) {
|
|
54
|
-
|
|
521
|
+
// Stop opening new discovery pages once a graceful shutdown begins mid-cycle. The entry guard above only covers a cycle that has not started; without this,
|
|
522
|
+
// a cycle already in its loop when shutdown closes the browser would call getCurrentBrowser() below and relaunch Chrome. Break rather than continue so no
|
|
523
|
+
// further service is processed; the in-flight service (if any) finishes and closes its own page via its finally.
|
|
524
|
+
if (deps.isGracefulShutdown()) {
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
const provider = deps.getProviderBySlug(slug);
|
|
55
528
|
if (!provider) {
|
|
56
529
|
continue;
|
|
57
530
|
}
|
|
@@ -61,56 +534,15 @@ async function runPrecacheCycle() {
|
|
|
61
534
|
skipped++;
|
|
62
535
|
continue;
|
|
63
536
|
}
|
|
64
|
-
const serviceElapsed = startTimer();
|
|
65
537
|
try {
|
|
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
|
-
provider.strategy.clearCache?.();
|
|
68
|
-
// eslint-disable-next-line no-await-in-loop
|
|
69
|
-
const browser = await getCurrentBrowser();
|
|
70
|
-
// eslint-disable-next-line no-await-in-loop
|
|
71
|
-
const page = await browser.newPage();
|
|
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
|
-
// 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
|
-
// audio. evaluateOnNewDocument runs before site JavaScript, so nothing slips through.
|
|
75
538
|
// eslint-disable-next-line no-await-in-loop
|
|
76
|
-
await
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
HTMLMediaElement.prototype.play = async function () {
|
|
80
|
-
this.muted = true;
|
|
81
|
-
return originalPlay.call(this);
|
|
82
|
-
};
|
|
83
|
-
});
|
|
84
|
-
registerManagedPage(page);
|
|
85
|
-
try {
|
|
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
|
-
if (!provider.handlesOwnNavigation) {
|
|
88
|
-
// eslint-disable-next-line no-await-in-loop
|
|
89
|
-
await page.goto(provider.guideUrl, { timeout: CONFIG.streaming.navigationTimeout, waitUntil: "networkidle2" });
|
|
90
|
-
}
|
|
91
|
-
// eslint-disable-next-line no-await-in-loop
|
|
92
|
-
const channels = await provider.discoverChannels(page);
|
|
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
|
-
// even without authentication, so a non-empty result alone does not prove paid access.
|
|
97
|
-
if ((channels.length > 0) && (!provider.validatePrecache || provider.validatePrecache(channels))) {
|
|
98
|
-
markDomainAuth(extractDomain(provider.guideUrl));
|
|
99
|
-
}
|
|
539
|
+
const channels = await precacheService(provider, deps);
|
|
540
|
+
// An empty walk cached nothing, so it is counted honestly as empty rather than folded into the success count.
|
|
541
|
+
if (channels.length > 0) {
|
|
100
542
|
succeeded++;
|
|
101
543
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
try {
|
|
105
|
-
// eslint-disable-next-line no-await-in-loop
|
|
106
|
-
await page.close();
|
|
107
|
-
}
|
|
108
|
-
catch {
|
|
109
|
-
// Page may already be closed if the browser disconnected during discovery.
|
|
110
|
-
}
|
|
111
|
-
// Re-minimize the browser window. Opening the temporary discovery page may have restored the window on macOS.
|
|
112
|
-
// eslint-disable-next-line no-await-in-loop
|
|
113
|
-
await minimizeBrowserWindow();
|
|
544
|
+
else {
|
|
545
|
+
emptySlugs.push(slug);
|
|
114
546
|
}
|
|
115
547
|
}
|
|
116
548
|
catch (error) {
|
|
@@ -118,11 +550,13 @@ async function runPrecacheCycle() {
|
|
|
118
550
|
}
|
|
119
551
|
}
|
|
120
552
|
const elapsed = (cycleElapsed() / 1000).toFixed(1).replace(/\.0$/, "");
|
|
121
|
-
const
|
|
122
|
-
|
|
553
|
+
const emptySuffix = (emptySlugs.length > 0) ? ", " + String(emptySlugs.length) + " returned no channels" : "";
|
|
554
|
+
const skippedSuffix = (skipped > 0) ? ", " + String(skipped) + " skipped (filtered)" : "";
|
|
555
|
+
LOG.info("Channel lineup precaching complete: %d service%s cached%s%s in %ss.", succeeded, (succeeded === 1) ? "" : "s", emptySuffix, skippedSuffix, elapsed);
|
|
556
|
+
armDeferredRetry(emptySlugs, deps);
|
|
123
557
|
}
|
|
124
558
|
finally {
|
|
125
|
-
|
|
559
|
+
releasePrecacheGuard(deps);
|
|
126
560
|
}
|
|
127
561
|
}
|
|
128
562
|
//# sourceMappingURL=precaching.js.map
|