prismcast 1.8.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/app.d.ts +1 -1
- package/dist/app.js +41 -13
- package/dist/app.js.map +1 -1
- package/dist/browser/cdp.d.ts +4 -6
- package/dist/browser/cdp.js +18 -22
- package/dist/browser/cdp.js.map +1 -1
- package/dist/browser/channelSelection.d.ts +41 -11
- package/dist/browser/channelSelection.js +75 -24
- package/dist/browser/channelSelection.js.map +1 -1
- package/dist/browser/display.d.ts +1 -1
- package/dist/browser/index.d.ts +8 -8
- package/dist/browser/index.js +44 -45
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/login.d.ts +2 -2
- package/dist/browser/login.js +1 -1
- package/dist/browser/manifestInterceptor.d.ts +77 -0
- package/dist/browser/manifestInterceptor.js +323 -0
- package/dist/browser/manifestInterceptor.js.map +1 -0
- package/dist/browser/precaching.d.ts +1 -1
- package/dist/browser/precaching.js +23 -23
- package/dist/browser/precaching.js.map +1 -1
- package/dist/browser/tuning/comcastPolymer.d.ts +1 -1
- package/dist/browser/tuning/cox.d.ts +1 -1
- package/dist/browser/tuning/directv.d.ts +1 -1
- package/dist/browser/tuning/directv.js +65 -69
- package/dist/browser/tuning/directv.js.map +1 -1
- package/dist/browser/tuning/fox.d.ts +1 -1
- package/dist/browser/tuning/fox.js +172 -15
- package/dist/browser/tuning/fox.js.map +1 -1
- package/dist/browser/tuning/hbo.d.ts +1 -1
- package/dist/browser/tuning/hbo.js +7 -7
- package/dist/browser/tuning/hbo.js.map +1 -1
- package/dist/browser/tuning/hulu.d.ts +1 -1
- package/dist/browser/tuning/hulu.js +120 -113
- package/dist/browser/tuning/hulu.js.map +1 -1
- package/dist/browser/tuning/shared.d.ts +3 -3
- package/dist/browser/tuning/shared.js +5 -5
- package/dist/browser/tuning/sling.d.ts +1 -1
- package/dist/browser/tuning/sling.js +30 -30
- package/dist/browser/tuning/sling.js.map +1 -1
- package/dist/browser/tuning/spectrum.d.ts +1 -1
- package/dist/browser/tuning/spectrum.js +21 -22
- package/dist/browser/tuning/spectrum.js.map +1 -1
- package/dist/browser/tuning/thumbnailRow.d.ts +1 -1
- package/dist/browser/tuning/thumbnailRow.js +1 -1
- package/dist/browser/tuning/tileClick.d.ts +1 -1
- package/dist/browser/tuning/tileClick.js +5 -5
- package/dist/browser/tuning/xfinity.d.ts +1 -1
- package/dist/browser/tuning/youtubeTv.d.ts +1 -1
- package/dist/browser/tuning/youtubeTv.js +17 -18
- package/dist/browser/tuning/youtubeTv.js.map +1 -1
- package/dist/browser/video.d.ts +20 -6
- package/dist/browser/video.js +30 -38
- package/dist/browser/video.js.map +1 -1
- package/dist/channels/index.d.ts +1 -2
- package/dist/channels/index.js +475 -442
- package/dist/channels/index.js.map +1 -1
- package/dist/config/channelForm.d.ts +58 -0
- package/dist/config/channelForm.js +129 -0
- package/dist/config/channelForm.js.map +1 -0
- package/dist/config/consistencyProbe.d.ts +7 -0
- package/dist/config/consistencyProbe.js +143 -0
- package/dist/config/consistencyProbe.js.map +1 -0
- package/dist/config/health.d.ts +7 -6
- package/dist/config/health.js +78 -65
- package/dist/config/health.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +21 -14
- package/dist/config/index.js.map +1 -1
- package/dist/config/paths.d.ts +9 -3
- package/dist/config/paths.js +12 -4
- package/dist/config/paths.js.map +1 -1
- package/dist/config/persistence.context.d.ts +8 -0
- package/dist/config/persistence.context.js +30 -0
- package/dist/config/persistence.context.js.map +1 -0
- package/dist/config/persistence.d.ts +157 -0
- package/dist/config/persistence.js +446 -0
- package/dist/config/persistence.js.map +1 -0
- package/dist/config/presets.d.ts +14 -4
- package/dist/config/presets.js +84 -75
- package/dist/config/presets.js.map +1 -1
- package/dist/config/profiles.d.ts +2 -2
- package/dist/config/profiles.js +7 -9
- package/dist/config/profiles.js.map +1 -1
- package/dist/config/servicePacks.d.ts +46 -0
- package/dist/config/{providerPacks.js → servicePacks.js} +50 -53
- package/dist/config/servicePacks.js.map +1 -0
- package/dist/config/services.d.ts +228 -0
- package/dist/config/services.js +772 -0
- package/dist/config/services.js.map +1 -0
- package/dist/config/sites.d.ts +3 -3
- package/dist/config/sites.js +75 -75
- package/dist/config/sites.js.map +1 -1
- package/dist/config/userChannels.d.ts +522 -54
- package/dist/config/userChannels.js +1343 -366
- package/dist/config/userChannels.js.map +1 -1
- package/dist/config/userConfig.d.ts +108 -13
- package/dist/config/userConfig.js +296 -143
- package/dist/config/userConfig.js.map +1 -1
- package/dist/config/userProfiles.d.ts +34 -16
- package/dist/config/userProfiles.js +189 -100
- package/dist/config/userProfiles.js.map +1 -1
- package/dist/hdhr/channelMap.d.ts +1 -1
- package/dist/hdhr/channelMap.js +10 -16
- package/dist/hdhr/channelMap.js.map +1 -1
- package/dist/hdhr/deviceId.js +10 -9
- package/dist/hdhr/deviceId.js.map +1 -1
- package/dist/hdhr/discover.js +4 -5
- package/dist/hdhr/discover.js.map +1 -1
- package/dist/hdhr/index.js +12 -19
- package/dist/hdhr/index.js.map +1 -1
- package/dist/index.js +12 -12
- package/dist/index.js.map +1 -1
- package/dist/native/codecInference.d.ts +44 -0
- package/dist/native/codecInference.js +327 -0
- package/dist/native/codecInference.js.map +1 -0
- package/dist/native/decrypt.d.ts +1 -1
- package/dist/native/index.d.ts +5 -5
- package/dist/native/index.js +45 -67
- package/dist/native/index.js.map +1 -1
- package/dist/native/probe.d.ts +27 -9
- package/dist/native/probe.js +159 -68
- package/dist/native/probe.js.map +1 -1
- package/dist/native/proxy.d.ts +4 -2
- package/dist/native/proxy.js +56 -53
- package/dist/native/proxy.js.map +1 -1
- package/dist/native/tokenExpiry.d.ts +22 -0
- package/dist/native/tokenExpiry.js +153 -0
- package/dist/native/tokenExpiry.js.map +1 -0
- package/dist/routes/assets.js +3 -3
- package/dist/routes/assets.js.map +1 -1
- package/dist/routes/auth.js +9 -13
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/components.js +2 -2
- package/dist/routes/components.js.map +1 -1
- package/dist/routes/config/channels/endpoints/browse.d.ts +6 -0
- package/dist/routes/config/channels/endpoints/browse.js +195 -0
- package/dist/routes/config/channels/endpoints/browse.js.map +1 -0
- package/dist/routes/config/channels/endpoints/bulk.d.ts +6 -0
- package/dist/routes/config/channels/endpoints/bulk.js +128 -0
- package/dist/routes/config/channels/endpoints/bulk.js.map +1 -0
- package/dist/routes/config/channels/endpoints/crud.d.ts +6 -0
- package/dist/routes/config/channels/endpoints/crud.js +504 -0
- package/dist/routes/config/channels/endpoints/crud.js.map +1 -0
- package/dist/routes/config/channels/endpoints/importExport.d.ts +6 -0
- package/dist/routes/config/channels/endpoints/importExport.js +131 -0
- package/dist/routes/config/channels/endpoints/importExport.js.map +1 -0
- package/dist/routes/config/channels/endpoints/predefined.d.ts +6 -0
- package/dist/routes/config/channels/endpoints/predefined.js +78 -0
- package/dist/routes/config/channels/endpoints/predefined.js.map +1 -0
- package/dist/routes/config/channels/endpoints/prefs.d.ts +6 -0
- package/dist/routes/config/channels/endpoints/prefs.js +58 -0
- package/dist/routes/config/channels/endpoints/prefs.js.map +1 -0
- package/dist/routes/config/channels/endpoints/service.d.ts +6 -0
- package/dist/routes/config/channels/endpoints/service.js +149 -0
- package/dist/routes/config/channels/endpoints/service.js.map +1 -0
- package/dist/routes/config/channels/endpoints/tags.d.ts +6 -0
- package/dist/routes/config/channels/endpoints/tags.js +185 -0
- package/dist/routes/config/channels/endpoints/tags.js.map +1 -0
- package/dist/routes/config/channels/http/handler.d.ts +10 -0
- package/dist/routes/config/channels/http/handler.js +20 -0
- package/dist/routes/config/channels/http/handler.js.map +1 -0
- package/dist/routes/config/channels/http/playlistHint.d.ts +25 -0
- package/dist/routes/config/channels/http/playlistHint.js +40 -0
- package/dist/routes/config/channels/http/playlistHint.js.map +1 -0
- package/dist/routes/config/channels/http/serviceWarning.d.ts +10 -0
- package/dist/routes/config/channels/http/serviceWarning.js +28 -0
- package/dist/routes/config/channels/http/serviceWarning.js.map +1 -0
- package/dist/routes/config/channels/index.d.ts +3 -3
- package/dist/routes/config/channels/index.js +2 -2
- package/dist/routes/config/channels/index.js.map +1 -1
- package/dist/routes/config/channels/{routes.d.ts → setup.d.ts} +1 -1
- package/dist/routes/config/channels/setup.js +23 -0
- package/dist/routes/config/channels/setup.js.map +1 -0
- package/dist/routes/config/channels/table.d.ts +13 -13
- package/dist/routes/config/channels/table.js +291 -184
- package/dist/routes/config/channels/table.js.map +1 -1
- package/dist/routes/config/http/envelope.d.ts +111 -0
- package/dist/routes/config/http/envelope.js +103 -0
- package/dist/routes/config/http/envelope.js.map +1 -0
- package/dist/routes/config/index.d.ts +9 -9
- package/dist/routes/config/index.js +5 -5
- package/dist/routes/config/index.js.map +1 -1
- package/dist/routes/config/{providers.js → services.js} +105 -99
- package/dist/routes/config/services.js.map +1 -0
- package/dist/routes/config/settings.d.ts +1 -1
- package/dist/routes/config/settings.js +85 -60
- package/dist/routes/config/settings.js.map +1 -1
- package/dist/routes/debug.js +17 -18
- package/dist/routes/debug.js.map +1 -1
- package/dist/routes/health.js +1 -1
- package/dist/routes/health.js.map +1 -1
- package/dist/routes/icons.js +2 -2
- package/dist/routes/icons.js.map +1 -1
- package/dist/routes/index.js +2 -2
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/logs.js +23 -34
- package/dist/routes/logs.js.map +1 -1
- package/dist/routes/mpegts.js +1 -1
- package/dist/routes/playlist.d.ts +5 -5
- package/dist/routes/playlist.js +84 -61
- package/dist/routes/playlist.js.map +1 -1
- package/dist/routes/root/content.js +82 -61
- package/dist/routes/root/content.js.map +1 -1
- package/dist/routes/root/index.js +2 -5
- package/dist/routes/root/index.js.map +1 -1
- package/dist/routes/root/scripts/channels.js +538 -581
- package/dist/routes/root/scripts/channels.js.map +1 -1
- package/dist/routes/root/scripts/config.js +868 -1022
- package/dist/routes/root/scripts/config.js.map +1 -1
- package/dist/routes/root/scripts/index.d.ts +4 -4
- package/dist/routes/root/scripts/shared.js +445 -197
- package/dist/routes/root/scripts/shared.js.map +1 -1
- package/dist/routes/root/scripts/status.d.ts +4 -0
- package/dist/routes/root/scripts/status.handlers.d.ts +182 -0
- package/dist/routes/root/scripts/status.handlers.js +713 -0
- package/dist/routes/root/scripts/status.handlers.js.map +1 -0
- package/dist/routes/root/scripts/status.js +85 -436
- package/dist/routes/root/scripts/status.js.map +1 -1
- package/dist/routes/root/styles.js +16 -8
- package/dist/routes/root/styles.js.map +1 -1
- package/dist/routes/services.d.ts +6 -0
- package/dist/routes/{providers.js → services.js} +58 -55
- package/dist/routes/services.js.map +1 -0
- package/dist/routes/sse.d.ts +16 -0
- package/dist/routes/sse.js +29 -0
- package/dist/routes/sse.js.map +1 -0
- package/dist/routes/streams.js +10 -23
- package/dist/routes/streams.js.map +1 -1
- package/dist/routes/ui.d.ts +2 -2
- package/dist/routes/ui.js +2 -0
- package/dist/routes/ui.js.map +1 -1
- package/dist/routes/upgrade.js +6 -7
- package/dist/routes/upgrade.js.map +1 -1
- package/dist/service/commands.context.d.ts +6 -0
- package/dist/service/commands.context.js +47 -0
- package/dist/service/commands.context.js.map +1 -0
- package/dist/service/commands.d.ts +53 -8
- package/dist/service/commands.js +172 -192
- package/dist/service/commands.js.map +1 -1
- package/dist/service/generators.context.d.ts +6 -0
- package/dist/service/generators.context.js +45 -0
- package/dist/service/generators.context.js.map +1 -0
- package/dist/service/generators.d.ts +56 -16
- package/dist/service/generators.js +509 -291
- package/dist/service/generators.js.map +1 -1
- package/dist/service/index.d.ts +2 -2
- package/dist/streaming/clients.d.ts +2 -2
- package/dist/streaming/clients.js +2 -2
- package/dist/streaming/codec.d.ts +2 -2
- package/dist/streaming/codec.js +0 -1
- package/dist/streaming/codec.js.map +1 -1
- package/dist/streaming/fmp4Segmenter.d.ts +2 -2
- package/dist/streaming/fmp4Segmenter.js +12 -12
- package/dist/streaming/hls.d.ts +7 -7
- package/dist/streaming/hls.js +104 -92
- package/dist/streaming/hls.js.map +1 -1
- package/dist/streaming/hlsResume.d.ts +3 -3
- package/dist/streaming/hlsResume.js +6 -6
- package/dist/streaming/hlsResume.js.map +1 -1
- package/dist/streaming/hlsSegments.d.ts +5 -2
- package/dist/streaming/hlsSegments.js +14 -9
- package/dist/streaming/hlsSegments.js.map +1 -1
- package/dist/streaming/monitor.d.ts +12 -12
- package/dist/streaming/monitor.js +98 -66
- package/dist/streaming/monitor.js.map +1 -1
- package/dist/streaming/mp4Parser.d.ts +34 -4
- package/dist/streaming/mp4Parser.js +111 -29
- package/dist/streaming/mp4Parser.js.map +1 -1
- package/dist/streaming/mpegts.d.ts +1 -1
- package/dist/streaming/mpegts.js +13 -10
- package/dist/streaming/mpegts.js.map +1 -1
- package/dist/streaming/playlistBuilder.d.ts +1 -1
- package/dist/streaming/playlistBuilder.js +2 -2
- package/dist/streaming/preroll.d.ts +9 -9
- package/dist/streaming/preroll.js +40 -42
- package/dist/streaming/preroll.js.map +1 -1
- package/dist/streaming/pretune.js +4 -4
- package/dist/streaming/pretune.js.map +1 -1
- package/dist/streaming/recovery.d.ts +3 -3
- package/dist/streaming/recovery.js +4 -4
- package/dist/streaming/registry.d.ts +6 -6
- package/dist/streaming/registry.js +4 -8
- package/dist/streaming/registry.js.map +1 -1
- package/dist/streaming/setup.d.ts +11 -10
- package/dist/streaming/setup.js +120 -71
- package/dist/streaming/setup.js.map +1 -1
- package/dist/streaming/showInfo.d.ts +9 -3
- package/dist/streaming/showInfo.js +33 -27
- package/dist/streaming/showInfo.js.map +1 -1
- package/dist/streaming/statusEmitter.d.ts +8 -7
- package/dist/streaming/statusEmitter.js +5 -5
- package/dist/streaming/statusEmitter.js.map +1 -1
- package/dist/types/channels.d.ts +139 -36
- package/dist/types/channels.js +39 -1
- package/dist/types/channels.js.map +1 -1
- package/dist/types/config.d.ts +22 -3
- package/dist/types/index.d.ts +9 -8
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/profiles.d.ts +14 -13
- package/dist/types/selection.d.ts +49 -5
- package/dist/types/shared.d.ts +1 -1
- package/dist/types/streaming.d.ts +2 -2
- package/dist/upgrade/commands.context.d.ts +6 -0
- package/dist/upgrade/commands.context.js +29 -0
- package/dist/upgrade/commands.context.js.map +1 -0
- package/dist/upgrade/commands.d.ts +29 -2
- package/dist/upgrade/commands.js +87 -134
- package/dist/upgrade/commands.js.map +1 -1
- package/dist/upgrade/detection.context.d.ts +6 -0
- package/dist/upgrade/detection.context.js +24 -0
- package/dist/upgrade/detection.context.js.map +1 -0
- package/dist/upgrade/detection.d.ts +140 -10
- package/dist/upgrade/detection.js +153 -66
- package/dist/upgrade/detection.js.map +1 -1
- package/dist/upgrade/index.d.ts +2 -2
- package/dist/utils/chromeFetch.d.ts +1 -1
- package/dist/utils/chromeFetch.js +2 -2
- package/dist/utils/clock.d.ts +14 -0
- package/dist/utils/clock.js +28 -0
- package/dist/utils/clock.js.map +1 -0
- package/dist/utils/debugFilter.d.ts +1 -1
- package/dist/utils/debugFilter.js +8 -6
- package/dist/utils/debugFilter.js.map +1 -1
- package/dist/utils/delay.d.ts +2 -3
- package/dist/utils/delay.js +10 -16
- package/dist/utils/delay.js.map +1 -1
- package/dist/utils/evaluate.js +12 -14
- package/dist/utils/evaluate.js.map +1 -1
- package/dist/utils/ffmpeg.context.d.ts +6 -0
- package/dist/utils/ffmpeg.context.js +31 -0
- package/dist/utils/ffmpeg.context.js.map +1 -0
- package/dist/utils/ffmpeg.d.ts +85 -29
- package/dist/utils/ffmpeg.js +126 -129
- package/dist/utils/ffmpeg.js.map +1 -1
- package/dist/utils/fileLogger.d.ts +18 -2
- package/dist/utils/fileLogger.js +60 -31
- package/dist/utils/fileLogger.js.map +1 -1
- package/dist/utils/format.d.ts +7 -1
- package/dist/utils/format.js +22 -2
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/html.js +1 -3
- package/dist/utils/html.js.map +1 -1
- package/dist/utils/index.d.ts +21 -20
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/logger.js +31 -45
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/m3u.d.ts +18 -3
- package/dist/utils/m3u.js +28 -14
- package/dist/utils/m3u.js.map +1 -1
- package/dist/utils/memo.d.ts +19 -0
- package/dist/utils/memo.js +53 -0
- package/dist/utils/memo.js.map +1 -0
- package/dist/utils/morganStream.js +4 -4
- package/dist/utils/morganStream.js.map +1 -1
- package/dist/utils/pid.d.ts +2 -2
- package/dist/utils/pid.js +2 -2
- package/dist/utils/platform.d.ts +1 -1
- package/dist/utils/platform.js +3 -2
- package/dist/utils/platform.js.map +1 -1
- package/dist/utils/retry.d.ts +2 -0
- package/dist/utils/retry.js +9 -7
- package/dist/utils/retry.js.map +1 -1
- package/dist/utils/sanitize.d.ts +2 -2
- package/dist/utils/sanitize.js +4 -4
- package/dist/utils/streamContext.d.ts +7 -0
- package/dist/utils/streamContext.js +10 -1
- package/dist/utils/streamContext.js.map +1 -1
- package/dist/utils/timing.d.ts +6 -3
- package/dist/utils/timing.js +10 -6
- package/dist/utils/timing.js.map +1 -1
- package/dist/utils/version.d.ts +3 -2
- package/dist/utils/version.js +15 -28
- package/dist/utils/version.js.map +1 -1
- package/package.json +14 -10
- package/dist/config/providerPacks.d.ts +0 -46
- package/dist/config/providerPacks.js.map +0 -1
- package/dist/config/providers.d.ts +0 -175
- package/dist/config/providers.js +0 -691
- package/dist/config/providers.js.map +0 -1
- package/dist/native/intercept.d.ts +0 -35
- package/dist/native/intercept.js +0 -184
- package/dist/native/intercept.js.map +0 -1
- package/dist/routes/config/channels/routes.js +0 -1477
- package/dist/routes/config/channels/routes.js.map +0 -1
- package/dist/routes/config/providers.js.map +0 -1
- package/dist/routes/providers.d.ts +0 -6
- package/dist/routes/providers.js.map +0 -1
- /package/dist/routes/config/{providers.d.ts → services.d.ts} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../../src/routes/root/scripts/status.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../../src/routes/root/scripts/status.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE5E;;;GAGG;AACH,MAAM,UAAU,oBAAoB;IAElC,oJAAoJ;IACpJ,mEAAmE;IACnE,MAAM,aAAa,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,GAAG,CAAC,CAAC,IAAI,GAAG,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEzH,+JAA+J;IAC/J,0JAA0J;IAC1J,sCAAsC;IACtC,MAAM,cAAc,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAEjF,6JAA6J;IAC7J,uJAAuJ;IACvJ,qJAAqJ;IACrJ,4JAA4J;IAC5J,2JAA2J;IAC3J,2BAA2B;IAC3B,MAAM,IAAI,GAAG;QAEX,eAAe;QACf,mBAAmB;QACnB,0BAA0B;QAC1B,qBAAqB;QACrB,sCAAsC;QACtC,kCAAkC;QAClC,qBAAqB;QACrB,uBAAuB;QACvB,+BAA+B;QAC/B,MAAM;QACN,uBAAuB;QACvB,6CAA6C;QAC7C,iCAAiC;QACjC,uCAAuC;QACvC,2BAA2B;QAC3B,4IAA4I;QAC5I,MAAM;QACN,2EAA2E;QAC3E,iCAAiC;QACjC,iCAAiC;QACjC,0DAA0D;QAC1D,6DAA6D;QAC7D,6CAA6C;QAC7C,mCAAmC;QACnC,0DAA0D;QAC1D,iDAAiD;QACjD,qCAAqC;QACrC,WAAW;QACX,OAAO;QACP,sBAAsB;QACtB,qEAAqE;QACrE,2EAA2E;QAC3E,+EAA+E;QAC/E,2FAA2F;QAC3F,2FAA2F;QAC3F,+EAA+E;QAC/E,+EAA+E;QAC/E,4DAA4D;QAC5D,KAAK;QACL,gEAAgE;QAChE,sEAAsE;QACtE,wEAAwE;QACxE,uBAAuB;QACvB,kDAAkD;QAClD,uBAAuB;QACvB,kFAAkF;QAClF,cAAc;QACd,uGAAuG;QACvG,0BAA0B;QAC1B,OAAO;KACR,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,OAAO,CAAE,UAAU,EAAE,aAAa,EAAE,cAAc,EAAE,IAAI,EAAE,WAAW,CAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrF,CAAC"}
|
|
@@ -252,9 +252,17 @@ export function generateLandingPageStyles() {
|
|
|
252
252
|
// Responsive: hide Key and all optional columns on phones.
|
|
253
253
|
"@media (max-width: 768px) { .channel-table .col-key, .channel-table td:nth-child(1), .channel-table th:nth-child(1) { display: none; } " +
|
|
254
254
|
OPTIONAL_COLUMNS.map((col) => ".channel-table ." + col.cssClass).join(", ") + " { display: none; } }",
|
|
255
|
-
// User channel row tinting to
|
|
255
|
+
// User-created channel row tinting. Applies only to pure user-created channels (no predefined baseline). Overrides of predefined channels use the
|
|
256
|
+
// .channel-override treatment below instead, matching the settings form's "modified from defaults" visual language.
|
|
256
257
|
".channel-table tr.user-channel { background: var(--user-channel-tint); }",
|
|
257
258
|
".channel-table tr.user-channel:hover { background: var(--user-channel-tint-hover); }",
|
|
259
|
+
// Predefined channel override indicator. Left-border accent + blue dot on the first cell matches the settings form's .form-group.modified treatment
|
|
260
|
+
// (border + dot = "modified from defaults"). The border targets td:first-child because border-collapse: separate prevents borders on <tr> elements. The
|
|
261
|
+
// dot is a ::before pseudo-element on the same cell so it's always the first visual element in the row, right next to the border. No background tint - the
|
|
262
|
+
// border and dot are the sole differentiators, exactly like modified settings.
|
|
263
|
+
".channel-table tr.channel-override td:first-child { border-left: 3px solid var(--interactive-primary); }",
|
|
264
|
+
".channel-table tr.channel-override td:first-child::before { content: ''; display: inline-block; width: 8px; height: 8px; " +
|
|
265
|
+
"background: var(--interactive-primary); border-radius: 50%; margin-right: 6px; vertical-align: middle; }",
|
|
258
266
|
// Disabled predefined channel row styling and hide-disabled toggle.
|
|
259
267
|
".channel-table tr.channel-disabled { opacity: 0.5; }",
|
|
260
268
|
".channel-table tr.channel-disabled td { color: var(--text-tertiary); }",
|
|
@@ -265,7 +273,7 @@ export function generateLandingPageStyles() {
|
|
|
265
273
|
".channel-table tr.channel-unavailable td { color: var(--text-tertiary); }",
|
|
266
274
|
".channel-table tr.channel-unavailable.channel-disabled { opacity: 0.5; }",
|
|
267
275
|
".channel-table.hide-disabled tr.channel-unavailable { display: none; }",
|
|
268
|
-
// Tag pills
|
|
276
|
+
// Tag pills - rounded labels for tag display in the channel table, tag manager modal, and edit form. The pill shape provides clear visual identity and
|
|
269
277
|
// scannability across all surfaces where tags appear.
|
|
270
278
|
".tag-badge { display: inline-block; padding: 1px 7px; margin: 1px 2px; font-size: 11px; border-radius: 10px; " +
|
|
271
279
|
"background: var(--surface-elevated); border: 1px solid var(--border-default); color: var(--text-secondary); font-weight: 500; }",
|
|
@@ -275,7 +283,7 @@ export function generateLandingPageStyles() {
|
|
|
275
283
|
".tag-checkbox-label { cursor: pointer; display: inline-flex; align-items: center; gap: 3px; }",
|
|
276
284
|
".tag-checkbox-label input { margin: 0; }",
|
|
277
285
|
".tag-checkbox-label .tag-badge { cursor: pointer; }",
|
|
278
|
-
// Inline tag edit portal
|
|
286
|
+
// Inline tag edit portal - shared dropdown portaled to <body> via JS. The td has the dropdown class so the document click handler recognizes it as a dropdown
|
|
279
287
|
// context. The display override preserves table-cell layout (the .dropdown class defaults to display: inline-block for div-based dropdowns).
|
|
280
288
|
"td.dropdown { display: table-cell; }",
|
|
281
289
|
".inline-tag-menu { min-width: 140px; }",
|
|
@@ -283,7 +291,7 @@ export function generateLandingPageStyles() {
|
|
|
283
291
|
".tag-editable:hover { background: var(--surface-hover); }",
|
|
284
292
|
".tag-rename-input { font-size: 11px; padding: 1px 4px; border: 1px solid var(--interactive-primary); border-radius: 10px; " +
|
|
285
293
|
"outline: none; width: 120px; }",
|
|
286
|
-
// Tag manager modal
|
|
294
|
+
// Tag manager modal - list of tags with add/delete/restore actions. Hint and error styling use shared wizard classes.
|
|
287
295
|
".tag-manager-add { display: flex; gap: 8px; margin-bottom: 8px; }",
|
|
288
296
|
".tag-manager-add input { flex: 1; padding: 4px 8px; font-size: 13px; border: 1px solid var(--form-input-border); border-radius: 4px; }",
|
|
289
297
|
".tag-manager-list { display: flex; flex-direction: column; gap: 4px; }",
|
|
@@ -355,7 +363,7 @@ export function generateLandingPageStyles() {
|
|
|
355
363
|
// Health indicator colors. The login icon only uses health-success (provider verified). The health icon uses both (channel last-tune result).
|
|
356
364
|
".health-success { color: var(--interactive-success); }",
|
|
357
365
|
".health-failed { color: var(--interactive-delete); }",
|
|
358
|
-
// Health icon is non-interactive
|
|
366
|
+
// Health icon is non-interactive - no hover effect, default cursor. The .user-channel override needs matching specificity (0-3-0) to prevent the
|
|
359
367
|
// tinted hover background from making the health icon look clickable on user-defined channel rows.
|
|
360
368
|
".btn-icon-health { cursor: default; }",
|
|
361
369
|
".btn-icon-health:hover { background: transparent; color: var(--text-secondary); }",
|
|
@@ -374,8 +382,8 @@ export function generateLandingPageStyles() {
|
|
|
374
382
|
// Channel toolbar with dropdown menus. Each dropdown button has an inline SVG icon + label + chevron.
|
|
375
383
|
".channel-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 10px; margin-bottom: 10px; }",
|
|
376
384
|
".channel-toolbar .toolbar-group { display: flex; align-items: center; gap: 6px; }",
|
|
377
|
-
".toolbar-
|
|
378
|
-
".toolbar-
|
|
385
|
+
".toolbar-icon-btn { display: inline-flex; align-items: center; gap: 5px; }",
|
|
386
|
+
".toolbar-icon-btn svg { flex-shrink: 0; vertical-align: middle; }",
|
|
379
387
|
".channel-summary { text-align: center; font-size: 12px; color: var(--text-secondary); margin-bottom: 8px; }",
|
|
380
388
|
// Dropdown menu used by the Import button in the channel toolbar.
|
|
381
389
|
".dropdown { position: relative; display: inline-block; }",
|
|
@@ -433,7 +441,7 @@ export function generateLandingPageStyles() {
|
|
|
433
441
|
// Other landing page styles.
|
|
434
442
|
".endpoint code { font-size: 13px; }",
|
|
435
443
|
// Modified value indicator styling.
|
|
436
|
-
".form-group.modified { border-left: 3px solid var(--interactive-primary); padding-left: 12px; }",
|
|
444
|
+
".form-group.modified, .form-row.modified { border-left: 3px solid var(--interactive-primary); padding-left: 12px; }",
|
|
437
445
|
".modified-dot { display: inline-block; width: 8px; height: 8px; background: var(--interactive-primary); border-radius: 50%; margin-right: 6px; ",
|
|
438
446
|
"vertical-align: middle; }",
|
|
439
447
|
// Per-setting reset button styling.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles.js","sourceRoot":"","sources":["../../../src/routes/root/styles.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"styles.js","sourceRoot":"","sources":["../../../src/routes/root/styles.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { getChannelListing, getChannelLogo, isPredefinedChannel } from "../config/userChannels.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getChannelServiceLabel, getResolvedChannel, getServiceGroup, getServiceTagForChannel, isServiceTagEnabled, resolveServiceKey } from "../config/services.js";
|
|
3
3
|
import { getCurrentBrowser, minimizeBrowserWindow, registerManagedPage, unregisterManagedPage } from "../browser/index.js";
|
|
4
4
|
import { getProviderBySlug, normalizeChannelName } from "../browser/channelSelection.js";
|
|
5
|
+
import { sendError, sendNotFoundError } from "./config/http/envelope.js";
|
|
5
6
|
import { CONFIG } from "../config/index.js";
|
|
6
7
|
import { LOG } from "../utils/index.js";
|
|
7
|
-
/* The
|
|
8
|
-
* navigates to the
|
|
9
|
-
* page is always closed in a finally block to prevent resource leaks. Concurrent requests for the same
|
|
8
|
+
/* The services endpoint exposes channel discovery for each registered service. A GET request to /services/:slug/channels creates a temporary browser page,
|
|
9
|
+
* navigates to the service's guide, runs the service's discoverChannels implementation, and returns a sorted JSON array of discovered channels. The temporary
|
|
10
|
+
* page is always closed in a finally block to prevent resource leaks. Concurrent requests for the same service are coalesced - only one discovery walk runs at a
|
|
10
11
|
* time, and subsequent requests piggyback on the in-flight result. A refresh=true request aborts any in-flight discovery and starts fresh.
|
|
11
12
|
*/
|
|
12
13
|
// Sentinel error used to identify aborted discoveries in the retry loop. Distinguishes abort rejections from genuine discovery failures so the loop only retries
|
|
@@ -21,16 +22,18 @@ const inflight = new Map();
|
|
|
21
22
|
/**
|
|
22
23
|
* Logs a discovery failure and sends a 500 error response.
|
|
23
24
|
* @param res - The Express response object.
|
|
24
|
-
* @param label - The
|
|
25
|
+
* @param label - The service's display label for log messages.
|
|
25
26
|
* @param error - The error that caused the failure.
|
|
26
27
|
*/
|
|
27
28
|
function sendDiscoveryError(res, label, error) {
|
|
28
29
|
const message = (error instanceof Error) ? error.message : String(error);
|
|
30
|
+
// Channel discovery failures are warnings rather than errors because they typically reflect provider DOM drift, network blips, or transient site outages
|
|
31
|
+
// (recoverable by retry) rather than server-side faults. The envelope helper normalizes the response shape; the LOG.warn level is preserved deliberately.
|
|
29
32
|
LOG.warn("Channel discovery failed for %s: %s.", label, message);
|
|
30
|
-
res
|
|
33
|
+
sendError(res, 500, { error: "Channel discovery failed: " + message + "." });
|
|
31
34
|
}
|
|
32
35
|
/**
|
|
33
|
-
* Runs
|
|
36
|
+
* Runs service channel discovery in a temporary browser page. Opens a new page, navigates to the service's guide URL, runs the discovery function, and returns
|
|
34
37
|
* the sorted results. The page is always closed in a finally block. If the abort signal fires (from a refresh=true request), the page is closed mid-discovery,
|
|
35
38
|
* causing Puppeteer operations to throw and the promise to reject with a DiscoveryAbortError.
|
|
36
39
|
* @param provider - The provider module to discover channels for.
|
|
@@ -40,7 +43,7 @@ function sendDiscoveryError(res, label, error) {
|
|
|
40
43
|
async function runDiscovery(provider, signal) {
|
|
41
44
|
let page = null;
|
|
42
45
|
// Close the page when the abort signal fires. This causes any in-progress Puppeteer operations to throw, propagating the cancellation through the discovery
|
|
43
|
-
// function without requiring explicit signal checking in each provider's implementation. The finally block also closes the page unconditionally
|
|
46
|
+
// function without requiring explicit signal checking in each provider's implementation. The finally block also closes the page unconditionally - the
|
|
44
47
|
// redundant close is idempotent (caught by try/catch).
|
|
45
48
|
const onAbort = () => {
|
|
46
49
|
if (page) {
|
|
@@ -59,8 +62,8 @@ async function runDiscovery(provider, signal) {
|
|
|
59
62
|
if (signal.aborted) {
|
|
60
63
|
throw new DiscoveryAbortError();
|
|
61
64
|
}
|
|
62
|
-
// Navigate to the
|
|
63
|
-
// We use networkidle2 rather than load because SPA-based
|
|
65
|
+
// Navigate to the service's guide URL unless the provider handles its own navigation (e.g., Hulu and Sling set up response interception before navigating).
|
|
66
|
+
// We use networkidle2 rather than load because SPA-based services (e.g., Hulu) have heavy async initialization that can prevent the load event from firing
|
|
64
67
|
// reliably. Network idle ensures all initial API data has arrived before the discovery function reads the DOM.
|
|
65
68
|
if (!provider.handlesOwnNavigation) {
|
|
66
69
|
await page.goto(provider.guideUrl, { timeout: CONFIG.streaming.navigationTimeout, waitUntil: "networkidle2" });
|
|
@@ -94,28 +97,28 @@ async function runDiscovery(provider, signal) {
|
|
|
94
97
|
}
|
|
95
98
|
}
|
|
96
99
|
/**
|
|
97
|
-
* Annotates discovered channels with lineup state by matching each channel's channelSelector against the
|
|
98
|
-
* uses the existing
|
|
100
|
+
* Annotates discovered channels with lineup state by matching each channel's channelSelector against the service variants for the browsed service. This
|
|
101
|
+
* uses the existing service group system to find which canonical channel each discovered channel corresponds to, avoiding fragile name-to-key normalization
|
|
99
102
|
* (predefined keys are hand-crafted and may not match generateChannelKey output).
|
|
100
103
|
*
|
|
101
|
-
* For each canonical in the listing, we check for a variant matching the browsed
|
|
102
|
-
* getResolvedChannel. This builds a channelSelector
|
|
104
|
+
* For each canonical in the listing, we check for a variant matching the browsed service (via getServiceGroup) and extract its channelSelector via
|
|
105
|
+
* getResolvedChannel. This builds a channelSelector -> lineup state map that the discovered channels are matched against.
|
|
103
106
|
*
|
|
104
|
-
* @param channels - The raw discovered channels from the
|
|
105
|
-
* @param
|
|
107
|
+
* @param channels - The raw discovered channels from the service.
|
|
108
|
+
* @param serviceSlug - The slug of the service being browsed (e.g., "spectrum", "hulu").
|
|
106
109
|
* @returns Annotated channels with lineup state where applicable.
|
|
107
110
|
*/
|
|
108
|
-
function annotateWithLineupState(channels,
|
|
109
|
-
// Build a channelSelector
|
|
110
|
-
// to this
|
|
111
|
-
// normalized channel name (e.g., "axstv" vs generateChannelKey("AXS TV")
|
|
111
|
+
function annotateWithLineupState(channels, serviceSlug) {
|
|
112
|
+
// Build a channelSelector -> lineup state mapping for the browsed service. For each canonical entry in the listing, we find the variant that corresponds
|
|
113
|
+
// to this service (if one exists) and index by that variant's channelSelector. This ensures matching works even when predefined keys don't match the
|
|
114
|
+
// normalized channel name (e.g., "axstv" vs generateChannelKey("AXS TV") -> "axs-tv").
|
|
112
115
|
//
|
|
113
|
-
// When two canonicals share the same channelSelector for a
|
|
116
|
+
// When two canonicals share the same channelSelector for a service (East/Pacific pairs like "Disney Channel"), the map stores an array of states. The
|
|
114
117
|
// annotation step then uses the discovery entry's stationId to disambiguate which canonical to assign.
|
|
115
118
|
const listing = getChannelListing();
|
|
116
119
|
const bySelector = new Map();
|
|
117
120
|
// Appends a lineup state to the map under the normalized key. Normalization (lowercase, collapsed whitespace) ensures matching is resilient to casing
|
|
118
|
-
// differences between predefined channelSelectors and
|
|
121
|
+
// differences between predefined channelSelectors and service discovery output (e.g., "Cartoon Network (East)" vs "cartoon network (east)").
|
|
119
122
|
function indexState(key, state) {
|
|
120
123
|
const normalized = normalizeChannelName(key);
|
|
121
124
|
const existing = bySelector.get(normalized);
|
|
@@ -128,35 +131,35 @@ function annotateWithLineupState(channels, providerSlug) {
|
|
|
128
131
|
}
|
|
129
132
|
for (const entry of listing) {
|
|
130
133
|
const canonicalKey = entry.key;
|
|
131
|
-
const resolvedKey =
|
|
132
|
-
const currentTag =
|
|
133
|
-
const
|
|
134
|
+
const resolvedKey = resolveServiceKey(canonicalKey);
|
|
135
|
+
const currentTag = getServiceTagForChannel(resolvedKey);
|
|
136
|
+
const currentService = getChannelServiceLabel(entry.channel);
|
|
134
137
|
const displayName = entry.channel.name ?? canonicalKey;
|
|
135
138
|
const source = isPredefinedChannel(canonicalKey) ? "predefined" : "user";
|
|
136
|
-
// Check the
|
|
137
|
-
const group =
|
|
138
|
-
// Determine whether the channel has at least one enabled
|
|
139
|
-
// the group's variants. This uses the existing
|
|
140
|
-
const canonicalTag =
|
|
141
|
-
let hasAlternatives = (canonicalTag !==
|
|
139
|
+
// Check the service group for variants. Used both for channelSelector matching and for computing hasAlternatives.
|
|
140
|
+
const group = getServiceGroup(canonicalKey);
|
|
141
|
+
// Determine whether the channel has at least one enabled service besides the browsed service. Check the canonical's own tag first, then iterate
|
|
142
|
+
// the group's variants. This uses the existing service tag and filter infrastructure.
|
|
143
|
+
const canonicalTag = getServiceTagForChannel(canonicalKey);
|
|
144
|
+
let hasAlternatives = (canonicalTag !== serviceSlug) && isServiceTagEnabled(canonicalTag);
|
|
142
145
|
if (!hasAlternatives && group) {
|
|
143
146
|
for (const variant of group.variants) {
|
|
144
|
-
const variantTag =
|
|
145
|
-
if ((variantTag !==
|
|
147
|
+
const variantTag = getServiceTagForChannel(variant.key);
|
|
148
|
+
if ((variantTag !== serviceSlug) && isServiceTagEnabled(variantTag)) {
|
|
146
149
|
hasAlternatives = true;
|
|
147
150
|
break;
|
|
148
151
|
}
|
|
149
152
|
}
|
|
150
153
|
}
|
|
151
154
|
const state = {
|
|
152
|
-
canonicalKey,
|
|
155
|
+
canonicalKey, currentService, currentTag, displayName, enabled: entry.enabled, hasAlternatives,
|
|
153
156
|
logoUrl: getChannelLogo(canonicalKey),
|
|
154
157
|
source, stationId: entry.channel.stationId
|
|
155
158
|
};
|
|
156
|
-
// Find the variant matching the browsed
|
|
159
|
+
// Find the variant matching the browsed service and store by its channelSelector.
|
|
157
160
|
if (group) {
|
|
158
161
|
for (const variant of group.variants) {
|
|
159
|
-
if (
|
|
162
|
+
if (getServiceTagForChannel(variant.key) === serviceSlug) {
|
|
160
163
|
const variantChannel = getResolvedChannel(variant.key);
|
|
161
164
|
if (variantChannel?.channelSelector) {
|
|
162
165
|
indexState(variantChannel.channelSelector, state);
|
|
@@ -165,8 +168,8 @@ function annotateWithLineupState(channels, providerSlug) {
|
|
|
165
168
|
}
|
|
166
169
|
}
|
|
167
170
|
}
|
|
168
|
-
// Also match the canonical itself if its
|
|
169
|
-
if ((currentTag ===
|
|
171
|
+
// Also match the canonical itself if its service tag matches (single-service channels or canonicals that point directly to this service).
|
|
172
|
+
if ((currentTag === serviceSlug) && entry.channel.channelSelector) {
|
|
170
173
|
indexState(entry.channel.channelSelector, state);
|
|
171
174
|
}
|
|
172
175
|
// Index by display name when it differs from the channelSelector, so discovered channels whose names don't match any predefined channelSelector can still
|
|
@@ -176,7 +179,7 @@ function annotateWithLineupState(channels, providerSlug) {
|
|
|
176
179
|
indexState(displayName, state);
|
|
177
180
|
}
|
|
178
181
|
}
|
|
179
|
-
// Annotate each discovered channel by matching its channelSelector against the
|
|
182
|
+
// Annotate each discovered channel by matching its channelSelector against the service-specific lookup. When the channelSelector doesn't match (e.g.,
|
|
180
183
|
// Xfinity uses callSigns like "ESPND" while predefined variants use display names like "ESPN"), the display name is tried as a fallback. When multiple
|
|
181
184
|
// canonicals share the same key (East/Pacific pairs), the discovery entry's stationId disambiguates which canonical to assign.
|
|
182
185
|
return channels.map((ch) => {
|
|
@@ -184,33 +187,33 @@ function annotateWithLineupState(channels, providerSlug) {
|
|
|
184
187
|
if (!states || (states.length === 0)) {
|
|
185
188
|
return ch;
|
|
186
189
|
}
|
|
187
|
-
// Single match
|
|
190
|
+
// Single match - no disambiguation needed.
|
|
188
191
|
if (states.length === 1) {
|
|
189
192
|
return { ...ch, lineup: states[0] };
|
|
190
193
|
}
|
|
191
|
-
// Multiple matches
|
|
194
|
+
// Multiple matches - use stationId to pick the right canonical. Fall back to the first match if stationId doesn't disambiguate.
|
|
192
195
|
const match = states.find((s) => s.stationId === ch.stationId) ?? states[0];
|
|
193
196
|
return { ...ch, lineup: match };
|
|
194
197
|
});
|
|
195
198
|
}
|
|
196
199
|
/**
|
|
197
|
-
* Creates the
|
|
200
|
+
* Creates the service channel discovery endpoint.
|
|
198
201
|
* @param app - The Express application.
|
|
199
202
|
*/
|
|
200
|
-
export function
|
|
201
|
-
app.get("/
|
|
202
|
-
const slug = req.params
|
|
203
|
+
export function setupServicesEndpoint(app) {
|
|
204
|
+
app.get("/services/:slug/channels", async (req, res) => {
|
|
205
|
+
const slug = req.params["slug"];
|
|
203
206
|
const provider = getProviderBySlug(slug);
|
|
204
207
|
if (!provider) {
|
|
205
|
-
res
|
|
208
|
+
sendNotFoundError(res, "Unknown service: " + slug + ".");
|
|
206
209
|
return;
|
|
207
210
|
}
|
|
208
|
-
// When refresh=true is requested, clear the
|
|
209
|
-
// against fresh data. This also resets warm tuning state (watch URLs, GUIDs), but the discovery walk repopulates the unified cache before returning
|
|
210
|
-
// subsequent tune resolves from the freshly populated cache as normal. If a discovery is already in flight, abort it first
|
|
211
|
+
// When refresh=true is requested, clear the service's caches (unified channel cache, row caches, fully-enumerated flags, etc.) so the discovery walk runs
|
|
212
|
+
// against fresh data. This also resets warm tuning state (watch URLs, GUIDs), but the discovery walk repopulates the unified cache before returning - any
|
|
213
|
+
// subsequent tune resolves from the freshly populated cache as normal. If a discovery is already in flight, abort it first - clearing the cache while a
|
|
211
214
|
// discovery is progressively populating it would corrupt its state.
|
|
212
|
-
const lineup = req.query
|
|
213
|
-
const refresh = req.query
|
|
215
|
+
const lineup = req.query["lineup"] === "true";
|
|
216
|
+
const refresh = req.query["refresh"] === "true";
|
|
214
217
|
if (refresh) {
|
|
215
218
|
const existing = inflight.get(slug);
|
|
216
219
|
if (existing) {
|
|
@@ -219,7 +222,7 @@ export function setupProvidersEndpoint(app) {
|
|
|
219
222
|
}
|
|
220
223
|
provider.strategy.clearCache?.();
|
|
221
224
|
}
|
|
222
|
-
// Check for cached discovery results before creating a browser page. When a prior tune or discovery call has already enumerated the
|
|
225
|
+
// Check for cached discovery results before creating a browser page. When a prior tune or discovery call has already enumerated the service's lineup, the
|
|
223
226
|
// cache is warm and we can return immediately without any browser interaction. Skipped when refresh=true since we just cleared the caches above.
|
|
224
227
|
if (!refresh) {
|
|
225
228
|
const cached = provider.getCachedChannels();
|
|
@@ -228,7 +231,7 @@ export function setupProvidersEndpoint(app) {
|
|
|
228
231
|
return;
|
|
229
232
|
}
|
|
230
233
|
}
|
|
231
|
-
// Coalesce concurrent requests. If a discovery is already in flight for this
|
|
234
|
+
// Coalesce concurrent requests. If a discovery is already in flight for this service, piggyback on the existing promise instead of spawning a redundant
|
|
232
235
|
// browser page. If the in-flight discovery was aborted (by a refresh=true request that arrived after we checked above), the promise rejects with a
|
|
233
236
|
// DiscoveryAbortError and we retry against whatever new entry replaced it in the map.
|
|
234
237
|
let entry = inflight.get(slug);
|
|
@@ -243,7 +246,7 @@ export function setupProvidersEndpoint(app) {
|
|
|
243
246
|
entry = { controller, promise };
|
|
244
247
|
inflight.set(slug, entry);
|
|
245
248
|
}
|
|
246
|
-
// Await the in-flight discovery. If it was aborted by a refresh=true request, a new discovery should now be in the map
|
|
249
|
+
// Await the in-flight discovery. If it was aborted by a refresh=true request, a new discovery should now be in the map - retry against that one. The caller
|
|
247
250
|
// doesn't know or care about the abort; they just want channels. Only DiscoveryAbortError triggers a retry; genuine failures are reported immediately.
|
|
248
251
|
for (;;) {
|
|
249
252
|
try {
|
|
@@ -266,4 +269,4 @@ export function setupProvidersEndpoint(app) {
|
|
|
266
269
|
}
|
|
267
270
|
});
|
|
268
271
|
}
|
|
269
|
-
//# sourceMappingURL=
|
|
272
|
+
//# sourceMappingURL=services.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"services.js","sourceRoot":"","sources":["../../src/routes/services.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Response } from "express";
|
|
2
|
+
/**
|
|
3
|
+
* SSE transport handle returned by installSseStream. Callers use sendEvent to push events with JSON payloads (eventType=null for an unnamed data event), and
|
|
4
|
+
* call close() during request teardown to clear the heartbeat timer.
|
|
5
|
+
*/
|
|
6
|
+
export interface SseStream {
|
|
7
|
+
close: () => void;
|
|
8
|
+
sendEvent: (eventType: string | null, data: unknown) => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Sets the SSE response headers, flushes them so the connection opens immediately, and starts the heartbeat. Returns helpers the caller uses to push events
|
|
12
|
+
* and to clean up the heartbeat timer when the connection closes.
|
|
13
|
+
* @param res - The Express response object.
|
|
14
|
+
* @returns An SseStream handle for sending events and tearing down.
|
|
15
|
+
*/
|
|
16
|
+
export declare function installSseStream(res: Response): SseStream;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Heartbeat cadence in milliseconds. Sent as a named heartbeat event to keep the connection alive through proxies and let clients detect staleness.
|
|
2
|
+
const HEARTBEAT_INTERVAL_MS = 30_000;
|
|
3
|
+
/**
|
|
4
|
+
* Sets the SSE response headers, flushes them so the connection opens immediately, and starts the heartbeat. Returns helpers the caller uses to push events
|
|
5
|
+
* and to clean up the heartbeat timer when the connection closes.
|
|
6
|
+
* @param res - The Express response object.
|
|
7
|
+
* @returns An SseStream handle for sending events and tearing down.
|
|
8
|
+
*/
|
|
9
|
+
export function installSseStream(res) {
|
|
10
|
+
// Cache-Control prevents proxies from buffering the stream; Connection: keep-alive ensures the connection stays open; Content-Type: text/event-stream is
|
|
11
|
+
// the SSE protocol marker the browser EventSource needs to recognize the response.
|
|
12
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
13
|
+
res.setHeader("Connection", "keep-alive");
|
|
14
|
+
res.setHeader("Content-Type", "text/event-stream");
|
|
15
|
+
// Flush headers immediately so the EventSource handshake completes before the first event arrives.
|
|
16
|
+
res.flushHeaders();
|
|
17
|
+
// Send a named heartbeat event every HEARTBEAT_INTERVAL_MS to keep the connection alive through proxies and allow clients to detect staleness.
|
|
18
|
+
const heartbeat = setInterval(() => res.write("event: heartbeat\ndata: \n\n"), HEARTBEAT_INTERVAL_MS);
|
|
19
|
+
return {
|
|
20
|
+
close: () => { clearInterval(heartbeat); },
|
|
21
|
+
sendEvent: (eventType, data) => {
|
|
22
|
+
if (eventType !== null) {
|
|
23
|
+
res.write("event: " + eventType + "\n");
|
|
24
|
+
}
|
|
25
|
+
res.write("data: " + JSON.stringify(data) + "\n\n");
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=sse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse.js","sourceRoot":"","sources":["../../src/routes/sse.ts"],"names":[],"mappings":"AAOA,oJAAoJ;AACpJ,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAYrC;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAa;IAE5C,yJAAyJ;IACzJ,mFAAmF;IACnF,GAAG,CAAC,SAAS,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;IAC3C,GAAG,CAAC,SAAS,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;IAC1C,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,mBAAmB,CAAC,CAAC;IAEnD,mGAAmG;IACnG,GAAG,CAAC,YAAY,EAAE,CAAC;IAEnB,+IAA+I;IAC/I,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,8BAA8B,CAAC,EAAE,qBAAqB,CAAC,CAAC;IAEtG,OAAO;QAEL,KAAK,EAAE,GAAS,EAAE,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAChD,SAAS,EAAE,CAAC,SAAwB,EAAE,IAAa,EAAQ,EAAE;YAE3D,IAAG,SAAS,KAAK,IAAI,EAAE,CAAC;gBAEtB,GAAG,CAAC,KAAK,CAAC,SAAS,GAAG,SAAS,GAAG,IAAI,CAAC,CAAC;YAC1C,CAAC;YAED,GAAG,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC;QACtD,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/routes/streams.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { getAllStreams, getStream, getStreamCount, getStreamMemoryUsage } from "../streaming/registry.js";
|
|
2
2
|
import { getStatusSnapshot, getStreamStatus, subscribeToStatus } from "../streaming/statusEmitter.js";
|
|
3
|
+
import { sendNotFoundError, sendSuccess, sendValidationError } from "./config/http/envelope.js";
|
|
3
4
|
import { CONFIG } from "../config/index.js";
|
|
4
5
|
import { emitCurrentSystemStatus } from "../browser/index.js";
|
|
6
|
+
import { installSseStream } from "./sse.js";
|
|
5
7
|
import { terminateStream } from "../streaming/lifecycle.js";
|
|
6
8
|
/* The streams endpoint provides visibility into active streams and allows operators to terminate streams via the API. This is useful for debugging and for
|
|
7
9
|
* integrations that need to manage stream lifecycle.
|
|
@@ -43,44 +45,29 @@ export function setupStreamsEndpoint(app) {
|
|
|
43
45
|
app.delete("/streams/:id", (req, res) => {
|
|
44
46
|
const streamIdParam = parseInt(req.params.id);
|
|
45
47
|
if (isNaN(streamIdParam)) {
|
|
46
|
-
res
|
|
48
|
+
sendValidationError(res, "Invalid stream ID.");
|
|
47
49
|
return;
|
|
48
50
|
}
|
|
49
51
|
const streamInfo = getStream(streamIdParam);
|
|
50
52
|
if (!streamInfo) {
|
|
51
|
-
res
|
|
53
|
+
sendNotFoundError(res, "Stream not found.");
|
|
52
54
|
return;
|
|
53
55
|
}
|
|
54
56
|
terminateStream(streamIdParam, streamInfo.info.storeKey, "API request");
|
|
55
57
|
void emitCurrentSystemStatus();
|
|
56
|
-
res
|
|
58
|
+
sendSuccess(res, { data: { streamId: streamIdParam }, message: "Stream terminated." });
|
|
57
59
|
});
|
|
58
60
|
/* The /streams/status endpoint provides real-time stream and system status via Server-Sent Events. Connected clients receive an initial snapshot of all streams
|
|
59
61
|
* and the system state, then receive updates as streams are added, removed, or their health changes.
|
|
60
62
|
*/
|
|
61
63
|
app.get("/streams/status", (req, res) => {
|
|
62
|
-
|
|
63
|
-
res.setHeader("Cache-Control", "no-cache");
|
|
64
|
-
res.setHeader("Connection", "keep-alive");
|
|
65
|
-
res.setHeader("Content-Type", "text/event-stream");
|
|
66
|
-
// Disable response buffering to ensure events are sent immediately.
|
|
67
|
-
res.flushHeaders();
|
|
64
|
+
const sse = installSseStream(res);
|
|
68
65
|
// Send the initial snapshot so clients have current state.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
// Subscribe to status events and forward them to the client.
|
|
73
|
-
const unsubscribe = subscribeToStatus((eventType, data) => {
|
|
74
|
-
res.write("event: " + eventType + "\n");
|
|
75
|
-
res.write("data: " + JSON.stringify(data) + "\n\n");
|
|
76
|
-
});
|
|
77
|
-
// Send a named heartbeat event every 30 seconds to keep the connection alive through proxies and allow clients to detect staleness.
|
|
78
|
-
const heartbeatInterval = setInterval(() => {
|
|
79
|
-
res.write("event: heartbeat\ndata: \n\n");
|
|
80
|
-
}, 30000);
|
|
81
|
-
// Clean up when the client disconnects.
|
|
66
|
+
sse.sendEvent("snapshot", getStatusSnapshot());
|
|
67
|
+
// Subscribe to status events and forward them to the client; the heartbeat is owned by installSseStream.
|
|
68
|
+
const unsubscribe = subscribeToStatus((eventType, data) => { sse.sendEvent(eventType, data); });
|
|
82
69
|
req.on("close", () => {
|
|
83
|
-
|
|
70
|
+
sse.close();
|
|
84
71
|
unsubscribe();
|
|
85
72
|
});
|
|
86
73
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streams.js","sourceRoot":"","sources":["../../src/routes/streams.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAC1G,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACtG,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAI5C,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAE5D;;GAEG;AAEH;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAY;IAE/C,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,IAAa,EAAE,GAAa,EAAQ,EAAE;QAEzD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEvB,MAAM,OAAO,GAcP,EAAE,CAAC;QAET,KAAI,MAAM,UAAU,IAAI,aAAa,EAAE,EAAE,CAAC;YAExC,MAAM,MAAM,GAAG,eAAe,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YAE9C,OAAO,CAAC,IAAI,CAAC;gBAEX,OAAO,EAAE,UAAU,CAAC,WAAW;gBAC/B,WAAW,EAAE,MAAM,EAAE,WAAW,IAAI,CAAC;gBACrC,OAAO,EAAE,MAAM,EAAE,OAAO,IAAI,EAAE;gBAC9B,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,UAAU,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC;gBACnE,eAAe,EAAE,MAAM,EAAE,eAAe,IAAI,CAAC;gBAC7C,MAAM,EAAE,MAAM,EAAE,MAAM,IAAI,SAAS;gBACnC,EAAE,EAAE,UAAU,CAAC,EAAE;gBACjB,OAAO,EAAE,MAAM,EAAE,OAAO,IAAI,EAAE;gBAC9B,MAAM,EAAE,oBAAoB,CAAC,UAAU,CAAC;gBACxC,gBAAgB,EAAE,MAAM,EAAE,gBAAgB,IAAI,CAAC;gBAC/C,QAAQ,EAAE,MAAM,EAAE,QAAQ,IAAI,EAAE;gBAChC,SAAS,EAAE,UAAU,CAAC,SAAS,CAAC,WAAW,EAAE;gBAC7C,GAAG,EAAE,UAAU,CAAC,GAAG;aACpB,CAAC,CAAC;QACL,CAAC;QAED,GAAG,CAAC,IAAI,CAAC;YAEP,KAAK,EAAE,cAAc,EAAE;YACvB,KAAK,EAAE,MAAM,CAAC,SAAS,CAAC,oBAAoB;YAC5C,OAAO,EAAE,OAAO;SACjB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,6JAA6J;IAC7J,iDAAiD;IACjD,GAAG,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,GAAY,EAAE,GAAa,EAAQ,EAAE;QAE/D,MAAM,aAAa,GAAG,QAAQ,CAAE,GAAG,CAAC,MAAyB,CAAC,EAAE,CAAC,CAAC;QAElE,IAAG,KAAK,CAAC,aAAa,CAAC,EAAE,CAAC;YAExB,
|
|
1
|
+
{"version":3,"file":"streams.js","sourceRoot":"","sources":["../../src/routes/streams.ts"],"names":[],"mappings":"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"}
|
package/dist/routes/ui.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { generateAlert, generateBadge, generateButton, generatePanelHeader, generateSection, generateSelect, generateSimpleAlert, generateStatusIndicator, generateTextInput } from "./components.
|
|
2
|
-
export type { AlertType, BadgeVariant, ButtonOptions, ButtonSize, ButtonVariant, SelectOption, SelectOptions, TextInputOptions } from "./components.
|
|
1
|
+
export { generateAlert, generateBadge, generateButton, generatePanelHeader, generateSection, generateSelect, generateSimpleAlert, generateStatusIndicator, generateTextInput } from "./components.ts";
|
|
2
|
+
export type { AlertType, BadgeVariant, ButtonOptions, ButtonSize, ButtonVariant, SelectOption, SelectOptions, TextInputOptions } from "./components.ts";
|
|
3
3
|
/**
|
|
4
4
|
* Options for configuring the tab script behavior.
|
|
5
5
|
*/
|
package/dist/routes/ui.js
CHANGED
|
@@ -120,6 +120,8 @@ export function generateBaseStyles() {
|
|
|
120
120
|
".form-warning { color: var(--status-warning-text); font-size: 12px; margin-top: 5px; padding: 6px 10px; background: var(--status-warning-bg); ",
|
|
121
121
|
"border-radius: var(--radius-sm); }",
|
|
122
122
|
".hint { color: var(--text-secondary); font-size: 12px; margin-top: 1px; margin-bottom: 15px; }",
|
|
123
|
+
".predefined-hint { padding: 8px 12px; background: var(--bg-offset); border-radius: var(--radius-sm); border-left: 3px solid var(--accent); }",
|
|
124
|
+
".predefined-hint a { color: var(--accent); }",
|
|
123
125
|
// Env badge in form labels.
|
|
124
126
|
".env-badge { display: inline-block; background: var(--badge-env-bg); color: var(--badge-env-text); padding: 2px 8px; ",
|
|
125
127
|
"border-radius: var(--radius-sm); font-size: 11px; font-weight: 600; margin-left: 10px; }",
|