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,16 +1,22 @@
|
|
|
1
1
|
import { LOG, delay, evaluateWithAbort, formatError } from "../../utils/index.js";
|
|
2
|
-
import { logAvailableChannels, normalizeChannelName, scrollAndClick } from "./shared.js";
|
|
2
|
+
import { installOrReplaceOnNewDocument, logAvailableChannels, normalizeChannelName, scrollAndClick } from "./shared.js";
|
|
3
3
|
import { CONFIG } from "../../config/index.js";
|
|
4
|
+
import { createProviderChannelCache } from "./cache.js";
|
|
5
|
+
import { searchVirtualizedGrid } from "./gridSearch.js";
|
|
4
6
|
// Unified channel cache for Hulu. Maps normalized channel names to their combined entry. Aliases (e.g., "abc" -> same entry as "wls") share object references for
|
|
5
|
-
// automatic propagation of fresh programs and EAB data. Cleared on browser disconnect via clearHuluCache().
|
|
6
|
-
|
|
7
|
+
// automatic propagation of fresh programs and EAB data. Cleared on browser disconnect via clearHuluCache(). An affiliate projects into discovery output with its
|
|
8
|
+
// network name as the channelSelector; every other channel projects with its display name.
|
|
9
|
+
const huluCache = createProviderChannelCache((entry) => {
|
|
10
|
+
const result = { channelSelector: entry.affiliate ?? entry.displayName, name: entry.displayName };
|
|
11
|
+
if (entry.affiliate) {
|
|
12
|
+
result.affiliate = entry.affiliate;
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
15
|
+
});
|
|
7
16
|
// Transient staging map for listing API response data. Maps channel UUIDs to their program schedules. Populated from listing API responses (which arrive before
|
|
8
17
|
// details responses due to the interceptor's listingCapturedPromise hold). Read by populateHuluChannelCache when the details response arrives, joining UUID-keyed
|
|
9
18
|
// programs with name-keyed entries. Also used to propagate fresh programs to existing cache entries when a new listing response arrives.
|
|
10
19
|
const huluListingStaging = new Map();
|
|
11
|
-
// Tracks whether a full discovery walk (with affiliate position inference) has completed. When true, buildHuluDiscoveredChannels can return comprehensive results
|
|
12
|
-
// including proper affiliate labeling. When false, getCachedChannels returns null to force a fresh discovery walk.
|
|
13
|
-
let huluFullyDiscovered = false;
|
|
14
20
|
// Tracks pages with details API response listeners to avoid duplicate registration. Mirrors the pagesWithListeners pattern in sling.ts.
|
|
15
21
|
const huluPagesWithListeners = new WeakSet();
|
|
16
22
|
// The Hulu live page URL. The evaluateOnNewDocument fetch interceptor swaps channel_id and content_eab_id in the playlist API request, making the app play the
|
|
@@ -18,12 +24,11 @@ const huluPagesWithListeners = new WeakSet();
|
|
|
18
24
|
// grid's Channels tab click triggers full API expansion, and the interceptor captures UUID+EAB from those expanded responses.
|
|
19
25
|
const HULU_LIVE_URL = "https://www.hulu.com/live";
|
|
20
26
|
/**
|
|
21
|
-
* Clears all Hulu caches: the unified channel cache
|
|
22
|
-
* browser restarts, since cached state may be stale in a new browser session.
|
|
27
|
+
* Clears all Hulu caches: the unified channel cache along with its completeness mark, and the listing staging map. Called by clearChannelSelectionCaches() in the
|
|
28
|
+
* coordinator when the browser restarts, since cached state may be stale in a new browser session.
|
|
23
29
|
*/
|
|
24
30
|
function clearHuluCache() {
|
|
25
|
-
|
|
26
|
-
huluFullyDiscovered = false;
|
|
31
|
+
huluCache.clear();
|
|
27
32
|
huluListingStaging.clear();
|
|
28
33
|
}
|
|
29
34
|
/**
|
|
@@ -34,13 +39,13 @@ function clearHuluCache() {
|
|
|
34
39
|
* @returns The actual cache key, or null if no match found.
|
|
35
40
|
*/
|
|
36
41
|
function resolveHuluCacheKey(normalizedName) {
|
|
37
|
-
if (
|
|
42
|
+
if (huluCache.map.has(normalizedName)) {
|
|
38
43
|
return normalizedName;
|
|
39
44
|
}
|
|
40
45
|
// Fuzzy fallback: strip all non-alphanumeric characters and compare. Handles formatting differences in spaces, hyphens, ampersands, and other punctuation
|
|
41
46
|
// between channelSelector definitions and Hulu's Details API channel_info.name values.
|
|
42
47
|
const stripped = normalizedName.replace(/[^a-z0-9]/g, "");
|
|
43
|
-
for (const key of
|
|
48
|
+
for (const key of huluCache.map.keys()) {
|
|
44
49
|
if (key.replace(/[^a-z0-9]/g, "") === stripped) {
|
|
45
50
|
return key;
|
|
46
51
|
}
|
|
@@ -49,13 +54,13 @@ function resolveHuluCacheKey(normalizedName) {
|
|
|
49
54
|
}
|
|
50
55
|
/**
|
|
51
56
|
* Looks up a channel in the unified cache by normalized name, with fuzzy fallback via resolveHuluCacheKey. Only used for lookups keyed by channelSelector -
|
|
52
|
-
* internal cache operations that use API-derived names should use
|
|
57
|
+
* internal cache operations that use API-derived names should use huluCache.map.get() directly.
|
|
53
58
|
* @param normalizedName - The normalized (lowercased, whitespace-collapsed) channel name to look up.
|
|
54
59
|
* @returns The matching cache entry, or null if no match found.
|
|
55
60
|
*/
|
|
56
61
|
function findHuluChannelEntry(normalizedName) {
|
|
57
62
|
const key = resolveHuluCacheKey(normalizedName);
|
|
58
|
-
return key ?
|
|
63
|
+
return key ? huluCache.map.get(key) ?? null : null;
|
|
59
64
|
}
|
|
60
65
|
/**
|
|
61
66
|
* Populates the unified channel cache from a details API response. For each item with channel_info, creates or updates the entry keyed by normalized name,
|
|
@@ -69,7 +74,7 @@ function populateHuluChannelCache(items) {
|
|
|
69
74
|
const info = item.channel_info;
|
|
70
75
|
if (info?.name && info.id) {
|
|
71
76
|
const normalized = normalizeChannelName(info.name);
|
|
72
|
-
const existing =
|
|
77
|
+
const existing = huluCache.map.get(normalized);
|
|
73
78
|
const programs = huluListingStaging.get(info.id) ?? existing?.programs;
|
|
74
79
|
if (existing) {
|
|
75
80
|
// Update in-place to preserve alias references and supplementary fields (rowNumber, affiliate).
|
|
@@ -80,12 +85,12 @@ function populateHuluChannelCache(items) {
|
|
|
80
85
|
}
|
|
81
86
|
}
|
|
82
87
|
else {
|
|
83
|
-
|
|
88
|
+
huluCache.map.set(normalized, { displayName: info.name, programs, uuid: info.id });
|
|
84
89
|
}
|
|
85
90
|
channelsSeen.add(info.name);
|
|
86
91
|
}
|
|
87
92
|
}
|
|
88
|
-
LOG.debug("tuning:hulu", "Details API: %s items, %s unique channels. Channel cache size: %s.", items.length, channelsSeen.size,
|
|
93
|
+
LOG.debug("tuning:hulu", "Details API: %s items, %s unique channels. Channel cache size: %s.", items.length, channelsSeen.size, huluCache.map.size);
|
|
89
94
|
}
|
|
90
95
|
/**
|
|
91
96
|
* Finds the currently-airing EAB from a program schedule array. Searches the programs for one whose airing window brackets the current time. Returns null if the
|
|
@@ -102,28 +107,6 @@ function findCurrentEabFromPrograms(programs) {
|
|
|
102
107
|
}
|
|
103
108
|
return null;
|
|
104
109
|
}
|
|
105
|
-
/**
|
|
106
|
-
* Derives a DiscoveredChannel array from the unified channel cache, deduplicating alias entries via Set reference equality. Affiliates produce entries with the
|
|
107
|
-
* network name as channelSelector; non-affiliates use their display name. Used by getCachedChannels and discoverHuluChannels when returning from warm cache.
|
|
108
|
-
* @returns Sorted array of discovered channels.
|
|
109
|
-
*/
|
|
110
|
-
function buildHuluDiscoveredChannels() {
|
|
111
|
-
const channels = [];
|
|
112
|
-
const seen = new Set();
|
|
113
|
-
for (const entry of huluChannelCache.values()) {
|
|
114
|
-
if (seen.has(entry)) {
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
seen.add(entry);
|
|
118
|
-
const result = { channelSelector: entry.affiliate ?? entry.displayName, name: entry.displayName };
|
|
119
|
-
if (entry.affiliate) {
|
|
120
|
-
result.affiliate = entry.affiliate;
|
|
121
|
-
}
|
|
122
|
-
channels.push(result);
|
|
123
|
-
}
|
|
124
|
-
channels.sort((a, b) => a.name.localeCompare(b.name));
|
|
125
|
-
return channels;
|
|
126
|
-
}
|
|
127
110
|
/**
|
|
128
111
|
* Reads all rendered channel containers from the guide grid, extracting their names from data-testid attributes and row numbers from sr-only text. Populates the
|
|
129
112
|
* unified channel cache with rowNumber and displayName as a side effect.
|
|
@@ -175,12 +158,12 @@ async function readRenderedChannels(page) {
|
|
|
175
158
|
rendered.push({ displayName: ch.displayName, domIndex, name: ch.name, rowNumber: ch.rowNumber });
|
|
176
159
|
// Cache the row number and display name for future direct-scroll lookups.
|
|
177
160
|
if (ch.rowNumber >= 0) {
|
|
178
|
-
const existing =
|
|
161
|
+
const existing = huluCache.map.get(ch.name);
|
|
179
162
|
if (existing) {
|
|
180
163
|
existing.rowNumber = ch.rowNumber;
|
|
181
164
|
}
|
|
182
165
|
else {
|
|
183
|
-
|
|
166
|
+
huluCache.map.set(ch.name, { displayName: ch.displayName, rowNumber: ch.rowNumber });
|
|
184
167
|
}
|
|
185
168
|
}
|
|
186
169
|
}
|
|
@@ -395,7 +378,7 @@ async function clickOnNowCellAndPlay(page, clickTarget, playSelector, channelNam
|
|
|
395
378
|
// Maximum number of on-now cell click attempts. The first click may not register if React hasn't finished hydrating the guide's event handlers.
|
|
396
379
|
const MAX_CLICK_ATTEMPTS = 3;
|
|
397
380
|
// Shorter timeout for the play button on non-final attempts. When a click registers, the play button appears in under 10ms - this timeout only determines how
|
|
398
|
-
// quickly we detect a missed click and retry. Keeping it low
|
|
381
|
+
// quickly we detect a missed click and retry. Keeping it low lets a failed attempt fall through to the next one promptly rather than stalling on a dead wait.
|
|
399
382
|
const RETRY_PLAY_TIMEOUT = 1000;
|
|
400
383
|
// Delay between click retries. Gives React additional time to finish hydrating event handlers.
|
|
401
384
|
const CLICK_RETRY_DELAY = 1500;
|
|
@@ -498,6 +481,9 @@ async function tryFastPathTune(page, entry, channelName) {
|
|
|
498
481
|
const currentEab = findCurrentEabFromPrograms(entry.programs);
|
|
499
482
|
if (currentEab) {
|
|
500
483
|
const injected = await page.evaluate((u, e) => {
|
|
484
|
+
// The __prismcast* bridge functions are installed on window by the evaluateOnNewDocument fetch interceptor earlier in this module. The double cast reads
|
|
485
|
+
// them off window without committing to a global declaration, and the typeof === "function" guard below tolerates their absence when the interceptor was
|
|
486
|
+
// not installed (for example on a profile that does not use fetch interception). The same rationale applies to the other reads of these bridge functions.
|
|
501
487
|
const resolver = window["__prismcastResolveDirectTune"];
|
|
502
488
|
if (typeof resolver === "function") {
|
|
503
489
|
return resolver(u, e);
|
|
@@ -561,8 +547,8 @@ async function releaseHeldPlaylist(page) {
|
|
|
561
547
|
* path (discoverHuluChannels) call this immediately after navigation so they share one source of truth for the profile-selector workaround.
|
|
562
548
|
*
|
|
563
549
|
* Selection policy: always the first profile. We do not expose a configurable name because the typical PrismCast deployment has a single primary profile, and an
|
|
564
|
-
* operator who wants a different one can reorder profiles in Hulu's account settings. The selected profile name is
|
|
565
|
-
*
|
|
550
|
+
* operator who wants a different one can reorder profiles in Hulu's account settings. The selected profile name is logged at INFO so the operator always
|
|
551
|
+
* knows which one was picked.
|
|
566
552
|
*
|
|
567
553
|
* The probe is cheap when no modal is present (one DOM evaluate) and best-effort when it is: any failure between detection and post-click navigation is logged
|
|
568
554
|
* and returns silently. The downstream #CHANNELS-wait timeout becomes the failure signal of last resort, so the existing error path stays intact.
|
|
@@ -617,10 +603,12 @@ async function handleProfileSelectorIfPresent(page) {
|
|
|
617
603
|
* virtualized list - only ~13 of ~124 rows exist in the DOM at any time, positioned absolutely within a tall spacer div. The virtualizer renders rows based on
|
|
618
604
|
* the page scroll position (`document.documentElement.scrollTop`), so we scroll to bring the target channel into the DOM, then interact with it directly.
|
|
619
605
|
*
|
|
620
|
-
*
|
|
606
|
+
* Four mechanisms handle different channel types:
|
|
621
607
|
* 1. Binary search with passive row number caching - primary mechanism for most channels (~800ms first time, ~200ms on cache hit)
|
|
622
608
|
* 2. Position-based inference - handles local affiliates when searching by network name (e.g., "ABC" finds the local call sign at the right sort position)
|
|
623
609
|
* 3. Linear scan fallback - safety net for raw call sign searches or any channel the binary search cannot find (~2.4 seconds)
|
|
610
|
+
* 4. Fast-path direct tune (tryFastPathTune) - after the channel is located, attempts a direct tune via unified cache injection or interceptor
|
|
611
|
+
* self-resolution; a successful outcome returns success without reaching the on-now-cell click or play-button steps
|
|
624
612
|
*
|
|
625
613
|
* The selection process:
|
|
626
614
|
* 1. Clear any "Who's Watching?" profile-selector modal that intercepts before guide interaction (see handleProfileSelectorIfPresent)
|
|
@@ -629,8 +617,9 @@ async function handleProfileSelectorIfPresent(page) {
|
|
|
629
617
|
* 4. Check the unified cache for a row number direct-scroll shortcut
|
|
630
618
|
* 5. Binary search: scroll to the midpoint row, read rendered channels (caching row numbers), check for exact match or infer local affiliate
|
|
631
619
|
* 6. If binary search fails, linear scan from top to bottom as a universal fallback
|
|
632
|
-
* 7.
|
|
633
|
-
* 8.
|
|
620
|
+
* 7. Attempt a fast-path direct tune via tryFastPathTune; on success, return immediately, skipping steps 8 and 9
|
|
621
|
+
* 8. Click the on-now program cell (`.LiveGuideProgram--first`) in the target channel's row to open the playback overlay
|
|
622
|
+
* 9. If playSelector is provided, wait for and click the play button to start live playback
|
|
634
623
|
* @param page - The Puppeteer page object.
|
|
635
624
|
* @param profile - The resolved site profile with a non-null channelSelector (channel name) and channelSelection config.
|
|
636
625
|
* @returns Result object with success status and optional failure reason.
|
|
@@ -684,13 +673,13 @@ async function guideGridStrategy(page, profile) {
|
|
|
684
673
|
}
|
|
685
674
|
}
|
|
686
675
|
if (!rowsVisible) {
|
|
687
|
-
return { reason: "Channel grid rows did not render.", success: false };
|
|
676
|
+
return { guideUnavailable: true, reason: "Channel grid rows did not render.", success: false };
|
|
688
677
|
}
|
|
689
678
|
// Normalize the channel name to lowercase for case-insensitive matching against data-testid suffixes.
|
|
690
679
|
const normalizedName = normalizeChannelName(channelName);
|
|
691
680
|
const gridMeta = await readGridMeta(page);
|
|
692
681
|
if (!gridMeta) {
|
|
693
|
-
return { reason: "Could not locate channel grid spacer element.", success: false };
|
|
682
|
+
return { guideUnavailable: true, reason: "Could not locate channel grid spacer element.", success: false };
|
|
694
683
|
}
|
|
695
684
|
const { gridDocTop, rowHeight, totalRows } = gridMeta;
|
|
696
685
|
// The name of the channel to click. This starts as the normalized target name but may be replaced by a local affiliate call sign via position inference.
|
|
@@ -704,84 +693,71 @@ async function guideGridStrategy(page, profile) {
|
|
|
704
693
|
// Read rendered channels to update the cache and confirm the channel is present.
|
|
705
694
|
const rendered = await readRenderedChannels(page);
|
|
706
695
|
if (rendered) {
|
|
707
|
-
|
|
696
|
+
/* The entry this shortcut trusted may be filed under a key that is not the name its row renders under - an affiliate's network-name alias, or a key the
|
|
697
|
+
* fuzzy lookup resolved through - so entry identity is what recognizes the row the scroll landed on. The exact-name pass runs first and the identity pass
|
|
698
|
+
* only picks up what it leaves, so a row rendering under the requested name always wins over one that merely shares its entry.
|
|
699
|
+
*/
|
|
700
|
+
const match = rendered.find((ch) => ch.name === normalizedName) ?? rendered.find((ch) => huluCache.map.get(ch.name) === cachedEntry);
|
|
708
701
|
if (match) {
|
|
709
|
-
return await clickOnNowCellAndPlay(page,
|
|
702
|
+
return await clickOnNowCellAndPlay(page, match.name, playSelector, channelName);
|
|
710
703
|
}
|
|
711
704
|
}
|
|
712
705
|
// Cache hit but channel not found at expected position. The guide may have changed. Clear the row number and fall through to binary search.
|
|
713
706
|
LOG.debug("tuning:hulu", "Guide cache miss for %s. Falling back to binary search.", channelName);
|
|
714
707
|
cachedEntry.rowNumber = undefined;
|
|
715
708
|
}
|
|
716
|
-
//
|
|
717
|
-
//
|
|
718
|
-
// channel names to narrow the range. The search converges in ~3-4 iterations because the 13-row render window covers a large fraction of the remaining range.
|
|
719
|
-
let low = 0;
|
|
720
|
-
let high = totalRows - 1;
|
|
709
|
+
// The ~13-row render window covers a large fraction of whatever range is left after each step, so the search converges in ~3-4 iterations; ten is a bound on
|
|
710
|
+
// pathological virtualizer behavior rather than the normal exit.
|
|
721
711
|
const maxIterations = 10;
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
}
|
|
736
|
-
// Check for an exact match first.
|
|
737
|
-
const exactMatch = rendered.find((ch) => ch.name === normalizedName);
|
|
738
|
-
if (exactMatch) {
|
|
739
|
-
found = true;
|
|
740
|
-
break;
|
|
741
|
-
}
|
|
742
|
-
// Determine binary search direction by comparing the target against the first and last rendered non-call-sign channel names. Call sign channels (W*/K*
|
|
743
|
-
// local affiliates) are excluded from direction comparison because they sort by hidden network name, not by their displayed call sign - using them for
|
|
744
|
-
// localeCompare would send the search the wrong way.
|
|
745
|
-
const nonCallSigns = rendered.filter((ch) => !CALL_SIGN_PATTERN.test(ch.name));
|
|
746
|
-
if (nonCallSigns.length === 0) {
|
|
747
|
-
// All rendered channels are call signs. Cannot determine direction. Move down and hope for better data.
|
|
748
|
-
low = mid + 1;
|
|
749
|
-
continue;
|
|
750
|
-
}
|
|
751
|
-
const first = nonCallSigns[0]?.name ?? "";
|
|
752
|
-
const last = nonCallSigns.at(-1)?.name ?? "";
|
|
753
|
-
if (normalizedName.localeCompare(first) < 0) {
|
|
754
|
-
// Target sorts before the first visible non-call-sign channel. Scroll up (toward lower row indices).
|
|
755
|
-
high = mid - 1;
|
|
756
|
-
continue;
|
|
757
|
-
}
|
|
758
|
-
if (normalizedName.localeCompare(last) > 0) {
|
|
759
|
-
// Target sorts after the last visible non-call-sign channel. Scroll down (toward higher row indices).
|
|
760
|
-
low = mid + 1;
|
|
761
|
-
continue;
|
|
762
|
-
}
|
|
763
|
-
// The target is alphabetically between the first and last rendered channels but was not found by exact data-testid match. This is the "missing" case - the
|
|
764
|
-
// channel may be a local affiliate whose call sign doesn't match the network name we're searching for. Try position-based inference.
|
|
765
|
-
const inferred = inferLocalAffiliate(rendered, normalizedName);
|
|
766
|
-
if (inferred) {
|
|
712
|
+
const outcome = await searchVirtualizedGrid({
|
|
713
|
+
// Call sign channels (W*/K* local affiliates) are excluded from the direction comparison because they sort by hidden network name, not by their displayed
|
|
714
|
+
// call sign - using them for localeCompare would send the search the wrong way.
|
|
715
|
+
isDirectionAnchor: (ch) => !CALL_SIGN_PATTERN.test(ch.name),
|
|
716
|
+
maxIterations,
|
|
717
|
+
nameOf: (ch) => ch.name,
|
|
718
|
+
onInRangeMiss: async (rendered) => {
|
|
719
|
+
// The target is alphabetically between the first and last rendered channels but was not found by exact data-testid match. This is the "missing" case - the
|
|
720
|
+
// channel may be a local affiliate whose call sign doesn't match the network name we're searching for. Try position-based inference.
|
|
721
|
+
const inferred = inferLocalAffiliate(rendered, normalizedName);
|
|
722
|
+
if (!inferred) {
|
|
723
|
+
return null;
|
|
724
|
+
}
|
|
767
725
|
LOG.debug("tuning:hulu", "Inferred local affiliate %s for network name %s.", inferred, channelName);
|
|
768
|
-
clickTarget = inferred;
|
|
769
|
-
found = true;
|
|
770
726
|
// Cross-reference the unified cache so the network name resolves to a warm-cache direct tune on subsequent requests. The details API returns the local
|
|
771
727
|
// call sign as channel_info.name, so the cache is keyed by call sign. The user's channelSelector uses the network name (e.g., "ABC"). Without this
|
|
772
728
|
// cross-reference, local affiliates would always fall through to cold-cache guide grid tunes because the names never match.
|
|
773
|
-
const inferredEntry =
|
|
729
|
+
const inferredEntry = huluCache.map.get(inferred);
|
|
774
730
|
if (inferredEntry) {
|
|
775
|
-
|
|
731
|
+
huluCache.map.set(normalizedName, inferredEntry);
|
|
776
732
|
LOG.debug("tuning:hulu", "Cross-referenced cache: %s -> %s (from inferred affiliate %s).", channelName, inferredEntry.uuid ?? "no-uuid", inferred);
|
|
777
|
-
// eslint-disable-next-line no-await-in-loop
|
|
778
733
|
const fastPathSuccess = await tryFastPathTune(page, inferredEntry, channelName);
|
|
779
734
|
if (fastPathSuccess) {
|
|
780
|
-
return { success: true };
|
|
735
|
+
return { resolved: { success: true } };
|
|
781
736
|
}
|
|
782
737
|
}
|
|
783
|
-
|
|
784
|
-
|
|
738
|
+
return { found: inferred };
|
|
739
|
+
},
|
|
740
|
+
probe: async (rowIndex) => {
|
|
741
|
+
await scrollToGuideRow(page, gridDocTop, rowHeight, rowIndex);
|
|
742
|
+
// Read all rendered channels, populating the unified cache with row numbers as a side effect.
|
|
743
|
+
const rendered = await readRenderedChannels(page);
|
|
744
|
+
// The rows go back in DOM order untouched, because DOM order is this guide's visual order: Hulu files most local affiliates under their hidden network
|
|
745
|
+
// name, so sorting by the displayed name would scramble the very positions the affiliate inference reads.
|
|
746
|
+
return { found: rendered?.find((ch) => ch.name === normalizedName)?.name ?? null, rows: rendered };
|
|
747
|
+
},
|
|
748
|
+
targetName: normalizedName,
|
|
749
|
+
totalRows
|
|
750
|
+
});
|
|
751
|
+
// The inference hook tuned the channel itself through the fast path, which leaves the post-search fast-path retry, the playlist release, and the click path
|
|
752
|
+
// with nothing left to do.
|
|
753
|
+
if (outcome.kind === "resolved") {
|
|
754
|
+
return outcome.result;
|
|
755
|
+
}
|
|
756
|
+
let found = false;
|
|
757
|
+
if (outcome.kind === "found") {
|
|
758
|
+
// The name to click: the target's own on an exact match, the local affiliate's call sign where position inference identified one.
|
|
759
|
+
clickTarget = outcome.found;
|
|
760
|
+
found = true;
|
|
785
761
|
}
|
|
786
762
|
// If binary search did not find the channel (and position inference didn't identify a local affiliate), fall back to a linear scan through all channels. This
|
|
787
763
|
// handles edge cases like raw call sign searches where localeCompare gives the wrong direction, or channels like "Lakeshore PBS" that sort by hidden network
|
|
@@ -806,7 +782,7 @@ async function guideGridStrategy(page, profile) {
|
|
|
806
782
|
if (!found) {
|
|
807
783
|
// Log available channels from the unified cache to help users identify the correct channelSelector value. The cache accumulates all channel names
|
|
808
784
|
// encountered during binary search and linear scan, so it contains most or all channels even though the virtualized grid only renders ~13 at a time.
|
|
809
|
-
const availableChannels = Array.from(
|
|
785
|
+
const availableChannels = Array.from(huluCache.map.keys()).toSorted();
|
|
810
786
|
if (availableChannels.length > 0) {
|
|
811
787
|
logAvailableChannels({
|
|
812
788
|
availableChannels,
|
|
@@ -833,29 +809,6 @@ async function guideGridStrategy(page, profile) {
|
|
|
833
809
|
// Click the on-now program cell and wait for the play button, with click retries to handle React hydration timing.
|
|
834
810
|
return await clickOnNowCellAndPlay(page, clickTarget, playSelector, channelName);
|
|
835
811
|
}
|
|
836
|
-
/**
|
|
837
|
-
* Wraps guideGridStrategy with a single retry after dismissing any stale overlay that may be covering the guide grid. After a failed click attempt on the on-now
|
|
838
|
-
* cell, the playback overlay or entity modal can remain open, obscuring the guide and preventing subsequent channel selection attempts from locating guide rows.
|
|
839
|
-
* Pressing Escape closes most modal overlays in React-based SPAs.
|
|
840
|
-
* @param page - The Puppeteer page object.
|
|
841
|
-
* @param profile - The resolved site profile with a non-null channelSelector (channel name) and channelSelection config.
|
|
842
|
-
* @returns Result object with success status and optional failure reason.
|
|
843
|
-
*/
|
|
844
|
-
async function guideGridWithRetry(page, profile) {
|
|
845
|
-
let result = await guideGridStrategy(page, profile);
|
|
846
|
-
if (!result.success) {
|
|
847
|
-
LOG.warn("Guide grid channel selection failed: %s. Dismissing overlay and retrying.", result.reason ?? "Unknown reason");
|
|
848
|
-
try {
|
|
849
|
-
await page.keyboard.press("Escape");
|
|
850
|
-
await delay(500);
|
|
851
|
-
}
|
|
852
|
-
catch (error) {
|
|
853
|
-
LOG.debug("tuning:hulu", "Could not dismiss guide overlay: %s.", formatError(error));
|
|
854
|
-
}
|
|
855
|
-
result = await guideGridStrategy(page, profile);
|
|
856
|
-
}
|
|
857
|
-
return result;
|
|
858
|
-
}
|
|
859
812
|
/**
|
|
860
813
|
* Sets up server-side response interception on the page to capture channel data from Hulu's guide APIs. As the live page loads, Hulu fetches program details from
|
|
861
814
|
* guide.hulu.com/guide/details in batches and program schedules from guide.hulu.com/guide/listing. We intercept both responses to populate the unified channel
|
|
@@ -886,19 +839,19 @@ function setupDetailsResponseInterception(page) {
|
|
|
886
839
|
const channelUuid = payload.slice(eqIdx + 1);
|
|
887
840
|
// Find the existing entry by UUID to create an alias (shared object reference). If no entry exists yet, create a new one with programs from staging.
|
|
888
841
|
let existingEntry = null;
|
|
889
|
-
for (const e of
|
|
842
|
+
for (const e of huluCache.map.values()) {
|
|
890
843
|
if (e.uuid === channelUuid) {
|
|
891
844
|
existingEntry = e;
|
|
892
845
|
break;
|
|
893
846
|
}
|
|
894
847
|
}
|
|
895
848
|
if (existingEntry) {
|
|
896
|
-
|
|
849
|
+
huluCache.map.set(name, existingEntry);
|
|
897
850
|
}
|
|
898
851
|
else {
|
|
899
|
-
|
|
852
|
+
huluCache.map.set(name, { displayName: name, programs: huluListingStaging.get(channelUuid), uuid: channelUuid });
|
|
900
853
|
}
|
|
901
|
-
LOG.debug("tuning:hulu", "Cached affiliate UUID from playlist: %s -> %s. Channel cache size: %s.", name, channelUuid,
|
|
854
|
+
LOG.debug("tuning:hulu", "Cached affiliate UUID from playlist: %s -> %s. Channel cache size: %s.", name, channelUuid, huluCache.map.size);
|
|
902
855
|
}
|
|
903
856
|
}
|
|
904
857
|
// Direct tune failure: the interceptor's hold period expired without resolving UUID+EAB for the target channel. The 503 response prevents Hulu from playing
|
|
@@ -941,7 +894,7 @@ function setupDetailsResponseInterception(page) {
|
|
|
941
894
|
}
|
|
942
895
|
// Propagate fresh programs to existing unified cache entries. Aliases share object references, so updating one entry automatically propagates to all
|
|
943
896
|
// aliases for the same channel.
|
|
944
|
-
for (const entry of
|
|
897
|
+
for (const entry of huluCache.map.values()) {
|
|
945
898
|
if (entry.uuid) {
|
|
946
899
|
const freshPrograms = huluListingStaging.get(entry.uuid);
|
|
947
900
|
if (freshPrograms) {
|
|
@@ -999,7 +952,7 @@ async function resolveHuluDirectUrl(channelSelector, page) {
|
|
|
999
952
|
const seenEntries = new Set();
|
|
1000
953
|
const allCachedUuids = [];
|
|
1001
954
|
const allCurrentEabs = [];
|
|
1002
|
-
for (const entry of
|
|
955
|
+
for (const entry of huluCache.map.values()) {
|
|
1003
956
|
if (!entry.uuid || seenEntries.has(entry)) {
|
|
1004
957
|
continue;
|
|
1005
958
|
}
|
|
@@ -1016,14 +969,21 @@ async function resolveHuluDirectUrl(channelSelector, page) {
|
|
|
1016
969
|
* immediately. On cold tunes, the guide grid's Channels tab click triggers full API expansion, and the interceptor holds the playlist request until both
|
|
1017
970
|
* UUID and EAB are captured from those responses. On all tunes, it captures listing API responses to build an in-page EAB map and expands subsequent
|
|
1018
971
|
* details API requests, populating the UUID cache to ~123 channels on a single page load. The script runs via evaluateOnNewDocument - it executes before
|
|
1019
|
-
* any page JavaScript, patching window.fetch so Hulu's module-scoped fetch reference captures the interceptor.
|
|
1020
|
-
*
|
|
972
|
+
* any page JavaScript, patching window.fetch so Hulu's module-scoped fetch reference captures the interceptor.
|
|
973
|
+
*
|
|
974
|
+
* The install goes through installOrReplaceOnNewDocument. Every tune resolves a direct URL first, so resolveHuluDirectUrl runs again on the same page when the
|
|
975
|
+
* monitor re-tunes it for recovery - and the UUID/EAB cache that drives this script's arguments can warm from empty (cold first tune) to
|
|
976
|
+
* populated (warm re-tune) in between. The script bakes those tokens in as evaluateOnNewDocument arguments, so re-installing each time is required to carry the
|
|
977
|
+
* CURRENT tokens; installOrReplaceOnNewDocument removes the prior script before adding the fresh one, keeping exactly one live interceptor with up-to-date
|
|
978
|
+
* arguments rather than an accumulating stack of competing window.fetch patches frozen at their respective install-time values. (Gating with installOncePerPage
|
|
979
|
+
* instead would freeze the cold first-tune's empty tokens and stall the warm recovery re-tune - the page would hold the playlist on a fast-path that the stale
|
|
980
|
+
* cold-mode script cannot complete, time out, and fall back to a full guide re-tune.)
|
|
1021
981
|
*/
|
|
1022
982
|
try {
|
|
1023
983
|
// Always hold the playlist request. On warm cache, the interceptor swaps immediately. On cold cache (including affiliates), the guide grid strategy will either
|
|
1024
984
|
// inject UUID+EAB via the fast path (resolving the held playlist) or release the hold before falling through to the click path.
|
|
1025
985
|
const attemptDirectTune = true;
|
|
1026
|
-
await page.evaluateOnNewDocument((initialUuid, initialEab, cachedUuids, cachedEabs, targetName, holdPlaylist) => {
|
|
986
|
+
await installOrReplaceOnNewDocument(page, "fetch-interceptor", async () => await page.evaluateOnNewDocument((initialUuid, initialEab, cachedUuids, cachedEabs, targetName, holdPlaylist) => {
|
|
1027
987
|
// evaluateOnNewDocument runs in every frame, including ad iframes. We only want to intercept fetches in the main frame - iframes don't make Hulu API
|
|
1028
988
|
// calls. The try/catch handles cross-origin iframes where accessing window.top throws a SecurityError.
|
|
1029
989
|
try {
|
|
@@ -1053,8 +1013,8 @@ async function resolveHuluDirectUrl(channelSelector, page) {
|
|
|
1053
1013
|
console.log("[HULU-DIAG] Fetch interceptor installed (" + ((uuid && eab) ? "warm" : "cold") + ")." + (uuid ? " uuid=" + uuid + " eab=" + (eab || "pending") : ""));
|
|
1054
1014
|
// Promise that resolves when both UUID and EAB are available for the playlist swap. On warm cache, resolves immediately (both injected at install time).
|
|
1055
1015
|
// On cold cache, resolves when the in-page listing and details API response parsers have captured both values. The playlist handler awaits this Promise
|
|
1056
|
-
// to hold the request until the target channel's data is ready. We use Promise.withResolvers() (Chrome 119+) for the deferred case so the resolver
|
|
1057
|
-
//
|
|
1016
|
+
// to hold the request until the target channel's data is ready. We use Promise.withResolvers() (Chrome 119+) for the deferred case so the resolver can be
|
|
1017
|
+
// invoked later by the in-page API parsers as they capture UUID and EAB, matching the resolver-extraction pattern of inPageTimeout below.
|
|
1058
1018
|
let directTuneResolve = null;
|
|
1059
1019
|
let directTunePromise;
|
|
1060
1020
|
if (uuid && eab) {
|
|
@@ -1089,8 +1049,9 @@ async function resolveHuluDirectUrl(channelSelector, page) {
|
|
|
1089
1049
|
}
|
|
1090
1050
|
// Injection endpoint for the guide grid strategy's fast-path tune. After binary search identifies the target channel and the unified cache provides the
|
|
1091
1051
|
// UUID and EAB, the strategy calls this function via page.evaluate to feed both values into the interceptor. The held playlist request then resumes with the
|
|
1092
|
-
// swapped channel_id and content_eab_id. Returns true if the injection was accepted (
|
|
1093
|
-
//
|
|
1052
|
+
// swapped channel_id and content_eab_id. Returns true if the injection was accepted (the deferred resolver is still present), false once that resolver has
|
|
1053
|
+
// been consumed - by self-resolution from API data or a prior injection (both via tryResolveDirectTune) or by __prismcastReleasePlaylist. The 8s playlist
|
|
1054
|
+
// hold timeout wins the Promise.race below without clearing the resolver, so it does not by itself flip this to false.
|
|
1094
1055
|
window["__prismcastResolveDirectTune"] = (u, e) => {
|
|
1095
1056
|
if (!directTuneResolve) {
|
|
1096
1057
|
return false;
|
|
@@ -1238,7 +1199,7 @@ async function resolveHuluDirectUrl(channelSelector, page) {
|
|
|
1238
1199
|
}
|
|
1239
1200
|
return await originalFetch(input, Object.assign({}, init ?? {}, { body }));
|
|
1240
1201
|
}
|
|
1241
|
-
//
|
|
1202
|
+
// The fetch replacement is async because it awaits the underlying fetch and request-body reads while rewriting matching requests.
|
|
1242
1203
|
window.fetch = async function (input, init) {
|
|
1243
1204
|
const url = (typeof input === "string") ? input : (input instanceof URL) ? input.href : input.url;
|
|
1244
1205
|
// Expand listing API requests to include all cached UUIDs. The mini-guide listing only sends ~10 channel UUIDs, but we have UUIDs from previous
|
|
@@ -1367,7 +1328,7 @@ async function resolveHuluDirectUrl(channelSelector, page) {
|
|
|
1367
1328
|
}
|
|
1368
1329
|
return originalFetch(input, init);
|
|
1369
1330
|
};
|
|
1370
|
-
}, cachedUuid ?? "", cachedEab ?? "", allCachedUuids, allCurrentEabs, normalizedName, attemptDirectTune);
|
|
1331
|
+
}, cachedUuid ?? "", cachedEab ?? "", allCachedUuids, allCurrentEabs, normalizedName, attemptDirectTune));
|
|
1371
1332
|
}
|
|
1372
1333
|
catch (error) {
|
|
1373
1334
|
LOG.debug("tuning:hulu", "Failed to install Hulu fetch interceptor: %s.", formatError(error));
|
|
@@ -1392,21 +1353,22 @@ async function resolveHuluDirectUrl(channelSelector, page) {
|
|
|
1392
1353
|
function invalidateHuluDirectUrl(channelSelector) {
|
|
1393
1354
|
const key = resolveHuluCacheKey(normalizeChannelName(channelSelector));
|
|
1394
1355
|
if (key) {
|
|
1395
|
-
|
|
1356
|
+
huluCache.map.delete(key);
|
|
1396
1357
|
}
|
|
1397
1358
|
}
|
|
1398
1359
|
/**
|
|
1399
1360
|
* Discovers all channels from Hulu Live TV by clicking the Channels tab to trigger full API expansion and performing a complete linear scan through the
|
|
1400
|
-
* virtualized guide grid.
|
|
1361
|
+
* virtualized guide grid. This function navigates to the Hulu live page itself. Detects local affiliates using the same CALL_SIGN_PATTERN and position-based
|
|
1401
1362
|
* inference logic as the tuning strategy. Affiliates get the network name as their selector; non-affiliates get their display name. Enriches unified cache
|
|
1402
1363
|
* entries with affiliate metadata for subsequent getCachedChannels derivation.
|
|
1403
|
-
* @param page - The Puppeteer page object
|
|
1364
|
+
* @param page - The Puppeteer page object; this function navigates it to the Hulu live page.
|
|
1404
1365
|
* @returns Array of discovered channels with affiliate detection and selector mapping.
|
|
1405
1366
|
*/
|
|
1406
1367
|
async function discoverHuluChannels(page) {
|
|
1407
1368
|
// Return from the unified cache if a full discovery walk (with affiliate inference) has already completed.
|
|
1408
|
-
|
|
1409
|
-
|
|
1369
|
+
const cached = huluCache.cached();
|
|
1370
|
+
if (cached) {
|
|
1371
|
+
return cached;
|
|
1410
1372
|
}
|
|
1411
1373
|
// Set up response interception BEFORE navigation so we capture the initial details and listing API responses during page load. These responses populate the
|
|
1412
1374
|
// unified channel cache with UUID, programs, and display names for all ~130 channels - warming the tuning cache as a side effect of discovery. The same
|
|
@@ -1495,15 +1457,15 @@ async function discoverHuluChannels(page) {
|
|
|
1495
1457
|
// Alias the network name to the affiliate's cache entry so that resolveHuluDirectUrl can look up local affiliates by their channelSelector (e.g., "PBS")
|
|
1496
1458
|
// without falling through to the guide grid. Without this alias, the first tune after precaching would always be cold for local affiliates because the
|
|
1497
1459
|
// cache is keyed by call sign (e.g., "wttw") and findHuluChannelEntry("pbs") would find no match.
|
|
1498
|
-
const affiliateEntry =
|
|
1460
|
+
const affiliateEntry = huluCache.map.get(callSign);
|
|
1499
1461
|
if (affiliateEntry) {
|
|
1500
|
-
|
|
1462
|
+
huluCache.map.set(network, affiliateEntry);
|
|
1501
1463
|
}
|
|
1502
1464
|
}
|
|
1503
1465
|
}
|
|
1504
|
-
// Enrich unified cache entries with affiliate metadata so
|
|
1466
|
+
// Enrich unified cache entries with affiliate metadata so the cache's projection carries proper affiliate labeling on subsequent warm reads.
|
|
1505
1467
|
for (const [callSign, network] of affiliateMap) {
|
|
1506
|
-
const entry =
|
|
1468
|
+
const entry = huluCache.map.get(callSign);
|
|
1507
1469
|
if (entry) {
|
|
1508
1470
|
entry.affiliate = network;
|
|
1509
1471
|
}
|
|
@@ -1520,29 +1482,22 @@ async function discoverHuluChannels(page) {
|
|
|
1520
1482
|
// Do not cache empty results - leave the flag false so subsequent calls retry the full walk. Empty results can indicate no Hulu + Live TV subscription.
|
|
1521
1483
|
if (discovered.length > 0) {
|
|
1522
1484
|
discovered.sort((a, b) => a.name.localeCompare(b.name));
|
|
1523
|
-
|
|
1485
|
+
// A full walk with affiliate position inference is what makes the cache comprehensive enough to serve warm reads. Anything short of it leaves the cache
|
|
1486
|
+
// unmarked, so a later discovery call runs the walk again rather than reporting a lineup missing its affiliate labeling.
|
|
1487
|
+
huluCache.markComplete();
|
|
1524
1488
|
}
|
|
1525
1489
|
return discovered;
|
|
1526
1490
|
}
|
|
1527
|
-
/**
|
|
1528
|
-
* Returns cached discovered channels from the unified channel cache, or null if a full discovery walk (with affiliate position inference) has not yet completed.
|
|
1529
|
-
* Derives the result on the fly from unified cache entries, deduplicating aliases via Set reference equality.
|
|
1530
|
-
* @returns Sorted array of discovered channels or null.
|
|
1531
|
-
*/
|
|
1532
|
-
function getHuluCachedChannels() {
|
|
1533
|
-
if (!huluFullyDiscovered || (huluChannelCache.size === 0)) {
|
|
1534
|
-
return null;
|
|
1535
|
-
}
|
|
1536
|
-
return buildHuluDiscoveredChannels();
|
|
1537
|
-
}
|
|
1538
1491
|
export const huluProvider = {
|
|
1539
1492
|
discoverChannels: discoverHuluChannels,
|
|
1540
|
-
getCachedChannels:
|
|
1493
|
+
getCachedChannels: huluCache.cached,
|
|
1541
1494
|
guideUrl: "https://www.hulu.com/live",
|
|
1542
1495
|
handlesOwnNavigation: true,
|
|
1543
1496
|
label: "Hulu",
|
|
1544
|
-
// Profile for Hulu Live TV which presents a guide grid of live channels. The channel list is revealed by clicking
|
|
1545
|
-
// is
|
|
1497
|
+
// Profile for Hulu Live TV which presents a guide grid of live channels. The channel list is revealed by clicking the #CHANNELS tab (listSelector), then the
|
|
1498
|
+
// target channel is located by matching its normalized name against the data-testid="live-guide-channel-kyber-{name}" attribute on the virtualized guide rows
|
|
1499
|
+
// (binary search to scroll the target into the render window, with position-based affiliate inference). Uses the fullscreen API (inherited from fullscreenApi)
|
|
1500
|
+
// plus a dedicated fullscreen button selector for the player's native
|
|
1546
1501
|
// maximize control. Requires selectReadyVideo because the page may have multiple video elements (ads, previews, main content). Uses waitForNetworkIdle because
|
|
1547
1502
|
// Hulu's SPA has heavy async initialization that often prevents the load event from firing within the retryOperation timeout; the graceful networkidle2 fallback
|
|
1548
1503
|
// in navigateToPage() allows execution to continue to channel selection even when background requests are still pending.
|
|
@@ -1560,7 +1515,7 @@ export const huluProvider = {
|
|
|
1560
1515
|
slug: "hulu",
|
|
1561
1516
|
strategy: {
|
|
1562
1517
|
clearCache: clearHuluCache,
|
|
1563
|
-
execute:
|
|
1518
|
+
execute: guideGridStrategy,
|
|
1564
1519
|
invalidateDirectUrl: invalidateHuluDirectUrl,
|
|
1565
1520
|
resolveDirectUrl: resolveHuluDirectUrl
|
|
1566
1521
|
},
|