prismcast 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/app.js +15 -5
- package/dist/app.js.map +1 -1
- package/dist/browser/cdp.d.ts +4 -6
- package/dist/browser/cdp.js +13 -17
- package/dist/browser/cdp.js.map +1 -1
- package/dist/browser/index.js +3 -3
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/precaching.d.ts +1 -1
- package/dist/browser/precaching.js +23 -23
- package/dist/browser/precaching.js.map +1 -1
- package/dist/browser/tuning/directv.js +4 -7
- package/dist/browser/tuning/directv.js.map +1 -1
- package/dist/browser/tuning/fox.js +1 -1
- package/dist/browser/tuning/fox.js.map +1 -1
- package/dist/browser/tuning/hbo.js +1 -1
- package/dist/browser/tuning/hbo.js.map +1 -1
- package/dist/browser/tuning/hulu.js +3 -3
- package/dist/browser/tuning/hulu.js.map +1 -1
- package/dist/browser/tuning/sling.js +2 -2
- package/dist/browser/tuning/sling.js.map +1 -1
- package/dist/channels/index.d.ts +1 -1
- package/dist/channels/index.js +416 -416
- package/dist/channels/index.js.map +1 -1
- package/dist/config/health.d.ts +3 -3
- package/dist/config/health.js +3 -3
- package/dist/config/index.js +2 -2
- package/dist/config/index.js.map +1 -1
- package/dist/config/persistence.d.ts +59 -0
- package/dist/config/persistence.js +127 -0
- package/dist/config/persistence.js.map +1 -0
- package/dist/config/servicePacks.d.ts +46 -0
- package/dist/config/{providerPacks.js → servicePacks.js} +28 -31
- package/dist/config/servicePacks.js.map +1 -0
- package/dist/config/services.d.ts +202 -0
- package/dist/config/{providers.js → services.js} +277 -210
- package/dist/config/services.js.map +1 -0
- package/dist/config/sites.js +61 -61
- package/dist/config/sites.js.map +1 -1
- package/dist/config/userChannels.d.ts +36 -27
- package/dist/config/userChannels.js +300 -212
- package/dist/config/userChannels.js.map +1 -1
- package/dist/config/userConfig.d.ts +11 -9
- package/dist/config/userConfig.js +53 -60
- package/dist/config/userConfig.js.map +1 -1
- package/dist/config/userProfiles.d.ts +31 -15
- package/dist/config/userProfiles.js +139 -96
- package/dist/config/userProfiles.js.map +1 -1
- package/dist/hdhr/channelMap.d.ts +1 -1
- package/dist/hdhr/channelMap.js +4 -7
- package/dist/hdhr/channelMap.js.map +1 -1
- package/dist/hdhr/index.js +6 -9
- package/dist/hdhr/index.js.map +1 -1
- package/dist/native/index.js +3 -3
- package/dist/native/index.js.map +1 -1
- package/dist/native/intercept.js +86 -89
- package/dist/native/intercept.js.map +1 -1
- package/dist/native/probe.js +2 -2
- package/dist/native/probe.js.map +1 -1
- package/dist/native/proxy.js +4 -4
- package/dist/native/proxy.js.map +1 -1
- package/dist/routes/assets.js +3 -3
- package/dist/routes/assets.js.map +1 -1
- package/dist/routes/auth.js +3 -3
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/config/channels/index.d.ts +1 -1
- 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.js +488 -460
- package/dist/routes/config/channels/routes.js.map +1 -1
- package/dist/routes/config/channels/table.d.ts +4 -4
- package/dist/routes/config/channels/table.js +206 -147
- package/dist/routes/config/channels/table.js.map +1 -1
- package/dist/routes/config/index.d.ts +2 -2
- package/dist/routes/config/index.js +3 -3
- package/dist/routes/config/index.js.map +1 -1
- package/dist/routes/config/{providers.js → services.js} +44 -39
- package/dist/routes/config/services.js.map +1 -0
- package/dist/routes/config/settings.js +47 -29
- package/dist/routes/config/settings.js.map +1 -1
- package/dist/routes/debug.js +5 -7
- package/dist/routes/debug.js.map +1 -1
- package/dist/routes/health.js +1 -1
- package/dist/routes/health.js.map +1 -1
- package/dist/routes/index.js +2 -2
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/logs.js +4 -4
- package/dist/routes/logs.js.map +1 -1
- package/dist/routes/playlist.d.ts +4 -4
- package/dist/routes/playlist.js +56 -44
- package/dist/routes/playlist.js.map +1 -1
- package/dist/routes/root/content.js +58 -58
- package/dist/routes/root/content.js.map +1 -1
- package/dist/routes/root/scripts/channels.js +529 -572
- package/dist/routes/root/scripts/channels.js.map +1 -1
- package/dist/routes/root/scripts/config.js +838 -992
- package/dist/routes/root/scripts/config.js.map +1 -1
- package/dist/routes/root/scripts/shared.js +431 -197
- package/dist/routes/root/scripts/shared.js.map +1 -1
- package/dist/routes/root/scripts/status.js +247 -196
- package/dist/routes/root/scripts/status.js.map +1 -1
- package/dist/routes/root/styles.js +12 -4
- package/dist/routes/root/styles.js.map +1 -1
- package/dist/routes/services.d.ts +6 -0
- package/dist/routes/{providers.js → services.js} +43 -43
- package/dist/routes/services.js.map +1 -0
- package/dist/routes/ui.js +2 -0
- package/dist/routes/ui.js.map +1 -1
- package/dist/service/commands.js +5 -8
- package/dist/service/commands.js.map +1 -1
- package/dist/service/generators.d.ts +1 -1
- package/dist/service/generators.js +143 -41
- package/dist/service/generators.js.map +1 -1
- package/dist/streaming/hls.d.ts +4 -4
- package/dist/streaming/hls.js +24 -23
- package/dist/streaming/hls.js.map +1 -1
- package/dist/streaming/hlsResume.js +1 -1
- package/dist/streaming/hlsResume.js.map +1 -1
- package/dist/streaming/monitor.d.ts +2 -2
- package/dist/streaming/monitor.js +19 -13
- package/dist/streaming/monitor.js.map +1 -1
- package/dist/streaming/preroll.js +1 -1
- package/dist/streaming/preroll.js.map +1 -1
- package/dist/streaming/registry.js +4 -8
- package/dist/streaming/registry.js.map +1 -1
- package/dist/streaming/setup.d.ts +2 -2
- package/dist/streaming/setup.js +23 -22
- package/dist/streaming/setup.js.map +1 -1
- package/dist/streaming/showInfo.js +8 -10
- package/dist/streaming/showInfo.js.map +1 -1
- package/dist/streaming/statusEmitter.d.ts +4 -3
- package/dist/streaming/statusEmitter.js +3 -3
- package/dist/streaming/statusEmitter.js.map +1 -1
- package/dist/types/channels.d.ts +15 -11
- package/dist/types/config.d.ts +2 -2
- package/dist/types/index.d.ts +2 -2
- package/dist/types/profiles.d.ts +9 -9
- package/dist/types/selection.d.ts +1 -1
- package/dist/types/shared.d.ts +1 -1
- package/dist/types/streaming.d.ts +1 -1
- package/dist/utils/chromeFetch.js +1 -1
- package/dist/utils/debugFilter.js +3 -3
- package/dist/utils/debugFilter.js.map +1 -1
- package/dist/utils/fileLogger.js +4 -3
- package/dist/utils/fileLogger.js.map +1 -1
- package/dist/utils/format.d.ts +7 -1
- package/dist/utils/format.js +21 -1
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/logger.js +17 -27
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/morganStream.js +2 -2
- package/dist/utils/morganStream.js.map +1 -1
- package/dist/utils/platform.js +3 -2
- package/dist/utils/platform.js.map +1 -1
- package/dist/utils/streamContext.d.ts +7 -0
- package/dist/utils/streamContext.js +10 -1
- package/dist/utils/streamContext.js.map +1 -1
- package/dist/utils/version.js +4 -4
- package/dist/utils/version.js.map +1 -1
- package/package.json +5 -7
- package/dist/config/providerPacks.d.ts +0 -46
- package/dist/config/providerPacks.js.map +0 -1
- package/dist/config/providers.d.ts +0 -175
- package/dist/config/providers.js.map +0 -1
- package/dist/routes/config/providers.js.map +0 -1
- package/dist/routes/providers.d.ts +0 -6
- package/dist/routes/providers.js.map +0 -1
- /package/dist/routes/config/{providers.d.ts → services.d.ts} +0 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/* Copyright(C) 2024-2026, HJD (https://github.com/hjdhjd). All rights reserved.
|
|
2
|
+
*
|
|
3
|
+
* persistence.ts: Transactional file store for PrismCast configuration persistence.
|
|
4
|
+
*/
|
|
5
|
+
import { LOG, stringifySorted } from "../utils/index.js";
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import { getDataDir } from "./paths.js";
|
|
8
|
+
const { promises: fsPromises } = fs;
|
|
9
|
+
/**
|
|
10
|
+
* Error thrown by `mutate()` when the backing file contains invalid JSON. Route handlers can catch this specifically to return 400 instead of 500. Background code
|
|
11
|
+
* paths that swallow errors via try/catch will handle it like any other Error.
|
|
12
|
+
*/
|
|
13
|
+
export class FileStoreParseError extends Error {
|
|
14
|
+
name = "FileStoreParseError";
|
|
15
|
+
constructor(label, filePath, parseMessage) {
|
|
16
|
+
super("Cannot modify " + label + " (" + filePath + "): file contains invalid JSON. " + parseMessage);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
// Store factory.
|
|
20
|
+
/**
|
|
21
|
+
* Creates a transactional file store instance. Each instance owns a single JSON file and provides serialized, atomic access to it.
|
|
22
|
+
*
|
|
23
|
+
* Safety guarantees:
|
|
24
|
+
* - **Atomic writes:** data is written to a `.tmp` file and renamed over the original. `rename()` is atomic on POSIX and NTFS.
|
|
25
|
+
* - **Serialization:** a promise chain ensures only one `mutate()` runs at a time. Concurrent callers queue behind the active operation.
|
|
26
|
+
* - **Corruption guard:** `mutate()` throws `FileStoreParseError` if the file contains invalid JSON, preventing save-over-corrupt cascades.
|
|
27
|
+
* - **Backup rotation:** before each write, the current file is copied to `.bak`. One-deep rotation provides a recovery path.
|
|
28
|
+
*
|
|
29
|
+
* @template T - The in-memory data type that callers mutate.
|
|
30
|
+
* @param options - Store configuration.
|
|
31
|
+
* @returns A FileStore instance with `read()` and `mutate()` methods.
|
|
32
|
+
*/
|
|
33
|
+
export function createFileStore(options) {
|
|
34
|
+
// Serialization queue. Each mutate() call chains onto this promise so operations execute one at a time.
|
|
35
|
+
let queue = Promise.resolve();
|
|
36
|
+
// Lazy data directory creation. The directory is created once on the first write, then skipped for subsequent writes.
|
|
37
|
+
let dataDirEnsured = false;
|
|
38
|
+
/**
|
|
39
|
+
* Reads the file from disk and parses it. Handles missing files (normal on first run), parse errors, and other read failures.
|
|
40
|
+
*/
|
|
41
|
+
async function read() {
|
|
42
|
+
const filePath = options.path();
|
|
43
|
+
try {
|
|
44
|
+
const content = await fsPromises.readFile(filePath, "utf-8");
|
|
45
|
+
try {
|
|
46
|
+
const data = options.parse(content);
|
|
47
|
+
return { data, parseError: false };
|
|
48
|
+
}
|
|
49
|
+
catch (parseError) {
|
|
50
|
+
const message = (parseError instanceof Error) ? parseError.message : String(parseError);
|
|
51
|
+
LOG.warn("Invalid JSON in %s file %s: %s. Using defaults.", options.label, filePath, message);
|
|
52
|
+
return { data: options.defaultValue(), parseError: true, parseErrorMessage: message };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
// File doesn't exist...this is normal on first run, use defaults.
|
|
57
|
+
if (error.code === "ENOENT") {
|
|
58
|
+
return { data: options.defaultValue(), parseError: false };
|
|
59
|
+
}
|
|
60
|
+
// Other read errors...log and use defaults.
|
|
61
|
+
LOG.warn("Failed to read %s file %s: %s. Using defaults.", options.label, filePath, (error instanceof Error) ? error.message : String(error));
|
|
62
|
+
return { data: options.defaultValue(), parseError: false };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Executes a single mutation: read, guard, mutate, backup, atomic write. Called under the serialization queue.
|
|
67
|
+
*/
|
|
68
|
+
async function doMutate(fn) {
|
|
69
|
+
const filePath = options.path();
|
|
70
|
+
// Read the current file state.
|
|
71
|
+
const result = await read();
|
|
72
|
+
// Corruption guard: refuse to modify a file that can't be parsed. This prevents the cascade where a corrupt file gets overwritten with nearly-empty data.
|
|
73
|
+
if (result.parseError) {
|
|
74
|
+
throw new FileStoreParseError(options.label, filePath, result.parseErrorMessage ?? "Unknown parse error.");
|
|
75
|
+
}
|
|
76
|
+
// Apply the caller's mutation. Callbacks modify data in place.
|
|
77
|
+
fn(result.data);
|
|
78
|
+
// Apply the beforeWrite transform to produce the serializable form.
|
|
79
|
+
const serializable = options.beforeWrite ? options.beforeWrite(result.data) : result.data;
|
|
80
|
+
const content = stringifySorted(serializable) + "\n";
|
|
81
|
+
// Ensure the data directory exists on the first write.
|
|
82
|
+
if (!dataDirEnsured) {
|
|
83
|
+
await fsPromises.mkdir(getDataDir(), { recursive: true });
|
|
84
|
+
dataDirEnsured = true;
|
|
85
|
+
}
|
|
86
|
+
// Backup: copy the current file to .bak before overwriting. Swallow ENOENT (file doesn't exist yet on first write).
|
|
87
|
+
const bakPath = filePath + ".bak";
|
|
88
|
+
try {
|
|
89
|
+
await fsPromises.copyFile(filePath, bakPath);
|
|
90
|
+
}
|
|
91
|
+
catch (backupError) {
|
|
92
|
+
if (backupError.code !== "ENOENT") {
|
|
93
|
+
LOG.warn("Failed to back up %s: %s.", filePath, (backupError instanceof Error) ? backupError.message : String(backupError));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Atomic write: write to a temp file, then rename over the original. rename() is atomic on POSIX and NTFS filesystems.
|
|
97
|
+
const tmpPath = filePath + ".tmp";
|
|
98
|
+
try {
|
|
99
|
+
await fsPromises.writeFile(tmpPath, content, "utf-8");
|
|
100
|
+
await fsPromises.rename(tmpPath, filePath);
|
|
101
|
+
}
|
|
102
|
+
catch (writeError) {
|
|
103
|
+
// Attempt cleanup of the temp file on failure.
|
|
104
|
+
try {
|
|
105
|
+
await fsPromises.unlink(tmpPath);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// Cleanup is best-effort.
|
|
109
|
+
}
|
|
110
|
+
throw writeError;
|
|
111
|
+
}
|
|
112
|
+
LOG.info("Saved %s to %s.", options.label, filePath);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Enqueues a mutation onto the serialization queue. The promise chain ensures only one mutation runs at a time. Errors propagate to the caller but do not
|
|
116
|
+
* break the chain for subsequent callers.
|
|
117
|
+
*/
|
|
118
|
+
async function mutate(fn) {
|
|
119
|
+
const operation = queue.then(async () => doMutate(fn));
|
|
120
|
+
// Swallow errors on the chain reference so future operations can proceed. The error still propagates to the caller via the returned promise.
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function -- Intentional no-op: errors are propagated to the caller via the returned promise.
|
|
122
|
+
queue = operation.catch(() => { });
|
|
123
|
+
return operation;
|
|
124
|
+
}
|
|
125
|
+
return { mutate, read };
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=persistence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistence.js","sourceRoot":"","sources":["../../src/config/persistence.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ServicePack } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Result of parsing a service pack.
|
|
4
|
+
*/
|
|
5
|
+
export interface ParseResult {
|
|
6
|
+
errors: string[];
|
|
7
|
+
pack?: ServicePack;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Summary of what was imported from a service pack. The primary import (profiles and domains) is tracked separately from the secondary channel import so that
|
|
11
|
+
* callers can report partial success accurately — profiles may save while channels fail.
|
|
12
|
+
*/
|
|
13
|
+
export interface ImportSummary {
|
|
14
|
+
channelsAdded: number;
|
|
15
|
+
domainsAdded: number;
|
|
16
|
+
errors: string[];
|
|
17
|
+
profilesAdded: number;
|
|
18
|
+
success: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Parses and validates a raw service pack import. Checks the version field, validates profiles and domains via the userProfiles validation functions.
|
|
22
|
+
* @param data - The raw JSON data to parse.
|
|
23
|
+
* @returns Parse result with validated pack or errors.
|
|
24
|
+
*/
|
|
25
|
+
export declare function parseServicePack(data: unknown): ParseResult;
|
|
26
|
+
/**
|
|
27
|
+
* Imports a validated service pack by writing its contents to profiles.json and optionally channels.json. Returns a summary of what was added.
|
|
28
|
+
* @param pack - The validated service pack to import.
|
|
29
|
+
* @param options - Import options. Set skipChannels to true to skip importing channels even if the pack contains them.
|
|
30
|
+
* @returns Summary of what was imported.
|
|
31
|
+
*/
|
|
32
|
+
export declare function importServicePack(pack: ServicePack, options?: {
|
|
33
|
+
skipChannels?: boolean;
|
|
34
|
+
}): Promise<ImportSummary>;
|
|
35
|
+
/**
|
|
36
|
+
* Exports one or more user profiles and their associated domain mappings as a service pack JSON object. Optionally includes channels that reference the
|
|
37
|
+
* selected profiles.
|
|
38
|
+
* @param profileKeys - Array of user profile keys to export.
|
|
39
|
+
* @param options - Export options controlling what to include.
|
|
40
|
+
* @returns The service pack object, or null if none of the requested profiles exist.
|
|
41
|
+
*/
|
|
42
|
+
export declare function exportServicePack(profileKeys: string[], options?: {
|
|
43
|
+
includeChannels?: boolean;
|
|
44
|
+
includeDomains?: boolean;
|
|
45
|
+
name?: string;
|
|
46
|
+
}): ServicePack | null;
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { getChannelListing,
|
|
2
|
-
import { getUserDomains, getUserProfiles,
|
|
1
|
+
import { getChannelListing, mutateChannels } from "./userChannels.js";
|
|
2
|
+
import { getUserDomains, getUserProfiles, mutateProfiles, normalizeLegacyProfileFlags, validateImportedProfiles } from "./userProfiles.js";
|
|
3
3
|
import { sanitizeString } from "../utils/index.js";
|
|
4
|
-
/*
|
|
4
|
+
/* Service packs bundle a profile, domain mapping(s), and optionally channels into a single JSON file for distribution. On import, the contents are validated
|
|
5
5
|
* and split into profiles.json and channels.json. On export, a user profile and its associated domain mappings and channels are packaged for sharing.
|
|
6
6
|
*/
|
|
7
|
-
// Current
|
|
7
|
+
// Current service pack format version.
|
|
8
8
|
const CURRENT_VERSION = 1;
|
|
9
9
|
/**
|
|
10
|
-
* Parses and validates a raw
|
|
10
|
+
* Parses and validates a raw service pack import. Checks the version field, validates profiles and domains via the userProfiles validation functions.
|
|
11
11
|
* @param data - The raw JSON data to parse.
|
|
12
12
|
* @returns Parse result with validated pack or errors.
|
|
13
13
|
*/
|
|
14
|
-
export function
|
|
14
|
+
export function parseServicePack(data) {
|
|
15
15
|
const errors = [];
|
|
16
16
|
if ((typeof data !== "object") || (data === null) || Array.isArray(data)) {
|
|
17
17
|
return { errors: ["Invalid format: expected a JSON object."] };
|
|
@@ -37,10 +37,13 @@ export function parseProviderPack(data) {
|
|
|
37
37
|
if (errors.length > 0) {
|
|
38
38
|
return { errors };
|
|
39
39
|
}
|
|
40
|
+
// Normalize legacy profile field names before validation so that old service packs with renamed fields (e.g., noVideo → staticCapture) pass validation cleanly.
|
|
41
|
+
const rawProfiles = raw.profiles;
|
|
42
|
+
normalizeLegacyProfileFlags(rawProfiles);
|
|
40
43
|
// Validate the profiles and domains using the shared validation pipeline.
|
|
41
44
|
const validationResult = validateImportedProfiles({
|
|
42
45
|
domains: raw.domains,
|
|
43
|
-
profiles:
|
|
46
|
+
profiles: rawProfiles
|
|
44
47
|
});
|
|
45
48
|
if (!validationResult.valid) {
|
|
46
49
|
return { errors: validationResult.errors };
|
|
@@ -52,7 +55,7 @@ export function parseProviderPack(data) {
|
|
|
52
55
|
errors.push("Invalid 'channels' field: expected an object.");
|
|
53
56
|
}
|
|
54
57
|
else {
|
|
55
|
-
// Basic channel structure validation — detailed validation happens during
|
|
58
|
+
// Basic channel structure validation — detailed validation happens during importServicePack.
|
|
56
59
|
for (const [key, value] of Object.entries(raw.channels)) {
|
|
57
60
|
if ((typeof value === "object") && (value !== null) && !Array.isArray(value)) {
|
|
58
61
|
const ch = value;
|
|
@@ -95,22 +98,20 @@ export function parseProviderPack(data) {
|
|
|
95
98
|
return { errors: [], pack };
|
|
96
99
|
}
|
|
97
100
|
/**
|
|
98
|
-
* Imports a validated
|
|
99
|
-
* @param pack - The validated
|
|
101
|
+
* Imports a validated service pack by writing its contents to profiles.json and optionally channels.json. Returns a summary of what was added.
|
|
102
|
+
* @param pack - The validated service pack to import.
|
|
100
103
|
* @param options - Import options. Set skipChannels to true to skip importing channels even if the pack contains them.
|
|
101
104
|
* @returns Summary of what was imported.
|
|
102
105
|
*/
|
|
103
|
-
export async function
|
|
106
|
+
export async function importServicePack(pack, options = {}) {
|
|
104
107
|
const errors = [];
|
|
105
|
-
// Merge profiles: add pack profiles to existing user profiles.
|
|
106
|
-
const existingProfiles = getUserProfiles();
|
|
107
|
-
const existingDomains = getUserDomains();
|
|
108
|
-
const mergedProfiles = { ...existingProfiles, ...pack.profiles };
|
|
109
|
-
const mergedDomains = { ...existingDomains, ...(pack.domains ?? {}) };
|
|
110
108
|
const profilesAdded = Object.keys(pack.profiles).length;
|
|
111
109
|
const domainsAdded = Object.keys(pack.domains ?? {}).length;
|
|
112
110
|
try {
|
|
113
|
-
await
|
|
111
|
+
await mutateProfiles((data) => {
|
|
112
|
+
Object.assign(data.profiles, pack.profiles);
|
|
113
|
+
Object.assign(data.domains, pack.domains ?? {});
|
|
114
|
+
});
|
|
114
115
|
}
|
|
115
116
|
catch (error) {
|
|
116
117
|
errors.push("Failed to save profiles: " + ((error instanceof Error) ? error.message : String(error)));
|
|
@@ -120,16 +121,12 @@ export async function importProviderPack(pack, options = {}) {
|
|
|
120
121
|
// profiles and domains were already saved successfully above.
|
|
121
122
|
let channelsAdded = 0;
|
|
122
123
|
if (!options.skipChannels && pack.channels && (Object.keys(pack.channels).length > 0)) {
|
|
124
|
+
const packChannels = pack.channels;
|
|
123
125
|
try {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
else {
|
|
129
|
-
const mergedChannels = { ...result.channels, ...pack.channels };
|
|
130
|
-
await saveUserChannels(mergedChannels);
|
|
131
|
-
channelsAdded = Object.keys(pack.channels).length;
|
|
132
|
-
}
|
|
126
|
+
await mutateChannels((channels) => {
|
|
127
|
+
Object.assign(channels, packChannels);
|
|
128
|
+
});
|
|
129
|
+
channelsAdded = Object.keys(packChannels).length;
|
|
133
130
|
}
|
|
134
131
|
catch (error) {
|
|
135
132
|
errors.push("Failed to save channels: " + ((error instanceof Error) ? error.message : String(error)));
|
|
@@ -144,13 +141,13 @@ export async function importProviderPack(pack, options = {}) {
|
|
|
144
141
|
};
|
|
145
142
|
}
|
|
146
143
|
/**
|
|
147
|
-
* Exports one or more user profiles and their associated domain mappings as a
|
|
144
|
+
* Exports one or more user profiles and their associated domain mappings as a service pack JSON object. Optionally includes channels that reference the
|
|
148
145
|
* selected profiles.
|
|
149
146
|
* @param profileKeys - Array of user profile keys to export.
|
|
150
147
|
* @param options - Export options controlling what to include.
|
|
151
|
-
* @returns The
|
|
148
|
+
* @returns The service pack object, or null if none of the requested profiles exist.
|
|
152
149
|
*/
|
|
153
|
-
export function
|
|
150
|
+
export function exportServicePack(profileKeys, options = {}) {
|
|
154
151
|
const userProfiles = getUserProfiles();
|
|
155
152
|
const matchedProfiles = {};
|
|
156
153
|
for (const key of profileKeys) {
|
|
@@ -188,7 +185,7 @@ export function exportProviderPack(profileKeys, options = {}) {
|
|
|
188
185
|
const channels = {};
|
|
189
186
|
for (const entry of listing) {
|
|
190
187
|
if (entry.channel.profile && keySet.has(entry.channel.profile)) {
|
|
191
|
-
// Strip hdhrEnabled from exported channels — it's a local deployment preference, not a
|
|
188
|
+
// Strip hdhrEnabled from exported channels — it's a local deployment preference, not a service configuration property.
|
|
192
189
|
const exportChannel = { ...entry.channel };
|
|
193
190
|
Reflect.deleteProperty(exportChannel, "hdhrEnabled");
|
|
194
191
|
channels[entry.key] = exportChannel;
|
|
@@ -200,4 +197,4 @@ export function exportProviderPack(profileKeys, options = {}) {
|
|
|
200
197
|
}
|
|
201
198
|
return pack;
|
|
202
199
|
}
|
|
203
|
-
//# sourceMappingURL=
|
|
200
|
+
//# sourceMappingURL=servicePacks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"servicePacks.js","sourceRoot":"","sources":["../../src/config/servicePacks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import type { Channel, ChannelMap, ChannelSortField, ServiceGroup, SortDirection } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Gets the service tag for a channel key. For channels in a service group, reads the pre-computed tag from the group variant entry (computed at group-building
|
|
4
|
+
* time by buildServiceGroups). For standalone channels not in any group, derives the tag from the channel's URL domain. This function should not be called with
|
|
5
|
+
* :predefined synthetic keys — those only exist inside service groups and their tags are available via the group's variant entries.
|
|
6
|
+
* @param key - The channel key.
|
|
7
|
+
* @returns The service tag string.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getServiceTagForChannel(key: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Returns the auth domain for a channel key. Domain is the natural auth boundary — browser cookies and sessions scope to it. Multi-channel services work correctly
|
|
12
|
+
* because all their channels share one domain, and canonical channels work correctly because each has its own domain.
|
|
13
|
+
* @param key - The channel key.
|
|
14
|
+
* @returns The extracted domain from the channel's URL, or empty string if the channel or URL cannot be resolved.
|
|
15
|
+
*/
|
|
16
|
+
export declare function getAuthDomainForChannel(key: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Returns all service tags for a channel (canonical tag + all variant suffix tags). Used to determine which services offer this channel.
|
|
19
|
+
* @param canonicalKey - The canonical channel key.
|
|
20
|
+
* @returns Array of service tag strings.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getChannelServiceTags(canonicalKey: string): string[];
|
|
23
|
+
/**
|
|
24
|
+
* Scans all service groups and collects unique service tags with display names. Display names are derived from the service field in DOMAIN_CONFIG entries that
|
|
25
|
+
* have a serviceTag.
|
|
26
|
+
* @returns Array of { displayName, domain, iconUrl, tag } objects sorted alphabetically by display name, with "direct" always first.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getAllServiceTags(): {
|
|
29
|
+
displayName: string;
|
|
30
|
+
domain?: string;
|
|
31
|
+
iconUrl?: string;
|
|
32
|
+
tag: string;
|
|
33
|
+
}[];
|
|
34
|
+
/**
|
|
35
|
+
* Gets the current enabled service tags.
|
|
36
|
+
* @returns Copy of the enabled services array. Empty means no filter (all shown).
|
|
37
|
+
*/
|
|
38
|
+
export declare function getEnabledServices(): string[];
|
|
39
|
+
/**
|
|
40
|
+
* Sets the enabled service tags. Empty array means "no filter" (all services shown).
|
|
41
|
+
* @param tags - The service tags to enable.
|
|
42
|
+
*/
|
|
43
|
+
export declare function setEnabledServices(tags: string[]): void;
|
|
44
|
+
/**
|
|
45
|
+
* Checks if a service tag is currently enabled. Returns true if the tag is enabled, if no filter is active (empty set), or if the tag is "direct".
|
|
46
|
+
* @param tag - The service tag to check.
|
|
47
|
+
* @returns True if the service is available.
|
|
48
|
+
*/
|
|
49
|
+
export declare function isServiceTagEnabled(tag: string): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Centralized availability check for the service filter. Returns true if the channel has at least one variant whose service tag is enabled.
|
|
52
|
+
* @param canonicalKey - The canonical channel key.
|
|
53
|
+
* @returns True if the channel passes the service filter.
|
|
54
|
+
*/
|
|
55
|
+
export declare function isChannelAvailableByService(canonicalKey: string): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Builds service groups by scanning all channels and grouping by canonicalKey. Variant entries declare their canonical via the canonicalKey field (set by the
|
|
58
|
+
* flattener for predefined channels, by the browse modal for user channels, and by the one-time migration for pre-existing user entries). This is a single
|
|
59
|
+
* pass over the merged channel map — one field, one mechanism for both predefined and user-defined variant relationships.
|
|
60
|
+
*
|
|
61
|
+
* User overrides of predefined channels (same key, different object reference) produce a two-entry group with "Custom" and the original predefined version,
|
|
62
|
+
* even for single-service channels that don't have canonicalKey-based variants.
|
|
63
|
+
*
|
|
64
|
+
* After building groups, validates stored service selections against the new variant structure and reverts any stale selections to the canonical default. This
|
|
65
|
+
* catches all staleness sources in one place: service key renames across versions, variant removal in code, user-deleted custom variants, and reverted
|
|
66
|
+
* predefined overrides.
|
|
67
|
+
* @param channels - The merged channel map (predefined + user channels).
|
|
68
|
+
* @returns Canonical keys whose service selections were stale and reverted. Empty array if all selections are valid. The caller decides whether to persist.
|
|
69
|
+
*/
|
|
70
|
+
export declare function buildServiceGroups(channels: ChannelMap): string[];
|
|
71
|
+
interface PredefinedChannelSummary {
|
|
72
|
+
canonicalKey: string;
|
|
73
|
+
name: string;
|
|
74
|
+
serviceCount: number;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Returns predefined channels whose canonical URL domain matches the given URL's domain. Used by the manual add form to show an inline hint when the user
|
|
78
|
+
* enters a URL that has predefined channels available. Returns an empty array when no predefined channels match.
|
|
79
|
+
* @param url - The URL to match against predefined channel domains.
|
|
80
|
+
* @returns Array of matching predefined channel summaries with canonical key, display name, and available service count.
|
|
81
|
+
*/
|
|
82
|
+
export declare function findPredefinedByDomain(url: string): PredefinedChannelSummary[];
|
|
83
|
+
/**
|
|
84
|
+
* Returns the full domain-to-predefined-channel index for client-side embedding. Used by the channels panel to embed predefined match data so the manual add
|
|
85
|
+
* form can show inline hints without a server round-trip. The returned object is keyed by concise domain with arrays of channel summaries as values.
|
|
86
|
+
* @returns Record mapping domains to predefined channel summaries.
|
|
87
|
+
*/
|
|
88
|
+
export declare function getPredefinedDomainMap(): Record<string, PredefinedChannelSummary[]>;
|
|
89
|
+
/**
|
|
90
|
+
* Resolves a URL to a friendly service display name. Checks built-in DOMAIN_CONFIG first for a stable, well-known service name, then falls back to
|
|
91
|
+
* getDomainConfig() which includes user domain mappings. This ordering prevents user domain overrides from corrupting display labels for predefined channel
|
|
92
|
+
* variants — a user mapping a built-in domain to a custom profile should not rename every service dropdown entry that uses that domain.
|
|
93
|
+
* @param url - The URL to resolve a service display name for.
|
|
94
|
+
* @returns The service display name, or the concise domain if no service name is configured.
|
|
95
|
+
*/
|
|
96
|
+
export declare function getServiceDisplayName(url: string): string;
|
|
97
|
+
/**
|
|
98
|
+
* Resolves the service display label for a channel. Checks in order: explicit `service` field on the channel, the channel's explicit profile resolved via
|
|
99
|
+
* user domain mappings, then URL-based built-in display name. This ensures channels assigned to user-defined profiles show the profile's service name rather
|
|
100
|
+
* than the built-in name for the URL domain.
|
|
101
|
+
* @param channel - The channel to resolve a label for.
|
|
102
|
+
* @returns The service display label.
|
|
103
|
+
*/
|
|
104
|
+
export declare function getChannelServiceLabel(channel: Channel): string;
|
|
105
|
+
export declare const VALID_SORT_FIELDS: Set<ChannelSortField>;
|
|
106
|
+
/**
|
|
107
|
+
* Extracts a sortable string value from a channel for the specified sort field. Channel numbers are zero-padded to 6 digits for correct numeric ordering within a
|
|
108
|
+
* string comparison. Service values use the display label for human-meaningful sort order. This is the single source of truth for channel sort key extraction,
|
|
109
|
+
* shared by both the server-side table renderer and the M3U playlist generator.
|
|
110
|
+
* @param channel - Fallback channel definition, used only when the selected service variant cannot be resolved (e.g., key not in the merged channel map).
|
|
111
|
+
* @param key - The canonical channel key. Used for key-based sorting and to resolve the selected service variant internally.
|
|
112
|
+
* @param field - The sort field to extract.
|
|
113
|
+
* @returns A lowercase string suitable for comparison-based sorting.
|
|
114
|
+
*/
|
|
115
|
+
export declare function getChannelSortKey(channel: Channel, key: string, field: ChannelSortField): string;
|
|
116
|
+
/**
|
|
117
|
+
* Compares two channels for sorting by the specified field and direction with a built-in channel name tiebreaker. The tiebreaker is always ascending so that rows
|
|
118
|
+
* within each group maintain a consistent alphabetical order regardless of the primary sort direction. This is the single comparator for all sort sites — server HTML
|
|
119
|
+
* render, client re-sort, and M3U playlist — to prevent ordering divergence.
|
|
120
|
+
* @param channelA - First channel definition.
|
|
121
|
+
* @param keyA - First channel key.
|
|
122
|
+
* @param channelB - Second channel definition.
|
|
123
|
+
* @param keyB - Second channel key.
|
|
124
|
+
* @param field - The sort field to compare.
|
|
125
|
+
* @param direction - Sort direction for the primary field.
|
|
126
|
+
* @returns A negative, zero, or positive number for sort ordering.
|
|
127
|
+
*/
|
|
128
|
+
export declare function compareChannelSort(channelA: Channel, keyA: string, channelB: Channel, keyB: string, field: ChannelSortField, direction: SortDirection): number;
|
|
129
|
+
/**
|
|
130
|
+
* Gets the service group for a channel key. Works with both canonical and variant keys.
|
|
131
|
+
* @param key - Any channel key in the group.
|
|
132
|
+
* @returns The service group if the channel is part of a multi-service group, undefined otherwise.
|
|
133
|
+
*/
|
|
134
|
+
export declare function getServiceGroup(key: string): ServiceGroup | undefined;
|
|
135
|
+
/**
|
|
136
|
+
* Checks if a channel key is a non-canonical service variant. Used to filter variants from channel listings.
|
|
137
|
+
* @param key - The channel key to check.
|
|
138
|
+
* @returns True if the key is a variant (not canonical) in a service group.
|
|
139
|
+
*/
|
|
140
|
+
export declare function isServiceVariant(key: string): boolean;
|
|
141
|
+
/**
|
|
142
|
+
* Checks if a channel has multiple service options. Used to determine whether to show a service dropdown in the UI.
|
|
143
|
+
* @param key - The channel key to check.
|
|
144
|
+
* @returns True if the channel has more than one service variant.
|
|
145
|
+
*/
|
|
146
|
+
export declare function hasMultipleServices(key: string): boolean;
|
|
147
|
+
/**
|
|
148
|
+
* Gets the canonical key for any channel key. For variant keys, returns the canonical key. For non-grouped or canonical keys, returns the input unchanged.
|
|
149
|
+
* Handles the PREDEFINED_SUFFIX used when a user has overridden a predefined channel.
|
|
150
|
+
* @param key - Any channel key.
|
|
151
|
+
* @returns The canonical key for the channel's service group, or the input key if not part of a group.
|
|
152
|
+
*/
|
|
153
|
+
export declare function getCanonicalKey(key: string): string;
|
|
154
|
+
/**
|
|
155
|
+
* Sets the user's service selections. Called when loading from channels.json.
|
|
156
|
+
* @param selections - Service selections keyed by canonical channel key.
|
|
157
|
+
*/
|
|
158
|
+
export declare function setServiceSelections(selections: Record<string, string>): void;
|
|
159
|
+
/**
|
|
160
|
+
* Gets all service selections.
|
|
161
|
+
* @returns Copy of the service selections object.
|
|
162
|
+
*/
|
|
163
|
+
export declare function getServiceSelections(): Record<string, string>;
|
|
164
|
+
/**
|
|
165
|
+
* Gets the service selection for a specific channel.
|
|
166
|
+
* @param canonicalKey - The canonical channel key.
|
|
167
|
+
* @returns The selected service key, or undefined if using the default.
|
|
168
|
+
*/
|
|
169
|
+
export declare function getServiceSelection(canonicalKey: string): string | undefined;
|
|
170
|
+
/**
|
|
171
|
+
* Sets the service selection for a channel.
|
|
172
|
+
* @param canonicalKey - The canonical channel key.
|
|
173
|
+
* @param serviceKey - The selected service key.
|
|
174
|
+
*/
|
|
175
|
+
export declare function setServiceSelection(canonicalKey: string, serviceKey: string): void;
|
|
176
|
+
/**
|
|
177
|
+
* Resolves a canonical channel key to the actual channel key based on user selection. If the user has selected a specific service for this channel, returns that
|
|
178
|
+
* service's key. Otherwise returns the canonical key (default service). When the service filter is active, falls back to the first enabled variant if the stored
|
|
179
|
+
* selection's service is filtered out.
|
|
180
|
+
*
|
|
181
|
+
* This function is a pure resolver with no side effects. Stale selection cleanup is handled by buildServiceGroups(), which validates all stored selections
|
|
182
|
+
* against the rebuilt variant structure every time groups are rebuilt - both at startup and after runtime channel mutations.
|
|
183
|
+
* @param canonicalKey - The canonical channel key.
|
|
184
|
+
* @returns The resolved service key to use for streaming.
|
|
185
|
+
*/
|
|
186
|
+
export declare function resolveServiceKey(canonicalKey: string): string;
|
|
187
|
+
/**
|
|
188
|
+
* Gets a channel with inheritance applied. For service variants, this merges the variant's properties with inherited properties from the canonical entry
|
|
189
|
+
* using the live channel data (which includes user overrides). Use `resolvePredefinedVariant()` when you need resolution against pure predefined data.
|
|
190
|
+
* @param key - The channel key (canonical or variant).
|
|
191
|
+
* @returns The complete channel with inheritance applied, or undefined if the channel doesn't exist.
|
|
192
|
+
*/
|
|
193
|
+
export declare function getResolvedChannel(key: string): Channel | undefined;
|
|
194
|
+
/**
|
|
195
|
+
* Resolves a variant channel key against pure predefined data (ignoring user overrides). This is used for revert detection — when the user's edits match a
|
|
196
|
+
* variant's predefined definition, the custom override can be removed and the service selection switched to that variant. For canonical keys, returns the raw
|
|
197
|
+
* predefined channel. For variant keys, applies the same inheritance rules as `getResolvedChannel()` but against `PREDEFINED_CHANNELS` instead of `channelsRef`.
|
|
198
|
+
* @param key - The channel key (canonical or variant).
|
|
199
|
+
* @returns The channel with inheritance applied against predefined data, or undefined if the key has no predefined definition.
|
|
200
|
+
*/
|
|
201
|
+
export declare function resolvePredefinedVariant(key: string): Channel | undefined;
|
|
202
|
+
export {};
|