prismcast 1.9.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/dist/app.d.ts +1 -1
- package/dist/app.js +27 -9
- package/dist/app.js.map +1 -1
- package/dist/browser/cdp.js +7 -7
- 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 +42 -43
- 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.js +4 -4
- 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 +61 -62
- package/dist/browser/tuning/directv.js.map +1 -1
- package/dist/browser/tuning/fox.d.ts +1 -1
- package/dist/browser/tuning/fox.js +171 -14
- package/dist/browser/tuning/fox.js.map +1 -1
- package/dist/browser/tuning/hbo.d.ts +1 -1
- package/dist/browser/tuning/hbo.js +6 -6
- package/dist/browser/tuning/hbo.js.map +1 -1
- package/dist/browser/tuning/hulu.d.ts +1 -1
- package/dist/browser/tuning/hulu.js +117 -110
- 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 +28 -28
- 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 +73 -40
- 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 +4 -3
- package/dist/config/health.js +75 -62
- package/dist/config/health.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +19 -12
- 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 +110 -12
- package/dist/config/persistence.js +351 -32
- package/dist/config/persistence.js.map +1 -1
- 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 +2 -2
- package/dist/config/servicePacks.js +28 -28
- package/dist/config/servicePacks.js.map +1 -1
- package/dist/config/services.d.ts +62 -36
- package/dist/config/services.js +150 -136
- package/dist/config/services.js.map +1 -1
- package/dist/config/sites.d.ts +3 -3
- package/dist/config/sites.js +16 -16
- package/dist/config/sites.js.map +1 -1
- package/dist/config/userChannels.d.ts +500 -41
- package/dist/config/userChannels.js +1201 -312
- package/dist/config/userChannels.js.map +1 -1
- package/dist/config/userConfig.d.ts +103 -10
- package/dist/config/userConfig.js +277 -117
- package/dist/config/userConfig.js.map +1 -1
- package/dist/config/userProfiles.d.ts +4 -2
- package/dist/config/userProfiles.js +72 -26
- package/dist/config/userProfiles.js.map +1 -1
- package/dist/hdhr/channelMap.js +8 -11
- 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 +6 -10
- 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 +42 -64
- package/dist/native/index.js.map +1 -1
- package/dist/native/probe.d.ts +27 -9
- package/dist/native/probe.js +158 -67
- package/dist/native/probe.js.map +1 -1
- package/dist/native/proxy.d.ts +4 -2
- package/dist/native/proxy.js +52 -49
- 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/auth.js +6 -10
- 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 +1 -1
- 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 +9 -9
- package/dist/routes/config/channels/table.js +124 -76
- 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 +2 -2
- package/dist/routes/config/index.js.map +1 -1
- package/dist/routes/config/services.js +69 -68
- package/dist/routes/config/services.js.map +1 -1
- package/dist/routes/config/settings.d.ts +1 -1
- package/dist/routes/config/settings.js +45 -38
- package/dist/routes/config/settings.js.map +1 -1
- package/dist/routes/debug.js +12 -11
- package/dist/routes/debug.js.map +1 -1
- package/dist/routes/icons.js +2 -2
- package/dist/routes/icons.js.map +1 -1
- package/dist/routes/logs.js +19 -30
- package/dist/routes/logs.js.map +1 -1
- package/dist/routes/mpegts.js +1 -1
- package/dist/routes/playlist.d.ts +1 -1
- package/dist/routes/playlist.js +37 -26
- package/dist/routes/playlist.js.map +1 -1
- package/dist/routes/root/content.js +27 -6
- 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 +16 -16
- package/dist/routes/root/scripts/channels.js.map +1 -1
- package/dist/routes/root/scripts/config.js +59 -59
- 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 +24 -10
- 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 +86 -488
- package/dist/routes/root/scripts/status.js.map +1 -1
- package/dist/routes/root/styles.js +5 -5
- package/dist/routes/services.js +18 -15
- package/dist/routes/services.js.map +1 -1
- 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/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 +171 -188
- 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 +499 -383
- 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 +3 -3
- package/dist/streaming/hls.js +81 -70
- package/dist/streaming/hls.js.map +1 -1
- package/dist/streaming/hlsResume.d.ts +3 -3
- package/dist/streaming/hlsResume.js +5 -5
- 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 +80 -54
- 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 +39 -41
- 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/setup.d.ts +9 -8
- package/dist/streaming/setup.js +97 -49
- package/dist/streaming/setup.js.map +1 -1
- package/dist/streaming/showInfo.d.ts +9 -3
- package/dist/streaming/showInfo.js +26 -18
- package/dist/streaming/showInfo.js.map +1 -1
- package/dist/streaming/statusEmitter.d.ts +4 -4
- package/dist/streaming/statusEmitter.js +2 -2
- package/dist/types/channels.d.ts +133 -34
- package/dist/types/channels.js +39 -1
- package/dist/types/channels.js.map +1 -1
- package/dist/types/config.d.ts +20 -1
- 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 +5 -4
- package/dist/types/selection.d.ts +48 -4
- package/dist/types/streaming.d.ts +1 -1
- 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 +1 -1
- 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 +6 -4
- 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 +58 -30
- package/dist/utils/fileLogger.js.map +1 -1
- package/dist/utils/format.d.ts +1 -1
- package/dist/utils/format.js +2 -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 +16 -20
- 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 +2 -2
- 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 -3
- 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/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 +14 -27
- package/dist/utils/version.js.map +1 -1
- package/package.json +13 -7
- package/dist/native/intercept.d.ts +0 -35
- package/dist/native/intercept.js +0 -181
- package/dist/native/intercept.js.map +0 -1
- package/dist/routes/config/channels/routes.js +0 -1505
- package/dist/routes/config/channels/routes.js.map +0 -1
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { getActiveTagVocabulary, getPredefinedChannel, getTagRegistry, isInVocabulary, setTagRegistry, tagsMatch, transformChannelTags } from "../../../../config/userChannels.js";
|
|
2
|
+
import { sendConflictError, sendError, sendNotFoundError, sendSuccess, sendValidationError } from "../../http/envelope.js";
|
|
3
|
+
import { LOG } from "../../../../utils/index.js";
|
|
4
|
+
import { PREDEFINED_TAGS } from "../../../../channels/index.js";
|
|
5
|
+
import { route } from "../http/handler.js";
|
|
6
|
+
// Pattern for valid tag names: must start and end with alphanumeric, may contain letters, numbers, spaces, and hyphens in between. Enforced on create and rename.
|
|
7
|
+
const TAG_NAME_PATTERN = /^[a-zA-Z0-9]([a-zA-Z0-9 -]*[a-zA-Z0-9])?$/;
|
|
8
|
+
// Maximum tag name length, enforced on create and rename.
|
|
9
|
+
const TAG_NAME_MAX_LENGTH = 30;
|
|
10
|
+
/**
|
|
11
|
+
* Validates a submitted tag name against the project's tag-name rules (required, length cap, character pattern). Returns an error message on failure or
|
|
12
|
+
* undefined when the name passes. Used by both the create and rename endpoints so the rules live in exactly one place.
|
|
13
|
+
* @param tag - The tag name to validate. Caller is expected to have already trimmed whitespace.
|
|
14
|
+
* @returns The user-facing validation error, or undefined when valid.
|
|
15
|
+
*/
|
|
16
|
+
function validateTagName(tag) {
|
|
17
|
+
if (tag.length === 0) {
|
|
18
|
+
return "Tag name is required.";
|
|
19
|
+
}
|
|
20
|
+
if (tag.length > TAG_NAME_MAX_LENGTH) {
|
|
21
|
+
return "Tag name must be " + String(TAG_NAME_MAX_LENGTH) + " characters or less.";
|
|
22
|
+
}
|
|
23
|
+
if (!TAG_NAME_PATTERN.test(tag)) {
|
|
24
|
+
return "Tag name must start and end with a letter or number, and contain only letters, numbers, spaces, and hyphens.";
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Registers the tag management endpoints on the Express application.
|
|
30
|
+
* @param app - The Express application.
|
|
31
|
+
*/
|
|
32
|
+
export function registerTagRoutes(app) {
|
|
33
|
+
// GET /config/tags - Returns the tag vocabulary and registry state.
|
|
34
|
+
app.get("/config/tags", route("read tags", (_req, res) => {
|
|
35
|
+
res.json({
|
|
36
|
+
active: getActiveTagVocabulary(),
|
|
37
|
+
predefined: [...PREDEFINED_TAGS],
|
|
38
|
+
registry: getTagRegistry(),
|
|
39
|
+
success: true
|
|
40
|
+
});
|
|
41
|
+
}));
|
|
42
|
+
// POST /config/tags - Create a new user tag. Validates the tag name and rejects duplicates against the active vocabulary or deleted predefined tags (which
|
|
43
|
+
// should be restored, not re-created).
|
|
44
|
+
app.post("/config/tags", route("create tag", async (req, res) => {
|
|
45
|
+
const body = req.body;
|
|
46
|
+
const tag = typeof body.tag === "string" ? body.tag.trim() : "";
|
|
47
|
+
const nameError = validateTagName(tag);
|
|
48
|
+
if (nameError) {
|
|
49
|
+
sendValidationError(res, nameError);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (isInVocabulary(tag)) {
|
|
53
|
+
sendConflictError(res, "Tag '" + tag + "' already exists.");
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const registry = getTagRegistry();
|
|
57
|
+
if (registry.deletedTags.some((t) => tagsMatch(t, tag))) {
|
|
58
|
+
sendConflictError(res, "Tag '" + tag + "' is a deleted predefined tag. Use restore instead of creating a new one.");
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
registry.tags.push(tag);
|
|
62
|
+
await setTagRegistry(registry);
|
|
63
|
+
LOG.info("Created tag: %s.", tag);
|
|
64
|
+
sendSuccess(res, { tags: true });
|
|
65
|
+
}));
|
|
66
|
+
// DELETE /config/tags/:tag - Delete a tag from the vocabulary and cascade to all channel assignments. For predefined tags, the tag is added to deletedTags. For
|
|
67
|
+
// user-created tags, the tag is removed from the user registry.
|
|
68
|
+
app.delete("/config/tags/:tag", route("delete tag", async (req, res) => {
|
|
69
|
+
const tag = req.params.tag?.trim() ?? "";
|
|
70
|
+
if (tag.length === 0) {
|
|
71
|
+
sendValidationError(res, "Tag name is required.");
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const registry = getTagRegistry();
|
|
75
|
+
const isPredefined = PREDEFINED_TAGS.some((t) => tagsMatch(t, tag));
|
|
76
|
+
const isUserTag = registry.tags.some((t) => tagsMatch(t, tag));
|
|
77
|
+
if (!isPredefined && !isUserTag) {
|
|
78
|
+
sendNotFoundError(res, "Tag '" + tag + "' not found.");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (isPredefined) {
|
|
82
|
+
const canonicalTag = PREDEFINED_TAGS.find((t) => tagsMatch(t, tag)) ?? tag;
|
|
83
|
+
// Already deleted - no-op, return current state without unnecessary I/O.
|
|
84
|
+
if (registry.deletedTags.some((t) => tagsMatch(t, tag))) {
|
|
85
|
+
sendSuccess(res, { tags: true });
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
registry.deletedTags.push(canonicalTag);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
registry.tags = registry.tags.filter((t) => !tagsMatch(t, tag));
|
|
92
|
+
}
|
|
93
|
+
await setTagRegistry(registry);
|
|
94
|
+
// Cascade: strip the deleted tag from every channel that has it. transformChannelTags handles loading, delta normalization, and persistence.
|
|
95
|
+
const { affectedKeys, error } = await transformChannelTags((entry) => entry.channel.tags?.some((t) => tagsMatch(t, tag)) === true, (tags) => tags.filter((t) => !tagsMatch(t, tag)));
|
|
96
|
+
if (error) {
|
|
97
|
+
sendError(res, 400, { error });
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
LOG.info("Deleted tag '%s' from vocabulary and %d channel assignments.", tag, affectedKeys.length);
|
|
101
|
+
sendSuccess(res, { affectedKeys, tags: true });
|
|
102
|
+
}));
|
|
103
|
+
// POST /config/tags/restore - Restore a previously deleted predefined tag. Removes it from deletedTags so it reappears in the active vocabulary, then cascade-
|
|
104
|
+
// restores the tag on predefined channels whose definition includes it.
|
|
105
|
+
app.post("/config/tags/restore", route("restore tag", async (req, res) => {
|
|
106
|
+
const body = req.body;
|
|
107
|
+
const tag = typeof body.tag === "string" ? body.tag.trim() : "";
|
|
108
|
+
if (tag.length === 0) {
|
|
109
|
+
sendValidationError(res, "Tag name is required.");
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const registry = getTagRegistry();
|
|
113
|
+
if (!registry.deletedTags.some((t) => tagsMatch(t, tag))) {
|
|
114
|
+
sendNotFoundError(res, "Tag '" + tag + "' is not a deleted predefined tag.");
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
registry.deletedTags = registry.deletedTags.filter((t) => !tagsMatch(t, tag));
|
|
118
|
+
await setTagRegistry(registry);
|
|
119
|
+
// Resolve the canonical predefined tag name for the restored tag so channel data uses the predefined casing.
|
|
120
|
+
const canonicalTag = PREDEFINED_TAGS.find((t) => tagsMatch(t, tag)) ?? tag;
|
|
121
|
+
// Cascade-restore: add the tag back to predefined channels whose definition includes it but whose current resolved tags don't (stripped during cascade
|
|
122
|
+
// delete). The normalizer strips the tags delta when the result matches the predefined definition, reverting the channel to its default state.
|
|
123
|
+
const { affectedKeys, error } = await transformChannelTags((entry) => {
|
|
124
|
+
const predefined = getPredefinedChannel(entry.key);
|
|
125
|
+
return (predefined?.tags?.some((t) => tagsMatch(t, tag)) === true) &&
|
|
126
|
+
(entry.channel.tags?.some((t) => tagsMatch(t, tag)) !== true);
|
|
127
|
+
}, (tags) => [...tags, canonicalTag]);
|
|
128
|
+
if (error) {
|
|
129
|
+
sendError(res, 400, { error });
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
LOG.info("Restored predefined tag '%s' on %d channels.", tag, affectedKeys.length);
|
|
133
|
+
sendSuccess(res, { affectedKeys, tags: true });
|
|
134
|
+
}));
|
|
135
|
+
// POST /config/tags/rename - Rename a tag atomically across the vocabulary and all channel assignments. Case-only renames are allowed (the identity is shared).
|
|
136
|
+
app.post("/config/tags/rename", route("rename tag", async (req, res) => {
|
|
137
|
+
const body = req.body;
|
|
138
|
+
const oldTag = typeof body.oldTag === "string" ? body.oldTag.trim() : "";
|
|
139
|
+
const newTag = typeof body.newTag === "string" ? body.newTag.trim() : "";
|
|
140
|
+
if (!oldTag || !newTag) {
|
|
141
|
+
sendValidationError(res, "Both old and new tag names are required.");
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (oldTag === newTag) {
|
|
145
|
+
sendValidationError(res, "New tag name must differ from the old name.");
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const newTagError = validateTagName(newTag);
|
|
149
|
+
if (newTagError) {
|
|
150
|
+
sendValidationError(res, newTagError);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (!isInVocabulary(oldTag)) {
|
|
154
|
+
sendNotFoundError(res, "Tag '" + oldTag + "' not found.");
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
// Collision check: allow case-only renames of the same identity, reject collisions with a different existing tag.
|
|
158
|
+
if (isInVocabulary(newTag) && !tagsMatch(oldTag, newTag)) {
|
|
159
|
+
sendConflictError(res, "Tag '" + newTag + "' already exists.");
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const registry = getTagRegistry();
|
|
163
|
+
const oldIsPredefined = PREDEFINED_TAGS.some((t) => tagsMatch(t, oldTag));
|
|
164
|
+
if (oldIsPredefined) {
|
|
165
|
+
// Predefined tag: "delete" the old (add to deletedTags using the canonical predefined form) and create the new as a user tag.
|
|
166
|
+
const canonicalOld = PREDEFINED_TAGS.find((t) => tagsMatch(t, oldTag)) ?? oldTag;
|
|
167
|
+
if (!registry.deletedTags.some((t) => tagsMatch(t, oldTag))) {
|
|
168
|
+
registry.deletedTags.push(canonicalOld);
|
|
169
|
+
}
|
|
170
|
+
registry.tags.push(newTag);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
registry.tags = registry.tags.map((t) => tagsMatch(t, oldTag) ? newTag : t);
|
|
174
|
+
}
|
|
175
|
+
await setTagRegistry(registry);
|
|
176
|
+
const { affectedKeys, error } = await transformChannelTags((entry) => entry.channel.tags?.some((t) => tagsMatch(t, oldTag)) === true, (tags) => tags.map((t) => tagsMatch(t, oldTag) ? newTag : t));
|
|
177
|
+
if (error) {
|
|
178
|
+
sendError(res, 400, { error });
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
LOG.info("Renamed tag '%s' to '%s' across %d channels.", oldTag, newTag, affectedKeys.length);
|
|
182
|
+
sendSuccess(res, { affectedKeys, tags: true });
|
|
183
|
+
}));
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=tags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tags.js","sourceRoot":"","sources":["../../../../../src/routes/config/channels/endpoints/tags.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Request, RequestHandler, Response } from "express";
|
|
2
|
+
/**
|
|
3
|
+
* Wraps an endpoint handler with uniform error handling. The returned RequestHandler awaits the handler's (possibly sync) completion and catches any exception,
|
|
4
|
+
* routing it through sendErrorResponse using the provided action label. Accepting both sync and async handlers lets endpoints that don't need to await (e.g.,
|
|
5
|
+
* reads that respond immediately) skip the async keyword without a lint warning.
|
|
6
|
+
* @param action - Human-readable description of the action for log messages (e.g., "toggle channel", "save channel"). Appears in the error log and 500 response.
|
|
7
|
+
* @param handler - The endpoint implementation. Receives the Express request and response. Should early-return after calling sendValidationError/sendSuccess.
|
|
8
|
+
* @returns An Express RequestHandler suitable for registering with app.get/post/put/patch/delete.
|
|
9
|
+
*/
|
|
10
|
+
export declare function route(action: string, handler: (req: Request, res: Response) => Promise<void> | void): RequestHandler;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { sendErrorResponse } from "../../http/envelope.js";
|
|
2
|
+
/**
|
|
3
|
+
* Wraps an endpoint handler with uniform error handling. The returned RequestHandler awaits the handler's (possibly sync) completion and catches any exception,
|
|
4
|
+
* routing it through sendErrorResponse using the provided action label. Accepting both sync and async handlers lets endpoints that don't need to await (e.g.,
|
|
5
|
+
* reads that respond immediately) skip the async keyword without a lint warning.
|
|
6
|
+
* @param action - Human-readable description of the action for log messages (e.g., "toggle channel", "save channel"). Appears in the error log and 500 response.
|
|
7
|
+
* @param handler - The endpoint implementation. Receives the Express request and response. Should early-return after calling sendValidationError/sendSuccess.
|
|
8
|
+
* @returns An Express RequestHandler suitable for registering with app.get/post/put/patch/delete.
|
|
9
|
+
*/
|
|
10
|
+
export function route(action, handler) {
|
|
11
|
+
return async (req, res) => {
|
|
12
|
+
try {
|
|
13
|
+
await handler(req, res);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
sendErrorResponse(res, error, action);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../../../../../src/routes/config/channels/http/handler.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D;;;;;;;GAOG;AACH,MAAM,UAAU,KAAK,CAAC,MAAc,EAAE,OAA8D;IAElG,OAAO,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QAExB,IAAI,CAAC;YAEH,MAAM,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC1B,CAAC;QAAC,OAAM,KAAK,EAAE,CAAC;YAEd,iBAAiB,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QACxC,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ChannelDelta, ResolvedChannel, StoredChannel } from "../../../../types/index.ts";
|
|
2
|
+
export declare const M3U_FIELDS: readonly ["channelNumber", "guideTitle", "logoUrl", "name", "stationId", "tvgShift"];
|
|
3
|
+
export declare const PLAYLIST_HINT = " Reload the playlist in Channels DVR to see this change.";
|
|
4
|
+
/**
|
|
5
|
+
* Returns PLAYLIST_HINT when a stored channel entry contains any M3U-affecting fields, otherwise an empty string. Used when reverting or removing an override to
|
|
6
|
+
* decide whether the response should include the reload hint.
|
|
7
|
+
* @param stored - The stored channel data (may be a delta or full definition).
|
|
8
|
+
* @returns The hint string, or an empty string when no M3U-affecting fields are present.
|
|
9
|
+
*/
|
|
10
|
+
export declare function playlistHintForStored(stored: StoredChannel | undefined): string;
|
|
11
|
+
/**
|
|
12
|
+
* Returns PLAYLIST_HINT when a delta contains any M3U-affecting field changes, otherwise an empty string. Used after storing a delta override to decide whether
|
|
13
|
+
* the response should include the reload hint.
|
|
14
|
+
* @param delta - The channel delta that was stored.
|
|
15
|
+
* @returns The hint string, or an empty string when no M3U-affecting fields changed.
|
|
16
|
+
*/
|
|
17
|
+
export declare function playlistHintForDelta(delta: ChannelDelta): string;
|
|
18
|
+
/**
|
|
19
|
+
* Returns PLAYLIST_HINT when any M3U-affecting field differs between two channel snapshots, otherwise an empty string. Used during an edit of a user channel
|
|
20
|
+
* (non-delta) where the entire channel record is rewritten - we compare before and after to decide whether the change is playlist-visible.
|
|
21
|
+
* @param previous - The channel record before the mutation, or undefined when adding a new channel.
|
|
22
|
+
* @param next - The channel record after the mutation.
|
|
23
|
+
* @returns The hint string, or an empty string when no M3U-affecting fields changed.
|
|
24
|
+
*/
|
|
25
|
+
export declare function playlistHintForChange(previous: ResolvedChannel | undefined, next: ResolvedChannel): string;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/* Fields that appear in the generated M3U playlist and affect Channels DVR's view of the channel. When any of these change, we append PLAYLIST_HINT so the
|
|
2
|
+
* user knows to reload the playlist in Channels DVR for the change to take effect. The `satisfies` constraint ensures every entry is a real ResolvedChannel
|
|
3
|
+
* property name, so renaming a Channel identity field flags this list at compile time instead of silently orphaning the check.
|
|
4
|
+
*/
|
|
5
|
+
export const M3U_FIELDS = ["channelNumber", "guideTitle", "logoUrl", "name", "stationId", "tvgShift"];
|
|
6
|
+
// Appended to success messages when an M3U-affecting field changed. Begins with a leading space so callers can concatenate directly onto their message.
|
|
7
|
+
export const PLAYLIST_HINT = " Reload the playlist in Channels DVR to see this change.";
|
|
8
|
+
/**
|
|
9
|
+
* Returns PLAYLIST_HINT when a stored channel entry contains any M3U-affecting fields, otherwise an empty string. Used when reverting or removing an override to
|
|
10
|
+
* decide whether the response should include the reload hint.
|
|
11
|
+
* @param stored - The stored channel data (may be a delta or full definition).
|
|
12
|
+
* @returns The hint string, or an empty string when no M3U-affecting fields are present.
|
|
13
|
+
*/
|
|
14
|
+
export function playlistHintForStored(stored) {
|
|
15
|
+
return stored && M3U_FIELDS.some((f) => f in stored) ? PLAYLIST_HINT : "";
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Returns PLAYLIST_HINT when a delta contains any M3U-affecting field changes, otherwise an empty string. Used after storing a delta override to decide whether
|
|
19
|
+
* the response should include the reload hint.
|
|
20
|
+
* @param delta - The channel delta that was stored.
|
|
21
|
+
* @returns The hint string, or an empty string when no M3U-affecting fields changed.
|
|
22
|
+
*/
|
|
23
|
+
export function playlistHintForDelta(delta) {
|
|
24
|
+
return M3U_FIELDS.some((f) => f in delta) ? PLAYLIST_HINT : "";
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Returns PLAYLIST_HINT when any M3U-affecting field differs between two channel snapshots, otherwise an empty string. Used during an edit of a user channel
|
|
28
|
+
* (non-delta) where the entire channel record is rewritten - we compare before and after to decide whether the change is playlist-visible.
|
|
29
|
+
* @param previous - The channel record before the mutation, or undefined when adding a new channel.
|
|
30
|
+
* @param next - The channel record after the mutation.
|
|
31
|
+
* @returns The hint string, or an empty string when no M3U-affecting fields changed.
|
|
32
|
+
*/
|
|
33
|
+
export function playlistHintForChange(previous, next) {
|
|
34
|
+
// Adds always change the playlist - a new channel is inherently a new M3U entry.
|
|
35
|
+
if (!previous) {
|
|
36
|
+
return PLAYLIST_HINT;
|
|
37
|
+
}
|
|
38
|
+
return M3U_FIELDS.some((f) => previous[f] !== next[f]) ? PLAYLIST_HINT : "";
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=playlistHint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playlistHint.js","sourceRoot":"","sources":["../../../../../src/routes/config/channels/http/playlistHint.ts"],"names":[],"mappings":"AAUA;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAE,eAAe,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,CAAwD,CAAC;AAE9J,wJAAwJ;AACxJ,MAAM,CAAC,MAAM,aAAa,GAAG,0DAA0D,CAAC;AAExF;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAiC;IAErE,OAAO,MAAM,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;AAC5E,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAmB;IAEtD,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;AACjE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAqC,EAAE,IAAqB;IAEhG,iFAAiF;IACjF,IAAG,CAAC,QAAQ,EAAE,CAAC;QAEb,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9E,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds a service filter warning payload when a URL's service tag is not in the active filter. Returns undefined when no filter is active, the URL resolves to
|
|
3
|
+
* the "direct" (non-service) tag, or the tag is already enabled.
|
|
4
|
+
* @param url - The channel URL to derive the service tag from.
|
|
5
|
+
* @returns The warning with display label and tag, or undefined when no warning is needed.
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildServiceFilterWarning(url: string): {
|
|
8
|
+
serviceLabel: string;
|
|
9
|
+
serviceTag: string;
|
|
10
|
+
} | undefined;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/* Copyright(C) 2024-2026, HJD (https://github.com/hjdhjd). All rights reserved.
|
|
2
|
+
*
|
|
3
|
+
* serviceWarning.ts: Shared helper for building the "service not in active filter" warning.
|
|
4
|
+
*
|
|
5
|
+
* When a user adds a channel whose service tag isn't in the active service filter, the channel would be hidden from the playlist and table immediately. The
|
|
6
|
+
* warning payload tells the client to show a toast with a one-click enable action. Both the browse endpoint (bulk add) and the CRUD endpoint (single add) use this
|
|
7
|
+
* helper so the warning policy is defined exactly once.
|
|
8
|
+
*/
|
|
9
|
+
import { getEnabledServices, getServiceDisplayName, isServiceTagEnabled } from "../../../../config/services.js";
|
|
10
|
+
import { getDomainConfig } from "../../../../config/sites.js";
|
|
11
|
+
/**
|
|
12
|
+
* Builds a service filter warning payload when a URL's service tag is not in the active filter. Returns undefined when no filter is active, the URL resolves to
|
|
13
|
+
* the "direct" (non-service) tag, or the tag is already enabled.
|
|
14
|
+
* @param url - The channel URL to derive the service tag from.
|
|
15
|
+
* @returns The warning with display label and tag, or undefined when no warning is needed.
|
|
16
|
+
*/
|
|
17
|
+
export function buildServiceFilterWarning(url) {
|
|
18
|
+
// No active filter means every service is visible - no warning needed.
|
|
19
|
+
if (getEnabledServices().length === 0) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
const tag = getDomainConfig(url)?.serviceTag;
|
|
23
|
+
if (tag && (tag !== "direct") && !isServiceTagEnabled(tag)) {
|
|
24
|
+
return { serviceLabel: getServiceDisplayName(url), serviceTag: tag };
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=serviceWarning.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serviceWarning.js","sourceRoot":"","sources":["../../../../../src/routes/config/channels/http/serviceWarning.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAChH,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CAAC,GAAW;IAEnD,uEAAuE;IACvE,IAAG,kBAAkB,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAErC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,GAAG,GAAG,eAAe,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC;IAE7C,IAAG,GAAG,IAAI,CAAC,GAAG,KAAK,QAAQ,CAAC,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,EAAE,CAAC;QAE1D,OAAO,EAAE,YAAY,EAAE,qBAAqB,CAAC,GAAG,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC;IACvE,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export type { ChannelRowHtml, ChannelTableCounts, ChannelTablePatch } from "./table.
|
|
2
|
-
export { OPTIONAL_COLUMNS, VALID_OPTIONAL_COLUMNS, buildChannelTablePatch, buildChannelTableState, generateChannelRowHtml, generateChannelsPanel, generateServiceFilterToolbar, generateTagFilterContent, generateTagManagerBody } from "./table.
|
|
3
|
-
export { setupChannelRoutes } from "./
|
|
1
|
+
export type { ChannelRowHtml, ChannelTableCounts, ChannelTablePatch } from "./table.ts";
|
|
2
|
+
export { OPTIONAL_COLUMNS, VALID_OPTIONAL_COLUMNS, buildChannelTablePatch, buildChannelTableState, generateChannelRowHtml, generateChannelsPanel, generateServiceFilterToolbar, generateTagFilterContent, generateTagManagerBody } from "./table.ts";
|
|
3
|
+
export { setupChannelRoutes } from "./setup.ts";
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { OPTIONAL_COLUMNS, VALID_OPTIONAL_COLUMNS, buildChannelTablePatch, buildChannelTableState, generateChannelRowHtml, generateChannelsPanel, generateServiceFilterToolbar, generateTagFilterContent, generateTagManagerBody } from "./table.js";
|
|
2
|
-
export { setupChannelRoutes } from "./
|
|
2
|
+
export { setupChannelRoutes } from "./setup.js";
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/routes/config/channels/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,qBAAqB,EAC9I,4BAA4B,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AACrG,OAAO,EAAE,kBAAkB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/routes/config/channels/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,qBAAqB,EAC9I,4BAA4B,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AACrG,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Express } from "express";
|
|
2
2
|
/**
|
|
3
|
-
* Installs all channel-
|
|
3
|
+
* Installs all channel-configuration route handlers on the Express application.
|
|
4
4
|
* @param app - The Express application.
|
|
5
5
|
*/
|
|
6
6
|
export declare function setupChannelRoutes(app: Express): void;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { registerBrowseRoutes } from "./endpoints/browse.js";
|
|
2
|
+
import { registerBulkRoutes } from "./endpoints/bulk.js";
|
|
3
|
+
import { registerCrudRoutes } from "./endpoints/crud.js";
|
|
4
|
+
import { registerImportExportRoutes } from "./endpoints/importExport.js";
|
|
5
|
+
import { registerPredefinedRoutes } from "./endpoints/predefined.js";
|
|
6
|
+
import { registerPrefsRoutes } from "./endpoints/prefs.js";
|
|
7
|
+
import { registerServiceRoutes } from "./endpoints/service.js";
|
|
8
|
+
import { registerTagRoutes } from "./endpoints/tags.js";
|
|
9
|
+
/**
|
|
10
|
+
* Installs all channel-configuration route handlers on the Express application.
|
|
11
|
+
* @param app - The Express application.
|
|
12
|
+
*/
|
|
13
|
+
export function setupChannelRoutes(app) {
|
|
14
|
+
registerBrowseRoutes(app);
|
|
15
|
+
registerBulkRoutes(app);
|
|
16
|
+
registerCrudRoutes(app);
|
|
17
|
+
registerImportExportRoutes(app);
|
|
18
|
+
registerPredefinedRoutes(app);
|
|
19
|
+
registerPrefsRoutes(app);
|
|
20
|
+
registerServiceRoutes(app);
|
|
21
|
+
registerTagRoutes(app);
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../../../src/routes/config/channels/setup.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AACzE,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAE7C,oBAAoB,CAAC,GAAG,CAAC,CAAC;IAC1B,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACxB,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACxB,0BAA0B,CAAC,GAAG,CAAC,CAAC;IAChC,wBAAwB,CAAC,GAAG,CAAC,CAAC;IAC9B,mBAAmB,CAAC,GAAG,CAAC,CAAC;IACzB,qBAAqB,CAAC,GAAG,CAAC,CAAC;IAC3B,iBAAiB,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ChannelListingEntry } from "../../../types/index.
|
|
2
|
-
import type { ProfileInfo } from "../../../config/profiles.
|
|
1
|
+
import type { ChannelListingEntry } from "../../../types/index.ts";
|
|
2
|
+
import type { ProfileInfo } from "../../../config/profiles.ts";
|
|
3
3
|
export declare const OPTIONAL_COLUMNS: readonly {
|
|
4
4
|
readonly align: string;
|
|
5
5
|
readonly cssClass: string;
|
|
@@ -9,14 +9,14 @@ export declare const OPTIONAL_COLUMNS: readonly {
|
|
|
9
9
|
}[];
|
|
10
10
|
export declare const VALID_OPTIONAL_COLUMNS: Set<string>;
|
|
11
11
|
/**
|
|
12
|
-
* Generates the tag column filter dropdown content. This is the single source of truth for the filter checkbox list
|
|
12
|
+
* Generates the tag column filter dropdown content. This is the single source of truth for the filter checkbox list - used for both the initial header render
|
|
13
13
|
* and for incremental updates after tag CRUD operations. The dropdown shell (button, dropdown-menu wrapper) is rendered by the header generator; this function
|
|
14
14
|
* provides only the inner content (checkbox labels + "Show All" item).
|
|
15
15
|
* @returns HTML string for the filter dropdown content.
|
|
16
16
|
*/
|
|
17
17
|
export declare function generateTagFilterContent(): string;
|
|
18
18
|
/**
|
|
19
|
-
* Generates the Tag Management modal body HTML. This is the single source of truth for tag manager content
|
|
19
|
+
* Generates the Tag Management modal body HTML. This is the single source of truth for tag manager content - used for both the initial server render and for
|
|
20
20
|
* incremental updates after tag CRUD operations. The endpoints return this HTML in the response so the client can replace the modal content without a page reload.
|
|
21
21
|
* @returns HTML string for the tag manager body (tag list, input field, deleted tags section).
|
|
22
22
|
*/
|
|
@@ -29,7 +29,7 @@ export interface ChannelRowHtml {
|
|
|
29
29
|
editRow: string;
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
|
-
* Generates the HTML for a single channel's table rows (display row and edit form row). All channels
|
|
32
|
+
* 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.
|
|
33
33
|
* The edit form is pre-populated with the effective (resolved) values so users see what they're changing. When called from generateChannelsPanel() which already
|
|
34
34
|
* has the listing entry, pass it via the entry parameter to avoid redundant getChannelListing() calls. POST handlers that generate a single row omit the
|
|
35
35
|
* parameter to trigger an internal lookup.
|
|
@@ -38,10 +38,10 @@ export interface ChannelRowHtml {
|
|
|
38
38
|
* @param entry - Optional pre-resolved listing entry. When omitted, looked up from getChannelListing().
|
|
39
39
|
* @returns Object with displayRow and editRow HTML strings.
|
|
40
40
|
*/
|
|
41
|
-
export declare function generateChannelRowHtml(key: string, profiles: ProfileInfo[], entry?: ChannelListingEntry): ChannelRowHtml;
|
|
41
|
+
export declare function generateChannelRowHtml(key: string, profiles: readonly ProfileInfo[], entry?: ChannelListingEntry): ChannelRowHtml;
|
|
42
42
|
/**
|
|
43
43
|
* Channel table state snapshot. Represents the complete set of summary counts for the channel header and scope toggles. Computed server-side as the single
|
|
44
|
-
* source of truth
|
|
44
|
+
* source of truth - the client applies these values directly to DOM elements without recalculation.
|
|
45
45
|
*/
|
|
46
46
|
export interface ChannelTableCounts {
|
|
47
47
|
disabled: number;
|
|
@@ -87,7 +87,7 @@ export interface ChannelTablePatch {
|
|
|
87
87
|
}>;
|
|
88
88
|
}
|
|
89
89
|
/**
|
|
90
|
-
* Computes the complete channel table state
|
|
90
|
+
* Computes the complete channel table state - summary counts and scope toggle counts - from the current channel listing. This is the server-side single source
|
|
91
91
|
* of truth for all count computation, replacing the client-side DOM-scanning approach. Called once per mutation as part of building a patch.
|
|
92
92
|
* @param listing - Optional pre-fetched listing. When omitted, fetches from getChannelListing(). Passing the listing avoids a redundant computation when
|
|
93
93
|
* buildChannelTablePatch already has it.
|
|
@@ -118,7 +118,7 @@ export declare function buildChannelTableState(listing?: ChannelListingEntry[]):
|
|
|
118
118
|
* @param profiles - Available profiles for the edit form dropdown.
|
|
119
119
|
* @returns A complete ChannelTablePatch ready for client-side application.
|
|
120
120
|
*/
|
|
121
|
-
export declare function buildChannelTablePatch(affectedKeys: string[], profiles: ProfileInfo[]): ChannelTablePatch;
|
|
121
|
+
export declare function buildChannelTablePatch(affectedKeys: readonly string[], profiles: readonly ProfileInfo[]): ChannelTablePatch;
|
|
122
122
|
/**
|
|
123
123
|
* Generates the service filter toolbar HTML with a multi-select dropdown and dismissable chips.
|
|
124
124
|
* @returns HTML string for the service filter toolbar.
|