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
|
@@ -3,8 +3,9 @@ import { compareChannelSort, getAllServiceTags, getAuthDomainForChannel, getChan
|
|
|
3
3
|
import { escapeHtml, formatTimeAgo } from "../../../utils/index.js";
|
|
4
4
|
import { getActiveTagVocabulary, getChannelCustomizations, getChannelEffectiveTags, getChannelListing, getChannelLogo, getChannelsParseErrorMessage, getEffectiveHdhrEnabled, getPredefinedScopeCounts, getTagRegistry, getUserChannelsFilePath, hasChannelsParseError, isPredefinedChannel, isPredefinedChannelDisabled, isUserChannel, isVisibleChannel, tagsMatch } from "../../../config/userChannels.js";
|
|
5
5
|
import { getCachedProviderChannels, getProviderDomainMap, getProviderGuideUrls, getProviderModuleInfo } from "../../../browser/channelSelection.js";
|
|
6
|
-
import { getChannelHealth,
|
|
6
|
+
import { getChannelHealth, getDomainAuthState } from "../../../config/health.js";
|
|
7
7
|
import { getProfileForChannel, getProfiles } from "../../../config/profiles.js";
|
|
8
|
+
import { ACTIONS } from "../../clientActions.js";
|
|
8
9
|
import { CONFIG } from "../../../config/index.js";
|
|
9
10
|
import { PREDEFINED_CHANNELS } from "../../../channels/index.js";
|
|
10
11
|
import { categorizeProfiles } from "../index.js";
|
|
@@ -34,7 +35,7 @@ export const OPTIONAL_COLUMNS = [
|
|
|
34
35
|
{ align: "left", cssClass: "col-selector", field: "channelSelector", label: "Selector", width: "130px" },
|
|
35
36
|
{ align: "left", cssClass: "col-tags", field: "tags", label: "Tags", width: "150px" }
|
|
36
37
|
];
|
|
37
|
-
// Total number of columns in the channels table
|
|
38
|
+
// Total number of columns in the channels table: the required columns plus every entry in OPTIONAL_COLUMNS.
|
|
38
39
|
const TOTAL_COLUMN_COUNT = 10;
|
|
39
40
|
// Valid optional column field names.
|
|
40
41
|
export const VALID_OPTIONAL_COLUMNS = new Set(OPTIONAL_COLUMNS.map((c) => c.field));
|
|
@@ -61,7 +62,7 @@ function generateTextField(id, name, label, value, options = {}) {
|
|
|
61
62
|
const modifiedDot = isModified ? "<span class=\"modified-dot\" title=\"Modified from predefined default\"></span>" : "";
|
|
62
63
|
const defaultAttr = (options.defaultValue !== undefined) ? " data-default=\"" + escapeHtml(options.defaultValue) + "\"" : "";
|
|
63
64
|
const resetBtn = isModified ? "<button type=\"button\" class=\"btn-reset\" title=\"Reset to predefined default\" aria-label=\"Reset to predefined default\" " +
|
|
64
|
-
"
|
|
65
|
+
"data-click-action=\"" + ACTIONS.resetChannelField + "\" data-field-id=\"" + id + "\">↻</button>" : "";
|
|
65
66
|
lines.push("<div class=\"form-row" + modifiedClass + "\">");
|
|
66
67
|
lines.push(modifiedDot + "<label for=\"" + id + "\">" + label + "</label>");
|
|
67
68
|
lines.push("<input class=\"form-input\" type=\"" + inputType + "\" id=\"" + id + "\" name=\"" + name + "\"" + required + listAttr + pattern +
|
|
@@ -97,7 +98,7 @@ function generateProfileDropdown(id, selectedProfile, profiles, showHint = true,
|
|
|
97
98
|
const modifiedDot = isModified ? "<span class=\"modified-dot\" title=\"Modified from predefined default\"></span>" : "";
|
|
98
99
|
const defaultAttr = (defaultProfile !== undefined) ? " data-default=\"" + escapeHtml(defaultProfile) + "\"" : "";
|
|
99
100
|
const resetBtn = isModified ? "<button type=\"button\" class=\"btn-reset\" title=\"Reset to predefined default\" aria-label=\"Reset to predefined default\" " +
|
|
100
|
-
"
|
|
101
|
+
"data-click-action=\"" + ACTIONS.resetChannelField + "\" data-field-id=\"" + id + "\">↻</button>" : "";
|
|
101
102
|
// Helper to generate option elements for a profile.
|
|
102
103
|
const renderOption = (profile) => {
|
|
103
104
|
const selected = (profile.name === selectedProfile) ? " selected" : "";
|
|
@@ -154,7 +155,7 @@ function generateProfileDropdown(id, selectedProfile, profiles, showHint = true,
|
|
|
154
155
|
if (showHint) {
|
|
155
156
|
lines.push("<div class=\"hint\">Autodetect uses predefined profiles for known sites. If video doesn't play or fullscreen fails, " +
|
|
156
157
|
"try experimenting with different profiles. ");
|
|
157
|
-
lines.push("<a href=\"#\"
|
|
158
|
+
lines.push("<a href=\"#\" data-click-action=\"" + ACTIONS.toggleProfileReference + "\" data-click-prevent-default>View profile reference</a></div>");
|
|
158
159
|
}
|
|
159
160
|
return lines;
|
|
160
161
|
}
|
|
@@ -170,7 +171,8 @@ function generateProfileReference(profiles) {
|
|
|
170
171
|
lines.push("<div id=\"profile-reference\" class=\"profile-reference\" style=\"display: none;\">");
|
|
171
172
|
lines.push("<div class=\"profile-reference-header\">");
|
|
172
173
|
lines.push("<h3>Profile Reference</h3>");
|
|
173
|
-
lines.push("<button type=\"button\" class=\"profile-reference-close\" aria-label=\"Close\"
|
|
174
|
+
lines.push("<button type=\"button\" class=\"profile-reference-close\" aria-label=\"Close\" data-click-action=\"" +
|
|
175
|
+
ACTIONS.toggleProfileReference + "\">\u2715</button>");
|
|
174
176
|
lines.push("</div>");
|
|
175
177
|
lines.push("<p class=\"reference-intro\">Profiles configure how PrismCast interacts with different video players. Autodetect uses predefined ");
|
|
176
178
|
lines.push("profiles for known sites. If video doesn't play or fullscreen fails, use this reference to experiment with different profiles.</p>");
|
|
@@ -275,18 +277,17 @@ function generateProfileReference(profiles) {
|
|
|
275
277
|
return lines.join("\n");
|
|
276
278
|
}
|
|
277
279
|
/**
|
|
278
|
-
* Generates HTML for the advanced fields section (station ID, channel selector, channel number,
|
|
280
|
+
* Generates HTML for the advanced fields section (station ID, guide title, logo URL, channel selector, channel number, tags, HDHomeRun toggle, force capture).
|
|
279
281
|
* @param idPrefix - Prefix for element IDs ("add" or "edit").
|
|
280
282
|
* @param options - Field values, defaults, and customization metadata. See AdvancedFieldOptions.
|
|
281
283
|
* @returns Array of HTML strings for the advanced fields section.
|
|
282
284
|
*/
|
|
283
285
|
function generateAdvancedFields(idPrefix, options = {}) {
|
|
284
|
-
const { channelNumberValue = "", channelSelectorValue = "", guideTitleValue = "", hdhrEnabled = true, logoUrlValue = "", showHints = true, stationIdValue = "", tagsValue = "" } = options;
|
|
286
|
+
const { channelNumberValue = "", channelSelectorValue = "", forceCapture = false, guideTitleValue = "", hdhrEnabled = true, logoUrlValue = "", showHints = true, stationIdValue = "", tagsValue = "" } = options;
|
|
285
287
|
const lines = [];
|
|
286
288
|
// Advanced fields toggle.
|
|
287
|
-
lines.push("<div class=\"advanced-toggle\"
|
|
288
|
-
"-advanced
|
|
289
|
-
"'Hide Advanced Options' : 'Show Advanced Options';\">Show Advanced Options</div>");
|
|
289
|
+
lines.push("<div class=\"advanced-toggle\" data-click-action=\"" + ACTIONS.toggleAdvancedFields + "\" data-advanced-id=\"" + idPrefix +
|
|
290
|
+
"-advanced\">Show Advanced Options</div>");
|
|
290
291
|
lines.push("<div id=\"" + idPrefix + "-advanced\" class=\"advanced-fields\">");
|
|
291
292
|
// Station ID.
|
|
292
293
|
const stationIdHint = showHints ? "Optional Gracenote station ID for guide data (tvc-guide-stationid)." : undefined;
|
|
@@ -335,7 +336,7 @@ function generateAdvancedFields(idPrefix, options = {}) {
|
|
|
335
336
|
const checked = currentTags.some((t) => tagsMatch(t, tag)) ? " checked" : "";
|
|
336
337
|
lines.push("<label class=\"tag-checkbox-label\">" +
|
|
337
338
|
"<input type=\"checkbox\" class=\"tag-checkbox\" data-tag=\"" + escapeHtml(tag) + "\"" + checked +
|
|
338
|
-
"
|
|
339
|
+
" data-change-action=\"" + ACTIONS.updateTagsHidden + "\">" +
|
|
339
340
|
"<span class=\"tag-badge\">" + escapeHtml(tag) + "</span></label>");
|
|
340
341
|
}
|
|
341
342
|
lines.push("</div>");
|
|
@@ -362,15 +363,35 @@ function generateAdvancedFields(idPrefix, options = {}) {
|
|
|
362
363
|
if (hdhrHint) {
|
|
363
364
|
lines.push("<div class=\"hint\">" + hdhrHint + "</div>");
|
|
364
365
|
}
|
|
366
|
+
// Force screen capture. There is no hidden-input partner here: this field's default is false, so an unchecked box submitting nothing is unambiguous and the
|
|
367
|
+
// stored shape stays sparse. The value="true" the checkbox carries is the exact token parseFormBody tests for.
|
|
368
|
+
const forceCaptureChecked = forceCapture ? " checked" : "";
|
|
369
|
+
// Customization is presence in stored deltas (identity field, lives on canonical entry); surfaced via customizedFields. The default value arrives
|
|
370
|
+
// pre-stringified from resetValueFor, which yields the empty string for a field no predefined source declares - and the empty string unchecks the box.
|
|
371
|
+
const forceCaptureModified = customized?.has("forceCapture") === true;
|
|
372
|
+
const forceCaptureModifiedClass = forceCaptureModified ? " modified" : "";
|
|
373
|
+
const forceCaptureModifiedDot = forceCaptureModified ? "<span class=\"modified-dot\" title=\"Modified from predefined default\"></span>" : "";
|
|
374
|
+
const forceCaptureDefaultAttr = (defs?.forceCapture !== undefined) ? " data-default=\"" + defs.forceCapture + "\"" : "";
|
|
375
|
+
const forceCaptureHint = showHints ?
|
|
376
|
+
"When checked, this channel always uses screen capture, skipping native HLS streaming. Useful for sites whose native stream misbehaves." :
|
|
377
|
+
undefined;
|
|
378
|
+
lines.push("<div class=\"form-row form-row-checkbox" + forceCaptureModifiedClass + "\">");
|
|
379
|
+
lines.push(forceCaptureModifiedDot + "<label for=\"" + idPrefix + "-forceCapture\">Force capture</label>");
|
|
380
|
+
lines.push("<input type=\"checkbox\" id=\"" + idPrefix + "-forceCapture\" name=\"forceCapture\" value=\"true\"" + forceCaptureChecked +
|
|
381
|
+
forceCaptureDefaultAttr + ">");
|
|
382
|
+
lines.push("</div>");
|
|
383
|
+
if (forceCaptureHint) {
|
|
384
|
+
lines.push("<div class=\"hint\">" + forceCaptureHint + "</div>");
|
|
385
|
+
}
|
|
365
386
|
// Close the advanced-fields wrapper opened at the top of this builder.
|
|
366
387
|
lines.push("</div>");
|
|
367
388
|
return lines;
|
|
368
389
|
}
|
|
369
390
|
/**
|
|
370
|
-
* Generates JavaScript variables for channel selector datalist population. Produces
|
|
391
|
+
* Generates JavaScript variables for channel selector datalist population. Produces four variables: `channelSelectorsByDomain` maps URL hostnames to known
|
|
371
392
|
* channel selector values (from predefined channels and cached service discovery), `serviceByDomain` maps service guide URL hostnames to service slugs for
|
|
372
|
-
* client-side async discovery,
|
|
373
|
-
* channels panel.
|
|
393
|
+
* client-side async discovery, `serviceGuideUrl` maps service slugs to their guide URLs for URL correction hints, and `predefinedByDomain` maps domains to
|
|
394
|
+
* predefined channel summaries so the manual add form can show inline hints without a server round-trip. Embedded as a `<script>` block in the channels panel.
|
|
374
395
|
*
|
|
375
396
|
* @returns JavaScript variable declarations ready to embed in a `<script>` tag.
|
|
376
397
|
*/
|
|
@@ -433,13 +454,14 @@ export function generateTagFilterContent() {
|
|
|
433
454
|
const vocabulary = getActiveTagVocabulary();
|
|
434
455
|
const lines = [];
|
|
435
456
|
for (const tag of vocabulary) {
|
|
436
|
-
lines.push("<label class=\"provider-option\"
|
|
437
|
-
"<input type=\"checkbox\" class=\"tag-filter-checkbox\" data-tag=\"" + escapeHtml(tag) + "\" checked
|
|
438
|
-
escapeHtml(tag) + "</label>");
|
|
457
|
+
lines.push("<label class=\"provider-option\" data-click-stop-propagation>" +
|
|
458
|
+
"<input type=\"checkbox\" class=\"tag-filter-checkbox\" data-tag=\"" + escapeHtml(tag) + "\" checked data-change-action=\"" +
|
|
459
|
+
ACTIONS.applyTagColumnFilter + "\"> " + escapeHtml(tag) + "</label>");
|
|
439
460
|
}
|
|
440
461
|
if (vocabulary.length > 0) {
|
|
441
462
|
lines.push("<div class=\"dropdown-divider\"></div>");
|
|
442
|
-
lines.push("<div class=\"dropdown-item\" id=\"tag-filter-toggle\"
|
|
463
|
+
lines.push("<div class=\"dropdown-item\" id=\"tag-filter-toggle\" data-click-action=\"" + ACTIONS.toggleTagColumnFilter +
|
|
464
|
+
"\" data-click-stop-propagation>Show None</div>");
|
|
443
465
|
}
|
|
444
466
|
return lines.join("");
|
|
445
467
|
}
|
|
@@ -455,26 +477,27 @@ export function generateTagManagerBody() {
|
|
|
455
477
|
const tagListItems = [];
|
|
456
478
|
for (const tag of vocabulary) {
|
|
457
479
|
tagListItems.push("<div class=\"tag-manager-item\" data-tag=\"" + escapeHtml(tag) + "\">" +
|
|
458
|
-
"<span class=\"tag-badge tag-editable\" title=\"Click to rename\"
|
|
459
|
-
"
|
|
460
|
-
"<button type=\"button\" class=\"btn-icon btn-icon-delete\" title=\"Delete tag\"
|
|
461
|
-
"
|
|
480
|
+
"<span class=\"tag-badge tag-editable\" title=\"Click to rename\" data-click-action=\"" + ACTIONS.startTagRename +
|
|
481
|
+
"\" data-tag-name=\"" + escapeHtml(tag) + "\">" + escapeHtml(tag) + "</span>" +
|
|
482
|
+
"<button type=\"button\" class=\"btn-icon btn-icon-delete\" title=\"Delete tag\" data-click-action=\"" +
|
|
483
|
+
ACTIONS.deleteTag + "\" data-tag-name=\"" + escapeHtml(tag) +
|
|
484
|
+
"\">" + ICON_DELETE + "</button></div>");
|
|
462
485
|
}
|
|
463
486
|
// Deleted predefined tags section - show restore option for tags the user has deleted.
|
|
464
487
|
const deletedItems = [];
|
|
465
488
|
for (const tag of registry.deletedTags) {
|
|
466
489
|
deletedItems.push("<div class=\"tag-manager-item tag-deleted\" data-tag=\"" + escapeHtml(tag) + "\">" +
|
|
467
490
|
"<span class=\"tag-badge tag-badge-deleted\">" + escapeHtml(tag) + "</span> <span class=\"tag-annotation\">(deleted)</span>" +
|
|
468
|
-
"<button type=\"button\" class=\"btn-icon\" title=\"Restore tag\"
|
|
469
|
-
"
|
|
491
|
+
"<button type=\"button\" class=\"btn-icon\" title=\"Restore tag\" data-click-action=\"" + ACTIONS.restoreTag + "\" data-tag-name=\"" + escapeHtml(tag) +
|
|
492
|
+
"\">" + ICON_REVERT + "</button></div>");
|
|
470
493
|
}
|
|
471
494
|
return "<div class=\"tag-manager\">" +
|
|
472
495
|
"<p class=\"wizard-hint\">Create and manage the tag vocabulary. Tags can be assigned to channels via the channel table " +
|
|
473
496
|
"for playlist filtering and organization.</p>" +
|
|
474
497
|
"<div class=\"tag-manager-add\">" +
|
|
475
498
|
"<input type=\"text\" id=\"tag-manager-input\" placeholder=\"New tag name\" maxlength=\"30\" " +
|
|
476
|
-
"pattern=\"[a-zA-Z0-9]([a-zA-Z0-9 -]*[a-zA-Z0-9])?\"
|
|
477
|
-
"<button type=\"button\" class=\"btn btn-primary btn-sm\"
|
|
499
|
+
"pattern=\"[a-zA-Z0-9]([a-zA-Z0-9 -]*[a-zA-Z0-9])?\" data-keydown-action=\"" + ACTIONS.createTagOnEnter + "\">" +
|
|
500
|
+
"<button type=\"button\" class=\"btn btn-primary btn-sm\" data-click-action=\"" + ACTIONS.createTag + "\">Add</button>" +
|
|
478
501
|
"</div>" +
|
|
479
502
|
"<div id=\"tag-manager-error\" class=\"wizard-error\" style=\"display: none;\"></div>" +
|
|
480
503
|
"<div id=\"tag-manager-list\" class=\"tag-manager-list\">" +
|
|
@@ -492,11 +515,11 @@ function generateTagManagementModal() {
|
|
|
492
515
|
return generateWizardModal({
|
|
493
516
|
body: generateTagManagerBody(),
|
|
494
517
|
buttons: [
|
|
495
|
-
{
|
|
518
|
+
{ action: ACTIONS.closeTagManager, label: "Done", position: "right", variant: "primary" }
|
|
496
519
|
],
|
|
520
|
+
closeAction: ACTIONS.closeTagManager,
|
|
497
521
|
id: "tag-manager-modal",
|
|
498
522
|
maxWidth: "420px",
|
|
499
|
-
onClose: "closeTagManager()",
|
|
500
523
|
title: "Manage Tags"
|
|
501
524
|
});
|
|
502
525
|
}
|
|
@@ -514,7 +537,7 @@ function generateBrowseModal() {
|
|
|
514
537
|
buttons: [
|
|
515
538
|
{ id: "browse-back", label: "Back", position: "left", role: "back", visible: false },
|
|
516
539
|
{ label: "Cancel", position: "right", role: "close" },
|
|
517
|
-
{ id: "browse-add-btn", label: "Apply",
|
|
540
|
+
{ action: ACTIONS.submitBrowseChannels, id: "browse-add-btn", label: "Apply", position: "right", variant: "primary", visible: false }
|
|
518
541
|
],
|
|
519
542
|
contentId: "browse-content",
|
|
520
543
|
dataBlocks: [
|
|
@@ -543,9 +566,9 @@ function generateSetupWizardModal() {
|
|
|
543
566
|
return generateWizardModal({
|
|
544
567
|
buttons: [
|
|
545
568
|
{ id: "setup-back", label: "Back", position: "left", role: "back", visible: false },
|
|
546
|
-
{ id: "setup-skip", label: "Skip Setup",
|
|
569
|
+
{ action: ACTIONS.skipSetup, id: "setup-skip", label: "Skip Setup", position: "right" },
|
|
547
570
|
{ id: "setup-next", label: "Next", position: "right", role: "next", variant: "primary" },
|
|
548
|
-
{ id: "setup-finish", label: "Finish",
|
|
571
|
+
{ action: ACTIONS.finishSetup, id: "setup-finish", label: "Finish", position: "right", variant: "primary", visible: false }
|
|
549
572
|
],
|
|
550
573
|
contentId: "setup-content",
|
|
551
574
|
dataAttributes: { "setup-completed": CONFIG.channels.setupCompleted ? "true" : "false" },
|
|
@@ -563,9 +586,8 @@ function generateSetupWizardModal() {
|
|
|
563
586
|
}
|
|
564
587
|
/**
|
|
565
588
|
* Generates the HTML for a single channel's table rows (display row and edit form row). All channels - predefined, override, and user-defined - get both rows.
|
|
566
|
-
* The edit form is pre-populated with the effective (resolved) values so users see what they're changing.
|
|
567
|
-
*
|
|
568
|
-
* parameter to trigger an internal lookup.
|
|
589
|
+
* The edit form is pre-populated with the effective (resolved) values so users see what they're changing. Callers pass a pre-resolved listing entry via the
|
|
590
|
+
* entry parameter to avoid redundant getChannelListing() calls; the parameter is optional so an internal lookup remains available as a fallback.
|
|
569
591
|
* @param key - The channel key.
|
|
570
592
|
* @param profiles - List of available profiles with descriptions for the dropdown.
|
|
571
593
|
* @param entry - Optional pre-resolved listing entry. When omitted, looked up from getChannelListing().
|
|
@@ -629,7 +651,7 @@ export function generateChannelRowHtml(key, profiles, entry) {
|
|
|
629
651
|
// to remove them from the DOM.
|
|
630
652
|
const currentSelection = resolveServiceKey(key);
|
|
631
653
|
displayLines.push("<select class=\"provider-select\" data-channel=\"" + escapeHtml(key) +
|
|
632
|
-
"\" title=\"Choose which streaming service delivers this channel\"
|
|
654
|
+
"\" title=\"Choose which streaming service delivers this channel\" data-change-action=\"" + ACTIONS.updateServiceSelection + "\"" + contentHidden + ">");
|
|
633
655
|
for (const variant of serviceGroup.variants) {
|
|
634
656
|
const selected = (variant.key === currentSelection) ? " selected" : "";
|
|
635
657
|
const optionHidden = !isServiceTagEnabled(variant.tag) ? " hidden" : "";
|
|
@@ -640,25 +662,27 @@ export function generateChannelRowHtml(key, profiles, entry) {
|
|
|
640
662
|
}
|
|
641
663
|
else {
|
|
642
664
|
// Single-service: wrap the service name in a span so client-side JS can toggle it with the no-service label. Uses profile-aware label resolution so
|
|
643
|
-
// channels with explicit profile assignments show the profile's service name rather than the
|
|
665
|
+
// channels with explicit profile assignments show the profile's service name rather than the builtin name for the URL domain.
|
|
644
666
|
displayLines.push("<span class=\"provider-name\"" + contentHidden + ">" +
|
|
645
667
|
escapeHtml(getChannelServiceLabel(channel)) + "</span>");
|
|
646
668
|
}
|
|
647
669
|
displayLines.push("</td>");
|
|
648
|
-
// Optional columns: Number, HDHR, Station ID, Profile, Selector
|
|
670
|
+
// Optional columns: Number, HDHR, Station ID, Profile, Selector - every optional column except Tags is rendered here individually; Tags is rendered
|
|
671
|
+
// separately below because it needs dropdown/portal markup. All are always rendered here; visibility is controlled by CSS classes on the table element.
|
|
649
672
|
const cellKey = escapeHtml(key);
|
|
650
673
|
displayLines.push("<td class=\"col-chnum editable-cell\" data-sort-value=\"" + escapeHtml(getChannelSortKey(channel, key, "channelNumber")) +
|
|
651
674
|
"\" data-field=\"channelNumber\" data-key=\"" + cellKey + "\" data-value=\"" + (channel.channelNumber ? escapeHtml(String(channel.channelNumber)) : "") +
|
|
652
|
-
"\"
|
|
653
|
-
"</td>");
|
|
675
|
+
"\" data-click-action=\"" + ACTIONS.startInlineEdit + "\">" +
|
|
676
|
+
(channel.channelNumber ? escapeHtml(String(channel.channelNumber)) : "<span class=\"text-muted\">–</span>") + "</td>");
|
|
654
677
|
// HDHR column: inline checkbox for quick toggling. The checkbox submits changes via the toggleHdhr() client-side function.
|
|
655
678
|
const hdhrCheckedAttr = getEffectiveHdhrEnabled(channel) ? " checked" : "";
|
|
656
679
|
displayLines.push("<td class=\"col-hdhr\" data-sort-value=\"" + escapeHtml(getChannelSortKey(channel, key, "hdhrEnabled")) +
|
|
657
680
|
"\"><input type=\"checkbox\" data-key=\"" + cellKey + "\"" + hdhrCheckedAttr +
|
|
658
|
-
"
|
|
681
|
+
" data-change-action=\"" + ACTIONS.toggleHdhr + "\" title=\"Include in HDHomeRun/Plex lineup\"></td>");
|
|
659
682
|
displayLines.push("<td class=\"col-stationid editable-cell\" data-sort-value=\"" + escapeHtml(getChannelSortKey(channel, key, "stationId")) +
|
|
660
683
|
"\" data-field=\"stationId\" data-key=\"" + cellKey + "\" data-value=\"" + (channel.stationId ? escapeHtml(channel.stationId) : "") +
|
|
661
|
-
"\"
|
|
684
|
+
"\" data-click-action=\"" + ACTIONS.startInlineEdit + "\">" +
|
|
685
|
+
(channel.stationId ? escapeHtml(channel.stationId) : "<span class=\"text-muted\">–</span>") + "</td>");
|
|
662
686
|
// Profile column: show explicit profile as-is, or the auto-resolved friendly name with "(auto)" suffix in muted style.
|
|
663
687
|
const profileSortKey = escapeHtml(getChannelSortKey(channel, key, "profile"));
|
|
664
688
|
if (channel.profile) {
|
|
@@ -684,8 +708,8 @@ export function generateChannelRowHtml(key, profiles, entry) {
|
|
|
684
708
|
"<span class=\"text-muted\">–</span>";
|
|
685
709
|
displayLines.push("<td class=\"col-tags editable-cell dropdown\" data-sort-value=\"" + escapeHtml(getChannelSortKey(channel, key, "tags")) +
|
|
686
710
|
"\" data-key=\"" + cellKey + "\" data-tags=\"" + escapeHtml(effectiveTags.join(",")) +
|
|
687
|
-
"\"
|
|
688
|
-
// Actions column with icon buttons
|
|
711
|
+
"\" data-click-action=\"" + ACTIONS.toggleInlineTagDropdown + "\">" + tagsHtml + "</td>");
|
|
712
|
+
// Actions column with icon buttons, one entry per position below: Edit (always), Login/placeholder, Health/placeholder, context-sensitive, and Copy URL.
|
|
689
713
|
displayLines.push("<td>");
|
|
690
714
|
displayLines.push("<div class=\"btn-group\">");
|
|
691
715
|
const escapedKey = escapeHtml(key);
|
|
@@ -693,17 +717,24 @@ export function generateChannelRowHtml(key, profiles, entry) {
|
|
|
693
717
|
const variantKey = resolveServiceKey(key);
|
|
694
718
|
const authDomain = getAuthDomainForChannel(variantKey);
|
|
695
719
|
// Position 1: Edit (all channels).
|
|
696
|
-
displayLines.push("<button type=\"button\" class=\"btn-icon btn-icon-edit\" title=\"Edit\" aria-label=\"Edit\"
|
|
697
|
-
"
|
|
698
|
-
// Position 2: Login for enabled channels (with domain auth color
|
|
699
|
-
// login coloring because they have no service concept.
|
|
720
|
+
displayLines.push("<button type=\"button\" class=\"btn-icon btn-icon-edit\" title=\"Edit\" aria-label=\"Edit\" " +
|
|
721
|
+
"data-click-action=\"" + ACTIONS.showEditForm + "\" data-channel-key=\"" + escapedKey + "\">" + ICON_EDIT + "</button>");
|
|
722
|
+
// Position 2: Login for enabled channels (with tri-state domain auth color: green verified, red needs-sign-in, neutral unknown), placeholder for disabled
|
|
723
|
+
// predefined. Custom channels (user-defined, not predefined) skip login coloring because they have no service concept.
|
|
700
724
|
if (!isDisabled) {
|
|
701
|
-
const
|
|
702
|
-
const
|
|
703
|
-
const
|
|
725
|
+
const authState = (isPredefined || isOverride) ? getDomainAuthState(authDomain) : null;
|
|
726
|
+
const needsLogin = authState?.status === "needsLogin";
|
|
727
|
+
const loginColorClass = (authState?.status === "verified") ? " health-success" : needsLogin ? " health-failed" : "";
|
|
728
|
+
// Lead the title with the action so users discover that the icon is interactive...the trailing fact reports the service-scoped auth state, since the
|
|
729
|
+
// auth domain is shared across every channel of the same service. The needs-sign-in title drops "or test" - signing in IS the remedy being asked for.
|
|
730
|
+
const serviceLabel = getChannelServiceLabel(channel);
|
|
731
|
+
const loginTitle = needsLogin ?
|
|
732
|
+
"Click to open this channel in PrismCast's Chrome to sign in. " + serviceLabel + " needs sign-in (detected " + formatTimeAgo(authState.timestamp) + ")." :
|
|
733
|
+
"Click to open this channel in PrismCast's Chrome to sign in or test. " + serviceLabel +
|
|
734
|
+
(authState ? " verified " + formatTimeAgo(authState.timestamp) : " not yet verified") + ".";
|
|
704
735
|
displayLines.push("<button type=\"button\" class=\"btn-icon btn-icon-login" + loginColorClass + "\" data-auth-domain=\"" +
|
|
705
|
-
escapeHtml(authDomain) + "\" title=\"" + loginTitle + "\" aria-label=\"Login\" " +
|
|
706
|
-
"
|
|
736
|
+
escapeHtml(authDomain) + "\" title=\"" + escapeHtml(loginTitle) + "\" aria-label=\"Login\" " +
|
|
737
|
+
"data-click-action=\"" + ACTIONS.startChannelLogin + "\" data-channel-key=\"" + escapedKey + "\">" + ICON_LOGIN + "</button>");
|
|
707
738
|
}
|
|
708
739
|
else {
|
|
709
740
|
displayLines.push("<span class=\"btn-icon-placeholder\"></span>");
|
|
@@ -713,7 +744,8 @@ export function generateChannelRowHtml(key, profiles, entry) {
|
|
|
713
744
|
const channelHealthResult = getChannelHealth(key, authDomain);
|
|
714
745
|
const healthColorClass = (channelHealthResult?.status === "success") ? " health-success" : (channelHealthResult?.status === "failed") ? " health-failed" : "";
|
|
715
746
|
const healthTitle = channelHealthResult ?
|
|
716
|
-
(channelHealthResult.status === "success" ? "
|
|
747
|
+
"Last tune " + (channelHealthResult.status === "success" ? "succeeded " : "failed ") + formatTimeAgo(channelHealthResult.timestamp) + "." :
|
|
748
|
+
"Not yet tuned.";
|
|
717
749
|
displayLines.push("<span class=\"btn-icon btn-icon-health" + healthColorClass + "\" title=\"" + healthTitle +
|
|
718
750
|
"\" aria-label=\"Channel health\">" + ICON_HEALTH + "</span>");
|
|
719
751
|
}
|
|
@@ -724,25 +756,27 @@ export function generateChannelRowHtml(key, profiles, entry) {
|
|
|
724
756
|
// property overrides are independent concerns - a predefined channel can be disabled AND have customizations, and the toggle always reflects the visibility state.
|
|
725
757
|
if (isPredefined) {
|
|
726
758
|
if (isDisabled) {
|
|
727
|
-
displayLines.push("<button type=\"button\" class=\"btn-icon btn-icon-enable\" title=\"Enable\" aria-label=\"Enable\"
|
|
728
|
-
escapedKey + "
|
|
759
|
+
displayLines.push("<button type=\"button\" class=\"btn-icon btn-icon-enable\" title=\"Enable\" aria-label=\"Enable\" " +
|
|
760
|
+
"data-click-action=\"" + ACTIONS.togglePredefinedChannel + "\" data-channel-key=\"" + escapedKey + "\" data-enabled=\"true\">" + ICON_ENABLE + "</button>");
|
|
729
761
|
}
|
|
730
762
|
else {
|
|
731
|
-
displayLines.push("<button type=\"button\" class=\"btn-icon btn-icon-disable\" title=\"Disable\" aria-label=\"Disable\"
|
|
732
|
-
escapedKey + "
|
|
763
|
+
displayLines.push("<button type=\"button\" class=\"btn-icon btn-icon-disable\" title=\"Disable\" aria-label=\"Disable\" " +
|
|
764
|
+
"data-click-action=\"" + ACTIONS.togglePredefinedChannel + "\" data-channel-key=\"" + escapedKey + "\" data-enabled=\"false\">" + ICON_DISABLE + "</button>");
|
|
733
765
|
}
|
|
734
766
|
}
|
|
735
767
|
else if (isUser) {
|
|
736
|
-
displayLines.push("<button type=\"button\" class=\"btn-icon btn-icon-delete\" title=\"Delete\" aria-label=\"Delete\"
|
|
737
|
-
escapedKey + "
|
|
768
|
+
displayLines.push("<button type=\"button\" class=\"btn-icon btn-icon-delete\" title=\"Delete\" aria-label=\"Delete\" " +
|
|
769
|
+
"data-click-action=\"" + ACTIONS.deleteChannel + "\" data-channel-key=\"" + escapedKey + "\">" + ICON_DELETE + "</button>");
|
|
738
770
|
}
|
|
739
771
|
// Position 5: Copy URL dropdown (all channels).
|
|
740
772
|
displayLines.push("<div class=\"dropdown copy-dropdown\">");
|
|
741
773
|
displayLines.push("<button type=\"button\" class=\"btn-icon btn-icon-copy\" title=\"Copy stream URL\" aria-label=\"Copy stream URL\" " +
|
|
742
|
-
"
|
|
774
|
+
"data-click-action=\"" + ACTIONS.toggleDropdown + "\">" + ICON_COPY + "</button>");
|
|
743
775
|
displayLines.push("<div class=\"dropdown-menu copy-url-menu\">");
|
|
744
|
-
displayLines.push("<div class=\"dropdown-item\"
|
|
745
|
-
|
|
776
|
+
displayLines.push("<div class=\"dropdown-item\" data-click-action=\"" + ACTIONS.copyStreamUrl + "\" data-protocol=\"hls\" data-channel-key=\"" +
|
|
777
|
+
escapedKey + "\">Copy HLS URL</div>");
|
|
778
|
+
displayLines.push("<div class=\"dropdown-item\" data-click-action=\"" + ACTIONS.copyStreamUrl + "\" data-protocol=\"mpegts\" data-channel-key=\"" +
|
|
779
|
+
escapedKey + "\">Copy MPEG-TS URL</div>");
|
|
746
780
|
displayLines.push("</div>");
|
|
747
781
|
displayLines.push("</div>");
|
|
748
782
|
displayLines.push("</div>");
|
|
@@ -756,7 +790,7 @@ export function generateChannelRowHtml(key, profiles, entry) {
|
|
|
756
790
|
editLines.push("<td colspan=\"" + String(TOTAL_COLUMN_COUNT) + "\">");
|
|
757
791
|
editLines.push("<div class=\"channel-form\" style=\"margin: 0;\">");
|
|
758
792
|
editLines.push("<h3>Edit Channel: " + escapedKey + "</h3>");
|
|
759
|
-
editLines.push("<form id=\"edit-channel-form-" + escapedKey + "\"
|
|
793
|
+
editLines.push("<form id=\"edit-channel-form-" + escapedKey + "\" data-submit-action=\"" + ACTIONS.submitChannelFormEdit + "\" data-submit-prevent-default>");
|
|
760
794
|
editLines.push("<input type=\"hidden\" name=\"action\" value=\"edit\">");
|
|
761
795
|
editLines.push("<input type=\"hidden\" name=\"key\" value=\"" + escapedKey + "\">");
|
|
762
796
|
// For override channels, fetch the customization provenance once. The accessor returns which fields the user has explicitly customized (across canonical and
|
|
@@ -816,12 +850,14 @@ export function generateChannelRowHtml(key, profiles, entry) {
|
|
|
816
850
|
defaults: isOverride ? {
|
|
817
851
|
channelNumber: resetValueFor("channelNumber"),
|
|
818
852
|
channelSelector: resetValueFor("channelSelector"),
|
|
853
|
+
forceCapture: resetValueFor("forceCapture"),
|
|
819
854
|
guideTitle: resetValueFor("guideTitle"),
|
|
820
855
|
hdhrEnabled: resetValueFor("hdhrEnabled"),
|
|
821
856
|
logoUrl: resetValueFor("logoUrl"),
|
|
822
857
|
stationId: resetValueFor("stationId"),
|
|
823
858
|
tags: resetValueFor("tags")
|
|
824
859
|
} : undefined,
|
|
860
|
+
forceCapture: channel.forceCapture === true,
|
|
825
861
|
guideTitleValue: channel.guideTitle ?? "",
|
|
826
862
|
hdhrEnabled: getEffectiveHdhrEnabled(channel),
|
|
827
863
|
logoUrlValue: channel.logoUrl ?? "",
|
|
@@ -831,10 +867,11 @@ export function generateChannelRowHtml(key, profiles, entry) {
|
|
|
831
867
|
// Form buttons.
|
|
832
868
|
editLines.push("<div class=\"form-buttons\">");
|
|
833
869
|
editLines.push("<button type=\"submit\" class=\"btn btn-primary\">Save Changes</button>");
|
|
834
|
-
editLines.push("<button type=\"button\" class=\"btn btn-secondary\"
|
|
870
|
+
editLines.push("<button type=\"button\" class=\"btn btn-secondary\" data-click-action=\"" + ACTIONS.hideEditForm +
|
|
871
|
+
"\" data-channel-key=\"" + escapedKey + "\">Cancel</button>");
|
|
835
872
|
if (isOverride) {
|
|
836
|
-
editLines.push("<button type=\"button\" class=\"btn btn-secondary btn-revert\"
|
|
837
|
-
"
|
|
873
|
+
editLines.push("<button type=\"button\" class=\"btn btn-secondary btn-revert\" data-click-action=\"" + ACTIONS.revertChannel + "\" data-channel-key=\"" + escapedKey +
|
|
874
|
+
"\">Revert to Defaults</button>");
|
|
838
875
|
}
|
|
839
876
|
editLines.push("</div>");
|
|
840
877
|
editLines.push("</form>");
|
|
@@ -895,7 +932,8 @@ function getHdhrCounts(listing) {
|
|
|
895
932
|
}
|
|
896
933
|
/**
|
|
897
934
|
* Computes per-tag channel counts for the Quick Actions tag bulk toggles. For each tag in the active vocabulary, counts how many enabled, service-available
|
|
898
|
-
* channels have that tag in their effective tags. Returns undefined when the tags column is
|
|
935
|
+
* channels have that tag in their effective tags. Returns undefined when the tags column is hidden or the active tag vocabulary is empty (no tag toggles to
|
|
936
|
+
* update).
|
|
899
937
|
* @param listing - The channel listing entries.
|
|
900
938
|
* @returns A record mapping tag names to { count, total }, or undefined when hidden.
|
|
901
939
|
*/
|
|
@@ -974,7 +1012,7 @@ export function generateServiceFilterToolbar() {
|
|
|
974
1012
|
lines.push("<div class=\"dropdown provider-dropdown\">");
|
|
975
1013
|
const buttonText = hasFilter ? "Filtered" : "All Services";
|
|
976
1014
|
lines.push("<button type=\"button\" class=\"btn btn-sm toolbar-icon-btn\" id=\"provider-filter-btn\" " +
|
|
977
|
-
"title=\"Filter channels by streaming service\"
|
|
1015
|
+
"title=\"Filter channels by streaming service\" data-click-action=\"" + ACTIONS.toggleDropdown + "\">" + ICON_FILTER + " " + buttonText + " ▾</button>");
|
|
978
1016
|
lines.push("<div class=\"dropdown-menu provider-dropdown-menu\">");
|
|
979
1017
|
for (const tagInfo of allTags) {
|
|
980
1018
|
const isDirectTag = tagInfo.tag === "direct";
|
|
@@ -983,7 +1021,7 @@ export function generateServiceFilterToolbar() {
|
|
|
983
1021
|
const disabledAttr = isDirectTag ? " disabled" : "";
|
|
984
1022
|
lines.push("<label class=\"provider-option\">");
|
|
985
1023
|
lines.push("<input type=\"checkbox\" data-tag=\"" + escapeHtml(tagInfo.tag) + "\"" + checkedAttr + disabledAttr +
|
|
986
|
-
"
|
|
1024
|
+
" data-change-action=\"" + ACTIONS.toggleServiceTag + "\"> " + serviceDisplaySpan(tagInfo.displayName, tagInfo.domain, tagInfo.iconUrl));
|
|
987
1025
|
lines.push("</label>");
|
|
988
1026
|
}
|
|
989
1027
|
lines.push("</div>");
|
|
@@ -1000,7 +1038,7 @@ export function generateServiceFilterToolbar() {
|
|
|
1000
1038
|
lines.push("<span class=\"provider-chip\" data-tag=\"" + escapeHtml(tag) + "\">" +
|
|
1001
1039
|
serviceDisplaySpan(displayName, chipTag?.domain, chipTag?.iconUrl, true) +
|
|
1002
1040
|
"<button type=\"button\" class=\"chip-close\" title=\"Remove " + escapeHtml(displayName) + " from filter\" aria-label=\"Remove " +
|
|
1003
|
-
escapeHtml(displayName) + "\"
|
|
1041
|
+
escapeHtml(displayName) + "\" data-click-action=\"" + ACTIONS.removeServiceChip + "\" data-tag-name=\"" + escapeHtml(tag) + "\">×</button></span>");
|
|
1004
1042
|
}
|
|
1005
1043
|
}
|
|
1006
1044
|
lines.push("</div>");
|
|
@@ -1031,7 +1069,7 @@ export function generateChannelsPanel(channelMessage, channelError, editingChann
|
|
|
1031
1069
|
lines.push("<p>Define and manage streaming channels for the playlist. Customized channels are highlighted.</p>");
|
|
1032
1070
|
lines.push("<p class=\"description-hint\">Tip: Use the <strong>service filter</strong> above to show only channels from services you subscribe to — ", "this also controls which channels Channels DVR sees in the playlist. Use the <strong>service dropdown</strong> on any multi-service channel to choose ", "which streaming service delivers it (e.g., Comedy Central via Hulu vs Sling). Click the <strong>edit icon</strong> to customize any channel's name, ", "Gracenote station ID, URL, or other properties.</p>");
|
|
1033
1071
|
lines.push("</div>");
|
|
1034
|
-
// Toolbar
|
|
1072
|
+
// Toolbar dropdown menus: Manage Channels (primary actions), Import/Export (data I/O), and Quick Actions (bulk operations). Each dropdown has an
|
|
1035
1073
|
// inline SVG icon + label + chevron for visual discoverability. Grouped menus reduce visual clutter and separate channel management from data I/O and bulk
|
|
1036
1074
|
// operations.
|
|
1037
1075
|
lines.push("<div class=\"channel-toolbar\">");
|
|
@@ -1039,7 +1077,7 @@ export function generateChannelsPanel(channelMessage, channelError, editingChann
|
|
|
1039
1077
|
// Manage Channels dropdown - primary channel creation and setup actions.
|
|
1040
1078
|
lines.push("<div class=\"dropdown\">");
|
|
1041
1079
|
lines.push("<button type=\"button\" class=\"btn btn-primary btn-sm toolbar-icon-btn\" title=\"Add, browse, or set up channels\" " +
|
|
1042
|
-
"
|
|
1080
|
+
"data-click-action=\"" + ACTIONS.toggleDropdown + "\">" + ICON_MANAGE + " Manage Channels ▾</button>");
|
|
1043
1081
|
lines.push("<div class=\"dropdown-menu\">");
|
|
1044
1082
|
// Dropdown item icons: tinted variants of the shared icons for visual differentiation in menu items. Each uses a distinct color so items are scannable at
|
|
1045
1083
|
// a glance. These are local to this function because the tint colors are context-specific to this dropdown.
|
|
@@ -1054,47 +1092,47 @@ export function generateChannelsPanel(channelMessage, channelError, editingChann
|
|
|
1054
1092
|
const TINTED_TAG = "<svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"#9b59b6\" stroke-width=\"1.5\" " +
|
|
1055
1093
|
"stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M1.5 8.7V2.5a1 1 0 011-1h6.2a1 1 0 01.7.3l5.1 5.1a1 1 0 010 1.4l-5.5 5.5a1 " +
|
|
1056
1094
|
"1 0 01-1.4 0L1.8 9.4a1 1 0 01-.3-.7z\"/><circle cx=\"5\" cy=\"5\" r=\"1\"/></svg>";
|
|
1057
|
-
lines.push("<div class=\"dropdown-item dropdown-item-icon\"
|
|
1058
|
-
"
|
|
1059
|
-
lines.push("<div class=\"dropdown-item dropdown-item-icon\"
|
|
1095
|
+
lines.push("<div class=\"dropdown-item dropdown-item-icon\" data-click-action=\"" + ACTIONS.showAddChannelForm + "\" data-click-close-dropdown>" + TINTED_ADD +
|
|
1096
|
+
" Add Channel</div>");
|
|
1097
|
+
lines.push("<div class=\"dropdown-item dropdown-item-icon\" data-click-action=\"" + ACTIONS.openBrowseModal + "\" data-click-close-dropdown>" + TINTED_BROWSE +
|
|
1060
1098
|
" Browse Service Channels</div>");
|
|
1061
|
-
lines.push("<div class=\"dropdown-item dropdown-item-icon\"
|
|
1099
|
+
lines.push("<div class=\"dropdown-item dropdown-item-icon\" data-click-action=\"" + ACTIONS.openTagManager + "\" data-click-close-dropdown>" + TINTED_TAG +
|
|
1062
1100
|
" Manage Tags</div>");
|
|
1063
|
-
lines.push("<div class=\"dropdown-item dropdown-item-icon\"
|
|
1101
|
+
lines.push("<div class=\"dropdown-item dropdown-item-icon\" data-click-action=\"" + ACTIONS.openSetupWizard + "\" data-click-close-dropdown>" + TINTED_SETUP +
|
|
1064
1102
|
" Service Setup</div>");
|
|
1065
1103
|
lines.push("</div>");
|
|
1066
1104
|
lines.push("</div>");
|
|
1067
1105
|
// Import / Export dropdown - data I/O operations.
|
|
1068
1106
|
lines.push("<div class=\"dropdown\">");
|
|
1069
1107
|
lines.push("<button type=\"button\" class=\"btn btn-secondary btn-sm toolbar-icon-btn\" title=\"Import or export channel data\" " +
|
|
1070
|
-
"
|
|
1108
|
+
"data-click-action=\"" + ACTIONS.toggleDropdown + "\">" + ICON_TRANSFER + " Import / Export ▾</button>");
|
|
1071
1109
|
lines.push("<div class=\"dropdown-menu\">");
|
|
1072
1110
|
const TINTED_IMPORT = "<svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"#5b8def\" stroke-width=\"1.5\" " +
|
|
1073
1111
|
"stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M8 2v8M5 7l3 3 3-3\"/><path d=\"M2 11v2a1 1 0 001 1h10a1 1 0 001-1v-2\"/></svg>";
|
|
1074
1112
|
const TINTED_EXPORT = "<svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"#22a563\" stroke-width=\"1.5\" " +
|
|
1075
1113
|
"stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M8 10V2M5 5l3-3 3 3\"/><path d=\"M2 11v2a1 1 0 001 1h10a1 1 0 001-1v-2\"/></svg>";
|
|
1076
|
-
lines.push("<div class=\"dropdown-item dropdown-item-icon\"
|
|
1114
|
+
lines.push("<div class=\"dropdown-item dropdown-item-icon\" data-click-action=\"" + ACTIONS.triggerChannelsImport + "\" data-click-close-dropdown>" +
|
|
1077
1115
|
TINTED_IMPORT + " Import Channels (JSON)</div>");
|
|
1078
|
-
lines.push("<div class=\"dropdown-item dropdown-item-icon\"
|
|
1116
|
+
lines.push("<div class=\"dropdown-item dropdown-item-icon\" data-click-action=\"" + ACTIONS.triggerM3uImport + "\" data-click-close-dropdown>" +
|
|
1079
1117
|
TINTED_IMPORT + " Import M3U Playlist</div>");
|
|
1080
1118
|
lines.push("<label class=\"dropdown-option\"><input type=\"checkbox\" id=\"m3u-replace-duplicates\"> Replace duplicates on M3U import</label>");
|
|
1081
1119
|
lines.push("<div class=\"dropdown-divider\"></div>");
|
|
1082
|
-
lines.push("<div class=\"dropdown-item dropdown-item-icon\"
|
|
1120
|
+
lines.push("<div class=\"dropdown-item dropdown-item-icon\" data-click-action=\"" + ACTIONS.exportChannels + "\" data-click-close-dropdown>" +
|
|
1083
1121
|
TINTED_EXPORT + " Export Channels (JSON)</div>");
|
|
1084
1122
|
lines.push("</div>");
|
|
1085
1123
|
lines.push("</div>");
|
|
1086
|
-
lines.push("<input type=\"file\" id=\"import-m3u-file\" accept=\".m3u,.m3u8\" style=\"display: none;\"
|
|
1124
|
+
lines.push("<input type=\"file\" id=\"import-m3u-file\" accept=\".m3u,.m3u8\" style=\"display: none;\" data-change-action=\"" + ACTIONS.importM3u + "\">");
|
|
1087
1125
|
// Visible column set, used by Quick Actions to gate column-dependent options and by the table header to set initial hide classes.
|
|
1088
1126
|
const visibleCols = new Set(CONFIG.channels.visibleColumns);
|
|
1089
1127
|
// Quick Actions dropdown - bulk operations for predefined channels.
|
|
1090
1128
|
lines.push("<div class=\"dropdown quick-actions-dropdown\">");
|
|
1091
1129
|
lines.push("<button type=\"button\" class=\"btn btn-secondary btn-sm toolbar-icon-btn\" title=\"Bulk operations for predefined channels\" " +
|
|
1092
|
-
"
|
|
1130
|
+
"data-click-action=\"" + ACTIONS.toggleDropdown + "\">" + ICON_BOLT + " Quick Actions ▾</button>");
|
|
1093
1131
|
lines.push("<div class=\"dropdown-menu\">");
|
|
1094
1132
|
// Compute initial toggle counts for predefined channel scopes. The server is the single source of truth - the client renders what we return here.
|
|
1095
1133
|
const scopeCounts = getPredefinedScopeCounts();
|
|
1096
|
-
//
|
|
1097
|
-
// native checkbox toggle - the server response drives the update.
|
|
1134
|
+
// One toggle row per predefined scope: checkbox + label + count. Clicking toggles the group via bulkTogglePredefined(). The data-click-prevent-default
|
|
1135
|
+
// attribute stops the native checkbox toggle - the server response drives the update.
|
|
1098
1136
|
const scopes = [
|
|
1099
1137
|
{ count: scopeCounts.all.enabled, label: "All Predefined", scope: "all", total: scopeCounts.all.total },
|
|
1100
1138
|
{ count: scopeCounts.east.enabled, label: "East Variants", scope: "east", total: scopeCounts.east.total },
|
|
@@ -1102,8 +1140,8 @@ export function generateChannelsPanel(channelMessage, channelError, editingChann
|
|
|
1102
1140
|
];
|
|
1103
1141
|
for (const s of scopes) {
|
|
1104
1142
|
const checked = (s.count === s.total) ? " checked" : "";
|
|
1105
|
-
lines.push("<label class=\"provider-option\"
|
|
1106
|
-
"
|
|
1143
|
+
lines.push("<label class=\"provider-option\" data-click-action=\"" + ACTIONS.bulkTogglePredefined + "\" data-scope=\"" + s.scope + "\" data-click-prevent-default>" +
|
|
1144
|
+
"<input type=\"checkbox\" class=\"scope-toggle\" data-scope=\"" + s.scope + "\"" + checked + "> " +
|
|
1107
1145
|
s.label + "<span class=\"quick-action-count\" data-scope=\"" + s.scope + "\" data-enabled=\"" + String(s.count) + "\" data-total=\"" + String(s.total) +
|
|
1108
1146
|
"\">" + String(s.count) + " of " + String(s.total) + " enabled</span></label>");
|
|
1109
1147
|
}
|
|
@@ -1115,7 +1153,7 @@ export function generateChannelsPanel(channelMessage, channelError, editingChann
|
|
|
1115
1153
|
lines.push("<div class=\"bulk-assign-row\">");
|
|
1116
1154
|
lines.push("<span>Set all channels to:</span>");
|
|
1117
1155
|
lines.push("<select id=\"bulk-assign-select\" class=\"bulk-assign-select\" title=\"Switch all multi-service channels to this service\" " +
|
|
1118
|
-
"
|
|
1156
|
+
"data-change-action=\"" + ACTIONS.bulkAssignService + "\">");
|
|
1119
1157
|
lines.push("<option value=\"\">Select service</option>");
|
|
1120
1158
|
for (const tagInfo of allTags) {
|
|
1121
1159
|
const hidden = (hasFilter && !enabled.includes(tagInfo.tag) && (tagInfo.tag !== "direct")) ? " hidden" : "";
|
|
@@ -1133,8 +1171,9 @@ export function generateChannelsPanel(channelMessage, channelError, editingChann
|
|
|
1133
1171
|
lines.push("<div class=\"bulk-assign-row\">");
|
|
1134
1172
|
lines.push("<span>Auto-number from:</span>");
|
|
1135
1173
|
lines.push("<input type=\"number\" id=\"auto-number-start\" class=\"auto-number-input\" value=\"1\" min=\"0\" max=\"99999\" " +
|
|
1136
|
-
"placeholder=\"Clear\"
|
|
1137
|
-
lines.push("<button type=\"button\" class=\"btn btn-sm btn-secondary\"
|
|
1174
|
+
"placeholder=\"Clear\" data-click-stop-propagation>");
|
|
1175
|
+
lines.push("<button type=\"button\" class=\"btn btn-sm btn-secondary\" data-click-action=\"" + ACTIONS.autoNumberChannels +
|
|
1176
|
+
"\" data-click-close-dropdown>Apply</button>");
|
|
1138
1177
|
lines.push("</div>");
|
|
1139
1178
|
lines.push("</div>");
|
|
1140
1179
|
// HDHR bulk toggle - tri-state checkbox to enable/disable all channels for the HDHomeRun lineup. Visible when the HDHR column is shown.
|
|
@@ -1143,7 +1182,7 @@ export function generateChannelsPanel(channelMessage, channelError, editingChann
|
|
|
1143
1182
|
const hdhrCheckedAttr = (hdhrCounts.enabled === hdhrCounts.total) ? " checked" : "";
|
|
1144
1183
|
const hdhrIndeterminate = ((hdhrCounts.enabled > 0) && (hdhrCounts.enabled < hdhrCounts.total)) ? " data-indeterminate=\"true\"" : "";
|
|
1145
1184
|
lines.push("<div id=\"quick-action-hdhr\"" + hdhrVisible + ">");
|
|
1146
|
-
lines.push("<label class=\"provider-option\"
|
|
1185
|
+
lines.push("<label class=\"provider-option\" data-click-action=\"" + ACTIONS.bulkToggleHdhr + "\" data-click-prevent-default>");
|
|
1147
1186
|
lines.push("<input type=\"checkbox\" id=\"hdhr-bulk-toggle\"" + hdhrCheckedAttr + hdhrIndeterminate + "> ");
|
|
1148
1187
|
lines.push("Include all in HDHR/Plex");
|
|
1149
1188
|
lines.push("<span class=\"quick-action-count\" id=\"hdhr-bulk-count\">" + String(hdhrCounts.enabled) + " of " + String(hdhrCounts.total) + "</span>");
|
|
@@ -1177,8 +1216,8 @@ export function generateChannelsPanel(channelMessage, channelError, editingChann
|
|
|
1177
1216
|
const count = tagCountMap.get(tag) ?? 0;
|
|
1178
1217
|
const checkedAttr = (count === totalEnabled) ? " checked" : "";
|
|
1179
1218
|
const indeterminate = ((count > 0) && (count < totalEnabled)) ? " data-indeterminate=\"true\"" : "";
|
|
1180
|
-
lines.push("<label class=\"provider-option\"
|
|
1181
|
-
"
|
|
1219
|
+
lines.push("<label class=\"provider-option\" data-click-action=\"" + ACTIONS.bulkToggleTag +
|
|
1220
|
+
"\" data-tag-name=\"" + escapeHtml(tag) + "\" data-click-prevent-default>" +
|
|
1182
1221
|
"<input type=\"checkbox\" class=\"tag-bulk-toggle\" data-tag=\"" + escapeHtml(tag) + "\"" + checkedAttr + indeterminate + "> " +
|
|
1183
1222
|
escapeHtml(tag) +
|
|
1184
1223
|
"<span class=\"quick-action-count\" data-tag-count=\"" + escapeHtml(tag) + "\">" +
|
|
@@ -1232,7 +1271,7 @@ export function generateChannelsPanel(channelMessage, channelError, editingChann
|
|
|
1232
1271
|
const addFormVisible = (showAddForm === true) || (formErrors && formErrors.has("key") && !editingChannelKey);
|
|
1233
1272
|
lines.push("<div id=\"add-channel-form\" class=\"channel-form\" style=\"display: " + (addFormVisible ? "block" : "none") + ";\">");
|
|
1234
1273
|
lines.push("<h3>Add New Channel</h3>");
|
|
1235
|
-
lines.push("<form id=\"add-channel-form-el\"
|
|
1274
|
+
lines.push("<form id=\"add-channel-form-el\" data-submit-action=\"" + ACTIONS.submitChannelFormAdd + "\" data-submit-prevent-default>");
|
|
1236
1275
|
lines.push("<input type=\"hidden\" name=\"action\" value=\"add\">");
|
|
1237
1276
|
// Service pills. Clicking a service auto-fills the URL field, which triggers the existing URL-change infrastructure (datalist population, profile
|
|
1238
1277
|
// resolution). The pills use serviceDisplaySpan for icon rendering via the shared processServiceDisplays path. Guide-grid services fill with their
|
|
@@ -1247,7 +1286,7 @@ export function generateChannelsPanel(channelMessage, channelError, editingChann
|
|
|
1247
1286
|
}
|
|
1248
1287
|
const pillUrl = addFormGuideUrls[tagInfo.tag] ?? (tagInfo.domain ? "https://" + tagInfo.domain + "/" : "");
|
|
1249
1288
|
lines.push("<button type=\"button\" class=\"provider-pill\" data-slug=\"" + escapeHtml(tagInfo.tag) + "\" data-url=\"" + escapeHtml(pillUrl) +
|
|
1250
|
-
"\"
|
|
1289
|
+
"\" data-click-action=\"" + ACTIONS.selectServicePill + "\">" + serviceDisplaySpan(tagInfo.displayName, tagInfo.domain, tagInfo.iconUrl) + "</button>");
|
|
1251
1290
|
}
|
|
1252
1291
|
lines.push("</div>");
|
|
1253
1292
|
lines.push("</div>");
|
|
@@ -1277,17 +1316,19 @@ export function generateChannelsPanel(channelMessage, channelError, editingChann
|
|
|
1277
1316
|
lines.push("<div id=\"add-predefined-hint\" class=\"hint predefined-hint\" style=\"display: none;\"></div>");
|
|
1278
1317
|
// Profile dropdown.
|
|
1279
1318
|
lines.push(...generateProfileDropdown("add-profile", formValues?.get("profile") ?? "", profiles));
|
|
1280
|
-
// Advanced fields (station ID, guide title, logo URL, channel selector, channel number, tags, HDHR).
|
|
1319
|
+
// Advanced fields (station ID, guide title, logo URL, channel selector, channel number, tags, HDHR, force capture). The force-capture read mirrors
|
|
1320
|
+
// parseFormBody's exact-"true" test, so a re-render after a validation error preserves whatever the user had checked.
|
|
1281
1321
|
lines.push(...generateAdvancedFields("add", {
|
|
1282
1322
|
channelNumberValue: formValues?.get("channelNumber") ?? "",
|
|
1283
1323
|
channelSelectorValue: formValues?.get("channelSelector") ?? "",
|
|
1324
|
+
forceCapture: formValues?.get("forceCapture") === "true",
|
|
1284
1325
|
hdhrEnabled: formValues?.get("hdhrEnabled") !== "false",
|
|
1285
1326
|
stationIdValue: formValues?.get("stationId") ?? ""
|
|
1286
1327
|
}));
|
|
1287
1328
|
// Form buttons.
|
|
1288
1329
|
lines.push("<div class=\"form-buttons\">");
|
|
1289
1330
|
lines.push("<button type=\"submit\" class=\"btn btn-primary\">Add Channel</button>");
|
|
1290
|
-
lines.push("<button type=\"button\" class=\"btn btn-secondary\"
|
|
1331
|
+
lines.push("<button type=\"button\" class=\"btn btn-secondary\" data-click-action=\"" + ACTIONS.hideAddChannelForm + "\">Cancel</button>");
|
|
1291
1332
|
lines.push("</div>");
|
|
1292
1333
|
lines.push("</form>");
|
|
1293
1334
|
// Close the add-channel-form wrapper opened at the top of this builder.
|
|
@@ -1326,14 +1367,14 @@ export function generateChannelsPanel(channelMessage, channelError, editingChann
|
|
|
1326
1367
|
{ cssClass: "col-provider", field: "provider", label: "Service" },
|
|
1327
1368
|
...OPTIONAL_COLUMNS
|
|
1328
1369
|
];
|
|
1329
|
-
// All sortable headers use the same DOM structure: <th
|
|
1330
|
-
// .sort-label to modify only the label - never touching other children like the Tags filter dropdown. Clicking anywhere on the <th> triggers sort;
|
|
1370
|
+
// All sortable headers use the same DOM structure: <th data-click-action> wraps a <span class="sort-label"> for the label text. The sort update logic
|
|
1371
|
+
// targets .sort-label to modify only the label - never touching other children like the Tags filter dropdown. Clicking anywhere on the <th> triggers sort;
|
|
1331
1372
|
// additional children (like the filter button) use event.stopPropagation() to prevent sort when interacting with them.
|
|
1332
1373
|
for (const hdr of sortableHeaders) {
|
|
1333
1374
|
const isActive = (sortField === hdr.field);
|
|
1334
1375
|
const activeIndicator = isActive ? ((sortDir === "asc") ? " ▲" : " ▼") : "";
|
|
1335
1376
|
lines.push("<th class=\"" + hdr.cssClass + " sortable\" data-sort-field=\"" + hdr.field +
|
|
1336
|
-
"\"
|
|
1377
|
+
"\" data-click-action=\"" + ACTIONS.channelTableSort + "\" data-field=\"" + hdr.field + "\">");
|
|
1337
1378
|
lines.push("<span class=\"sort-label\">" + hdr.label + activeIndicator + "</span>");
|
|
1338
1379
|
// Tags header: additional filter dropdown alongside the sort label. The dropdown is a client-side-only view filter (transient, not persisted) that
|
|
1339
1380
|
// shows/hides rows based on their data-channel-tags attribute. The dropdown content comes from generateTagFilterContent() - the single source of truth
|
|
@@ -1341,7 +1382,7 @@ export function generateChannelsPanel(channelMessage, channelError, editingChann
|
|
|
1341
1382
|
if (hdr.field === "tags") {
|
|
1342
1383
|
lines.push("<div class=\"dropdown tag-filter-dropdown\" style=\"display: inline;\">");
|
|
1343
1384
|
lines.push("<button type=\"button\" class=\"btn-icon btn-tag-filter\" title=\"Filter by tag\" " +
|
|
1344
|
-
"
|
|
1385
|
+
"data-click-action=\"" + ACTIONS.toggleDropdown + "\" data-click-stop-propagation>" + ICON_FILTER + "</button>");
|
|
1345
1386
|
lines.push("<div class=\"dropdown-menu\" id=\"tag-filter-menu\">" + generateTagFilterContent() + "</div>");
|
|
1346
1387
|
lines.push("</div>");
|
|
1347
1388
|
// Playlist hint icon. Hidden by default, shown by applyTagColumnFilter() when the filter is active. Clicking opens a popover with the playlist URL
|
|
@@ -1349,7 +1390,7 @@ export function generateChannelsPanel(channelMessage, channelError, editingChann
|
|
|
1349
1390
|
lines.push("<div class=\"dropdown\" style=\"display: inline;\">");
|
|
1350
1391
|
lines.push("<button type=\"button\" class=\"btn-icon btn-playlist-hint\" id=\"playlist-hint-btn\" " +
|
|
1351
1392
|
"title=\"Playlist URL for this filter\" style=\"display: none;\" " +
|
|
1352
|
-
"
|
|
1393
|
+
"data-click-action=\"" + ACTIONS.showPlaylistHint + "\" data-click-stop-propagation>" + ICON_LINK + "</button>");
|
|
1353
1394
|
lines.push("<div class=\"dropdown-menu playlist-hint-menu\"></div>");
|
|
1354
1395
|
lines.push("</div>");
|
|
1355
1396
|
}
|
|
@@ -1359,15 +1400,15 @@ export function generateChannelsPanel(channelMessage, channelError, editingChann
|
|
|
1359
1400
|
lines.push("<th class=\"col-actions\"><span>Actions</span>");
|
|
1360
1401
|
lines.push("<div class=\"dropdown column-picker\">");
|
|
1361
1402
|
lines.push("<button type=\"button\" class=\"btn-icon btn-col-picker\" title=\"Table options\" aria-label=\"Table options\" " +
|
|
1362
|
-
"
|
|
1403
|
+
"data-click-action=\"" + ACTIONS.toggleDropdown + "\">⋮</button>");
|
|
1363
1404
|
lines.push("<div class=\"dropdown-menu column-picker-menu\">");
|
|
1364
|
-
lines.push("<label class=\"provider-option\"><input type=\"checkbox\" id=\"show-disabled-toggle\"
|
|
1405
|
+
lines.push("<label class=\"provider-option\"><input type=\"checkbox\" id=\"show-disabled-toggle\" data-change-action=\"" + ACTIONS.toggleDisabledVisibility + "\"> " +
|
|
1365
1406
|
"Show disabled channels</label>");
|
|
1366
1407
|
lines.push("<div class=\"dropdown-divider\"></div>");
|
|
1367
1408
|
for (const col of OPTIONAL_COLUMNS) {
|
|
1368
1409
|
const checked = visibleCols.has(col.field) ? " checked" : "";
|
|
1369
1410
|
lines.push("<label class=\"provider-option\"><input type=\"checkbox\" data-col-class=\"" + col.cssClass + "\" data-col-field=\"" + col.field +
|
|
1370
|
-
"\"
|
|
1411
|
+
"\" data-change-action=\"" + ACTIONS.toggleColumn + "\"" + checked + "> " + col.label + "</label>");
|
|
1371
1412
|
}
|
|
1372
1413
|
lines.push("</div>");
|
|
1373
1414
|
lines.push("</div>");
|
|
@@ -1403,7 +1444,7 @@ export function generateChannelsPanel(channelMessage, channelError, editingChann
|
|
|
1403
1444
|
const vocabulary = getActiveTagVocabulary();
|
|
1404
1445
|
lines.push("<div id=\"inline-tag-portal\" class=\"dropdown-menu inline-tag-menu\">");
|
|
1405
1446
|
for (const tag of vocabulary) {
|
|
1406
|
-
lines.push("<label class=\"provider-option\"
|
|
1447
|
+
lines.push("<label class=\"provider-option\" data-click-stop-propagation>" +
|
|
1407
1448
|
"<input type=\"checkbox\" class=\"inline-tag-checkbox\" data-tag=\"" + escapeHtml(tag) + "\"> " + escapeHtml(tag) + "</label>");
|
|
1408
1449
|
}
|
|
1409
1450
|
lines.push("</div>");
|