@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.
Files changed (234) hide show
  1. package/LICENSE.md +7 -0
  2. package/README.md +347 -0
  3. package/bin/prismcast +6 -0
  4. package/dist/app.d.ts +6 -0
  5. package/dist/app.js +315 -0
  6. package/dist/app.js.map +1 -0
  7. package/dist/browser/cdp.d.ts +38 -0
  8. package/dist/browser/cdp.js +155 -0
  9. package/dist/browser/cdp.js.map +1 -0
  10. package/dist/browser/channelSelection.d.ts +65 -0
  11. package/dist/browser/channelSelection.js +202 -0
  12. package/dist/browser/channelSelection.js.map +1 -0
  13. package/dist/browser/display.d.ts +34 -0
  14. package/dist/browser/display.js +54 -0
  15. package/dist/browser/display.js.map +1 -0
  16. package/dist/browser/index.d.ts +205 -0
  17. package/dist/browser/index.js +1205 -0
  18. package/dist/browser/index.js.map +1 -0
  19. package/dist/browser/tuning/fox.d.ts +2 -0
  20. package/dist/browser/tuning/fox.js +83 -0
  21. package/dist/browser/tuning/fox.js.map +1 -0
  22. package/dist/browser/tuning/hbo.d.ts +2 -0
  23. package/dist/browser/tuning/hbo.js +237 -0
  24. package/dist/browser/tuning/hbo.js.map +1 -0
  25. package/dist/browser/tuning/hulu.d.ts +2 -0
  26. package/dist/browser/tuning/hulu.js +550 -0
  27. package/dist/browser/tuning/hulu.js.map +1 -0
  28. package/dist/browser/tuning/sling.d.ts +2 -0
  29. package/dist/browser/tuning/sling.js +518 -0
  30. package/dist/browser/tuning/sling.js.map +1 -0
  31. package/dist/browser/tuning/thumbnailRow.d.ts +2 -0
  32. package/dist/browser/tuning/thumbnailRow.js +108 -0
  33. package/dist/browser/tuning/thumbnailRow.js.map +1 -0
  34. package/dist/browser/tuning/tileClick.d.ts +2 -0
  35. package/dist/browser/tuning/tileClick.js +103 -0
  36. package/dist/browser/tuning/tileClick.js.map +1 -0
  37. package/dist/browser/tuning/youtubeTv.d.ts +2 -0
  38. package/dist/browser/tuning/youtubeTv.js +182 -0
  39. package/dist/browser/tuning/youtubeTv.js.map +1 -0
  40. package/dist/browser/video.d.ts +289 -0
  41. package/dist/browser/video.js +996 -0
  42. package/dist/browser/video.js.map +1 -0
  43. package/dist/channels/index.d.ts +3 -0
  44. package/dist/channels/index.js +392 -0
  45. package/dist/channels/index.js.map +1 -0
  46. package/dist/config/index.d.ts +53 -0
  47. package/dist/config/index.js +233 -0
  48. package/dist/config/index.js.map +1 -0
  49. package/dist/config/presets.d.ts +98 -0
  50. package/dist/config/presets.js +241 -0
  51. package/dist/config/presets.js.map +1 -0
  52. package/dist/config/profiles.d.ts +79 -0
  53. package/dist/config/profiles.js +245 -0
  54. package/dist/config/profiles.js.map +1 -0
  55. package/dist/config/providers.d.ts +120 -0
  56. package/dist/config/providers.js +450 -0
  57. package/dist/config/providers.js.map +1 -0
  58. package/dist/config/sites.d.ts +22 -0
  59. package/dist/config/sites.js +377 -0
  60. package/dist/config/sites.js.map +1 -0
  61. package/dist/config/userChannels.d.ts +178 -0
  62. package/dist/config/userChannels.js +543 -0
  63. package/dist/config/userChannels.js.map +1 -0
  64. package/dist/config/userConfig.d.ts +235 -0
  65. package/dist/config/userConfig.js +913 -0
  66. package/dist/config/userConfig.js.map +1 -0
  67. package/dist/hdhr/channelMap.d.ts +21 -0
  68. package/dist/hdhr/channelMap.js +82 -0
  69. package/dist/hdhr/channelMap.js.map +1 -0
  70. package/dist/hdhr/deviceId.d.ts +11 -0
  71. package/dist/hdhr/deviceId.js +84 -0
  72. package/dist/hdhr/deviceId.js.map +1 -0
  73. package/dist/hdhr/discover.d.ts +6 -0
  74. package/dist/hdhr/discover.js +155 -0
  75. package/dist/hdhr/discover.js.map +1 -0
  76. package/dist/hdhr/index.d.ts +9 -0
  77. package/dist/hdhr/index.js +87 -0
  78. package/dist/hdhr/index.js.map +1 -0
  79. package/dist/index.d.ts +1 -0
  80. package/dist/index.js +144 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/routes/assets.d.ts +6 -0
  83. package/dist/routes/assets.js +79 -0
  84. package/dist/routes/assets.js.map +1 -0
  85. package/dist/routes/auth.d.ts +6 -0
  86. package/dist/routes/auth.js +77 -0
  87. package/dist/routes/auth.js.map +1 -0
  88. package/dist/routes/channels.d.ts +6 -0
  89. package/dist/routes/channels.js +40 -0
  90. package/dist/routes/channels.js.map +1 -0
  91. package/dist/routes/components.d.ts +138 -0
  92. package/dist/routes/components.js +210 -0
  93. package/dist/routes/components.js.map +1 -0
  94. package/dist/routes/config.d.ts +72 -0
  95. package/dist/routes/config.js +1977 -0
  96. package/dist/routes/config.js.map +1 -0
  97. package/dist/routes/debug.d.ts +6 -0
  98. package/dist/routes/debug.js +274 -0
  99. package/dist/routes/debug.js.map +1 -0
  100. package/dist/routes/health.d.ts +6 -0
  101. package/dist/routes/health.js +85 -0
  102. package/dist/routes/health.js.map +1 -0
  103. package/dist/routes/hls.d.ts +6 -0
  104. package/dist/routes/hls.js +25 -0
  105. package/dist/routes/hls.js.map +1 -0
  106. package/dist/routes/index.d.ts +19 -0
  107. package/dist/routes/index.js +49 -0
  108. package/dist/routes/index.js.map +1 -0
  109. package/dist/routes/logs.d.ts +6 -0
  110. package/dist/routes/logs.js +164 -0
  111. package/dist/routes/logs.js.map +1 -0
  112. package/dist/routes/mpegts.d.ts +6 -0
  113. package/dist/routes/mpegts.js +19 -0
  114. package/dist/routes/mpegts.js.map +1 -0
  115. package/dist/routes/play.d.ts +6 -0
  116. package/dist/routes/play.js +18 -0
  117. package/dist/routes/play.js.map +1 -0
  118. package/dist/routes/playlist.d.ts +36 -0
  119. package/dist/routes/playlist.js +134 -0
  120. package/dist/routes/playlist.js.map +1 -0
  121. package/dist/routes/root.d.ts +6 -0
  122. package/dist/routes/root.js +2920 -0
  123. package/dist/routes/root.js.map +1 -0
  124. package/dist/routes/streams.d.ts +6 -0
  125. package/dist/routes/streams.js +88 -0
  126. package/dist/routes/streams.js.map +1 -0
  127. package/dist/routes/theme.d.ts +15 -0
  128. package/dist/routes/theme.js +275 -0
  129. package/dist/routes/theme.js.map +1 -0
  130. package/dist/routes/ui.d.ts +56 -0
  131. package/dist/routes/ui.js +354 -0
  132. package/dist/routes/ui.js.map +1 -0
  133. package/dist/service/commands.d.ts +41 -0
  134. package/dist/service/commands.js +391 -0
  135. package/dist/service/commands.js.map +1 -0
  136. package/dist/service/generators.d.ts +33 -0
  137. package/dist/service/generators.js +432 -0
  138. package/dist/service/generators.js.map +1 -0
  139. package/dist/service/index.d.ts +2 -0
  140. package/dist/service/index.js +7 -0
  141. package/dist/service/index.js.map +1 -0
  142. package/dist/streaming/clients.d.ts +48 -0
  143. package/dist/streaming/clients.js +114 -0
  144. package/dist/streaming/clients.js.map +1 -0
  145. package/dist/streaming/fmp4Segmenter.d.ts +61 -0
  146. package/dist/streaming/fmp4Segmenter.js +461 -0
  147. package/dist/streaming/fmp4Segmenter.js.map +1 -0
  148. package/dist/streaming/hls.d.ts +120 -0
  149. package/dist/streaming/hls.js +722 -0
  150. package/dist/streaming/hls.js.map +1 -0
  151. package/dist/streaming/hlsSegments.d.ts +54 -0
  152. package/dist/streaming/hlsSegments.js +162 -0
  153. package/dist/streaming/hlsSegments.js.map +1 -0
  154. package/dist/streaming/lifecycle.d.ts +33 -0
  155. package/dist/streaming/lifecycle.js +185 -0
  156. package/dist/streaming/lifecycle.js.map +1 -0
  157. package/dist/streaming/monitor.d.ts +74 -0
  158. package/dist/streaming/monitor.js +1310 -0
  159. package/dist/streaming/monitor.js.map +1 -0
  160. package/dist/streaming/mp4Parser.d.ts +74 -0
  161. package/dist/streaming/mp4Parser.js +566 -0
  162. package/dist/streaming/mp4Parser.js.map +1 -0
  163. package/dist/streaming/mpegts.d.ts +14 -0
  164. package/dist/streaming/mpegts.js +248 -0
  165. package/dist/streaming/mpegts.js.map +1 -0
  166. package/dist/streaming/registry.d.ts +119 -0
  167. package/dist/streaming/registry.js +127 -0
  168. package/dist/streaming/registry.js.map +1 -0
  169. package/dist/streaming/setup.d.ts +135 -0
  170. package/dist/streaming/setup.js +670 -0
  171. package/dist/streaming/setup.js.map +1 -0
  172. package/dist/streaming/showInfo.d.ts +30 -0
  173. package/dist/streaming/showInfo.js +362 -0
  174. package/dist/streaming/showInfo.js.map +1 -0
  175. package/dist/streaming/statusEmitter.d.ts +125 -0
  176. package/dist/streaming/statusEmitter.js +139 -0
  177. package/dist/streaming/statusEmitter.js.map +1 -0
  178. package/dist/types/index.d.ts +403 -0
  179. package/dist/types/index.js +6 -0
  180. package/dist/types/index.js.map +1 -0
  181. package/dist/utils/debugFilter.d.ts +38 -0
  182. package/dist/utils/debugFilter.js +157 -0
  183. package/dist/utils/debugFilter.js.map +1 -0
  184. package/dist/utils/delay.d.ts +6 -0
  185. package/dist/utils/delay.js +15 -0
  186. package/dist/utils/delay.js.map +1 -0
  187. package/dist/utils/errors.d.ts +15 -0
  188. package/dist/utils/errors.js +40 -0
  189. package/dist/utils/errors.js.map +1 -0
  190. package/dist/utils/evaluate.d.ts +51 -0
  191. package/dist/utils/evaluate.js +124 -0
  192. package/dist/utils/evaluate.js.map +1 -0
  193. package/dist/utils/ffmpeg.d.ts +65 -0
  194. package/dist/utils/ffmpeg.js +317 -0
  195. package/dist/utils/ffmpeg.js.map +1 -0
  196. package/dist/utils/fileLogger.d.ts +25 -0
  197. package/dist/utils/fileLogger.js +248 -0
  198. package/dist/utils/fileLogger.js.map +1 -0
  199. package/dist/utils/format.d.ts +16 -0
  200. package/dist/utils/format.js +46 -0
  201. package/dist/utils/format.js.map +1 -0
  202. package/dist/utils/html.d.ts +6 -0
  203. package/dist/utils/html.js +24 -0
  204. package/dist/utils/html.js.map +1 -0
  205. package/dist/utils/index.d.ts +15 -0
  206. package/dist/utils/index.js +20 -0
  207. package/dist/utils/index.js.map +1 -0
  208. package/dist/utils/logEmitter.d.ts +17 -0
  209. package/dist/utils/logEmitter.js +30 -0
  210. package/dist/utils/logEmitter.js.map +1 -0
  211. package/dist/utils/logger.d.ts +82 -0
  212. package/dist/utils/logger.js +219 -0
  213. package/dist/utils/logger.js.map +1 -0
  214. package/dist/utils/m3u.d.ts +32 -0
  215. package/dist/utils/m3u.js +148 -0
  216. package/dist/utils/m3u.js.map +1 -0
  217. package/dist/utils/morganStream.d.ts +7 -0
  218. package/dist/utils/morganStream.js +33 -0
  219. package/dist/utils/morganStream.js.map +1 -0
  220. package/dist/utils/platform.d.ts +64 -0
  221. package/dist/utils/platform.js +157 -0
  222. package/dist/utils/platform.js.map +1 -0
  223. package/dist/utils/retry.d.ts +15 -0
  224. package/dist/utils/retry.js +82 -0
  225. package/dist/utils/retry.js.map +1 -0
  226. package/dist/utils/streamContext.d.ts +28 -0
  227. package/dist/utils/streamContext.js +33 -0
  228. package/dist/utils/streamContext.js.map +1 -0
  229. package/dist/utils/version.d.ts +37 -0
  230. package/dist/utils/version.js +228 -0
  231. package/dist/utils/version.js.map +1 -0
  232. package/package.json +92 -0
  233. package/prismcast.png +0 -0
  234. package/prismcast.svg +74 -0
