@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,79 @@
1
+ import { DEFAULT_SITE_PROFILE, DOMAIN_CONFIG, SITE_PROFILES, getDomainConfig } from "./sites.js";
2
+ import type { ProfileCategory, ProfileResolutionResult, ResolvedSiteProfile } from "../types/index.js";
3
+ import type { DomainConfig } from "./sites.js";
4
+ export { DEFAULT_SITE_PROFILE, DOMAIN_CONFIG, SITE_PROFILES, getDomainConfig };
5
+ export type { DomainConfig };
6
+ /**
7
+ * Resolves a named profile from SITE_PROFILES, handling inheritance and merging with the default profile to ensure all required properties are present. Inheritance
8
+ * is resolved recursively, with child profile properties overriding parent properties.
9
+ *
10
+ * The resolution process:
11
+ *
12
+ * 1. Start with a copy of DEFAULT_SITE_PROFILE
13
+ * 2. If the profile extends another, recursively resolve the parent and merge its properties
14
+ * 3. Merge the current profile's properties, overriding any inherited values
15
+ * 4. Return the fully-resolved profile with all flags set
16
+ *
17
+ * Metadata properties (description, extends) are stripped during resolution - they exist only for documentation and inheritance specification.
18
+ *
19
+ * @param profileName - The name of the profile to resolve.
20
+ * @returns The merged site profile containing all behavior flags.
21
+ */
22
+ export declare function resolveProfile(profileName: string | undefined): ResolvedSiteProfile;
23
+ /**
24
+ * Resolves the site profile for a given URL by looking it up in DOMAIN_CONFIG via getDomainConfig(), which tries the full hostname first for subdomain-specific
25
+ * overrides before falling back to the concise domain. Falls back to the default profile if no matching domain is found or the matching domain has no profile
26
+ * configured.
27
+ * @param url - The URL to resolve a profile for.
28
+ * @returns The site profile containing behavior flags.
29
+ */
30
+ export declare function getProfileForUrl(url: string | undefined): ProfileResolutionResult;
31
+ /**
32
+ * Resolves the site profile for a channel. Channels can explicitly declare their profile by name, which takes precedence over URL-based detection. This is useful
33
+ * when:
34
+ * - A channel's URL domain doesn't match the expected behavior pattern
35
+ * - The same domain serves multiple channel types needing different handling
36
+ * - A channel needs a custom combination of flags not covered by existing profiles
37
+ *
38
+ * The special value "auto" triggers URL-based domain detection, equivalent to omitting the profile property. This allows channels to explicitly opt into domain
39
+ * detection rather than relying on the implicit behavior of an absent property.
40
+ *
41
+ * Channel-specific properties like channelSelector are merged into the resolved profile, allowing channels to extend profiles with additional configuration.
42
+ *
43
+ * @param channel - The channel object with url and optional profile properties.
44
+ * @returns The site profile containing behavior flags.
45
+ */
46
+ export declare function getProfileForChannel(channel: {
47
+ channelSelector?: string;
48
+ profile?: string;
49
+ url?: string;
50
+ } | undefined): ProfileResolutionResult;
51
+ /**
52
+ * Validates all profile configurations including inheritance chains, domain mappings, and channel references. Throws an error if any validation fails. This
53
+ * function runs at startup before the server begins accepting connections.
54
+ *
55
+ * Validation checks:
56
+ *
57
+ * 1. Circular inheritance detection - walks the extends chain for each profile to detect cycles
58
+ * 2. Invalid extends references - ensures all extends targets exist
59
+ * 3. Domain mapping validation - ensures all domain profile references exist
60
+ * 4. Channel profile validation - ensures all channel profile references exist
61
+ *
62
+ * @throws If any profile configuration is invalid.
63
+ */
64
+ export declare function validateProfiles(): void;
65
+ /**
66
+ * Profile information for UI display, including name, description, category, and summary.
67
+ */
68
+ export interface ProfileInfo {
69
+ category: ProfileCategory;
70
+ description: string;
71
+ name: string;
72
+ summary: string;
73
+ }
74
+ /**
75
+ * Returns all profiles with their descriptions, categories, and summaries, sorted alphabetically by name. Used by the channel configuration UI to populate the
76
+ * profile dropdown with tooltips and the profile reference section.
77
+ * @returns Array of profile info objects.
78
+ */
79
+ export declare function getProfiles(): ProfileInfo[];
@@ -0,0 +1,245 @@
1
+ /* Copyright(C) 2024-2026, HJD (https://github.com/hjdhjd). All rights reserved.
2
+ *
3
+ * profiles.ts: Site profile resolution and validation for PrismCast.
4
+ */
5
+ import { DEFAULT_SITE_PROFILE, DOMAIN_CONFIG, SITE_PROFILES, getDomainConfig } from "./sites.js";
6
+ import { CHANNELS } from "../channels/index.js";
7
+ import { extractDomain } from "../utils/index.js";
8
+ // Re-export site data so existing consumers can import from either module.
9
+ export { DEFAULT_SITE_PROFILE, DOMAIN_CONFIG, SITE_PROFILES, getDomainConfig };
10
+ /* Profile resolution is the process of determining which behavior flags to use for a given stream. The resolution process handles inheritance, merging parent and
11
+ * child profile properties, and falling back to defaults for unspecified flags.
12
+ *
13
+ * Resolution order (highest to lowest priority):
14
+ *
15
+ * 1. Channel's explicit profile property (if specified)
16
+ * 2. URL-based detection via DOMAIN_CONFIG
17
+ * 3. DEFAULT_SITE_PROFILE
18
+ *
19
+ * Within a profile, inheritance works as follows:
20
+ *
21
+ * 1. Start with DEFAULT_SITE_PROFILE for base values
22
+ * 2. Apply parent profile properties (if extends is set)
23
+ * 3. Apply current profile properties (overriding parent)
24
+ *
25
+ * This allows profiles like "embeddedDynamicMultiVideo" to inherit iframe handling from "embeddedPlayer" which inherits API fullscreen from "fullscreenApi",
26
+ * building up the complete set of behavior flags through the inheritance chain.
27
+ */
28
+ /**
29
+ * Resolves a named profile from SITE_PROFILES, handling inheritance and merging with the default profile to ensure all required properties are present. Inheritance
30
+ * is resolved recursively, with child profile properties overriding parent properties.
31
+ *
32
+ * The resolution process:
33
+ *
34
+ * 1. Start with a copy of DEFAULT_SITE_PROFILE
35
+ * 2. If the profile extends another, recursively resolve the parent and merge its properties
36
+ * 3. Merge the current profile's properties, overriding any inherited values
37
+ * 4. Return the fully-resolved profile with all flags set
38
+ *
39
+ * Metadata properties (description, extends) are stripped during resolution - they exist only for documentation and inheritance specification.
40
+ *
41
+ * @param profileName - The name of the profile to resolve.
42
+ * @returns The merged site profile containing all behavior flags.
43
+ */
44
+ export function resolveProfile(profileName) {
45
+ // No profile specified - return the default.
46
+ if (!profileName) {
47
+ return { ...DEFAULT_SITE_PROFILE };
48
+ }
49
+ // The conditional below guards against runtime scenarios where profileName is not in SITE_PROFILES, even though TypeScript's Record type doesn't capture this.
50
+ // This can happen if a channel references a typo'd profile name or if profiles are modified at runtime.
51
+ const profile = SITE_PROFILES[profileName];
52
+ if (!profile) {
53
+ return { ...DEFAULT_SITE_PROFILE };
54
+ }
55
+ // Start with default values. This ensures all flags have a value even if not specified in the profile.
56
+ let resolved = { ...DEFAULT_SITE_PROFILE };
57
+ // Apply parent profile first if inheritance exists. This allows child profiles to override parent properties while inheriting the rest.
58
+ if (profile.extends) {
59
+ const parent = resolveProfile(profile.extends);
60
+ resolved = { ...resolved, ...parent };
61
+ }
62
+ // Apply current profile properties, excluding metadata fields that should not be in the resolved profile. The category, description, extends, and summary
63
+ // properties are for UI categorization, documentation, inheritance specification, and UI display only.
64
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
65
+ const { category: _category, description: _description, extends: _extends, summary: _summary, ...profileFlags } = profile;
66
+ resolved = { ...resolved, ...profileFlags };
67
+ return resolved;
68
+ }
69
+ /**
70
+ * Resolves the site profile for a given URL by looking it up in DOMAIN_CONFIG via getDomainConfig(), which tries the full hostname first for subdomain-specific
71
+ * overrides before falling back to the concise domain. Falls back to the default profile if no matching domain is found or the matching domain has no profile
72
+ * configured.
73
+ * @param url - The URL to resolve a profile for.
74
+ * @returns The site profile containing behavior flags.
75
+ */
76
+ export function getProfileForUrl(url) {
77
+ // No URL provided - return the default.
78
+ if (!url) {
79
+ return { profile: { ...DEFAULT_SITE_PROFILE }, profileName: "default" };
80
+ }
81
+ // Look up the domain configuration, trying the full hostname first for subdomain-specific overrides (e.g., "tv.youtube.com" before "youtube.com").
82
+ const config = getDomainConfig(url);
83
+ // Resolve the profile from the domain configuration, falling back to the default profile for unrecognized domains or domains without a profile entry.
84
+ const profile = config?.profile ? resolveProfile(config.profile) : { ...DEFAULT_SITE_PROFILE };
85
+ const profileName = config?.profile ?? "default";
86
+ // Merge domain-level properties that represent site policies rather than player behaviors. maxContinuousPlayback is a site-imposed session limit, not a player
87
+ // characteristic, so it lives in DOMAIN_CONFIG rather than in site profiles. Note: getProfileForChannel() performs this same merge for the explicit-profile path
88
+ // where this function is bypassed. If adding new domain-level policies here, update getProfileForChannel() as well.
89
+ if (config?.maxContinuousPlayback !== undefined) {
90
+ profile.maxContinuousPlayback = config.maxContinuousPlayback;
91
+ }
92
+ return { profile, profileName };
93
+ }
94
+ /**
95
+ * Resolves the site profile for a channel. Channels can explicitly declare their profile by name, which takes precedence over URL-based detection. This is useful
96
+ * when:
97
+ * - A channel's URL domain doesn't match the expected behavior pattern
98
+ * - The same domain serves multiple channel types needing different handling
99
+ * - A channel needs a custom combination of flags not covered by existing profiles
100
+ *
101
+ * The special value "auto" triggers URL-based domain detection, equivalent to omitting the profile property. This allows channels to explicitly opt into domain
102
+ * detection rather than relying on the implicit behavior of an absent property.
103
+ *
104
+ * Channel-specific properties like channelSelector are merged into the resolved profile, allowing channels to extend profiles with additional configuration.
105
+ *
106
+ * @param channel - The channel object with url and optional profile properties.
107
+ * @returns The site profile containing behavior flags.
108
+ */
109
+ export function getProfileForChannel(channel) {
110
+ // No channel provided - return the default.
111
+ if (!channel) {
112
+ return { profile: { ...DEFAULT_SITE_PROFILE }, profileName: "default" };
113
+ }
114
+ let profile;
115
+ let profileName;
116
+ // If the channel specifies an explicit profile name, use it directly. This takes precedence over URL-based detection. The value "auto" is treated as unset,
117
+ // falling through to URL-based detection below.
118
+ if (channel.profile && (channel.profile !== "auto")) {
119
+ profile = resolveProfile(channel.profile);
120
+ profileName = channel.profile;
121
+ }
122
+ else if (channel.url) {
123
+ // Fall back to URL-based profile detection.
124
+ const result = getProfileForUrl(channel.url);
125
+ profile = result.profile;
126
+ profileName = result.profileName;
127
+ }
128
+ else {
129
+ profile = { ...DEFAULT_SITE_PROFILE };
130
+ profileName = "default";
131
+ }
132
+ // If the resolved profile requires channel selection (strategy other than "none") but the channel has no channelSelector, the hostname-specific domain entry
133
+ // (e.g., watch.sling.com → slingLive) is not appropriate for this direct-URL channel. Fall back to the concise domain entry (e.g., sling.com →
134
+ // embeddedVolumeLock) which provides generic player handling for direct URLs on the same service.
135
+ if ((profile.channelSelection.strategy !== "none") && !channel.channelSelector && channel.url) {
136
+ const conciseConfig = DOMAIN_CONFIG[extractDomain(channel.url)];
137
+ if (conciseConfig?.profile && (conciseConfig.profile !== profileName)) {
138
+ profile = resolveProfile(conciseConfig.profile);
139
+ profileName = conciseConfig.profile;
140
+ }
141
+ }
142
+ // Merge domain-level site policies that apply regardless of how the profile was resolved. These represent site-imposed constraints (like session duration limits)
143
+ // rather than player behaviors, so they must always be applied based on the channel's URL even when the player profile is explicitly overridden. For the URL-based
144
+ // path above, getProfileForUrl() already merges these — the re-application here is idempotent. For the explicit-profile path, this fills the gap.
145
+ if (channel.url) {
146
+ const domainConfig = getDomainConfig(channel.url);
147
+ if (domainConfig?.maxContinuousPlayback !== undefined) {
148
+ profile = { ...profile, maxContinuousPlayback: domainConfig.maxContinuousPlayback };
149
+ }
150
+ }
151
+ // Merge channel-specific properties into the profile. Currently only channelSelector is supported, which specifies a CSS selector for the channel button in
152
+ // multi-channel player pages. The channelSelector on the channel overrides any channelSelector from the profile.
153
+ if (channel.channelSelector) {
154
+ profile = { ...profile, channelSelector: channel.channelSelector };
155
+ }
156
+ return { profile, profileName };
157
+ }
158
+ /* Before starting the server, we validate all profile configurations to catch errors early. Invalid configurations would cause runtime failures that are difficult
159
+ * to diagnose:
160
+ *
161
+ * - Invalid profile references (typos in extends or profile properties)
162
+ * - Circular inheritance chains (A extends B extends A)
163
+ * - Domain mappings pointing to non-existent profiles
164
+ * - Channel definitions referencing non-existent profiles
165
+ *
166
+ * By validating upfront at startup, we provide clear error messages and prevent the server from starting in a misconfigured state. This is especially important
167
+ * because profile errors might not surface until a specific channel is streamed, which could be hours after startup.
168
+ */
169
+ /**
170
+ * Validates all profile configurations including inheritance chains, domain mappings, and channel references. Throws an error if any validation fails. This
171
+ * function runs at startup before the server begins accepting connections.
172
+ *
173
+ * Validation checks:
174
+ *
175
+ * 1. Circular inheritance detection - walks the extends chain for each profile to detect cycles
176
+ * 2. Invalid extends references - ensures all extends targets exist
177
+ * 3. Domain mapping validation - ensures all domain profile references exist
178
+ * 4. Channel profile validation - ensures all channel profile references exist
179
+ *
180
+ * @throws If any profile configuration is invalid.
181
+ */
182
+ export function validateProfiles() {
183
+ const errors = [];
184
+ // Check for circular inheritance and invalid extends references in profiles. We walk the extends chain for each profile, tracking visited profiles to detect
185
+ // cycles.
186
+ for (const profileName of Object.keys(SITE_PROFILES)) {
187
+ const visited = new Set();
188
+ let current = profileName;
189
+ while (current) {
190
+ // If we've seen this profile before, we have a circular reference.
191
+ if (visited.has(current)) {
192
+ errors.push(["Circular inheritance detected in profile: ", profileName].join(""));
193
+ break;
194
+ }
195
+ visited.add(current);
196
+ // TypeScript's Record type doesn't capture that the key may not exist at runtime.
197
+ const profile = SITE_PROFILES[current];
198
+ current = profile?.extends;
199
+ // Check if the extends target exists. This catches typos and references to deleted profiles.
200
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
201
+ if ((current !== undefined) && !SITE_PROFILES[current]) {
202
+ errors.push(["Profile ", profileName, " extends non-existent profile: ", current].join(""));
203
+ break;
204
+ }
205
+ }
206
+ }
207
+ // Validate all domain-to-profile mappings reference existing profiles. This catches typos in DOMAIN_CONFIG profile entries.
208
+ for (const [domain, config] of Object.entries(DOMAIN_CONFIG)) {
209
+ if (config.profile) {
210
+ const domainProfile = SITE_PROFILES[config.profile];
211
+ if (!domainProfile) {
212
+ errors.push(["Domain ", domain, " references non-existent profile: ", config.profile].join(""));
213
+ }
214
+ }
215
+ }
216
+ // Validate all channel profile references point to existing profiles. This catches typos in channel definitions.
217
+ for (const [channelName, channel] of Object.entries(CHANNELS)) {
218
+ const channelProfile = channel.profile;
219
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
220
+ if ((channelProfile !== undefined) && (channelProfile !== "auto") && !SITE_PROFILES[channelProfile]) {
221
+ errors.push(["Channel ", channelName, " references non-existent profile: ", channelProfile].join(""));
222
+ }
223
+ }
224
+ // If any errors were found, throw with all error messages for comprehensive feedback.
225
+ if (errors.length > 0) {
226
+ throw new Error(["Profile validation failed:\n ", errors.join("\n ")].join(""));
227
+ }
228
+ }
229
+ /**
230
+ * Returns all profiles with their descriptions, categories, and summaries, sorted alphabetically by name. Used by the channel configuration UI to populate the
231
+ * profile dropdown with tooltips and the profile reference section.
232
+ * @returns Array of profile info objects.
233
+ */
234
+ export function getProfiles() {
235
+ return Object.keys(SITE_PROFILES).sort().map((name) => {
236
+ const profile = SITE_PROFILES[name];
237
+ return {
238
+ category: profile.category ?? "special",
239
+ description: profile.description ?? "",
240
+ name,
241
+ summary: profile.summary ?? profile.description ?? ""
242
+ };
243
+ });
244
+ }
245
+ //# sourceMappingURL=profiles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../src/config/profiles.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,120 @@
1
+ import type { Channel, ChannelMap, ProviderGroup } from "../types/index.js";
2
+ /**
3
+ * Gets the provider tag for a channel key. For variant keys (e.g., "espn-hulu"), extracts the suffix after the canonical prefix (e.g., "hulu"). For canonical keys,
4
+ * looks up the URL domain via getDomainConfig() and reads the providerTag field, falling back to "direct" if not found.
5
+ * @param key - The channel key.
6
+ * @returns The provider tag string.
7
+ */
8
+ export declare function getProviderTagForChannel(key: string): string;
9
+ /**
10
+ * Returns all provider tags for a channel (canonical tag + all variant suffix tags). Used to determine which providers offer this channel.
11
+ * @param canonicalKey - The canonical channel key.
12
+ * @returns Array of provider tag strings.
13
+ */
14
+ export declare function getChannelProviderTags(canonicalKey: string): string[];
15
+ /**
16
+ * Scans all provider groups and collects unique provider tags with display names. Display names are derived from the provider field in DOMAIN_CONFIG entries that
17
+ * have a providerTag, with any trailing parenthetical stripped (e.g., "Hulu (Live Guide)" becomes "Hulu").
18
+ * @returns Array of { displayName, tag } objects sorted alphabetically by display name, with "direct" always first.
19
+ */
20
+ export declare function getAllProviderTags(): {
21
+ displayName: string;
22
+ tag: string;
23
+ }[];
24
+ /**
25
+ * Gets the current enabled provider tags.
26
+ * @returns Copy of the enabled providers array. Empty means no filter (all shown).
27
+ */
28
+ export declare function getEnabledProviders(): string[];
29
+ /**
30
+ * Sets the enabled provider tags. Empty array means "no filter" (all providers shown).
31
+ * @param tags - The provider tags to enable.
32
+ */
33
+ export declare function setEnabledProviders(tags: string[]): void;
34
+ /**
35
+ * Checks if a provider tag is currently enabled. Returns true if the tag is enabled, if no filter is active (empty set), or if the tag is "direct".
36
+ * @param tag - The provider tag to check.
37
+ * @returns True if the provider is available.
38
+ */
39
+ export declare function isProviderTagEnabled(tag: string): boolean;
40
+ /**
41
+ * Centralized availability check for the provider filter. Returns true if the channel has at least one variant whose provider tag is enabled.
42
+ * @param canonicalKey - The canonical channel key.
43
+ * @returns True if the channel passes the provider filter.
44
+ */
45
+ export declare function isChannelAvailableByProvider(canonicalKey: string): boolean;
46
+ /**
47
+ * Builds provider groups by scanning all channels and grouping them by key patterns. A key like "espn-disneyplus" is a variant of "espn" because it starts with
48
+ * "espn-". Should be called at startup after channels are loaded.
49
+ * @param channels - The merged channel map (predefined + user channels).
50
+ */
51
+ export declare function buildProviderGroups(channels: ChannelMap): void;
52
+ /**
53
+ * Resolves a URL to a friendly provider display name. Checks DOMAIN_CONFIG via getDomainConfig() for a provider name, trying the full hostname first for
54
+ * subdomain-specific overrides before falling back to the concise domain. Returns the raw domain string if no provider name is configured.
55
+ * @param url - The URL to resolve a provider display name for.
56
+ * @returns The provider display name, or the concise domain if no provider name is configured.
57
+ */
58
+ export declare function getProviderDisplayName(url: string): string;
59
+ /**
60
+ * Gets the provider group for a channel key. Works with both canonical and variant keys.
61
+ * @param key - Any channel key in the group.
62
+ * @returns The provider group if the channel is part of a multi-provider group, undefined otherwise.
63
+ */
64
+ export declare function getProviderGroup(key: string): ProviderGroup | undefined;
65
+ /**
66
+ * Checks if a channel key is a non-canonical provider variant. Used to filter variants from channel listings.
67
+ * @param key - The channel key to check.
68
+ * @returns True if the key is a variant (not canonical) in a provider group.
69
+ */
70
+ export declare function isProviderVariant(key: string): boolean;
71
+ /**
72
+ * Checks if a channel has multiple provider options. Used to determine whether to show a provider dropdown in the UI.
73
+ * @param key - The channel key to check.
74
+ * @returns True if the channel has more than one provider variant.
75
+ */
76
+ export declare function hasMultipleProviders(key: string): boolean;
77
+ /**
78
+ * 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.
79
+ * Handles the PREDEFINED_SUFFIX used when a user has overridden a predefined channel.
80
+ * @param key - Any channel key.
81
+ * @returns The canonical key for the channel's provider group, or the input key if not part of a group.
82
+ */
83
+ export declare function getCanonicalKey(key: string): string;
84
+ /**
85
+ * Sets the user's provider selections. Called when loading from channels.json.
86
+ * @param selections - Provider selections keyed by canonical channel key.
87
+ */
88
+ export declare function setProviderSelections(selections: Record<string, string>): void;
89
+ /**
90
+ * Gets all provider selections.
91
+ * @returns Copy of the provider selections object.
92
+ */
93
+ export declare function getProviderSelections(): Record<string, string>;
94
+ /**
95
+ * Gets the provider selection for a specific channel.
96
+ * @param canonicalKey - The canonical channel key.
97
+ * @returns The selected provider key, or undefined if using the default.
98
+ */
99
+ export declare function getProviderSelection(canonicalKey: string): string | undefined;
100
+ /**
101
+ * Sets the provider selection for a channel.
102
+ * @param canonicalKey - The canonical channel key.
103
+ * @param providerKey - The selected provider key.
104
+ */
105
+ export declare function setProviderSelection(canonicalKey: string, providerKey: string): void;
106
+ /**
107
+ * Resolves a canonical channel key to the actual channel key based on user selection. If the user has selected a specific provider for this channel, returns that
108
+ * provider's key. Otherwise returns the canonical key (default provider). When the provider filter is active, falls back to the first enabled variant if the stored
109
+ * selection's provider is filtered out.
110
+ * @param canonicalKey - The canonical channel key.
111
+ * @returns The resolved provider key to use for streaming.
112
+ */
113
+ export declare function resolveProviderKey(canonicalKey: string): string;
114
+ /**
115
+ * Gets a channel with inheritance applied. For provider variants, this merges the variant's properties with inherited properties from the canonical entry.
116
+ * Inherited properties: `name`, `stationId` (variant takes precedence). `channelSelector` is not inherited — it is provider-specific.
117
+ * @param key - The channel key (canonical or variant).
118
+ * @returns The complete channel with inheritance applied, or undefined if the channel doesn't exist.
119
+ */
120
+ export declare function getResolvedChannel(key: string): Channel | undefined;