@bnhf/prismcast 1.3.4-2026.2.19
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/LICENSE.md +7 -0
- package/README.md +347 -0
- package/bin/prismcast +6 -0
- package/dist/app.d.ts +6 -0
- package/dist/app.js +315 -0
- package/dist/app.js.map +1 -0
- package/dist/browser/cdp.d.ts +38 -0
- package/dist/browser/cdp.js +155 -0
- package/dist/browser/cdp.js.map +1 -0
- package/dist/browser/channelSelection.d.ts +65 -0
- package/dist/browser/channelSelection.js +202 -0
- package/dist/browser/channelSelection.js.map +1 -0
- package/dist/browser/display.d.ts +34 -0
- package/dist/browser/display.js +54 -0
- package/dist/browser/display.js.map +1 -0
- package/dist/browser/index.d.ts +205 -0
- package/dist/browser/index.js +1205 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/tuning/fox.d.ts +2 -0
- package/dist/browser/tuning/fox.js +83 -0
- package/dist/browser/tuning/fox.js.map +1 -0
- package/dist/browser/tuning/hbo.d.ts +2 -0
- package/dist/browser/tuning/hbo.js +237 -0
- package/dist/browser/tuning/hbo.js.map +1 -0
- package/dist/browser/tuning/hulu.d.ts +2 -0
- package/dist/browser/tuning/hulu.js +550 -0
- package/dist/browser/tuning/hulu.js.map +1 -0
- package/dist/browser/tuning/sling.d.ts +2 -0
- package/dist/browser/tuning/sling.js +518 -0
- package/dist/browser/tuning/sling.js.map +1 -0
- package/dist/browser/tuning/thumbnailRow.d.ts +2 -0
- package/dist/browser/tuning/thumbnailRow.js +108 -0
- package/dist/browser/tuning/thumbnailRow.js.map +1 -0
- package/dist/browser/tuning/tileClick.d.ts +2 -0
- package/dist/browser/tuning/tileClick.js +103 -0
- package/dist/browser/tuning/tileClick.js.map +1 -0
- package/dist/browser/tuning/youtubeTv.d.ts +2 -0
- package/dist/browser/tuning/youtubeTv.js +182 -0
- package/dist/browser/tuning/youtubeTv.js.map +1 -0
- package/dist/browser/video.d.ts +289 -0
- package/dist/browser/video.js +996 -0
- package/dist/browser/video.js.map +1 -0
- package/dist/channels/index.d.ts +3 -0
- package/dist/channels/index.js +392 -0
- package/dist/channels/index.js.map +1 -0
- package/dist/config/index.d.ts +53 -0
- package/dist/config/index.js +233 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/presets.d.ts +98 -0
- package/dist/config/presets.js +241 -0
- package/dist/config/presets.js.map +1 -0
- package/dist/config/profiles.d.ts +79 -0
- package/dist/config/profiles.js +245 -0
- package/dist/config/profiles.js.map +1 -0
- package/dist/config/providers.d.ts +120 -0
- package/dist/config/providers.js +450 -0
- package/dist/config/providers.js.map +1 -0
- package/dist/config/sites.d.ts +22 -0
- package/dist/config/sites.js +377 -0
- package/dist/config/sites.js.map +1 -0
- package/dist/config/userChannels.d.ts +178 -0
- package/dist/config/userChannels.js +543 -0
- package/dist/config/userChannels.js.map +1 -0
- package/dist/config/userConfig.d.ts +235 -0
- package/dist/config/userConfig.js +913 -0
- package/dist/config/userConfig.js.map +1 -0
- package/dist/hdhr/channelMap.d.ts +21 -0
- package/dist/hdhr/channelMap.js +82 -0
- package/dist/hdhr/channelMap.js.map +1 -0
- package/dist/hdhr/deviceId.d.ts +11 -0
- package/dist/hdhr/deviceId.js +84 -0
- package/dist/hdhr/deviceId.js.map +1 -0
- package/dist/hdhr/discover.d.ts +6 -0
- package/dist/hdhr/discover.js +155 -0
- package/dist/hdhr/discover.js.map +1 -0
- package/dist/hdhr/index.d.ts +9 -0
- package/dist/hdhr/index.js +87 -0
- package/dist/hdhr/index.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +144 -0
- package/dist/index.js.map +1 -0
- package/dist/routes/assets.d.ts +6 -0
- package/dist/routes/assets.js +79 -0
- package/dist/routes/assets.js.map +1 -0
- package/dist/routes/auth.d.ts +6 -0
- package/dist/routes/auth.js +77 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/channels.d.ts +6 -0
- package/dist/routes/channels.js +40 -0
- package/dist/routes/channels.js.map +1 -0
- package/dist/routes/components.d.ts +138 -0
- package/dist/routes/components.js +210 -0
- package/dist/routes/components.js.map +1 -0
- package/dist/routes/config.d.ts +72 -0
- package/dist/routes/config.js +1977 -0
- package/dist/routes/config.js.map +1 -0
- package/dist/routes/debug.d.ts +6 -0
- package/dist/routes/debug.js +274 -0
- package/dist/routes/debug.js.map +1 -0
- package/dist/routes/health.d.ts +6 -0
- package/dist/routes/health.js +85 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/hls.d.ts +6 -0
- package/dist/routes/hls.js +25 -0
- package/dist/routes/hls.js.map +1 -0
- package/dist/routes/index.d.ts +19 -0
- package/dist/routes/index.js +49 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/logs.d.ts +6 -0
- package/dist/routes/logs.js +164 -0
- package/dist/routes/logs.js.map +1 -0
- package/dist/routes/mpegts.d.ts +6 -0
- package/dist/routes/mpegts.js +19 -0
- package/dist/routes/mpegts.js.map +1 -0
- package/dist/routes/play.d.ts +6 -0
- package/dist/routes/play.js +18 -0
- package/dist/routes/play.js.map +1 -0
- package/dist/routes/playlist.d.ts +36 -0
- package/dist/routes/playlist.js +134 -0
- package/dist/routes/playlist.js.map +1 -0
- package/dist/routes/root.d.ts +6 -0
- package/dist/routes/root.js +2920 -0
- package/dist/routes/root.js.map +1 -0
- package/dist/routes/streams.d.ts +6 -0
- package/dist/routes/streams.js +88 -0
- package/dist/routes/streams.js.map +1 -0
- package/dist/routes/theme.d.ts +15 -0
- package/dist/routes/theme.js +275 -0
- package/dist/routes/theme.js.map +1 -0
- package/dist/routes/ui.d.ts +56 -0
- package/dist/routes/ui.js +354 -0
- package/dist/routes/ui.js.map +1 -0
- package/dist/service/commands.d.ts +41 -0
- package/dist/service/commands.js +391 -0
- package/dist/service/commands.js.map +1 -0
- package/dist/service/generators.d.ts +33 -0
- package/dist/service/generators.js +432 -0
- package/dist/service/generators.js.map +1 -0
- package/dist/service/index.d.ts +2 -0
- package/dist/service/index.js +7 -0
- package/dist/service/index.js.map +1 -0
- package/dist/streaming/clients.d.ts +48 -0
- package/dist/streaming/clients.js +114 -0
- package/dist/streaming/clients.js.map +1 -0
- package/dist/streaming/fmp4Segmenter.d.ts +61 -0
- package/dist/streaming/fmp4Segmenter.js +461 -0
- package/dist/streaming/fmp4Segmenter.js.map +1 -0
- package/dist/streaming/hls.d.ts +120 -0
- package/dist/streaming/hls.js +722 -0
- package/dist/streaming/hls.js.map +1 -0
- package/dist/streaming/hlsSegments.d.ts +54 -0
- package/dist/streaming/hlsSegments.js +162 -0
- package/dist/streaming/hlsSegments.js.map +1 -0
- package/dist/streaming/lifecycle.d.ts +33 -0
- package/dist/streaming/lifecycle.js +185 -0
- package/dist/streaming/lifecycle.js.map +1 -0
- package/dist/streaming/monitor.d.ts +74 -0
- package/dist/streaming/monitor.js +1310 -0
- package/dist/streaming/monitor.js.map +1 -0
- package/dist/streaming/mp4Parser.d.ts +74 -0
- package/dist/streaming/mp4Parser.js +566 -0
- package/dist/streaming/mp4Parser.js.map +1 -0
- package/dist/streaming/mpegts.d.ts +14 -0
- package/dist/streaming/mpegts.js +248 -0
- package/dist/streaming/mpegts.js.map +1 -0
- package/dist/streaming/registry.d.ts +119 -0
- package/dist/streaming/registry.js +127 -0
- package/dist/streaming/registry.js.map +1 -0
- package/dist/streaming/setup.d.ts +135 -0
- package/dist/streaming/setup.js +670 -0
- package/dist/streaming/setup.js.map +1 -0
- package/dist/streaming/showInfo.d.ts +30 -0
- package/dist/streaming/showInfo.js +362 -0
- package/dist/streaming/showInfo.js.map +1 -0
- package/dist/streaming/statusEmitter.d.ts +125 -0
- package/dist/streaming/statusEmitter.js +139 -0
- package/dist/streaming/statusEmitter.js.map +1 -0
- package/dist/types/index.d.ts +403 -0
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/debugFilter.d.ts +38 -0
- package/dist/utils/debugFilter.js +157 -0
- package/dist/utils/debugFilter.js.map +1 -0
- package/dist/utils/delay.d.ts +6 -0
- package/dist/utils/delay.js +15 -0
- package/dist/utils/delay.js.map +1 -0
- package/dist/utils/errors.d.ts +15 -0
- package/dist/utils/errors.js +40 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/evaluate.d.ts +51 -0
- package/dist/utils/evaluate.js +124 -0
- package/dist/utils/evaluate.js.map +1 -0
- package/dist/utils/ffmpeg.d.ts +65 -0
- package/dist/utils/ffmpeg.js +317 -0
- package/dist/utils/ffmpeg.js.map +1 -0
- package/dist/utils/fileLogger.d.ts +25 -0
- package/dist/utils/fileLogger.js +248 -0
- package/dist/utils/fileLogger.js.map +1 -0
- package/dist/utils/format.d.ts +16 -0
- package/dist/utils/format.js +46 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/html.d.ts +6 -0
- package/dist/utils/html.js +24 -0
- package/dist/utils/html.js.map +1 -0
- package/dist/utils/index.d.ts +15 -0
- package/dist/utils/index.js +20 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logEmitter.d.ts +17 -0
- package/dist/utils/logEmitter.js +30 -0
- package/dist/utils/logEmitter.js.map +1 -0
- package/dist/utils/logger.d.ts +82 -0
- package/dist/utils/logger.js +219 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/m3u.d.ts +32 -0
- package/dist/utils/m3u.js +148 -0
- package/dist/utils/m3u.js.map +1 -0
- package/dist/utils/morganStream.d.ts +7 -0
- package/dist/utils/morganStream.js +33 -0
- package/dist/utils/morganStream.js.map +1 -0
- package/dist/utils/platform.d.ts +64 -0
- package/dist/utils/platform.js +157 -0
- package/dist/utils/platform.js.map +1 -0
- package/dist/utils/retry.d.ts +15 -0
- package/dist/utils/retry.js +82 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/streamContext.d.ts +28 -0
- package/dist/utils/streamContext.js +33 -0
- package/dist/utils/streamContext.js.map +1 -0
- package/dist/utils/version.d.ts +37 -0
- package/dist/utils/version.js +228 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +92 -0
- package/prismcast.png +0 -0
- package/prismcast.svg +74 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/* Copyright(C) 2024-2026, HJD (https://github.com/hjdhjd). All rights reserved.
|
|
2
|
+
*
|
|
3
|
+
* index.ts: Entry point for PrismCast.
|
|
4
|
+
*/
|
|
5
|
+
import { LOG, formatError, getPackageVersion, initDebugFilter, setDebugLogging } from "./utils/index.js";
|
|
6
|
+
import { CONFIG } from "./config/index.js";
|
|
7
|
+
import { flushLogBufferSync } from "./utils/fileLogger.js";
|
|
8
|
+
import { handleServiceCommand } from "./service/index.js";
|
|
9
|
+
import { killStaleChrome } from "./browser/index.js";
|
|
10
|
+
import { startServer } from "./app.js";
|
|
11
|
+
/* These handlers catch unhandled promise rejections and uncaught exceptions to prevent the process from crashing. For a livestreaming server, process stability is
|
|
12
|
+
* critical - a single unhandled error should not terminate all active streams. The handlers log the error and allow the process to continue. Individual stream
|
|
13
|
+
* failures are handled by circuit breakers, and actual browser crashes are handled by the browser's disconnected event.
|
|
14
|
+
*/
|
|
15
|
+
process.on("unhandledRejection", (reason) => {
|
|
16
|
+
LOG.error("Unhandled promise rejection: %s.", formatError(reason));
|
|
17
|
+
});
|
|
18
|
+
process.on("uncaughtException", (error) => {
|
|
19
|
+
LOG.error("Uncaught exception: %s.", formatError(error));
|
|
20
|
+
});
|
|
21
|
+
/* The entry point supports basic command-line arguments for common operations like changing the port, showing help, and displaying the version.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Prints usage information to the console.
|
|
25
|
+
*/
|
|
26
|
+
function printUsage() {
|
|
27
|
+
/* eslint-disable no-console */
|
|
28
|
+
console.log("Usage: prismcast [command] [options]");
|
|
29
|
+
console.log("");
|
|
30
|
+
console.log("Commands:");
|
|
31
|
+
console.log(" service Manage PrismCast as a system service");
|
|
32
|
+
console.log(" Run 'prismcast service --help' for details");
|
|
33
|
+
console.log("");
|
|
34
|
+
console.log("Options:");
|
|
35
|
+
console.log(" -c, --console Log to console instead of file (for Docker or debugging)");
|
|
36
|
+
console.log(" -d, --debug Enable debug logging (verbose output for troubleshooting)");
|
|
37
|
+
console.log(" -h, --help Show this help message");
|
|
38
|
+
console.log(" -p, --port <port> Set server port (default: 5589)");
|
|
39
|
+
console.log(" -v, --version Show version number");
|
|
40
|
+
console.log("");
|
|
41
|
+
console.log("If no command is specified, starts the PrismCast server.");
|
|
42
|
+
console.log("");
|
|
43
|
+
console.log("Environment Variables:");
|
|
44
|
+
console.log(" PORT HTTP server port");
|
|
45
|
+
console.log(" HOST HTTP server bind address");
|
|
46
|
+
console.log(" QUALITY_PRESET Video quality preset (480p, 720p, 1080p, 1080p-high, 4k)");
|
|
47
|
+
console.log(" VIDEO_BITRATE Video bitrate (bps)");
|
|
48
|
+
console.log(" AUDIO_BITRATE Audio bitrate (bps)");
|
|
49
|
+
console.log(" FRAME_RATE Target frame rate");
|
|
50
|
+
console.log(" CHROME_BIN Path to Chrome executable");
|
|
51
|
+
console.log(" LOG_MAX_SIZE Maximum log file size in bytes (default: 1048576)");
|
|
52
|
+
console.log(" PRISMCAST_DEBUG Debug category filter (e.g., 'tuning:hulu', 'recovery', '*,-streaming:segmenter')");
|
|
53
|
+
/* eslint-enable no-console */
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Parses command-line arguments and applies overrides to the configuration.
|
|
57
|
+
* @returns Parsed argument flags.
|
|
58
|
+
*/
|
|
59
|
+
function parseArgs() {
|
|
60
|
+
const args = process.argv.slice(2);
|
|
61
|
+
let consoleLogging = false;
|
|
62
|
+
let debugLogging = false;
|
|
63
|
+
for (let i = 0; i < args.length; i++) {
|
|
64
|
+
const arg = args[i];
|
|
65
|
+
if ((arg === "-c") || (arg === "--console")) {
|
|
66
|
+
consoleLogging = true;
|
|
67
|
+
}
|
|
68
|
+
if ((arg === "-d") || (arg === "--debug")) {
|
|
69
|
+
debugLogging = true;
|
|
70
|
+
}
|
|
71
|
+
if ((arg === "-h") || (arg === "--help")) {
|
|
72
|
+
printUsage();
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
if ((arg === "-p") || (arg === "--port")) {
|
|
76
|
+
const port = parseInt(args[++i]);
|
|
77
|
+
if (!isNaN(port)) {
|
|
78
|
+
CONFIG.server.port = port;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if ((arg === "-v") || (arg === "--version")) {
|
|
82
|
+
// eslint-disable-next-line no-console
|
|
83
|
+
console.log("PrismCast v" + getPackageVersion());
|
|
84
|
+
process.exit(0);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return { consoleLogging, debugLogging };
|
|
88
|
+
}
|
|
89
|
+
/* Check for subcommands before parsing flags. Subcommands are handled separately and exit after completion.
|
|
90
|
+
*/
|
|
91
|
+
const rawArgs = process.argv.slice(2);
|
|
92
|
+
const subcommand = rawArgs[0];
|
|
93
|
+
// Handle the 'service' subcommand for managing PrismCast as a system service.
|
|
94
|
+
if (subcommand === "service") {
|
|
95
|
+
handleServiceCommand(rawArgs.slice(1)).then((exitCode) => {
|
|
96
|
+
process.exit(exitCode);
|
|
97
|
+
}).catch((error) => {
|
|
98
|
+
// eslint-disable-next-line no-console
|
|
99
|
+
console.error("Service command failed: " + formatError(error));
|
|
100
|
+
process.exit(1);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
/* The main entry point parses command-line arguments, starts the server, and handles any fatal errors that occur during initialization. If startup fails, we exit
|
|
105
|
+
* with a non-zero code to signal the failure to process managers.
|
|
106
|
+
*/
|
|
107
|
+
const parsedArgs = parseArgs();
|
|
108
|
+
// Enable debug logging before starting the server so debug messages during startup are captured. The PRISMCAST_DEBUG environment variable takes precedence over
|
|
109
|
+
// the --debug CLI flag, allowing fine-grained category selection.
|
|
110
|
+
const debugEnv = process.env.PRISMCAST_DEBUG;
|
|
111
|
+
if (debugEnv) {
|
|
112
|
+
initDebugFilter(debugEnv);
|
|
113
|
+
}
|
|
114
|
+
else if (parsedArgs.debugLogging) {
|
|
115
|
+
setDebugLogging(true);
|
|
116
|
+
}
|
|
117
|
+
/* Safety net for server exit paths. When the process exits — whether via process.exit(1) from a fatal startup error, an unrecoverable exception, or any other
|
|
118
|
+
* termination — we ensure Chrome processes are cleaned up and buffered log entries are flushed to disk. Without this, fatal exits during startup (e.g., capture
|
|
119
|
+
* probe timeout) silently orphan Chrome processes and lose diagnostic messages that are still in the file logger's write buffer.
|
|
120
|
+
*
|
|
121
|
+
* The 'exit' event runs synchronously, so only synchronous operations are safe here. killStaleChrome() uses execSync internally, and flushLogBufferSync() writes
|
|
122
|
+
* directly to the filesystem. The graceful shutdown path (SIGTERM/SIGINT) handles cleanup via async closeBrowser() and shutdownFileLogger() — this handler is a
|
|
123
|
+
* fallback for paths that bypass graceful shutdown.
|
|
124
|
+
*
|
|
125
|
+
* This is registered only in the server branch — not for service subcommands like `prismcast service status`. Running killStaleChrome() from a service
|
|
126
|
+
* subcommand would kill Chrome belonging to the running PrismCast server instance.
|
|
127
|
+
*/
|
|
128
|
+
process.on("exit", () => {
|
|
129
|
+
// Flush logs first — this is the critical operation. The error messages from a failed startup are sitting in the write buffer and must reach disk before the
|
|
130
|
+
// process terminates. Chrome will die when its pipes break since the parent is exiting; killing it explicitly is belt-and-suspenders.
|
|
131
|
+
flushLogBufferSync();
|
|
132
|
+
try {
|
|
133
|
+
killStaleChrome();
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// Best-effort cleanup. If pkill fails for any reason, the process is exiting anyway.
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
startServer(parsedArgs.consoleLogging).catch((error) => {
|
|
140
|
+
LOG.error("Fatal startup error occurred: %s.", formatError(error));
|
|
141
|
+
process.exit(1);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { dirname, join } from "path";
|
|
2
|
+
import { fileURLToPath } from "url";
|
|
3
|
+
import { readFile } from "fs/promises";
|
|
4
|
+
/* This module serves static assets like the logo and favicon. The assets are read from the project root directory at startup and cached in memory for efficient
|
|
5
|
+
* serving.
|
|
6
|
+
*/
|
|
7
|
+
// Cached asset data. Populated on first request and reused for subsequent requests.
|
|
8
|
+
let svgData = null;
|
|
9
|
+
let pngData = null;
|
|
10
|
+
// Resolve the project root directory (two levels up from src/routes/).
|
|
11
|
+
const projectRoot = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
12
|
+
/**
|
|
13
|
+
* Loads an asset file from the project root, caching it for subsequent requests.
|
|
14
|
+
* @param filename - The filename to load from the project root.
|
|
15
|
+
* @returns The file contents as a Buffer, or null if the file could not be read.
|
|
16
|
+
*/
|
|
17
|
+
async function loadAsset(filename) {
|
|
18
|
+
try {
|
|
19
|
+
return await readFile(join(projectRoot, filename));
|
|
20
|
+
}
|
|
21
|
+
catch (_error) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Configures routes for serving static assets (logo and favicon).
|
|
27
|
+
* @param app - The Express application.
|
|
28
|
+
*/
|
|
29
|
+
export function setupAssetEndpoints(app) {
|
|
30
|
+
// Serve the SVG favicon.
|
|
31
|
+
app.get("/favicon.svg", async (_req, res) => {
|
|
32
|
+
svgData ??= await loadAsset("prismcast.svg");
|
|
33
|
+
if (svgData) {
|
|
34
|
+
res.setHeader("Content-Type", "image/svg+xml");
|
|
35
|
+
res.setHeader("Cache-Control", "public, max-age=86400");
|
|
36
|
+
res.send(svgData);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
res.status(404).send("Not found");
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
// Serve the PNG favicon (alternative for browsers that prefer PNG).
|
|
43
|
+
app.get("/favicon.png", async (_req, res) => {
|
|
44
|
+
pngData ??= await loadAsset("prismcast.png");
|
|
45
|
+
if (pngData) {
|
|
46
|
+
res.setHeader("Content-Type", "image/png");
|
|
47
|
+
res.setHeader("Cache-Control", "public, max-age=86400");
|
|
48
|
+
res.send(pngData);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
res.status(404).send("Not found");
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
// Serve the PNG logo (used for apple-touch-icon which requires PNG format).
|
|
55
|
+
app.get("/logo.png", async (_req, res) => {
|
|
56
|
+
pngData ??= await loadAsset("prismcast.png");
|
|
57
|
+
if (pngData) {
|
|
58
|
+
res.setHeader("Content-Type", "image/png");
|
|
59
|
+
res.setHeader("Cache-Control", "public, max-age=86400");
|
|
60
|
+
res.send(pngData);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
res.status(404).send("Not found");
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
// Serve the SVG logo (used for the homepage header).
|
|
67
|
+
app.get("/logo.svg", async (_req, res) => {
|
|
68
|
+
svgData ??= await loadAsset("prismcast.svg");
|
|
69
|
+
if (svgData) {
|
|
70
|
+
res.setHeader("Content-Type", "image/svg+xml");
|
|
71
|
+
res.setHeader("Cache-Control", "public, max-age=86400");
|
|
72
|
+
res.send(svgData);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
res.status(404).send("Not found");
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=assets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../../src/routes/assets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { endLoginMode, getLoginStatus, startLoginMode } from "../browser/index.js";
|
|
2
|
+
import { getResolvedChannel, resolveProviderKey } from "../config/providers.js";
|
|
3
|
+
import { getDomainConfig } from "../config/sites.js";
|
|
4
|
+
/**
|
|
5
|
+
* Sets up authentication routes for channel login.
|
|
6
|
+
* @param app - The Express application.
|
|
7
|
+
*/
|
|
8
|
+
export function setupAuthEndpoint(app) {
|
|
9
|
+
/**
|
|
10
|
+
* POST /auth/login - Start login mode for a channel or URL.
|
|
11
|
+
*
|
|
12
|
+
* Request body:
|
|
13
|
+
* - channel: The channel key (e.g., "nbc") - will be resolved to URL
|
|
14
|
+
* - url: Direct URL to navigate to
|
|
15
|
+
*
|
|
16
|
+
* One of channel or url must be provided.
|
|
17
|
+
*/
|
|
18
|
+
app.post("/auth/login", async (req, res) => {
|
|
19
|
+
const body = req.body;
|
|
20
|
+
// Resolve URL from channel or use provided URL.
|
|
21
|
+
let url;
|
|
22
|
+
if (body.channel) {
|
|
23
|
+
// Resolve provider selection and get the channel. This respects both user-defined channel overrides and provider selections (e.g., Disney+ vs ESPN.com).
|
|
24
|
+
const resolvedKey = resolveProviderKey(body.channel);
|
|
25
|
+
const channel = getResolvedChannel(resolvedKey);
|
|
26
|
+
if (!channel) {
|
|
27
|
+
const response = { error: "Channel not found.", success: false };
|
|
28
|
+
res.status(404).json(response);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
url = channel.url;
|
|
32
|
+
}
|
|
33
|
+
else if (body.url) {
|
|
34
|
+
url = body.url;
|
|
35
|
+
}
|
|
36
|
+
if (!url) {
|
|
37
|
+
const response = { error: "Either channel or url must be provided.", success: false };
|
|
38
|
+
res.status(400).json(response);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
// Check if the domain has a login URL override. Some sites (e.g., Fox.com) show different authentication options on their homepage vs their player page.
|
|
42
|
+
const domainConfig = getDomainConfig(url);
|
|
43
|
+
if (domainConfig?.loginUrl) {
|
|
44
|
+
url = domainConfig.loginUrl;
|
|
45
|
+
}
|
|
46
|
+
// Start login mode.
|
|
47
|
+
const result = await startLoginMode(url);
|
|
48
|
+
if (result.success) {
|
|
49
|
+
const response = { message: "Login mode started. Complete authentication in the browser window.", success: true };
|
|
50
|
+
res.json(response);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
const response = { error: result.error, success: false };
|
|
54
|
+
res.status(409).json(response);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
/**
|
|
58
|
+
* POST /auth/done - End login mode.
|
|
59
|
+
*
|
|
60
|
+
* Called when the user clicks "Done" in the web UI to indicate authentication is complete.
|
|
61
|
+
*/
|
|
62
|
+
app.post("/auth/done", async (_req, res) => {
|
|
63
|
+
await endLoginMode();
|
|
64
|
+
const response = { message: "Login mode ended.", success: true };
|
|
65
|
+
res.json(response);
|
|
66
|
+
});
|
|
67
|
+
/**
|
|
68
|
+
* GET /auth/status - Get current login status.
|
|
69
|
+
*
|
|
70
|
+
* Returns whether login mode is active and details about the current login session.
|
|
71
|
+
*/
|
|
72
|
+
app.get("/auth/status", (_req, res) => {
|
|
73
|
+
const status = getLoginStatus();
|
|
74
|
+
res.json(status);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/routes/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { getChannelListing } from "../config/userChannels.js";
|
|
2
|
+
/**
|
|
3
|
+
* Creates an endpoint to list all channels with their source and enabled status.
|
|
4
|
+
* @param app - The Express application.
|
|
5
|
+
*/
|
|
6
|
+
export function setupChannelsEndpoint(app) {
|
|
7
|
+
app.get("/channels", (_req, res) => {
|
|
8
|
+
const listing = getChannelListing();
|
|
9
|
+
const channels = [];
|
|
10
|
+
for (const entry of listing) {
|
|
11
|
+
// Build the response entry with required fields. Canonical channels always have name; fallback to key is defensive.
|
|
12
|
+
const channelEntry = {
|
|
13
|
+
enabled: entry.enabled,
|
|
14
|
+
key: entry.key,
|
|
15
|
+
name: entry.channel.name ?? entry.key,
|
|
16
|
+
source: entry.source,
|
|
17
|
+
url: entry.channel.url
|
|
18
|
+
};
|
|
19
|
+
// Add optional fields only when present.
|
|
20
|
+
if (entry.channel.channelNumber !== undefined) {
|
|
21
|
+
channelEntry.channelNumber = entry.channel.channelNumber;
|
|
22
|
+
}
|
|
23
|
+
if (entry.channel.channelSelector) {
|
|
24
|
+
channelEntry.channelSelector = entry.channel.channelSelector;
|
|
25
|
+
}
|
|
26
|
+
if (entry.channel.profile) {
|
|
27
|
+
channelEntry.profile = entry.channel.profile;
|
|
28
|
+
}
|
|
29
|
+
if (entry.channel.stationId) {
|
|
30
|
+
channelEntry.stationId = entry.channel.stationId;
|
|
31
|
+
}
|
|
32
|
+
channels.push(channelEntry);
|
|
33
|
+
}
|
|
34
|
+
res.json({
|
|
35
|
+
channels,
|
|
36
|
+
count: channels.length
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=channels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channels.js","sourceRoot":"","sources":["../../src/routes/channels.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAuC9D;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAY;IAEhD,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,IAAa,EAAE,GAAa,EAAQ,EAAE;QAE1D,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;QACpC,MAAM,QAAQ,GAAmB,EAAE,CAAC;QAEpC,KAAI,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAE3B,oHAAoH;YACpH,MAAM,YAAY,GAAiB;gBAEjC,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,IAAI,KAAK,CAAC,GAAG;gBACrC,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG;aACvB,CAAC;YAEF,yCAAyC;YACzC,IAAG,KAAK,CAAC,OAAO,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;gBAE7C,YAAY,CAAC,aAAa,GAAG,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC;YAC3D,CAAC;YAED,IAAG,KAAK,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;gBAEjC,YAAY,CAAC,eAAe,GAAG,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC;YAC/D,CAAC;YAED,IAAG,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;gBAEzB,YAAY,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;YAC/C,CAAC;YAED,IAAG,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;gBAE3B,YAAY,CAAC,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC;YACnD,CAAC;YAED,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC9B,CAAC;QAED,GAAG,CAAC,IAAI,CAAC;YAEP,QAAQ;YACR,KAAK,EAAE,QAAQ,CAAC,MAAM;SACvB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Alert type for styling.
|
|
3
|
+
*/
|
|
4
|
+
export type AlertType = "error" | "success" | "warning";
|
|
5
|
+
/**
|
|
6
|
+
* Generates an alert box HTML with title and message.
|
|
7
|
+
* @param type - The alert type (success, error, warning).
|
|
8
|
+
* @param title - The alert title.
|
|
9
|
+
* @param message - The alert message (can include HTML).
|
|
10
|
+
* @param escapeMessage - Whether to escape the message HTML (default true).
|
|
11
|
+
* @returns HTML string for the alert box.
|
|
12
|
+
*/
|
|
13
|
+
export declare function generateAlert(type: AlertType, title: string, message: string, escapeMessage?: boolean): string;
|
|
14
|
+
/**
|
|
15
|
+
* Generates a simple alert box without a title.
|
|
16
|
+
* @param type - The alert type (success, error, warning).
|
|
17
|
+
* @param message - The alert message.
|
|
18
|
+
* @returns HTML string for the alert box.
|
|
19
|
+
*/
|
|
20
|
+
export declare function generateSimpleAlert(type: AlertType, message: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Button variant for styling.
|
|
23
|
+
*/
|
|
24
|
+
export type ButtonVariant = "danger" | "delete" | "edit" | "primary" | "secondary";
|
|
25
|
+
/**
|
|
26
|
+
* Button size for styling.
|
|
27
|
+
*/
|
|
28
|
+
export type ButtonSize = "md" | "sm";
|
|
29
|
+
/**
|
|
30
|
+
* Options for button generation.
|
|
31
|
+
*/
|
|
32
|
+
export interface ButtonOptions {
|
|
33
|
+
className?: string;
|
|
34
|
+
disabled?: boolean;
|
|
35
|
+
id?: string;
|
|
36
|
+
onclick?: string;
|
|
37
|
+
size?: ButtonSize;
|
|
38
|
+
type?: "button" | "reset" | "submit";
|
|
39
|
+
variant: ButtonVariant;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Generates a button HTML element.
|
|
43
|
+
* @param label - The button label text.
|
|
44
|
+
* @param options - Button configuration options.
|
|
45
|
+
* @returns HTML string for the button.
|
|
46
|
+
*/
|
|
47
|
+
export declare function generateButton(label: string, options: ButtonOptions): string;
|
|
48
|
+
/**
|
|
49
|
+
* Badge variant for styling.
|
|
50
|
+
*/
|
|
51
|
+
export type BadgeVariant = "builtin" | "custom" | "env" | "flag" | "override";
|
|
52
|
+
/**
|
|
53
|
+
* Generates a badge HTML element.
|
|
54
|
+
* @param label - The badge label text.
|
|
55
|
+
* @param variant - The badge style variant.
|
|
56
|
+
* @returns HTML string for the badge.
|
|
57
|
+
*/
|
|
58
|
+
export declare function generateBadge(label: string, variant: BadgeVariant): string;
|
|
59
|
+
/**
|
|
60
|
+
* Generates a status indicator (colored dot with label).
|
|
61
|
+
* @param status - The status type (healthy, error, etc.).
|
|
62
|
+
* @param label - The label text.
|
|
63
|
+
* @returns HTML string for the status indicator.
|
|
64
|
+
*/
|
|
65
|
+
export declare function generateStatusIndicator(status: string, label: string): string;
|
|
66
|
+
/**
|
|
67
|
+
* Options for text input generation.
|
|
68
|
+
*/
|
|
69
|
+
export interface TextInputOptions {
|
|
70
|
+
disabled?: boolean;
|
|
71
|
+
hint?: string;
|
|
72
|
+
id: string;
|
|
73
|
+
max?: number;
|
|
74
|
+
min?: number;
|
|
75
|
+
name: string;
|
|
76
|
+
pattern?: string;
|
|
77
|
+
placeholder?: string;
|
|
78
|
+
required?: boolean;
|
|
79
|
+
step?: string;
|
|
80
|
+
type?: string;
|
|
81
|
+
value?: string;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Generates a form row with label and text input.
|
|
85
|
+
* @param label - The input label.
|
|
86
|
+
* @param options - Input configuration options.
|
|
87
|
+
* @returns HTML string for the form row.
|
|
88
|
+
*/
|
|
89
|
+
export declare function generateTextInput(label: string, options: TextInputOptions): string;
|
|
90
|
+
/**
|
|
91
|
+
* Option item for select dropdown.
|
|
92
|
+
*/
|
|
93
|
+
export interface SelectOption {
|
|
94
|
+
label: string;
|
|
95
|
+
selected?: boolean;
|
|
96
|
+
value: string;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Options for select dropdown generation.
|
|
100
|
+
*/
|
|
101
|
+
export interface SelectOptions {
|
|
102
|
+
disabled?: boolean;
|
|
103
|
+
hint?: string;
|
|
104
|
+
id: string;
|
|
105
|
+
name: string;
|
|
106
|
+
options: SelectOption[];
|
|
107
|
+
required?: boolean;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Generates a form row with label and select dropdown.
|
|
111
|
+
* @param label - The select label.
|
|
112
|
+
* @param config - Select configuration options.
|
|
113
|
+
* @returns HTML string for the form row.
|
|
114
|
+
*/
|
|
115
|
+
export declare function generateSelect(label: string, config: SelectOptions): string;
|
|
116
|
+
/**
|
|
117
|
+
* Generates a code block with optional copy button.
|
|
118
|
+
* @param content - The code content.
|
|
119
|
+
* @param showCopyButton - Whether to show a copy button.
|
|
120
|
+
* @param copyButtonId - ID for the copy button (required if showCopyButton is true).
|
|
121
|
+
* @returns HTML string for the code block.
|
|
122
|
+
*/
|
|
123
|
+
export declare function generateCodeBlock(content: string, showCopyButton?: boolean, copyButtonId?: string): string;
|
|
124
|
+
/**
|
|
125
|
+
* Generates a section container with optional heading.
|
|
126
|
+
* @param content - The section content (HTML).
|
|
127
|
+
* @param heading - Optional section heading.
|
|
128
|
+
* @param headingLevel - Heading level (2 or 3, default 3).
|
|
129
|
+
* @returns HTML string for the section.
|
|
130
|
+
*/
|
|
131
|
+
export declare function generateSection(content: string, heading?: string, headingLevel?: 2 | 3): string;
|
|
132
|
+
/**
|
|
133
|
+
* Generates a panel header with title and optional action button.
|
|
134
|
+
* @param title - The panel title.
|
|
135
|
+
* @param actionHtml - Optional action button HTML (not escaped).
|
|
136
|
+
* @returns HTML string for the panel header.
|
|
137
|
+
*/
|
|
138
|
+
export declare function generatePanelHeader(title: string, actionHtml?: string): string;
|