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
package/dist/native/index.js
CHANGED
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
*
|
|
3
3
|
* index.ts: Coordinator for native HLS streaming - manifest interception, DRM probe, and proxy lifecycle.
|
|
4
4
|
*/
|
|
5
|
-
import { LOG,
|
|
5
|
+
import { LOG, boundedWait, formatError, startTimer } from "../utils/index.js";
|
|
6
6
|
import { clearProbeCache, probeManifest } from "./probe.js";
|
|
7
|
-
import { installManifestInterceptor, removeManifestInterceptor } from "../browser/manifestInterceptor.js";
|
|
8
7
|
import { createNativeProxy } from "./proxy.js";
|
|
9
8
|
import { fetchDecryptionKey } from "./decrypt.js";
|
|
10
9
|
import { parseTokenExpiry } from "./tokenExpiry.js";
|
|
@@ -15,17 +14,35 @@ import { parseTokenExpiry } from "./tokenExpiry.js";
|
|
|
15
14
|
* 2. If no manifest was intercepted: return null (fall back to capture)
|
|
16
15
|
* 3. Probe the manifest for encryption type
|
|
17
16
|
* 4. If DRM: log the reason, return null
|
|
18
|
-
* 5. If
|
|
17
|
+
* 5. If the client is an MPEG-TS consumer and the stream has a separate audio rendition: return null (incompatible PAT/PMT, fall back to capture)
|
|
18
|
+
* 6. If AES-128: pre-fetch the decryption key, returning null if it is inaccessible
|
|
19
|
+
* 7. If clear or AES-128 (with key in hand): create and return the native proxy
|
|
19
20
|
*
|
|
20
|
-
* Token refresh: When the intercepted URL contains expiration tokens, we schedule a timer
|
|
21
|
-
*
|
|
22
|
-
*
|
|
21
|
+
* Token refresh: When the intercepted URL contains expiration tokens, we schedule a SINGLE timer aimed at the next expiry boundary - the earlier of the master URL's
|
|
22
|
+
* and the polled variant URL's expirations, minus a comfortable margin. The refresh first attempts a direct Node.js fetch of the master manifest URL (no browser
|
|
23
|
+
* involvement), falling back to a browser page reload when the master URL itself has expired. Each refresh reschedules from the boundary it is aiming at, not from a
|
|
24
|
+
* shrinking-but-unchanging master expiry, so the schedule never degenerates into a per-cycle re-probe loop in the final minutes before expiry. The proxy
|
|
25
|
+
* continues serving cached segments during the refresh.
|
|
26
|
+
*
|
|
27
|
+
* A refresh re-runs the same ranked selection a tune runs, narrowed to the candidates the running proxy can absorb, so a stream heals in both directions as the
|
|
28
|
+
* service's ladder changes underneath it. A refreshed manifest offering nothing that pipeline can serve is declined outright rather than half-applied: the
|
|
29
|
+
* refresh reports failure, and the monitor's own escalation owns the one designed transition from relaying back to capture.
|
|
30
|
+
*
|
|
31
|
+
* Two triggers reach the refresh - the boundary timer above and the monitor's failure-triggered recovery - and they share one attempt: whichever arrives second
|
|
32
|
+
* awaits the one in flight and reads its real outcome, because running two against a single browser page races their navigations and the monitor steers on the
|
|
33
|
+
* boolean it reads back. An attempt that fails on a live proxy warns and re-arms itself, doubling the delay per consecutive failure up to the refresh margin and
|
|
34
|
+
* riding the proxy's one timer slot, so a stream that stumbles keeps its proactive chain instead of coasting until its tokens die.
|
|
23
35
|
*/
|
|
24
36
|
// Time in milliseconds before token expiry to trigger a refresh. We refresh 5 minutes early to ensure the new manifest is ready before the old one expires.
|
|
25
37
|
const TOKEN_REFRESH_MARGIN = 300000;
|
|
26
|
-
// Minimum delay in milliseconds before a token refresh fires.
|
|
27
|
-
//
|
|
38
|
+
// Minimum delay in milliseconds before a token refresh fires. This is the absolute floor for any scheduled refresh: when a boundary lands at or in the past (an
|
|
39
|
+
// already-expired or imminently-expiring token), we still wait at least this long so the timer cannot fire back-to-back and thrash. It is NOT the steady-state
|
|
40
|
+
// cadence - inside the margin window we schedule a single refresh aimed at the actual expiry boundary, not a repeating MIN_REFRESH_DELAY poll (see scheduleTokenRefresh).
|
|
28
41
|
const MIN_REFRESH_DELAY = 30000;
|
|
42
|
+
// The longest delay Node's setTimeout accepts: a 32-bit signed millisecond count, roughly 24.8 days. A boundary further out than that is clamped to this ceiling,
|
|
43
|
+
// where the refresh fires early, re-probes, and reschedules against the boundary it re-derives - one harmless extra refresh per ceiling interval. An unclamped
|
|
44
|
+
// delay would instead overflow the timer, which Node fires immediately, turning a distant expiry into a refresh loop running at the speed of the network.
|
|
45
|
+
const MAX_TIMER_DELAY_MS = 2147483647;
|
|
29
46
|
// Minimum remaining token lifetime (in milliseconds) for a direct-fetched variant URL to be considered usable. If the variant URL's token expires sooner than this,
|
|
30
47
|
// the direct fetch result is discarded and we fall back to a page reload to get a genuinely fresh token. This prevents handing the proxy a variant URL that expires
|
|
31
48
|
// almost immediately. Set low (5s) because the proxy only needs the variant URL to survive one poll cycle (~3s). A higher threshold (e.g., 30s) would cause Fox.com
|
|
@@ -35,54 +52,56 @@ const MIN_USABLE_TOKEN_LIFETIME = 5000;
|
|
|
35
52
|
const INTERCEPTION_AWAIT_TIMEOUT = 5000;
|
|
36
53
|
/**
|
|
37
54
|
* Attempts to upgrade a stream from screen capture to native HLS streaming. Returns the native proxy on success, or null if the stream is not viable for native
|
|
38
|
-
* consumption (DRM, interception timeout, or probe failure).
|
|
55
|
+
* consumption (DRM, interception timeout, or probe failure).
|
|
39
56
|
*
|
|
40
57
|
* @param options - Options for the native streaming attempt.
|
|
41
58
|
* @returns The native stream result on success, or null to fall back to capture.
|
|
42
59
|
*/
|
|
43
60
|
export async function attemptNativeStreaming(options) {
|
|
44
|
-
const { channelName, interceptionPromise, mpegTsClient, onError, page, streamId, streamIdStr, url } = options;
|
|
61
|
+
const { channelName, interceptionPromise, mpegTsClient, onError, onFeedApplied, page, probeIdentity, reestablishManifest, streamId, streamIdStr, url } = options;
|
|
45
62
|
const elapsed = startTimer();
|
|
46
63
|
LOG.debug("native:coordinator", "Attempting native streaming for %s.", channelName);
|
|
47
|
-
// Await the manifest interception with a short
|
|
48
|
-
// already be captured or close to it.
|
|
49
|
-
//
|
|
64
|
+
// Await the manifest interception with a short bound. The CDP listener was installed before navigation, so by the time we get here the manifest should
|
|
65
|
+
// already be captured or close to it. A lapse is an ordinary outcome here - it simply means capture serves this tune - so the wait is value-shaped and its
|
|
66
|
+
// null joins the interception's own null on the fallback branch below.
|
|
50
67
|
let interception;
|
|
51
|
-
const timeout = cancellableTimeout(INTERCEPTION_AWAIT_TIMEOUT);
|
|
52
68
|
try {
|
|
53
|
-
|
|
54
|
-
interception = (result === false) ? null : result;
|
|
69
|
+
interception = await boundedWait(interceptionPromise, INTERCEPTION_AWAIT_TIMEOUT);
|
|
55
70
|
}
|
|
56
71
|
catch (error) {
|
|
57
72
|
LOG.debug("native:coordinator", "Manifest interception error for %s: %s.", channelName, formatError(error));
|
|
58
73
|
return null;
|
|
59
74
|
}
|
|
60
|
-
finally {
|
|
61
|
-
timeout.cancel();
|
|
62
|
-
}
|
|
63
75
|
if (!interception) {
|
|
64
76
|
LOG.debug("native:coordinator", "No manifest intercepted for %s in %sms. Falling back to capture.", channelName, elapsed());
|
|
65
77
|
return null;
|
|
66
78
|
}
|
|
67
79
|
LOG.debug("native:coordinator", "Manifest intercepted for %s in %sms.", channelName, elapsed());
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
80
|
+
/* Probe the intercepted URL and normalize the result to a MediaFeed. The probe handles both master and media playlists transparently; this code path does not
|
|
81
|
+
* need to know which kind arrived. Tune admission refuses a window of at most one segment: the interceptor latches the first master on the wire, and for a
|
|
82
|
+
* service that fronts its player with a per-session bumper that master describes the bumper rather than the channel. Declining lands on the null path below,
|
|
83
|
+
* where capture serves the channel the relay could not.
|
|
84
|
+
*/
|
|
85
|
+
const mediaFeed = await probeManifest(interception.manifestUrl, probeIdentity, { rejectStaticPlaylists: true });
|
|
71
86
|
if (!mediaFeed) {
|
|
72
87
|
LOG.debug("native:coordinator", "Probe failed for %s. Falling back to capture.", channelName);
|
|
73
|
-
removeManifestInterceptor(interception.cdpSession);
|
|
74
88
|
return null;
|
|
75
89
|
}
|
|
76
90
|
if (mediaFeed.encryption === "drm") {
|
|
77
91
|
LOG.debug("native:coordinator", "Native streaming not viable for %s: DRM-protected stream.", channelName);
|
|
78
|
-
removeManifestInterceptor(interception.cdpSession);
|
|
79
92
|
return null;
|
|
80
93
|
}
|
|
81
94
|
// Separate audio renditions (e.g., Google DAI on BET/VH1) cannot be served to MPEG-TS clients because the independent video and audio MPEG-TS segments have
|
|
82
95
|
// incompatible PAT/PMT tables and variable packet sizes from ad splicing. HLS clients (Channels DVR) handle separate audio renditions natively via master playlist.
|
|
83
96
|
if (mpegTsClient && mediaFeed.audioVariantUrl) {
|
|
84
97
|
LOG.debug("native:coordinator", "Native streaming not viable for %s: separate audio rendition incompatible with MPEG-TS clients.", channelName);
|
|
85
|
-
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
/* The proxy is built around the feed's container, so a feed that carries none cannot become a pipeline. Only a DRM classification leaves it null, and that
|
|
101
|
+
* path returned above, so this guard answers a state the flow does not reach rather than inventing a container to satisfy the option's type.
|
|
102
|
+
*/
|
|
103
|
+
if (mediaFeed.container === null) {
|
|
104
|
+
LOG.debug("native:coordinator", "Native streaming not viable for %s: the probe returned no container classification.", channelName);
|
|
86
105
|
return null;
|
|
87
106
|
}
|
|
88
107
|
LOG.debug("native:coordinator", "Native streaming viable for %s (%s, variant: %s).", channelName, mediaFeed.encryption, mediaFeed.bestVariantUrl.slice(0, 80));
|
|
@@ -94,21 +113,20 @@ export async function attemptNativeStreaming(options) {
|
|
|
94
113
|
prefetchedKey = await fetchDecryptionKey(mediaFeed.keyUrl);
|
|
95
114
|
if (!prefetchedKey) {
|
|
96
115
|
LOG.debug("native:coordinator", "Native streaming not viable for %s: decryption key inaccessible.", channelName);
|
|
97
|
-
removeManifestInterceptor(interception.cdpSession);
|
|
98
116
|
return null;
|
|
99
117
|
}
|
|
100
118
|
LOG.debug("native:coordinator", "AES-128 decryption key pre-fetched for %s.", channelName);
|
|
101
119
|
}
|
|
102
|
-
// Create the native proxy. The CDP session is
|
|
103
|
-
//
|
|
120
|
+
// Create the native proxy. The manifest interceptor's underlying CDP session is owned and disposed internally by the interceptor - the proxy holds no session
|
|
121
|
+
// references. For AES-128 streams, the pre-fetched key is passed so the proxy does not need to fetch it again on the first segment. The preroll segment count
|
|
104
122
|
// determines the segment index offset (reserving index space for preroll). Streams with separate audio cannot use preroll because the preroll content is muxed
|
|
105
123
|
// video+audio and can't be split into separate renditions.
|
|
106
124
|
const hasSeparateAudio = mediaFeed.audioVariantUrl !== null;
|
|
107
125
|
const proxyPrerollSegmentCount = (!hasSeparateAudio && options.prerollSegmentCount) ? options.prerollSegmentCount : 0;
|
|
108
126
|
const proxy = createNativeProxy({
|
|
109
127
|
audioVariantUrl: mediaFeed.audioVariantUrl,
|
|
110
|
-
cdpSession: interception.cdpSession,
|
|
111
128
|
channelName,
|
|
129
|
+
container: mediaFeed.container,
|
|
112
130
|
encryption: mediaFeed.encryption,
|
|
113
131
|
keyUrl: mediaFeed.keyUrl,
|
|
114
132
|
onError,
|
|
@@ -119,59 +137,175 @@ export async function attemptNativeStreaming(options) {
|
|
|
119
137
|
streamIdStr,
|
|
120
138
|
variantUrl: mediaFeed.bestVariantUrl
|
|
121
139
|
});
|
|
122
|
-
// Schedule token refresh if
|
|
140
|
+
// Schedule token refresh if either URL contains expiration tokens. We pass the variant URL the proxy will poll so the refresh boundary is the earlier of the
|
|
141
|
+
// master and variant expirations.
|
|
123
142
|
scheduleTokenRefresh({
|
|
124
143
|
channelName,
|
|
125
|
-
masterUrl: interception.
|
|
144
|
+
masterUrl: interception.manifestUrl,
|
|
145
|
+
onFeedApplied,
|
|
126
146
|
page,
|
|
147
|
+
probeIdentity,
|
|
127
148
|
proxy,
|
|
149
|
+
reestablishManifest,
|
|
128
150
|
streamIdStr,
|
|
129
|
-
url
|
|
151
|
+
url,
|
|
152
|
+
variantUrl: mediaFeed.bestVariantUrl
|
|
130
153
|
});
|
|
131
154
|
LOG.debug("timing:native", "Native streaming setup completed for %s in %sms.", channelName, elapsed());
|
|
132
|
-
return { bandwidth: mediaFeed.bandwidth, codec: mediaFeed.codec, hasAudio: mediaFeed.audioVariantUrl !== null, proxy,
|
|
155
|
+
return { bandwidth: mediaFeed.bandwidth, codec: mediaFeed.codec, container: mediaFeed.container, hasAudio: mediaFeed.audioVariantUrl !== null, proxy,
|
|
156
|
+
resolution: mediaFeed.resolution };
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Computes the absolute moment (milliseconds since the Unix epoch) at which the next token refresh must occur, or null when neither URL carries an expiry token.
|
|
160
|
+
*
|
|
161
|
+
* The boundary is the earlier of the master URL's and the variant URL's expirations. The master URL governs which refresh strategy can run (a direct re-fetch is
|
|
162
|
+
* possible only while the master token is alive; once it expires only a page reload mints a new master), while the variant URL is what the proxy polls for segments.
|
|
163
|
+
* Taking the minimum guarantees the proxy never holds a dead variant URL and the refresh never tries a direct fetch against an expired master.
|
|
164
|
+
*
|
|
165
|
+
* @param masterUrl - The master manifest URL whose token gates the direct-fetch strategy.
|
|
166
|
+
* @param variantUrl - The variant URL the proxy polls for segments.
|
|
167
|
+
* @returns The earliest expiry in milliseconds since the epoch, or null when neither URL carries an expiry token.
|
|
168
|
+
*/
|
|
169
|
+
function computeRefreshBoundary(masterUrl, variantUrl) {
|
|
170
|
+
const masterExpiry = parseTokenExpiry(masterUrl);
|
|
171
|
+
const variantExpiry = parseTokenExpiry(variantUrl);
|
|
172
|
+
if ((masterExpiry === null) && (variantExpiry === null)) {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
// At least one expiry exists. Default the missing one to positive infinity so Math.min selects the URL that actually constrains the boundary.
|
|
176
|
+
return Math.min(masterExpiry ?? Number.POSITIVE_INFINITY, variantExpiry ?? Number.POSITIVE_INFINITY);
|
|
133
177
|
}
|
|
134
178
|
/**
|
|
135
|
-
* Schedules a token refresh timer
|
|
136
|
-
*
|
|
179
|
+
* Schedules a SINGLE token refresh timer aimed at the next expiry boundary - the earlier of the master URL's and the polled variant URL's expirations. There are two
|
|
180
|
+
* regimes:
|
|
181
|
+
*
|
|
182
|
+
* 1. **Comfortable margin** (more than TOKEN_REFRESH_MARGIN remains): schedule the refresh TOKEN_REFRESH_MARGIN before the boundary, so the fresh manifest is ready
|
|
183
|
+
* well before the old one expires.
|
|
184
|
+
*
|
|
185
|
+
* 2. **Inside the margin window** (TOKEN_REFRESH_MARGIN or less remains): re-fetching the same master URL does not extend its lifetime, so re-probing it on a fixed
|
|
186
|
+
* cadence is wasted work. We schedule exactly one refresh aimed at the boundary itself (clamped to MIN_REFRESH_DELAY so an already-expired or imminent boundary
|
|
187
|
+
* cannot fire back-to-back). When that refresh fires the master token is spent, the direct fetch fails, and the page-reload path mints a genuinely new master URL
|
|
188
|
+
* - once, at the boundary, not every MIN_REFRESH_DELAY for the final minutes before expiry.
|
|
189
|
+
*
|
|
190
|
+
* This is the core of the busy-loop fix: each refresh reschedules from the boundary it aims at, never from a shrinking-but-unchanging master expiry, so the cadence
|
|
191
|
+
* is a single boundary-targeted timer rather than a per-cycle re-probe.
|
|
137
192
|
*
|
|
138
193
|
* @param options - Token refresh options.
|
|
139
194
|
*/
|
|
140
195
|
function scheduleTokenRefresh(options) {
|
|
141
|
-
const { channelName, masterUrl, page, proxy, streamIdStr, url } = options;
|
|
142
|
-
const
|
|
143
|
-
if (
|
|
144
|
-
LOG.debug("native:token", "No token expiry found in manifest
|
|
196
|
+
const { channelName, masterUrl, onFeedApplied, page, probeIdentity, proxy, reestablishManifest, streamIdStr, url, variantUrl } = options;
|
|
197
|
+
const boundary = computeRefreshBoundary(masterUrl, variantUrl);
|
|
198
|
+
if (boundary === null) {
|
|
199
|
+
LOG.debug("native:token", "No token expiry found in manifest URLs for %s.", channelName);
|
|
145
200
|
return;
|
|
146
201
|
}
|
|
147
|
-
const timeUntilExpiry =
|
|
148
|
-
|
|
202
|
+
const timeUntilExpiry = boundary - Date.now();
|
|
203
|
+
// Outside the margin we lead the boundary by TOKEN_REFRESH_MARGIN; inside it we aim straight at the boundary so the direct fetch fails into a page reload exactly
|
|
204
|
+
// once. The floor of MIN_REFRESH_DELAY keeps a past-due or imminent boundary from thrashing, and the ceiling of MAX_TIMER_DELAY_MS keeps a distant one inside
|
|
205
|
+
// the range setTimeout can actually represent.
|
|
206
|
+
const lead = (timeUntilExpiry > TOKEN_REFRESH_MARGIN) ? TOKEN_REFRESH_MARGIN : 0;
|
|
207
|
+
const refreshIn = Math.min(Math.max(MIN_REFRESH_DELAY, timeUntilExpiry - lead), MAX_TIMER_DELAY_MS);
|
|
149
208
|
LOG.debug("native:token", "Token expires in %ss for %s. Refresh scheduled in %ss.", Math.round(timeUntilExpiry / 1000), channelName, Math.round(refreshIn / 1000));
|
|
150
209
|
// Store the timer handle on the proxy so it can be cancelled if the proxy is stopped before the timer fires. Pass the master URL so the refresh can attempt a
|
|
151
210
|
// direct fetch before falling back to a page reload.
|
|
152
211
|
const timer = setTimeout(() => {
|
|
153
|
-
void refreshNativeManifest({ channelName, masterUrl, page, proxy, streamIdStr, url });
|
|
212
|
+
void refreshNativeManifest({ channelName, masterUrl, onFeedApplied, page, probeIdentity, proxy, reestablishManifest, streamIdStr, url });
|
|
154
213
|
}, refreshIn);
|
|
155
214
|
proxy.setTokenRefreshTimer(timer);
|
|
156
215
|
}
|
|
157
216
|
/**
|
|
158
|
-
* Refreshes a native stream's manifest to obtain fresh auth tokens
|
|
217
|
+
* Refreshes a native stream's manifest to obtain fresh auth tokens, serialized across callers and re-armed when an attempt fails.
|
|
218
|
+
*
|
|
219
|
+
* Two triggers reach this function: the proactive timer aimed at the token's expiry boundary, and the monitor's failure-triggered recovery. They can arrive
|
|
220
|
+
* within the same cycle, and two refreshes running against one browser page race each other's navigation, so an attempt already in flight is the attempt every
|
|
221
|
+
* caller gets - the later caller awaits it and reads its real settled outcome, which matters because the monitor steers its escalation on the boolean it reads
|
|
222
|
+
* back here.
|
|
223
|
+
*
|
|
224
|
+
* A failed refresh on a live proxy warns and re-arms itself with a doubling delay bounded by the refresh margin, so one transient failure cannot leave a stream
|
|
225
|
+
* with no proactive refresh for the rest of its life. The retry rides the proxy's single timer slot, the same one the boundary schedule uses.
|
|
226
|
+
*
|
|
227
|
+
* @param options - Refresh options. masterUrl is optional - when provided, direct fetch is attempted first. probeIdentity is the stream's own, so the probe on
|
|
228
|
+
* either strategy reads and writes the cache under the identity the tune established.
|
|
229
|
+
* @returns True if the refresh succeeded (proxy updated with new manifest), false otherwise.
|
|
230
|
+
*/
|
|
231
|
+
export async function refreshNativeManifest(options) {
|
|
232
|
+
const { channelName, proxy, streamIdStr } = options;
|
|
233
|
+
const streamLog = LOG.withStreamId(streamIdStr);
|
|
234
|
+
const pending = proxy.getPendingRefresh();
|
|
235
|
+
if (pending) {
|
|
236
|
+
return pending;
|
|
237
|
+
}
|
|
238
|
+
/* What goes into the slot is a promise that never rejects: the attempt's own throw is caught and normalized to false here, before the promise is shared, so a
|
|
239
|
+
* caller awaiting the slot can never be handed a rejection from machinery it did not start. The store below happens before this attempt reaches its first
|
|
240
|
+
* suspension point, which is what makes the slot visible to a caller arriving in the same tick.
|
|
241
|
+
*/
|
|
242
|
+
const attempt = (async () => {
|
|
243
|
+
try {
|
|
244
|
+
return await runManifestRefresh(options, streamLog);
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
streamLog.debug("native:token", "Manifest refresh failed for %s: %s.", channelName, formatError(error));
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
})();
|
|
251
|
+
proxy.setPendingRefresh(attempt);
|
|
252
|
+
let succeeded = false;
|
|
253
|
+
try {
|
|
254
|
+
succeeded = await attempt;
|
|
255
|
+
if (succeeded) {
|
|
256
|
+
// The stream is refreshing again, so the backoff retires: whatever fails next starts over at the floor delay.
|
|
257
|
+
proxy.clearRefreshFailures();
|
|
258
|
+
}
|
|
259
|
+
else if (!proxy.isStopped()) {
|
|
260
|
+
/* A failure on a live proxy is the field's early warning that a stream is heading for token death - every refusal inside the attempt reports at debug, so
|
|
261
|
+
* this is the one line an operator sees - and it re-arms the chain the failure would otherwise have ended. The delay doubles per consecutive failure up to
|
|
262
|
+
* the refresh margin, read from a count captured once so the warn and the timer cannot disagree. The re-arm rides the proxy's single timer slot: arming
|
|
263
|
+
* retires whatever is there, and stop() retires whichever is live, so there is never a second timer to leak. Nothing caps the retries because the stream's
|
|
264
|
+
* own lifecycle already does - a token that stays dead drives segment failures into the proxy's error threshold, and the fallback to capture that follows
|
|
265
|
+
* stops the proxy and its timer with it.
|
|
266
|
+
*/
|
|
267
|
+
const failures = proxy.noteRefreshFailure();
|
|
268
|
+
const retryIn = Math.min(MIN_REFRESH_DELAY * (2 ** (failures - 1)), TOKEN_REFRESH_MARGIN);
|
|
269
|
+
streamLog.warn("The token refresh for %s did not complete. Retrying in %s seconds.", channelName, Math.round(retryIn / 1000));
|
|
270
|
+
proxy.setTokenRefreshTimer(setTimeout(() => {
|
|
271
|
+
void refreshNativeManifest(options);
|
|
272
|
+
}, retryIn));
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
/* Only the bookkeeping above can throw here, since the attempt settles rather than rejects. Catching it keeps the outcome the attempt reached intact and,
|
|
277
|
+
* more to the point, keeps this promise from rejecting: both production callers void or await it without a catch of their own, so an escape would kill the
|
|
278
|
+
* very re-arm chain this function exists to keep alive.
|
|
279
|
+
*/
|
|
280
|
+
streamLog.warn("The token refresh bookkeeping for %s did not complete: %s.", channelName, formatError(error));
|
|
281
|
+
}
|
|
282
|
+
finally {
|
|
283
|
+
proxy.setPendingRefresh(null);
|
|
284
|
+
}
|
|
285
|
+
return succeeded;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Runs one manifest refresh attempt. Two strategies are tried in order:
|
|
159
289
|
*
|
|
160
290
|
* 1. **Direct fetch** (if masterUrl is provided): Re-fetches the master manifest URL from Node.js without involving the browser. This avoids the visible page reload
|
|
161
291
|
* that disrupts the browser tab. The CDN returns the manifest with current token values as long as the URL's own auth token hasn't expired. When the master URL
|
|
162
292
|
* expires, the direct fetch returns 403 and we fall through to strategy 2.
|
|
163
293
|
*
|
|
164
|
-
* 2. **
|
|
165
|
-
*
|
|
294
|
+
* 2. **Channel re-establishment**: Runs the stream's own tune again on the page through the capability the streaming layer supplied, so fresh authentication and a
|
|
295
|
+
* fresh master manifest arrive by exactly the semantics that established the stream - which is what keeps a guide-tuned stream on its own channel across the
|
|
296
|
+
* reload. This is the only path that generates genuinely new tokens and is required when the master URL itself has expired.
|
|
166
297
|
*
|
|
167
298
|
* L2 recovery (failure-triggered) from the monitor omits masterUrl, going straight to page reload since the stream is already failing and needs a full refresh.
|
|
168
299
|
*
|
|
169
|
-
*
|
|
300
|
+
* Every point at which this attempt resumes after an await re-checks whether the proxy stopped meanwhile, because a stream can terminate at any of them and a
|
|
301
|
+
* stopped proxy must not be written to.
|
|
302
|
+
*
|
|
303
|
+
* @param options - Refresh options, as handed to refreshNativeManifest.
|
|
304
|
+
* @param streamLog - The stream-scoped logger the caller already holds.
|
|
170
305
|
* @returns True if the refresh succeeded (proxy updated with new manifest), false otherwise.
|
|
171
306
|
*/
|
|
172
|
-
|
|
173
|
-
const { channelName, masterUrl, page,
|
|
174
|
-
const streamLog = LOG.withStreamId(streamIdStr);
|
|
307
|
+
async function runManifestRefresh(options, streamLog) {
|
|
308
|
+
const { channelName, masterUrl, page, probeIdentity, proxy, reestablishManifest } = options;
|
|
175
309
|
if (proxy.isStopped()) {
|
|
176
310
|
return false;
|
|
177
311
|
}
|
|
@@ -180,118 +314,145 @@ export async function refreshNativeManifest(options) {
|
|
|
180
314
|
// Strategy 1: Direct fetch. Re-fetch the master manifest URL from Node.js to get fresh variant URLs without reloading the browser page. This works as long as the
|
|
181
315
|
// master URL's own CDN auth token hasn't expired. When it does expire, probeManifest returns null (403) and we fall through to the page reload strategy.
|
|
182
316
|
if (masterUrl) {
|
|
183
|
-
const directResult = await tryDirectManifestRefresh(masterUrl,
|
|
317
|
+
const directResult = await tryDirectManifestRefresh(masterUrl, proxy.getPipelineShape(), probeIdentity, streamLog);
|
|
184
318
|
if (directResult) {
|
|
185
319
|
// Check if the proxy was stopped during the async probe (e.g., stream terminated while probing).
|
|
186
320
|
if (proxy.isStopped()) {
|
|
187
321
|
return false;
|
|
188
322
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
323
|
+
/* Apply the fresh feed, carrying the master URL forward: a direct fetch does not mint a new master, and the one in hand may still serve further direct
|
|
324
|
+
* fetches. The boundary the next schedule aims at therefore comes from the earlier of that unchanging master expiry and the freshly-fetched variant's,
|
|
325
|
+
* which is what keeps a still-valid master from being re-probed on a cadence of its own.
|
|
326
|
+
*/
|
|
327
|
+
const applied = applyRefreshedFeed(directResult, masterUrl, options, streamLog);
|
|
328
|
+
if (applied) {
|
|
329
|
+
streamLog.debug("native:token", "Manifest refresh completed for %s via direct fetch in %sms.", channelName, refreshElapsed());
|
|
193
330
|
}
|
|
194
|
-
|
|
195
|
-
// Schedule the next refresh with the same master URL. It may still be valid for subsequent direct fetches. Once it expires, the direct fetch will fail and
|
|
196
|
-
// the page reload fallback will generate a new master URL.
|
|
197
|
-
scheduleTokenRefresh({
|
|
198
|
-
channelName,
|
|
199
|
-
masterUrl,
|
|
200
|
-
page,
|
|
201
|
-
proxy,
|
|
202
|
-
streamIdStr,
|
|
203
|
-
url
|
|
204
|
-
});
|
|
205
|
-
return true;
|
|
331
|
+
return applied;
|
|
206
332
|
}
|
|
207
|
-
|
|
333
|
+
/* The direct fetch produced nothing usable - the master's token has expired, the fetch failed, or the manifest offered nothing this pipeline can absorb -
|
|
334
|
+
* so the page reload gets the second opinion. A fresh session can mint a ladder the running pipeline fits again, and a decline that persists through the
|
|
335
|
+
* reload leaves through the failure path below.
|
|
336
|
+
*/
|
|
208
337
|
streamLog.debug("native:token", "Direct manifest fetch failed for %s. Falling back to page reload.", channelName);
|
|
209
338
|
}
|
|
210
|
-
// Strategy 2:
|
|
339
|
+
// Strategy 2: Channel re-establishment. Reload the page through the stream's own tune to trigger fresh authentication and intercept a new master manifest.
|
|
211
340
|
if (page.isClosed()) {
|
|
212
341
|
streamLog.debug("native:token", "Manifest refresh failed for %s: page is closed.", channelName);
|
|
213
342
|
return false;
|
|
214
343
|
}
|
|
215
344
|
try {
|
|
216
|
-
//
|
|
217
|
-
//
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
streamLog.debug("native:token", "Manifest refresh failed for %s: could not install interceptor.", channelName);
|
|
221
|
-
return false;
|
|
222
|
-
}
|
|
223
|
-
// Navigate the page back to the channel URL to trigger fresh authentication. The interceptor captures .m3u8 requests generated by this navigation. Note: this
|
|
224
|
-
// uses a bare page.goto without the service's site profile (no waitForNetworkIdle, scroll options, etc.). This is acceptable because native streaming only
|
|
225
|
-
// activates for services whose video players load the HLS manifest during basic page load. Services requiring profile-aware navigation for manifest delivery
|
|
226
|
-
// would have failed the initial interception and would not be in native mode.
|
|
227
|
-
await page.goto(url, { timeout: 30000, waitUntil: "domcontentloaded" });
|
|
228
|
-
// Check if the proxy was stopped while we were navigating (e.g., stream terminated during the page load).
|
|
229
|
-
if (proxy.isStopped()) {
|
|
230
|
-
return false;
|
|
231
|
-
}
|
|
232
|
-
// Signal finalize and await the interception result. Token refresh always navigates directly (no guide grid), so the first manifest is correct.
|
|
233
|
-
handle.finalize(true);
|
|
234
|
-
const newInterception = await handle.promise;
|
|
345
|
+
// Re-establish the channel through the streaming layer's capability: the same tune machinery that established this stream navigates, selects, and
|
|
346
|
+
// adjudicates, so the interception below carries the tuned channel's manifest rather than whatever the reloaded page produced first. A null means the
|
|
347
|
+
// re-establishment failed or was rejected by the provider verifier; the refresh fails with it and the monitor's ladder owns the escalation.
|
|
348
|
+
const newInterception = await reestablishManifest(page);
|
|
235
349
|
if (!newInterception) {
|
|
236
|
-
streamLog.debug("native:token", "Manifest refresh failed for %s:
|
|
350
|
+
streamLog.debug("native:token", "Manifest refresh failed for %s: the channel could not be re-established.", channelName);
|
|
237
351
|
return false;
|
|
238
352
|
}
|
|
239
|
-
// Check if the proxy was stopped while we were waiting for the interception.
|
|
353
|
+
// Check if the proxy was stopped while we were waiting for the interception. The interceptor has already disposed its observer on resolution; no further
|
|
354
|
+
// cleanup is required.
|
|
240
355
|
if (proxy.isStopped()) {
|
|
241
|
-
removeManifestInterceptor(newInterception.cdpSession);
|
|
242
356
|
return false;
|
|
243
357
|
}
|
|
244
|
-
// Probe the new manifest to get the updated variant URL.
|
|
245
|
-
//
|
|
246
|
-
const refreshedFeed = await probeManifest(newInterception.
|
|
358
|
+
// Probe the new manifest to get the updated variant URL. The interceptor has already released its observer by the time the promise resolves, so a probe
|
|
359
|
+
// failure here only requires giving up on this refresh attempt - no session bookkeeping to unwind.
|
|
360
|
+
const refreshedFeed = await probeManifest(newInterception.manifestUrl, probeIdentity, { pipelineShape: proxy.getPipelineShape() });
|
|
247
361
|
if (!refreshedFeed) {
|
|
248
362
|
streamLog.debug("native:token", "Manifest refresh failed for %s: probe failed on new manifest.", channelName);
|
|
249
|
-
removeManifestInterceptor(newInterception.cdpSession);
|
|
250
363
|
return false;
|
|
251
364
|
}
|
|
252
|
-
// Check if the proxy was stopped during the probe.
|
|
365
|
+
// Check if the proxy was stopped during the probe.
|
|
253
366
|
if (proxy.isStopped()) {
|
|
254
|
-
removeManifestInterceptor(newInterception.cdpSession);
|
|
255
367
|
return false;
|
|
256
368
|
}
|
|
257
|
-
//
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
proxy.updateAudioVariantUrl(refreshedFeed.audioVariantUrl);
|
|
369
|
+
// Apply the fresh feed, carrying the NEW master URL from this interception forward: subsequent direct fetches use it until it expires, and the next boundary
|
|
370
|
+
// is the earlier of its expiry and the freshly-probed variant's.
|
|
371
|
+
const applied = applyRefreshedFeed(refreshedFeed, newInterception.manifestUrl, options, streamLog);
|
|
372
|
+
if (applied) {
|
|
373
|
+
streamLog.debug("native:token", "Manifest refresh completed for %s via page reload in %sms.", channelName, refreshElapsed());
|
|
263
374
|
}
|
|
264
|
-
|
|
265
|
-
// Schedule the next proactive token refresh with the NEW master URL from the fresh interception. Subsequent direct fetches will use this URL until it expires.
|
|
266
|
-
scheduleTokenRefresh({
|
|
267
|
-
channelName,
|
|
268
|
-
masterUrl: newInterception.masterManifestUrl,
|
|
269
|
-
page,
|
|
270
|
-
proxy,
|
|
271
|
-
streamIdStr,
|
|
272
|
-
url
|
|
273
|
-
});
|
|
274
|
-
return true;
|
|
375
|
+
return applied;
|
|
275
376
|
}
|
|
276
377
|
catch (error) {
|
|
277
378
|
streamLog.debug("native:token", "Manifest refresh failed for %s: %s.", channelName, formatError(error));
|
|
278
|
-
// Clear the probe cache so a subsequent attempt re-probes.
|
|
279
|
-
|
|
379
|
+
// Clear the probe cache so a subsequent attempt re-probes. The clear addresses the stream's own identity, which is what every probe on this stream reads and
|
|
380
|
+
// writes under.
|
|
381
|
+
clearProbeCache(probeIdentity.key);
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Applies an accepted refresh to the running proxy and schedules the next one. Both strategies converge here, so the sequence a successful refresh performs - the
|
|
387
|
+
* URL swaps, the quality report, the reschedule - has exactly one home, and a future check on whether the refreshed feed still describes the same channel has one
|
|
388
|
+
* place to live.
|
|
389
|
+
*
|
|
390
|
+
* @param feed - The accepted feed, already selected against the proxy's pipeline shape.
|
|
391
|
+
* @param masterUrl - The master URL the next schedule should carry, which is the caller's to name: a direct fetch reuses the master it re-fetched, a page reload
|
|
392
|
+
* carries the one its fresh interception produced.
|
|
393
|
+
* @param options - The refresh options this cycle was invoked with, threaded into the reschedule so the chain outlives this cycle.
|
|
394
|
+
* @param streamLog - The stream-scoped logger.
|
|
395
|
+
* @returns True when the feed was applied, false when it was declined.
|
|
396
|
+
*/
|
|
397
|
+
function applyRefreshedFeed(feed, masterUrl, options, streamLog) {
|
|
398
|
+
const { channelName, onFeedApplied, page, probeIdentity, proxy, reestablishManifest, streamIdStr, url } = options;
|
|
399
|
+
/* A separate-audio pipeline polls two manifests, so it must be handed both URLs or neither: a video URL swapped without its audio would leave the two tracks
|
|
400
|
+
* on different CDN sessions. Selection admits only candidates whose audio topology matches this pipeline's, so an accepted feed arriving here without a
|
|
401
|
+
* rendition contradicts the selection that produced it - the refresh declines the whole application rather than performing half of it. Declining rather than
|
|
402
|
+
* throwing keeps a background cycle survivable: the false return is the same outcome every other refusal produces, and the caller already handles it.
|
|
403
|
+
*/
|
|
404
|
+
const { separateAudio } = proxy.getPipelineShape();
|
|
405
|
+
const audioVariantUrl = separateAudio ? feed.audioVariantUrl : null;
|
|
406
|
+
if (separateAudio && (audioVariantUrl === null)) {
|
|
407
|
+
streamLog.debug("native:token", "Declining the refreshed feed for %s: it carries no audio rendition for a pipeline whose audio is a separate one.", channelName);
|
|
280
408
|
return false;
|
|
281
409
|
}
|
|
410
|
+
proxy.updateVariantUrl(feed.bestVariantUrl);
|
|
411
|
+
if (audioVariantUrl !== null) {
|
|
412
|
+
proxy.updateAudioVariantUrl(audioVariantUrl);
|
|
413
|
+
}
|
|
414
|
+
/* Report the fresh quality facts upward. The callback belongs to the streaming layer, which owns the registry entry these facts are written to, and its
|
|
415
|
+
* failure must not undo an application that has already happened: a swap that succeeded is worth more than the status display it feeds, so a throw is warned
|
|
416
|
+
* about and the refresh still counts as the success it was.
|
|
417
|
+
*/
|
|
418
|
+
if (onFeedApplied) {
|
|
419
|
+
try {
|
|
420
|
+
onFeedApplied({ bandwidth: feed.bandwidth, codec: feed.codec, resolution: feed.resolution });
|
|
421
|
+
}
|
|
422
|
+
catch (error) {
|
|
423
|
+
streamLog.warn("The refreshed stream quality could not be recorded for %s: %s.", channelName, formatError(error));
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
scheduleTokenRefresh({
|
|
427
|
+
channelName,
|
|
428
|
+
masterUrl,
|
|
429
|
+
onFeedApplied,
|
|
430
|
+
page,
|
|
431
|
+
probeIdentity,
|
|
432
|
+
proxy,
|
|
433
|
+
reestablishManifest,
|
|
434
|
+
streamIdStr,
|
|
435
|
+
url,
|
|
436
|
+
variantUrl: feed.bestVariantUrl
|
|
437
|
+
});
|
|
438
|
+
return true;
|
|
282
439
|
}
|
|
283
440
|
/**
|
|
284
441
|
* Attempts to refresh the manifest by directly fetching the master manifest URL from Node.js. Returns a fresh MediaFeed if the fetch succeeds and the variant URL
|
|
285
442
|
* has sufficient token lifetime remaining, or null if the direct fetch should be abandoned in favor of a page reload.
|
|
286
443
|
*
|
|
287
444
|
* @param masterUrl - The master manifest URL to re-fetch.
|
|
288
|
-
* @param
|
|
445
|
+
* @param pipelineShape - The running proxy's compatibility envelope, which the probe selects within.
|
|
446
|
+
* @param probeIdentity - The stream's probe-cache identity. The master URL passing through here carries session tokens that rotate on every refresh, so it is
|
|
447
|
+
* never what the cache is keyed or stamped by; the stream's own identity is.
|
|
289
448
|
* @param streamLog - The stream-scoped logger.
|
|
290
449
|
* @returns A MediaFeed with a fresh variant URL, or null on failure.
|
|
291
450
|
*/
|
|
292
|
-
async function tryDirectManifestRefresh(masterUrl,
|
|
293
|
-
const mediaFeed = await probeManifest(masterUrl,
|
|
294
|
-
|
|
451
|
+
async function tryDirectManifestRefresh(masterUrl, pipelineShape, probeIdentity, streamLog) {
|
|
452
|
+
const mediaFeed = await probeManifest(masterUrl, probeIdentity, { pipelineShape });
|
|
453
|
+
// A null feed is the only refusal to read here. A constrained probe hands back nothing the running pipeline cannot absorb - a mismatched encryption kind,
|
|
454
|
+
// container, or audio topology comes back as null - so the admission question is answered before this line, in the one place that can answer it completely.
|
|
455
|
+
if (!mediaFeed) {
|
|
295
456
|
return null;
|
|
296
457
|
}
|
|
297
458
|
// Verify the variant URL's token hasn't already expired or is about to expire. Parse the expiry from the variant URL (not the master URL) since that's what the
|
|
@@ -301,7 +462,7 @@ async function tryDirectManifestRefresh(masterUrl, channelName, streamLog) {
|
|
|
301
462
|
if (variantExpiry) {
|
|
302
463
|
const remaining = variantExpiry - Date.now();
|
|
303
464
|
if (remaining < MIN_USABLE_TOKEN_LIFETIME) {
|
|
304
|
-
streamLog.debug("native:token", "Direct fetch returned near-expired variant for %s (%ss remaining). Discarding.",
|
|
465
|
+
streamLog.debug("native:token", "Direct fetch returned near-expired variant for %s (%ss remaining). Discarding.", probeIdentity.key, Math.round(remaining / 1000));
|
|
305
466
|
return null;
|
|
306
467
|
}
|
|
307
468
|
}
|