@@ -0,0 +1,432 @@
1
+ import { SERVICE_ID, SERVICE_NAME, getLogsDirectory, getNodeExecutablePath, getPlatform, getPrismCastEntryPoint, getPrismCastWorkingDirectory, getServiceFileDirectory, getServiceFilePath } from "../utils/platform.js";
2
+ import { execSync } from "node:child_process";
3
+ import fs from "node:fs";
4
+ const { promises: fsPromises } = fs;
5
+ /* Generates a launchd property list (plist) file for macOS. The plist is installed to ~/Library/LaunchAgents/ and configured with:
6
+ * - RunAtLoad: Start when user logs in
7
+ * - KeepAlive: Restart automatically if the process exits
8
+ * - StandardOutPath/StandardErrorPath: Capture stdout/stderr to ~/.prismcast/logs/
9
+ */
10
+ /**
11
+ * Escapes a string for use in XML by replacing special characters with entities.
12
+ * @param str - The string to escape.
13
+ * @returns The escaped string safe for XML.
14
+ */
15
+ function escapeXml(str) {
16
+ return str
17
+ .replace(/&/g, "&")
18
+ .replace(/</g, "&lt;")
19
+ .replace(/>/g, "&gt;")
20
+ .replace(/"/g, "&quot;")
21
+ .replace(/'/g, "&apos;");
22
+ }
23
+ /**
24
+ * Creates a launchd service generator for macOS.
25
+ * @returns A ServiceGenerator for launchd.
26
+ */
27
+ function createLaunchdGenerator() {
28
+ return {
29
+ generate(options) {
30
+ const nodePath = getNodeExecutablePath();
31
+ const entryPoint = getPrismCastEntryPoint();
32
+ const workingDir = getPrismCastWorkingDirectory();
33
+ const logsDir = getLogsDirectory();
34
+ // Build environment variables section. Always include PRISMCAST_SERVICE=1 for service detection.
35
+ const envVars = { PRISMCAST_SERVICE: "1", ...options.envVars };
36
+ // Generate the environment dictionary entries.
37
+ const envEntries = Object.entries(envVars)
38
+ .sort(([a], [b]) => a.localeCompare(b))
39
+ .map(([key, value]) => [
40
+ " <key>" + escapeXml(key) + "</key>",
41
+ " <string>" + escapeXml(value) + "</string>"
42
+ ].join("\n"))
43
+ .join("\n");
44
+ // Generate the plist content.
45
+ return [
46
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
47
+ "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">",
48
+ "<plist version=\"1.0\">",
49
+ "<dict>",
50
+ " <key>Label</key>",
51
+ " <string>" + escapeXml(SERVICE_ID) + "</string>",
52
+ "",
53
+ " <key>ProgramArguments</key>",
54
+ " <array>",
55
+ " <string>" + escapeXml(nodePath) + "</string>",
56
+ " <string>" + escapeXml(entryPoint) + "</string>",
57
+ " </array>",
58
+ "",
59
+ " <key>WorkingDirectory</key>",
60
+ " <string>" + escapeXml(workingDir) + "</string>",
61
+ "",
62
+ " <key>EnvironmentVariables</key>",
63
+ " <dict>",
64
+ envEntries,
65
+ " </dict>",
66
+ "",
67
+ " <key>RunAtLoad</key>",
68
+ " <true/>",
69
+ "",
70
+ " <key>KeepAlive</key>",
71
+ " <true/>",
72
+ "",
73
+ " <key>StandardOutPath</key>",
74
+ " <string>" + escapeXml(logsDir + "/service-stdout.log") + "</string>",
75
+ "",
76
+ " <key>StandardErrorPath</key>",
77
+ " <string>" + escapeXml(logsDir + "/service-stderr.log") + "</string>",
78
+ "</dict>",
79
+ "</plist>",
80
+ ""
81
+ ].join("\n");
82
+ },
83
+ getInstallPath() {
84
+ return getServiceFilePath();
85
+ },
86
+ async install(content) {
87
+ const installPath = this.getInstallPath();
88
+ const installDir = getServiceFileDirectory();
89
+ const logsDir = getLogsDirectory();
90
+ // Ensure directories exist.
91
+ await fsPromises.mkdir(installDir, { recursive: true });
92
+ await fsPromises.mkdir(logsDir, { recursive: true });
93
+ // Write the plist file.
94
+ await fsPromises.writeFile(installPath, content, "utf8");
95
+ // Load the service with launchctl.
96
+ try {
97
+ execSync("launchctl load -w \"" + installPath + "\"", { stdio: "pipe" });
98
+ }
99
+ catch {
100
+ // If load fails, try to unload first then reload (handles reinstall case).
101
+ try {
102
+ execSync("launchctl unload \"" + installPath + "\"", { stdio: "pipe" });
103
+ }
104
+ catch {
105
+ // Ignore unload errors.
106
+ }
107
+ execSync("launchctl load -w \"" + installPath + "\"", { stdio: "pipe" });
108
+ }
109
+ },
110
+ // eslint-disable-next-line @typescript-eslint/require-await
111
+ async isInstalled() {
112
+ return fs.existsSync(this.getInstallPath());
113
+ },
114
+ // eslint-disable-next-line @typescript-eslint/require-await
115
+ async isRunning() {
116
+ try {
117
+ // Use launchctl list | grep to get tab-separated output: "PID\tStatus\tLabel". The grep exits non-zero if not found.
118
+ const result = execSync("launchctl list | grep " + SERVICE_ID, { encoding: "utf8", stdio: "pipe" });
119
+ // Parse the PID from the first column. Format: "12345\t0\tcom.github.hjdhjd.prismcast" or "-\t0\t..." if loaded but not running.
120
+ const pid = result.trim().split("\t")[0];
121
+ // PID is "-" when loaded but process not running, or a number when actually running.
122
+ return (pid !== "-") && !isNaN(Number(pid));
123
+ }
124
+ catch {
125
+ return false;
126
+ }
127
+ },
128
+ platform: "darwin",
129
+ serviceManager: "launchd",
130
+ // eslint-disable-next-line @typescript-eslint/require-await
131
+ async start() {
132
+ const installPath = this.getInstallPath();
133
+ // Load the plist if not already loaded. This is idempotent — if already loaded, launchd logs a warning but doesn't fail.
134
+ try {
135
+ execSync("launchctl load -w \"" + installPath + "\"", { stdio: "pipe" });
136
+ }
137
+ catch {
138
+ // Ignore — may already be loaded.
139
+ }
140
+ // Explicitly start the service. This handles the case where the plist is loaded but the process isn't running (e.g., after a crash).
141
+ execSync("launchctl start " + SERVICE_ID, { stdio: "pipe" });
142
+ },
143
+ // eslint-disable-next-line @typescript-eslint/require-await
144
+ async stop() {
145
+ const installPath = this.getInstallPath();
146
+ execSync("launchctl unload \"" + installPath + "\"", { stdio: "pipe" });
147
+ },
148
+ async uninstall() {
149
+ const installPath = this.getInstallPath();
150
+ // Unload the service first.
151
+ try {
152
+ execSync("launchctl unload \"" + installPath + "\"", { stdio: "pipe" });
153
+ }
154
+ catch {
155
+ // Ignore errors if service wasn't loaded.
156
+ }
157
+ // Remove the plist file.
158
+ if (fs.existsSync(installPath)) {
159
+ await fsPromises.unlink(installPath);
160
+ }
161
+ }
162
+ };
163
+ }
164
+ /* Generates a systemd user service unit file for Linux. The unit is installed to ~/.config/systemd/user/ and configured with:
165
+ * - Restart=always: Restart automatically if the process exits
166
+ * - RestartSec=5: Wait 5 seconds before restarting
167
+ * - WantedBy=default.target: Start when user session begins
168
+ */
169
+ /**
170
+ * Creates a systemd service generator for Linux.
171
+ * @returns A ServiceGenerator for systemd.
172
+ */
173
+ function createSystemdGenerator() {
174
+ return {
175
+ generate(options) {
176
+ const nodePath = getNodeExecutablePath();
177
+ const entryPoint = getPrismCastEntryPoint();
178
+ const workingDir = getPrismCastWorkingDirectory();
179
+ // Build environment variables. Always include PRISMCAST_SERVICE=1 for service detection.
180
+ const envVars = { PRISMCAST_SERVICE: "1", ...options.envVars };
181
+ // Generate Environment= lines, sorted alphabetically.
182
+ const envLines = Object.entries(envVars)
183
+ .sort(([a], [b]) => a.localeCompare(b))
184
+ .map(([key, value]) => "Environment=\"" + key + "=" + value + "\"")
185
+ .join("\n");
186
+ // Generate the unit file content.
187
+ return [
188
+ "[Unit]",
189
+ "Description=" + SERVICE_NAME + " Streaming Server",
190
+ "After=network.target",
191
+ "",
192
+ "[Service]",
193
+ "Type=simple",
194
+ "ExecStart=" + nodePath + " " + entryPoint,
195
+ "WorkingDirectory=" + workingDir,
196
+ "Restart=always",
197
+ "RestartSec=5",
198
+ envLines,
199
+ "",
200
+ "[Install]",
201
+ "WantedBy=default.target",
202
+ ""
203
+ ].join("\n");
204
+ },
205
+ getInstallPath() {
206
+ return getServiceFilePath();
207
+ },
208
+ async install(content) {
209
+ const installPath = this.getInstallPath();
210
+ const installDir = getServiceFileDirectory();
211
+ const logsDir = getLogsDirectory();
212
+ // Ensure directories exist.
213
+ await fsPromises.mkdir(installDir, { recursive: true });
214
+ await fsPromises.mkdir(logsDir, { recursive: true });
215
+ // Write the unit file.
216
+ await fsPromises.writeFile(installPath, content, "utf8");
217
+ // Reload systemd to pick up the new unit file.
218
+ try {
219
+ execSync("systemctl --user daemon-reload", { stdio: "pipe" });
220
+ }
221
+ catch {
222
+ // Ignore if systemctl isn't available (shouldn't happen on systemd systems).
223
+ }
224
+ // Enable and start the service.
225
+ execSync("systemctl --user enable prismcast.service", { stdio: "pipe" });
226
+ execSync("systemctl --user start prismcast.service", { stdio: "pipe" });
227
+ },
228
+ // eslint-disable-next-line @typescript-eslint/require-await
229
+ async isInstalled() {
230
+ return fs.existsSync(this.getInstallPath());
231
+ },
232
+ // eslint-disable-next-line @typescript-eslint/require-await
233
+ async isRunning() {
234
+ try {
235
+ const result = execSync("systemctl --user is-active prismcast.service", { encoding: "utf8", stdio: "pipe" });
236
+ return result.trim() === "active";
237
+ }
238
+ catch {
239
+ return false;
240
+ }
241
+ },
242
+ platform: "linux",
243
+ serviceManager: "systemd",
244
+ // eslint-disable-next-line @typescript-eslint/require-await
245
+ async start() {
246
+ execSync("systemctl --user start prismcast.service", { stdio: "pipe" });
247
+ },
248
+ // eslint-disable-next-line @typescript-eslint/require-await
249
+ async stop() {
250
+ execSync("systemctl --user stop prismcast.service", { stdio: "pipe" });
251
+ },
252
+ async uninstall() {
253
+ const installPath = this.getInstallPath();
254
+ // Stop and disable the service.
255
+ try {
256
+ execSync("systemctl --user stop prismcast.service", { stdio: "pipe" });
257
+ }
258
+ catch {
259
+ // Ignore if not running.
260
+ }
261
+ try {
262
+ execSync("systemctl --user disable prismcast.service", { stdio: "pipe" });
263
+ }
264
+ catch {
265
+ // Ignore if not enabled.
266
+ }
267
+ // Remove the unit file.
268
+ if (fs.existsSync(installPath)) {
269
+ await fsPromises.unlink(installPath);
270
+ }
271
+ // Reload systemd.
272
+ try {
273
+ execSync("systemctl --user daemon-reload", { stdio: "pipe" });
274
+ }
275
+ catch {
276
+ // Ignore.
277
+ }
278
+ }
279
+ };
280
+ }
281
+ /* Uses Windows Task Scheduler via schtasks.exe to create a task that runs at user logon. Unlike launchd and systemd, Task Scheduler doesn't have built-in process
282
+ * supervision, so we configure the task to restart on failure. A marker file is used to track installation state.
283
+ */
284
+ /**
285
+ * Creates a Windows Task Scheduler generator.
286
+ * @returns A ServiceGenerator for Windows Task Scheduler.
287
+ */
288
+ function createWindowsSchedulerGenerator() {
289
+ const taskName = SERVICE_NAME;
290
+ return {
291
+ generate(options) {
292
+ const nodePath = getNodeExecutablePath();
293
+ const entryPoint = getPrismCastEntryPoint();
294
+ const workingDir = getPrismCastWorkingDirectory();
295
+ // Build environment variables for the command. We'll set them in the task action using cmd /c set.
296
+ const envVars = { PRISMCAST_SERVICE: "1", ...options.envVars };
297
+ // Generate environment variable SET commands.
298
+ const envSets = Object.entries(envVars)
299
+ .sort(([a], [b]) => a.localeCompare(b))
300
+ .map(([key, value]) => "set \"" + key + "=" + value + "\"")
301
+ .join(" && ");
302
+ // Return the command to run (used by install).
303
+ return "cmd /c \"cd /d \"" + workingDir + "\" && " + envSets + " && \"" + nodePath + "\" \"" + entryPoint + "\"\"";
304
+ },
305
+ getInstallPath() {
306
+ return getServiceFilePath();
307
+ },
308
+ async install(content) {
309
+ const installPath = this.getInstallPath();
310
+ const installDir = getServiceFileDirectory();
311
+ const logsDir = getLogsDirectory();
312
+ // Ensure directories exist.
313
+ await fsPromises.mkdir(installDir, { recursive: true });
314
+ await fsPromises.mkdir(logsDir, { recursive: true });
315
+ // Delete existing task if it exists.
316
+ try {
317
+ execSync("schtasks /Delete /TN \"" + taskName + "\" /F", { stdio: "pipe" });
318
+ }
319
+ catch {
320
+ // Ignore if task doesn't exist.
321
+ }
322
+ // Create the scheduled task. The /SC ONLOGON runs at user logon. /RL HIGHEST gives elevated privileges if needed.
323
+ const createCmd = "schtasks /Create /TN \"" + taskName + "\" /TR \"" + content + "\" /SC ONLOGON /RL HIGHEST /F";
324
+ execSync(createCmd, { stdio: "pipe" });
325
+ // Write marker file to indicate installation.
326
+ await fsPromises.writeFile(installPath, "Installed at " + new Date().toISOString() + "\n", "utf8");
327
+ // Start the task immediately.
328
+ try {
329
+ execSync("schtasks /Run /TN \"" + taskName + "\"", { stdio: "pipe" });
330
+ }
331
+ catch {
332
+ // Ignore if start fails.
333
+ }
334
+ },
335
+ // eslint-disable-next-line @typescript-eslint/require-await
336
+ async isInstalled() {
337
+ try {
338
+ execSync("schtasks /Query /TN \"" + taskName + "\"", { stdio: "pipe" });
339
+ return true;
340
+ }
341
+ catch {
342
+ return false;
343
+ }
344
+ },
345
+ // eslint-disable-next-line @typescript-eslint/require-await
346
+ async isRunning() {
347
+ try {
348
+ const result = execSync("schtasks /Query /TN \"" + taskName + "\" /FO CSV /NH", { encoding: "utf8", stdio: "pipe" });
349
+ return result.includes("Running");
350
+ }
351
+ catch {
352
+ return false;
353
+ }
354
+ },
355
+ platform: "windows",
356
+ serviceManager: "windows-scheduler",
357
+ // eslint-disable-next-line @typescript-eslint/require-await
358
+ async start() {
359
+ execSync("schtasks /Run /TN \"" + taskName + "\"", { stdio: "pipe" });
360
+ },
361
+ // eslint-disable-next-line @typescript-eslint/require-await
362
+ async stop() {
363
+ execSync("schtasks /End /TN \"" + taskName + "\"", { stdio: "pipe" });
364
+ },
365
+ async uninstall() {
366
+ const installPath = this.getInstallPath();
367
+ // Delete the scheduled task.
368
+ try {
369
+ execSync("schtasks /Delete /TN \"" + taskName + "\" /F", { stdio: "pipe" });
370
+ }
371
+ catch {
372
+ // Ignore if task doesn't exist.
373
+ }
374
+ // Remove marker file.
375
+ if (fs.existsSync(installPath)) {
376
+ await fsPromises.unlink(installPath);
377
+ }
378
+ }
379
+ };
380
+ }
381
+ /* Returns the appropriate service generator for the current platform.
382
+ */
383
+ /**
384
+ * Returns the service generator for the current platform.
385
+ * @returns The appropriate ServiceGenerator, or null if the platform is not supported.
386
+ */
387
+ export function getServiceGenerator() {
388
+ switch (getPlatform()) {
389
+ case "darwin": {
390
+ return createLaunchdGenerator();
391
+ }
392
+ case "linux": {
393
+ return createSystemdGenerator();
394
+ }
395
+ case "windows": {
396
+ return createWindowsSchedulerGenerator();
397
+ }
398
+ default: {
399
+ return null;
400
+ }
401
+ }
402
+ }
403
+ /**
404
+ * Collects environment variables that should be persisted in the service file. This includes settings that differ from defaults or have been explicitly configured.
405
+ * @returns A record of environment variable names to values.
406
+ */
407
+ export function collectServiceEnvironment() {
408
+ const envVars = {};
409
+ // Always capture PATH so that FFmpeg and other tools can be found. Service managers like launchd use a minimal PATH by default (/usr/bin:/bin:/usr/sbin:/sbin)
410
+ // which doesn't include Homebrew or other common tool locations.
411
+ if (process.env.PATH) {
412
+ envVars.PATH = process.env.PATH;
413
+ }
414
+ // Include key settings if they're set via environment. These are the settings most likely to be intentionally configured.
415
+ const keysToCapture = [
416
+ "AUDIO_BITRATE",
417
+ "CAPTURE_MODE",
418
+ "CHROME_BIN",
419
+ "FRAME_RATE",
420
+ "HOST",
421
+ "QUALITY_PRESET",
422
+ "VIDEO_BITRATE"
423
+ ];
424
+ for (const key of keysToCapture) {
425
+ const value = process.env[key];
426
+ if (value !== undefined) {
427
+ envVars[key] = value;
428
+ }
429
+ }
430
+ return envVars;
431
+ }
432
+ //# sourceMappingURL=generators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generators.js","sourceRoot":"","sources":["../../src/service/generators.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export * from "./commands.js";
2
+ export * from "./generators.js";
@@ -0,0 +1,7 @@
1
+ /* Copyright(C) 2024-2026, HJD (https://github.com/hjdhjd). All rights reserved.
2
+ *
3
+ * index.ts: Service module exports for PrismCast.
4
+ */
5
+ export * from "./commands.js";
6
+ export * from "./generators.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/service/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Client type classification by protocol.
3
+ */
4
+ export type ClientType = "hls" | "mpegts";
5
+ /**
6
+ * Count of clients for a specific client type.
7
+ */
8
+ export interface ClientTypeCount {
9
+ count: number;
10
+ type: ClientType;
11
+ }
12
+ /**
13
+ * Aggregated client summary for a stream.
14
+ */
15
+ export interface ClientSummary {
16
+ clients: ClientTypeCount[];
17
+ total: number;
18
+ }
19
+ /**
20
+ * Registers or refreshes a client for a stream. For HLS clients, this updates the lastSeen timestamp to prevent TTL expiration. For MPEG-TS clients, this creates
21
+ * the client entry on connect. Guards against registration after stream termination by verifying the stream still exists in the registry.
22
+ * @param streamId - The numeric stream ID.
23
+ * @param clientAddress - The raw client IP address.
24
+ * @param protocol - The protocol ("hls" or "mpegts").
25
+ */
26
+ export declare function registerClient(streamId: number, clientAddress: string, protocol: ClientType): void;
27
+ /**
28
+ * Unregisters a client from a stream. Used for MPEG-TS clients on disconnect. HLS clients are not explicitly unregistered — they expire via TTL cleanup in
29
+ * getClientSummary(). If the client map has already been cleared (e.g., during stream termination), this is a no-op.
30
+ * @param streamId - The numeric stream ID.
31
+ * @param clientAddress - The raw client IP address.
32
+ * @param protocol - The protocol ("hls" or "mpegts").
33
+ */
34
+ export declare function unregisterClient(streamId: number, clientAddress: string, protocol: ClientType): void;
35
+ /**
36
+ * Returns an aggregated client summary for a stream. Performs lazy TTL cleanup of stale HLS clients as a side effect — entries that have not been refreshed within
37
+ * HLS_CLIENT_TTL are removed. MPEG-TS clients are managed explicitly via register/unregister and are never expired by TTL.
38
+ *
39
+ * Called by the monitor every ~2 seconds during status emission.
40
+ * @param streamId - The numeric stream ID.
41
+ * @returns The client summary with total count and per-type breakdown.
42
+ */
43
+ export declare function getClientSummary(streamId: number): ClientSummary;
44
+ /**
45
+ * Removes all client tracking data for a stream. Called during stream termination to prevent memory leaks.
46
+ * @param streamId - The numeric stream ID.
47
+ */
48
+ export declare function clearClients(streamId: number): void;
@@ -0,0 +1,114 @@
1
+ /* Copyright(C) 2024-2026, HJD (https://github.com/hjdhjd). All rights reserved.
2
+ *
3
+ * clients.ts: Client tracking for PrismCast streams.
4
+ */
5
+ import { getStream } from "./registry.js";
6
+ // Constants.
7
+ // HLS clients that have not sent a playlist request within this window are considered disconnected and removed during the next summary query.
8
+ const HLS_CLIENT_TTL = 30000;
9
+ // State.
10
+ // Per-stream client maps. Outer key is numeric stream ID, inner key is a composite string "protocol:address".
11
+ const clientMaps = new Map();
12
+ // Registration.
13
+ /**
14
+ * Normalizes an IP address by stripping the IPv6-mapped IPv4 prefix. This prevents the same client from appearing twice when Express reports the address as
15
+ * "::ffff:192.168.1.50" on some requests and "192.168.1.50" on others.
16
+ * @param address - The raw IP address string.
17
+ * @returns The normalized address.
18
+ */
19
+ function normalizeAddress(address) {
20
+ return address.startsWith("::ffff:") ? address.slice(7) : address;
21
+ }
22
+ /**
23
+ * Registers or refreshes a client for a stream. For HLS clients, this updates the lastSeen timestamp to prevent TTL expiration. For MPEG-TS clients, this creates
24
+ * the client entry on connect. Guards against registration after stream termination by verifying the stream still exists in the registry.
25
+ * @param streamId - The numeric stream ID.
26
+ * @param clientAddress - The raw client IP address.
27
+ * @param protocol - The protocol ("hls" or "mpegts").
28
+ */
29
+ export function registerClient(streamId, clientAddress, protocol) {
30
+ // Guard against registration after stream termination. If the stream no longer exists in the registry, skip silently to prevent orphaned client entries.
31
+ if (!getStream(streamId)) {
32
+ return;
33
+ }
34
+ const address = normalizeAddress(clientAddress);
35
+ const key = protocol + ":" + address;
36
+ let clients = clientMaps.get(streamId);
37
+ if (!clients) {
38
+ clients = new Map();
39
+ clientMaps.set(streamId, clients);
40
+ }
41
+ clients.set(key, {
42
+ clientAddress: address,
43
+ lastSeen: Date.now(),
44
+ protocol
45
+ });
46
+ }
47
+ /**
48
+ * Unregisters a client from a stream. Used for MPEG-TS clients on disconnect. HLS clients are not explicitly unregistered — they expire via TTL cleanup in
49
+ * getClientSummary(). If the client map has already been cleared (e.g., during stream termination), this is a no-op.
50
+ * @param streamId - The numeric stream ID.
51
+ * @param clientAddress - The raw client IP address.
52
+ * @param protocol - The protocol ("hls" or "mpegts").
53
+ */
54
+ export function unregisterClient(streamId, clientAddress, protocol) {
55
+ const clients = clientMaps.get(streamId);
56
+ if (!clients) {
57
+ return;
58
+ }
59
+ const address = normalizeAddress(clientAddress);
60
+ const key = protocol + ":" + address;
61
+ clients.delete(key);
62
+ // Clean up empty maps to prevent memory accumulation from streams with no remaining clients.
63
+ if (clients.size === 0) {
64
+ clientMaps.delete(streamId);
65
+ }
66
+ }
67
+ // Summary.
68
+ /**
69
+ * Returns an aggregated client summary for a stream. Performs lazy TTL cleanup of stale HLS clients as a side effect — entries that have not been refreshed within
70
+ * HLS_CLIENT_TTL are removed. MPEG-TS clients are managed explicitly via register/unregister and are never expired by TTL.
71
+ *
72
+ * Called by the monitor every ~2 seconds during status emission.
73
+ * @param streamId - The numeric stream ID.
74
+ * @returns The client summary with total count and per-type breakdown.
75
+ */
76
+ export function getClientSummary(streamId) {
77
+ const clients = clientMaps.get(streamId);
78
+ if (!clients) {
79
+ return { clients: [], total: 0 };
80
+ }
81
+ // Expire stale HLS clients.
82
+ const now = Date.now();
83
+ for (const [key, client] of clients) {
84
+ if ((client.protocol === "hls") && ((now - client.lastSeen) > HLS_CLIENT_TTL)) {
85
+ clients.delete(key);
86
+ }
87
+ }
88
+ // Clean up empty maps.
89
+ if (clients.size === 0) {
90
+ clientMaps.delete(streamId);
91
+ return { clients: [], total: 0 };
92
+ }
93
+ // Aggregate by protocol.
94
+ const typeCounts = new Map();
95
+ for (const client of clients.values()) {
96
+ typeCounts.set(client.protocol, (typeCounts.get(client.protocol) ?? 0) + 1);
97
+ }
98
+ // Build sorted result.
99
+ const result = [];
100
+ for (const [type, count] of typeCounts) {
101
+ result.push({ count, type });
102
+ }
103
+ result.sort((a, b) => a.type.localeCompare(b.type));
104
+ return { clients: result, total: clients.size };
105
+ }
106
+ // Cleanup.
107
+ /**
108
+ * Removes all client tracking data for a stream. Called during stream termination to prevent memory leaks.
109
+ * @param streamId - The numeric stream ID.
110
+ */
111
+ export function clearClients(streamId) {
112
+ clientMaps.delete(streamId);
113
+ }
114
+ //# sourceMappingURL=clients.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clients.js","sourceRoot":"","sources":["../../src/streaming/clients.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAwD1C,aAAa;AAEb,8IAA8I;AAC9I,MAAM,cAAc,GAAG,KAAK,CAAC;AAE7B,SAAS;AAET,8GAA8G;AAC9G,MAAM,UAAU,GAAG,IAAI,GAAG,EAAqC,CAAC;AAEhE,gBAAgB;AAEhB;;;;;GAKG;AACH,SAAS,gBAAgB,CAAC,OAAe;IAEvC,OAAO,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;AACpE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,aAAqB,EAAE,QAAoB;IAE1F,yJAAyJ;IACzJ,IAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;QAExB,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAG,gBAAgB,CAAC,aAAa,CAAC,CAAC;IAChD,MAAM,GAAG,GAAG,QAAQ,GAAG,GAAG,GAAG,OAAO,CAAC;IAErC,IAAI,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAEvC,IAAG,CAAC,OAAO,EAAE,CAAC;QAEZ,OAAO,GAAG,IAAI,GAAG,EAAE,CAAC;QACpB,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACpC,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE;QAEf,aAAa,EAAE,OAAO;QACtB,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE;QACpB,QAAQ;KACT,CAAC,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB,EAAE,aAAqB,EAAE,QAAoB;IAE5F,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAEzC,IAAG,CAAC,OAAO,EAAE,CAAC;QAEZ,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAG,gBAAgB,CAAC,aAAa,CAAC,CAAC;IAChD,MAAM,GAAG,GAAG,QAAQ,GAAG,GAAG,GAAG,OAAO,CAAC;IAErC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAEpB,6FAA6F;IAC7F,IAAG,OAAO,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QAEtB,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;AACH,CAAC;AAED,WAAW;AAEX;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAE/C,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAEzC,IAAG,CAAC,OAAO,EAAE,CAAC;QAEZ,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IACnC,CAAC;IAED,4BAA4B;IAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAEvB,KAAI,MAAM,CAAE,GAAG,EAAE,MAAM,CAAE,IAAI,OAAO,EAAE,CAAC;QAErC,IAAG,CAAC,MAAM,CAAC,QAAQ,KAAK,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,cAAc,CAAC,EAAE,CAAC;YAE7E,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED,uBAAuB;IACvB,IAAG,OAAO,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QAEtB,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAE5B,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IACnC,CAAC;IAED,yBAAyB;IACzB,MAAM,UAAU,GAAG,IAAI,GAAG,EAAsB,CAAC;IAEjD,KAAI,MAAM,MAAM,IAAI,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QAErC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9E,CAAC;IAED,uBAAuB;IACvB,MAAM,MAAM,GAAsB,EAAE,CAAC;IAErC,KAAI,MAAM,CAAE,IAAI,EAAE,KAAK,CAAE,IAAI,UAAU,EAAE,CAAC;QAExC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAEpD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;AAClD,CAAC;AAED,WAAW;AAEX;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB;IAE3C,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC9B,CAAC"}