prismcast 1.4.2 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/dist/app.js +18 -1
  2. package/dist/app.js.map +1 -1
  3. package/dist/browser/cdp.d.ts +2 -1
  4. package/dist/browser/cdp.js +44 -23
  5. package/dist/browser/cdp.js.map +1 -1
  6. package/dist/browser/channelSelection.d.ts +45 -1
  7. package/dist/browser/channelSelection.js +164 -27
  8. package/dist/browser/channelSelection.js.map +1 -1
  9. package/dist/browser/display.d.ts +0 -4
  10. package/dist/browser/display.js +0 -7
  11. package/dist/browser/display.js.map +1 -1
  12. package/dist/browser/index.d.ts +5 -0
  13. package/dist/browser/index.js +14 -0
  14. package/dist/browser/index.js.map +1 -1
  15. package/dist/browser/precaching.d.ts +5 -0
  16. package/dist/browser/precaching.js +112 -0
  17. package/dist/browser/precaching.js.map +1 -0
  18. package/dist/browser/tuning/directv.d.ts +2 -0
  19. package/dist/browser/tuning/directv.js +723 -0
  20. package/dist/browser/tuning/directv.js.map +1 -0
  21. package/dist/browser/tuning/fox.d.ts +2 -2
  22. package/dist/browser/tuning/fox.js +141 -22
  23. package/dist/browser/tuning/fox.js.map +1 -1
  24. package/dist/browser/tuning/hbo.d.ts +2 -2
  25. package/dist/browser/tuning/hbo.js +110 -41
  26. package/dist/browser/tuning/hbo.js.map +1 -1
  27. package/dist/browser/tuning/hulu.d.ts +2 -2
  28. package/dist/browser/tuning/hulu.js +442 -168
  29. package/dist/browser/tuning/hulu.js.map +1 -1
  30. package/dist/browser/tuning/sling.d.ts +2 -2
  31. package/dist/browser/tuning/sling.js +200 -56
  32. package/dist/browser/tuning/sling.js.map +1 -1
  33. package/dist/browser/tuning/youtubeTv.d.ts +2 -2
  34. package/dist/browser/tuning/youtubeTv.js +243 -57
  35. package/dist/browser/tuning/youtubeTv.js.map +1 -1
  36. package/dist/browser/video.d.ts +1 -1
  37. package/dist/browser/video.js +4 -2
  38. package/dist/browser/video.js.map +1 -1
  39. package/dist/channels/index.js +280 -34
  40. package/dist/channels/index.js.map +1 -1
  41. package/dist/config/health.d.ts +78 -0
  42. package/dist/config/health.js +202 -0
  43. package/dist/config/health.js.map +1 -0
  44. package/dist/config/paths.d.ts +10 -0
  45. package/dist/config/paths.js +14 -0
  46. package/dist/config/paths.js.map +1 -1
  47. package/dist/config/profiles.d.ts +6 -4
  48. package/dist/config/profiles.js +65 -14
  49. package/dist/config/profiles.js.map +1 -1
  50. package/dist/config/providerPacks.d.ts +46 -0
  51. package/dist/config/providerPacks.js +200 -0
  52. package/dist/config/providerPacks.js.map +1 -0
  53. package/dist/config/providers.d.ts +36 -3
  54. package/dist/config/providers.js +168 -8
  55. package/dist/config/providers.js.map +1 -1
  56. package/dist/config/sites.d.ts +1 -12
  57. package/dist/config/sites.js +38 -5
  58. package/dist/config/sites.js.map +1 -1
  59. package/dist/config/userChannels.d.ts +12 -0
  60. package/dist/config/userChannels.js +81 -14
  61. package/dist/config/userChannels.js.map +1 -1
  62. package/dist/config/userConfig.d.ts +5 -1
  63. package/dist/config/userConfig.js +58 -6
  64. package/dist/config/userConfig.js.map +1 -1
  65. package/dist/config/userProfiles.d.ts +81 -0
  66. package/dist/config/userProfiles.js +424 -0
  67. package/dist/config/userProfiles.js.map +1 -0
  68. package/dist/routes/config/channels/index.d.ts +3 -0
  69. package/dist/routes/config/channels/index.js +3 -0
  70. package/dist/routes/config/channels/index.js.map +1 -0
  71. package/dist/routes/config/channels/routes.d.ts +6 -0
  72. package/dist/routes/config/channels/routes.js +797 -0
  73. package/dist/routes/config/channels/routes.js.map +1 -0
  74. package/dist/routes/config/channels/table.d.ts +46 -0
  75. package/dist/routes/config/channels/table.js +811 -0
  76. package/dist/routes/config/channels/table.js.map +1 -0
  77. package/dist/routes/config/index.d.ts +37 -0
  78. package/dist/routes/config/index.js +81 -0
  79. package/dist/routes/config/index.js.map +1 -0
  80. package/dist/routes/config/providers.d.ts +18 -0
  81. package/dist/routes/config/providers.js +573 -0
  82. package/dist/routes/config/providers.js.map +1 -0
  83. package/dist/routes/config/settings.d.ts +39 -0
  84. package/dist/routes/config/settings.js +820 -0
  85. package/dist/routes/config/settings.js.map +1 -0
  86. package/dist/routes/index.d.ts +3 -2
  87. package/dist/routes/index.js +7 -4
  88. package/dist/routes/index.js.map +1 -1
  89. package/dist/routes/playlist.d.ts +5 -1
  90. package/dist/routes/playlist.js +32 -7
  91. package/dist/routes/playlist.js.map +1 -1
  92. package/dist/routes/providers.d.ts +6 -0
  93. package/dist/routes/providers.js +166 -0
  94. package/dist/routes/providers.js.map +1 -0
  95. package/dist/routes/root/content.d.ts +32 -0
  96. package/dist/routes/root/content.js +857 -0
  97. package/dist/routes/root/content.js.map +1 -0
  98. package/dist/routes/root/index.js +197 -0
  99. package/dist/routes/root/index.js.map +1 -0
  100. package/dist/routes/root/scripts/channels.d.ts +1 -0
  101. package/dist/routes/root/scripts/channels.js +745 -0
  102. package/dist/routes/root/scripts/channels.js.map +1 -0
  103. package/dist/routes/root/scripts/config.d.ts +1 -0
  104. package/dist/routes/root/scripts/config.js +1662 -0
  105. package/dist/routes/root/scripts/config.js.map +1 -0
  106. package/dist/routes/root/scripts/index.d.ts +3 -0
  107. package/dist/routes/root/scripts/index.js +8 -0
  108. package/dist/routes/root/scripts/index.js.map +1 -0
  109. package/dist/routes/root/scripts/status.d.ts +1 -0
  110. package/dist/routes/root/scripts/status.js +436 -0
  111. package/dist/routes/root/scripts/status.js.map +1 -0
  112. package/dist/routes/root/styles.d.ts +5 -0
  113. package/dist/routes/root/styles.js +447 -0
  114. package/dist/routes/root/styles.js.map +1 -0
  115. package/dist/routes/ui.js +8 -3
  116. package/dist/routes/ui.js.map +1 -1
  117. package/dist/service/commands.d.ts +5 -3
  118. package/dist/service/commands.js +117 -33
  119. package/dist/service/commands.js.map +1 -1
  120. package/dist/service/generators.d.ts +27 -0
  121. package/dist/service/generators.js +97 -6
  122. package/dist/service/generators.js.map +1 -1
  123. package/dist/streaming/hls.js +18 -1
  124. package/dist/streaming/hls.js.map +1 -1
  125. package/dist/streaming/lifecycle.js +1 -1
  126. package/dist/streaming/lifecycle.js.map +1 -1
  127. package/dist/streaming/monitor.d.ts +1 -38
  128. package/dist/streaming/monitor.js +2 -289
  129. package/dist/streaming/monitor.js.map +1 -1
  130. package/dist/streaming/recovery.d.ts +143 -0
  131. package/dist/streaming/recovery.js +286 -0
  132. package/dist/streaming/recovery.js.map +1 -0
  133. package/dist/streaming/registry.d.ts +1 -1
  134. package/dist/streaming/setup.d.ts +1 -1
  135. package/dist/streaming/setup.js +3 -1
  136. package/dist/streaming/setup.js.map +1 -1
  137. package/dist/streaming/statusEmitter.d.ts +4 -2
  138. package/dist/streaming/statusEmitter.js +5 -0
  139. package/dist/streaming/statusEmitter.js.map +1 -1
  140. package/dist/types/channels.d.ts +68 -0
  141. package/dist/types/channels.js +2 -0
  142. package/dist/types/channels.js.map +1 -0
  143. package/dist/types/config.d.ts +127 -0
  144. package/dist/types/config.js +2 -0
  145. package/dist/types/config.js.map +1 -0
  146. package/dist/types/index.d.ts +7 -429
  147. package/dist/types/index.js +1 -5
  148. package/dist/types/index.js.map +1 -1
  149. package/dist/types/profiles.d.ts +141 -0
  150. package/dist/types/profiles.js +2 -0
  151. package/dist/types/profiles.js.map +1 -0
  152. package/dist/types/selection.d.ts +101 -0
  153. package/dist/types/selection.js +6 -0
  154. package/dist/types/selection.js.map +1 -0
  155. package/dist/types/shared.d.ts +7 -0
  156. package/dist/types/shared.js +6 -0
  157. package/dist/types/shared.js.map +1 -0
  158. package/dist/types/streaming.d.ts +96 -0
  159. package/dist/types/streaming.js +2 -0
  160. package/dist/types/streaming.js.map +1 -0
  161. package/dist/utils/debugFilter.js +3 -1
  162. package/dist/utils/debugFilter.js.map +1 -1
  163. package/dist/utils/format.d.ts +7 -0
  164. package/dist/utils/format.js +22 -0
  165. package/dist/utils/format.js.map +1 -1
  166. package/dist/utils/index.d.ts +1 -0
  167. package/dist/utils/index.js +1 -0
  168. package/dist/utils/index.js.map +1 -1
  169. package/dist/utils/sanitize.d.ts +14 -0
  170. package/dist/utils/sanitize.js +37 -0
  171. package/dist/utils/sanitize.js.map +1 -0
  172. package/package.json +5 -3
  173. package/dist/routes/config.d.ts +0 -76
  174. package/dist/routes/config.js +0 -2187
  175. package/dist/routes/config.js.map +0 -1
  176. package/dist/routes/root.js +0 -3158
  177. package/dist/routes/root.js.map +0 -1
  178. /package/dist/routes/{root.d.ts → root/index.d.ts} +0 -0
@@ -0,0 +1,141 @@
1
+ import type { ChannelMap } from "./channels.js";
2
+ import type { Nullable } from "./shared.js";
3
+ /**
4
+ * Available channel selection strategies. Each strategy implements a different approach to finding and selecting channels in a multi-channel player UI.
5
+ *
6
+ * - "directvGrid": Tune via webpack injection — captures __webpack_require__, extracts the Redux store from the React fiber tree, matches by channel name, and
7
+ * dispatches playConsumable. Falls back to logo aria-label click when the interceptor fails. Used by DirecTV Stream.
8
+ * - "foxGrid": Find channel by station code in a non-virtualized guide grid, click the channel logo button via DOM .click(). Used by Fox.com.
9
+ * - "guideGrid": Find channel by exact-matching image alt text, click nearest clickable ancestor. Optionally clicks a tab to reveal the list first. Used by Hulu
10
+ * Live TV.
11
+ * - "hboGrid": Discover the HBO tab page URL from the homepage menu bar, scrape the live channel tile rail for a matching channel name, and navigate to the watch
12
+ * URL. Caches the tab URL across tunes with stale-cache fallback. Used by HBO Max.
13
+ * - "none": No channel selection needed (single-channel sites). This is the default.
14
+ * - "slingGrid": Find channel by data-testid in a virtualized A-Z guide grid, scroll via binary search on .guide-cell scrollTop, click the on-now program
15
+ * cell. Used by Sling TV.
16
+ * - "thumbnailRow": Find channel element using the profile's matchSelector (defaults to image URL matching), click adjacent element on the same row. Used by
17
+ * USA Network.
18
+ * - "tileClick": Find channel element using the profile's matchSelector (defaults to image URL matching), click tile, then optionally click play button if
19
+ * playSelector is configured. Used by Disney+.
20
+ * - "youtubeGrid": Find channel by aria-label in a non-virtualized EPG grid, extract the watch URL, and navigate directly. Used by YouTube TV.
21
+ */
22
+ export type ChannelSelectionStrategy = "directvGrid" | "foxGrid" | "guideGrid" | "hboGrid" | "none" | "slingGrid" | "thumbnailRow" | "tileClick" | "youtubeGrid";
23
+ /**
24
+ * Configuration for channel selection behavior within a site profile.
25
+ */
26
+ export interface ChannelSelectionConfig {
27
+ listSelector?: string;
28
+ matchSelector?: string;
29
+ playSelector?: string;
30
+ scrollSelector?: string;
31
+ scrollTarget?: string;
32
+ scrollToBottom?: boolean;
33
+ strategy: ChannelSelectionStrategy;
34
+ }
35
+ /**
36
+ * UI category for profile grouping in dropdowns and reference documentation. Profiles are grouped by their fullscreen mechanism and special characteristics.
37
+ * - "api": Profiles using the JavaScript fullscreen API (including embedded iframe and click-to-play variants).
38
+ * - "custom": User-defined profiles created via the profile builder wizard or imported from provider packs.
39
+ * - "keyboard": Profiles using keyboard shortcuts (typically the 'f' key) for fullscreen.
40
+ * - "multiChannel": Multi-channel profiles requiring a channel selector for tile or thumbnail-based channel selection.
41
+ * - "special": Special-purpose profiles like static page capture.
42
+ */
43
+ export type ProfileCategory = "api" | "custom" | "keyboard" | "multiChannel" | "special";
44
+ /**
45
+ * Site profile definition with optional flags. All flags are optional because profiles can inherit from other profiles, and only the flags that differ from the
46
+ * parent need to be specified. The DEFAULT_SITE_PROFILE provides baseline values for any flags not set through inheritance.
47
+ */
48
+ export interface SiteProfile {
49
+ category?: ProfileCategory;
50
+ channelSelection?: ChannelSelectionConfig;
51
+ channelSelector?: Nullable<string>;
52
+ clickSelector?: Nullable<string>;
53
+ clickToPlay?: boolean;
54
+ description?: string;
55
+ extends?: string;
56
+ summary?: string;
57
+ fullscreenKey?: Nullable<string>;
58
+ fullscreenSelector?: Nullable<string>;
59
+ hideSelector?: Nullable<string>;
60
+ lockVolumeProperties?: boolean;
61
+ needsIframeHandling?: boolean;
62
+ noVideo?: boolean;
63
+ selectReadyVideo?: boolean;
64
+ useRequestFullscreen?: boolean;
65
+ waitForNetworkIdle?: boolean;
66
+ }
67
+ /**
68
+ * Fully-resolved site profile with all flags having concrete values. After resolving inheritance chains and applying defaults, every flag has a definite boolean
69
+ * or string value. This interface is used by stream handling code that needs to check profile flags without worrying about undefined values.
70
+ */
71
+ export interface ResolvedSiteProfile {
72
+ channelSelection: ChannelSelectionConfig;
73
+ channelSelector: Nullable<string>;
74
+ clickSelector: Nullable<string>;
75
+ clickToPlay: boolean;
76
+ fullscreenKey: Nullable<string>;
77
+ fullscreenSelector: Nullable<string>;
78
+ hideSelector: Nullable<string>;
79
+ lockVolumeProperties: boolean;
80
+ maxContinuousPlayback: Nullable<number>;
81
+ needsIframeHandling: boolean;
82
+ noVideo: boolean;
83
+ selectReadyVideo: boolean;
84
+ useRequestFullscreen: boolean;
85
+ waitForNetworkIdle: boolean;
86
+ }
87
+ /**
88
+ * Result of resolving a site profile. Includes both the resolved profile configuration and the name of the profile that was matched. The name indicates whether the
89
+ * profile came from a channel hint, domain-based autodetection, or the default fallback.
90
+ */
91
+ export interface ProfileResolutionResult {
92
+ profile: ResolvedSiteProfile;
93
+ profileName: string;
94
+ }
95
+ /**
96
+ * Domain-level configuration associating domain patterns with site profiles and provider display names. Each entry can specify a site profile for behavior
97
+ * configuration and/or a provider display name for friendly UI labels. Used by both built-in domain mappings in sites.ts and user-defined mappings in profiles.json.
98
+ */
99
+ export interface DomainConfig {
100
+ loginUrl?: string;
101
+ maxContinuousPlayback?: number;
102
+ profile?: string;
103
+ provider?: string;
104
+ providerTag?: string;
105
+ }
106
+ /**
107
+ * Storage format for profiles.json. Contains user-defined site profiles and domain mappings that extend or override the built-in configurations.
108
+ */
109
+ export interface UserProfilesFile {
110
+ domains?: Record<string, DomainConfig>;
111
+ profiles?: Record<string, SiteProfile>;
112
+ }
113
+ /**
114
+ * Result of loading user profiles from the profiles.json file.
115
+ */
116
+ export interface UserProfilesLoadResult {
117
+ domains: Record<string, DomainConfig>;
118
+ parseError: boolean;
119
+ parseErrorMessage?: string;
120
+ profiles: Record<string, SiteProfile>;
121
+ }
122
+ /**
123
+ * Provider pack distribution format. Bundles a profile, domain mapping(s), and optionally channels for a streaming provider into a single JSON file. On import,
124
+ * its contents are split and written to profiles.json and channels.json.
125
+ */
126
+ export interface ProviderPack {
127
+ channels?: ChannelMap;
128
+ domains?: Record<string, DomainConfig>;
129
+ name: string;
130
+ profiles: Record<string, SiteProfile>;
131
+ version: number;
132
+ }
133
+ /**
134
+ * Validation result for profile and domain imports.
135
+ */
136
+ export interface ProfilesValidationResult {
137
+ domains: Record<string, DomainConfig>;
138
+ errors: string[];
139
+ profiles: Record<string, SiteProfile>;
140
+ valid: boolean;
141
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=profiles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../src/types/profiles.ts"],"names":[],"mappings":""}
@@ -0,0 +1,101 @@
1
+ import type { ChannelSelectionStrategy, ResolvedSiteProfile } from "./profiles.js";
2
+ import type { Frame, Page } from "puppeteer-core";
3
+ import type { Nullable } from "./shared.js";
4
+ export interface ChannelSelectionProfile extends ResolvedSiteProfile {
5
+ channelSelector: string;
6
+ }
7
+ export declare function isChannelSelectionProfile(profile: ResolvedSiteProfile): profile is ChannelSelectionProfile;
8
+ /**
9
+ * Result of attempting to select a channel from a multi-channel player UI.
10
+ */
11
+ export interface ChannelSelectorResult {
12
+ directTune?: boolean;
13
+ reason?: string;
14
+ success: boolean;
15
+ }
16
+ /**
17
+ * The strategy function signature. All strategies take the Puppeteer page and a narrowed profile with guaranteed non-null channelSelector.
18
+ */
19
+ export type ChannelStrategyHandler = (page: Page, profile: ChannelSelectionProfile) => Promise<ChannelSelectorResult>;
20
+ /**
21
+ * The complete contract for a channel selection strategy. Each provider file exports a single object implementing this interface. The coordinator accesses all
22
+ * provider behavior through these hooks — no strategy-specific imports or hardcoded strategy name checks outside the registry.
23
+ */
24
+ export interface ChannelStrategyEntry {
25
+ /**
26
+ * Resets all module-level caches (row positions, discovered URLs, watch URLs). Called on browser restart when cached state may be stale.
27
+ */
28
+ clearCache?: () => void;
29
+ /**
30
+ * Selects the target channel in the provider's guide UI. Receives a Puppeteer page and a profile with a guaranteed non-null channelSelector. Must handle its
31
+ * own retry logic (e.g., overlay dismiss) and return a result indicating success or failure with a diagnostic reason.
32
+ */
33
+ execute: ChannelStrategyHandler;
34
+ /**
35
+ * Removes a cached watch URL after it failed to produce a working stream. Called by the coordinator when a cached direct navigation fails.
36
+ */
37
+ invalidateDirectUrl?: (channelSelector: string) => void;
38
+ /**
39
+ * Returns a watch URL for direct navigation, bypassing guide page loading. Implementations may perform async work such as fetching current asset IDs from
40
+ * provider APIs. The page parameter allows setting up response interception or accessing browser context when needed (e.g., cold cache setup).
41
+ */
42
+ resolveDirectUrl?: (channelSelector: string, page: Page) => Promise<Nullable<string>>;
43
+ }
44
+ /**
45
+ * Standardized output shape for a channel discovered from a provider's guide. Produced by each provider's discoverChannels implementation and returned as
46
+ * a JSON array from the GET /providers/:slug/channels endpoint. Mirrors the shape of channel definitions in channels/index.ts so discovery output can be
47
+ * used directly to populate new entries.
48
+ */
49
+ export interface DiscoveredChannel {
50
+ affiliate?: string;
51
+ channelSelector: string;
52
+ name: string;
53
+ tier?: string;
54
+ }
55
+ /**
56
+ * Unified provider contract that bundles identity metadata, tuning strategy, and channel discovery into a single registry entry. Each provider tuning file
57
+ * exports one ProviderModule. The coordinator builds its strategy dispatch lookup from provider modules at evaluation time. Generic strategies (thumbnailRow,
58
+ * tileClick) remain bare ChannelStrategyEntry objects — they are not providers.
59
+ */
60
+ export interface ProviderModule {
61
+ /**
62
+ * Discovers all available channels from the provider's guide. The route handler navigates to guideUrl before calling this function unless handlesOwnNavigation
63
+ * is set. Returns a standardized DiscoveredChannel array.
64
+ */
65
+ discoverChannels: (page: Page) => Promise<DiscoveredChannel[]>;
66
+ /**
67
+ * Returns cached discovered channels if the provider has already fully enumerated its lineup from a previous tune or discovery call, or null if no enumeration
68
+ * has occurred. When non-null, the route handler can skip browser page creation entirely and return the cached result immediately.
69
+ */
70
+ getCachedChannels: () => Nullable<DiscoveredChannel[]>;
71
+ guideUrl: string;
72
+ handlesOwnNavigation?: boolean;
73
+ label: string;
74
+ slug: string;
75
+ strategy: ChannelStrategyEntry;
76
+ strategyName: ChannelSelectionStrategy;
77
+ validatePrecache?: (channels: DiscoveredChannel[]) => boolean;
78
+ validateTune?: (channelSelector: string) => boolean;
79
+ }
80
+ /**
81
+ * Coordinates for a click target, used when clicking channel selector elements.
82
+ */
83
+ export interface ClickTarget {
84
+ x: number;
85
+ y: number;
86
+ }
87
+ /**
88
+ * Result of tuning to a channel, containing the video context needed for monitoring.
89
+ */
90
+ export interface TuneResult {
91
+ context: Frame | Page;
92
+ directTune?: boolean;
93
+ }
94
+ /**
95
+ * Browser chrome dimensions (toolbars, borders) calculated by comparing window.outerHeight/Width to window.innerHeight/Width. Used to set window size such that
96
+ * the viewport (content area) matches our target dimensions.
97
+ */
98
+ export interface UiSize {
99
+ height: number;
100
+ width: number;
101
+ }
@@ -0,0 +1,6 @@
1
+ // Type guard that proves channelSelector is a non-empty string. Matches the original !channelSelector truthiness check, which rejects both null and empty string.
2
+ // Used by the coordinator before dispatching to strategy functions.
3
+ export function isChannelSelectionProfile(profile) {
4
+ return (profile.channelSelector !== null) && (profile.channelSelector.length > 0);
5
+ }
6
+ //# sourceMappingURL=selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection.js","sourceRoot":"","sources":["../../src/types/selection.ts"],"names":[],"mappings":"AAeA,kKAAkK;AAClK,oEAAoE;AACpE,MAAM,UAAU,yBAAyB,CAAC,OAA4B;IAEpE,OAAO,CAAC,OAAO,CAAC,eAAe,KAAK,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACpF,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * A utility type that represents a value that can be null.
3
+ * @typeParam T - The type that can be nullable.
4
+ */
5
+ export type Nullable<T> = T | null;
6
+ export type ChannelSortField = "channelNumber" | "channelSelector" | "key" | "name" | "profile" | "provider" | "stationId";
7
+ export type SortDirection = "asc" | "desc";
@@ -0,0 +1,6 @@
1
+ /* Copyright(C) 2024-2026, HJD (https://github.com/hjdhjd). All rights reserved.
2
+ *
3
+ * shared.ts: Shared utility types used across all PrismCast type domains.
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/types/shared.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,96 @@
1
+ import type { Nullable } from "./shared.js";
2
+ import type { Page } from "puppeteer-core";
3
+ import type { RecoveryMetrics } from "../streaming/recovery.js";
4
+ /**
5
+ * Information about an active streaming session. Created when a stream request is received and deleted when the stream ends.
6
+ */
7
+ export interface StreamInfo {
8
+ channelName: Nullable<string>;
9
+ id: number;
10
+ page: Page;
11
+ startTime: Date;
12
+ stopMonitor: Nullable<() => RecoveryMetrics>;
13
+ url: string;
14
+ }
15
+ /**
16
+ * Snapshot of a video element's playback state. Collected by the playback health monitor to detect stalls, errors, and other problems.
17
+ */
18
+ export interface VideoState {
19
+ currentTime: number;
20
+ ended: boolean;
21
+ error: boolean;
22
+ muted: boolean;
23
+ networkState: number;
24
+ paused: boolean;
25
+ readyState: number;
26
+ time: number;
27
+ volume: number;
28
+ }
29
+ /**
30
+ * Strategy for selecting a video element when multiple are present. "selectFirstVideo" takes the first video in DOM order; "selectReadyVideo" finds the video
31
+ * with readyState >= 3, which typically identifies the actively playing main content rather than preloaded ads.
32
+ */
33
+ export type VideoSelectorType = "selectFirstVideo" | "selectReadyVideo";
34
+ /**
35
+ * Result of URL validation indicating whether the URL is safe to navigate to.
36
+ */
37
+ export interface UrlValidationResult {
38
+ reason?: string;
39
+ valid: boolean;
40
+ }
41
+ /**
42
+ * Alias for UrlValidationResult maintained for backward compatibility with existing code.
43
+ */
44
+ export type UrlValidation = UrlValidationResult;
45
+ /**
46
+ * Health check response structure returned by the /health endpoint.
47
+ */
48
+ export interface HealthStatus {
49
+ browser: {
50
+ connected: boolean;
51
+ pageCount: number;
52
+ };
53
+ captureMode: string;
54
+ chrome: Nullable<string>;
55
+ clients: {
56
+ byType: {
57
+ count: number;
58
+ type: string;
59
+ }[];
60
+ total: number;
61
+ };
62
+ ffmpegAvailable: boolean;
63
+ memory: {
64
+ heapTotal: number;
65
+ heapUsed: number;
66
+ rss: number;
67
+ segmentBuffers: number;
68
+ };
69
+ message?: string;
70
+ status: "degraded" | "healthy" | "unhealthy";
71
+ streams: {
72
+ active: number;
73
+ limit: number;
74
+ };
75
+ timestamp: string;
76
+ uptime: number;
77
+ version: string;
78
+ }
79
+ /**
80
+ * Information about a single active stream as returned by the /streams endpoint.
81
+ */
82
+ export interface StreamListItem {
83
+ channel: Nullable<string>;
84
+ duration: number;
85
+ id: number;
86
+ startTime: string;
87
+ url: string;
88
+ }
89
+ /**
90
+ * Response structure for the /streams endpoint.
91
+ */
92
+ export interface StreamListResponse {
93
+ count: number;
94
+ limit: number;
95
+ streams: StreamListItem[];
96
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=streaming.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming.js","sourceRoot":"","sources":["../../src/types/streaming.ts"],"names":[],"mappings":""}
@@ -123,6 +123,7 @@ export const DEBUG_CATEGORIES = [
123
123
  { category: "browser", description: "Browser lifecycle: launch, close, stale page cleanup, restart." },
124
124
  { category: "browser:video", description: "Video context, fullscreen, volume locking, playback." },
125
125
  { category: "config", description: "Provider groups, version checking." },
126
+ { category: "precache", description: "Channel lineup precaching: deferred runs, provider filter skips." },
126
127
  { category: "recovery", description: "General recovery: browser re-minimize, monitor abort." },
127
128
  { category: "recovery:context", description: "Video context: frame detachment, re-search." },
128
129
  { category: "recovery:nav", description: "Page navigation recovery: new tab detection, URL validation." },
@@ -131,7 +132,6 @@ export const DEBUG_CATEGORIES = [
131
132
  { category: "retry", description: "Retry attempts, page-closed aborts." },
132
133
  { category: "streaming:ffmpeg", description: "FFmpeg stderr output, pipe errors." },
133
134
  { category: "streaming:hls", description: "HLS segment storage, page close errors." },
134
- { category: "streaming:mpegts", description: "MPEG-TS remuxer errors, client connect/disconnect." },
135
135
  { category: "streaming:segmenter", description: "fMP4 parsing: keyframes, init segments, duration clamping." },
136
136
  { category: "streaming:setup", description: "Stream setup: redirect resolution, profile override, capture init." },
137
137
  { category: "streaming:showinfo", description: "Channels DVR show name lookups, device mapping." },
@@ -141,9 +141,11 @@ export const DEBUG_CATEGORIES = [
141
141
  { category: "timing:startup", description: "Stream startup: init segment, first playlist, capture ready." },
142
142
  { category: "timing:tab", description: "Tab replacement: old tab cleanup, new page creation." },
143
143
  { category: "timing:tune", description: "Tune waterfall: navigation, channel selection, video ready." },
144
+ { category: "tuning:directv", description: "DirecTV Stream: interceptor tuning, cache, logo click fallback." },
144
145
  { category: "tuning:hbo", description: "HBO Max: tab URL discovery, channel rail, navigation." },
145
146
  { category: "tuning:hulu", description: "Hulu Live guide grid: binary search, cache, click retries." },
146
147
  { category: "tuning:sling", description: "Sling TV guide grid: binary search, cache, click retries." },
148
+ { category: "tuning:tileClick", description: "Tile click strategy: scroll phase, matchSelector poll, play button retries, modal dismiss." },
147
149
  { category: "tuning:yttv", description: "YouTube TV EPG grid navigation." }
148
150
  ];
149
151
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"debugFilter.js","sourceRoot":"","sources":["../../src/utils/debugFilter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"debugFilter.js","sourceRoot":"","sources":["../../src/utils/debugFilter.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,13 @@
7
7
  * @returns Formatted duration string.
8
8
  */
9
9
  export declare function formatDuration(ms: number): string;
10
+ /**
11
+ * Formats a Unix millisecond timestamp as a human-readable relative time string (e.g., "2 minutes ago", "3 hours ago", "5 days ago"). Used for health indicator
12
+ * tooltips where absolute timestamps would be harder to interpret at a glance.
13
+ * @param timestamp - Unix millisecond timestamp.
14
+ * @returns Relative time string.
15
+ */
16
+ export declare function formatTimeAgo(timestamp: number): string;
10
17
  /**
11
18
  * Extracts a concise domain from a URL by keeping only the last two portions of the hostname (e.g., "watch.foodnetwork.com" becomes "foodnetwork.com",
12
19
  * "www.hulu.com" becomes "hulu.com"). Used as a standard domain key for DOMAIN_CONFIG lookups and as a display fallback when no provider name is configured.
@@ -23,6 +23,28 @@ export function formatDuration(ms) {
23
23
  }
24
24
  return [String(seconds), "s"].join("");
25
25
  }
26
+ /**
27
+ * Formats a Unix millisecond timestamp as a human-readable relative time string (e.g., "2 minutes ago", "3 hours ago", "5 days ago"). Used for health indicator
28
+ * tooltips where absolute timestamps would be harder to interpret at a glance.
29
+ * @param timestamp - Unix millisecond timestamp.
30
+ * @returns Relative time string.
31
+ */
32
+ export function formatTimeAgo(timestamp) {
33
+ const seconds = Math.floor((Date.now() - timestamp) / 1000);
34
+ if (seconds < 60) {
35
+ return "just now";
36
+ }
37
+ const minutes = Math.floor(seconds / 60);
38
+ if (minutes < 60) {
39
+ return String(minutes) + (minutes === 1 ? " minute ago" : " minutes ago");
40
+ }
41
+ const hours = Math.floor(minutes / 60);
42
+ if (hours < 24) {
43
+ return String(hours) + (hours === 1 ? " hour ago" : " hours ago");
44
+ }
45
+ const days = Math.floor(hours / 24);
46
+ return String(days) + (days === 1 ? " day ago" : " days ago");
47
+ }
26
48
  /**
27
49
  * Extracts a concise domain from a URL by keeping only the last two portions of the hostname (e.g., "watch.foodnetwork.com" becomes "foodnetwork.com",
28
50
  * "www.hulu.com" becomes "hulu.com"). Used as a standard domain key for DOMAIN_CONFIG lookups and as a display fallback when no provider name is configured.
@@ -1 +1 @@
1
- {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,EAAU;IAEvC,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,YAAY,GAAG,EAAE,CAAC;IAElC,IAAG,KAAK,GAAG,CAAC,EAAE,CAAC;QAEb,OAAO,CAAE,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,GAAG,CAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,IAAG,OAAO,GAAG,CAAC,EAAE,CAAC;QAEf,OAAO,CAAE,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,GAAG,CAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,OAAO,CAAE,MAAM,CAAC,OAAO,CAAC,EAAE,GAAG,CAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW;IAEvC,IAAI,CAAC;QAEH,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;QACvC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAElC,uHAAuH;QACvH,IAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAEpB,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnC,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAAC,MAAM,CAAC;QAEP,OAAO,GAAG,CAAC;IACb,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,EAAU;IAEvC,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,YAAY,GAAG,EAAE,CAAC;IAElC,IAAG,KAAK,GAAG,CAAC,EAAE,CAAC;QAEb,OAAO,CAAE,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,GAAG,CAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,IAAG,OAAO,GAAG,CAAC,EAAE,CAAC;QAEf,OAAO,CAAE,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,GAAG,CAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,OAAO,CAAE,MAAM,CAAC,OAAO,CAAC,EAAE,GAAG,CAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,SAAiB;IAE7C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;IAE5D,IAAG,OAAO,GAAG,EAAE,EAAE,CAAC;QAEhB,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IAEzC,IAAG,OAAO,GAAG,EAAE,EAAE,CAAC;QAEhB,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;IAC5E,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IAEvC,IAAG,KAAK,GAAG,EAAE,EAAE,CAAC;QAEd,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;IACpE,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;IAEpC,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;AAChE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW;IAEvC,IAAI,CAAC;QAEH,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;QACvC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAElC,uHAAuH;QACvH,IAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAEpB,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnC,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAAC,MAAM,CAAC;QAEP,OAAO,GAAG,CAAC;IACb,CAAC;AACH,CAAC"}
@@ -11,5 +11,6 @@ export * from "./m3u.js";
11
11
  export * from "./morganStream.js";
12
12
  export * from "./platform.js";
13
13
  export * from "./retry.js";
14
+ export * from "./sanitize.js";
14
15
  export * from "./streamContext.js";
15
16
  export * from "./version.js";
@@ -15,6 +15,7 @@ export * from "./m3u.js";
15
15
  export * from "./morganStream.js";
16
16
  export * from "./platform.js";
17
17
  export * from "./retry.js";
18
+ export * from "./sanitize.js";
18
19
  export * from "./streamContext.js";
19
20
  export * from "./version.js";
20
21
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Strips non-printable characters from a string and trims whitespace. Replaces `.trim()` at data-collection points to ensure string values contain only visible
3
+ * content. Safe to call on already-clean strings — the regex replacement is a no-op when no non-printable characters are present.
4
+ * @param value - The string to sanitize.
5
+ * @returns The sanitized string with non-printable characters removed and whitespace trimmed.
6
+ */
7
+ export declare function sanitizeString(value: string): string;
8
+ /**
9
+ * Tests whether a string contains any non-printable characters. Used for startup warnings when loading persisted data — the warning alerts users to corruption
10
+ * without modifying the loaded values.
11
+ * @param value - The string to test.
12
+ * @returns True if the string contains at least one non-printable character.
13
+ */
14
+ export declare function containsNonPrintable(value: string): boolean;
@@ -0,0 +1,37 @@
1
+ /* Copyright(C) 2024-2026, HJD (https://github.com/hjdhjd). All rights reserved.
2
+ *
3
+ * sanitize.ts: Input sanitization utilities for PrismCast.
4
+ */
5
+ /* Non-printable characters can silently corrupt string values when introduced via copy-paste operations or hand-edited JSON files. The most common case is null bytes
6
+ * (\x00) interleaved between every visible character, which breaks CSS selector matching, URL parsing, and channel identification without any visible indication in
7
+ * the UI. These utilities strip non-printable characters at data-collection boundaries so downstream code can trust that string values contain only visible content.
8
+ *
9
+ * The regex targets control characters and Unicode special characters that have no visible representation: C0 controls (except TAB, LF, CR), DEL, BOM, zero-width
10
+ * spaces, and Unicode line/paragraph separators. TAB (\x09), LF (\x0A), and CR (\x0D) are preserved because they appear legitimately in multi-line text fields.
11
+ */
12
+ // Pattern matching non-printable characters. Covers: C0 controls except TAB/LF/CR, DEL, BOM, zero-width spaces, directional markers, and line/paragraph separators.
13
+ // The no-control-regex rule is suppressed because this regex genuinely needs to match control characters — that is its entire purpose.
14
+ // eslint-disable-next-line no-control-regex
15
+ const NON_PRINTABLE_PATTERN = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F\uFEFF\u200B-\u200F\u2028-\u2029]/g;
16
+ /**
17
+ * Strips non-printable characters from a string and trims whitespace. Replaces `.trim()` at data-collection points to ensure string values contain only visible
18
+ * content. Safe to call on already-clean strings — the regex replacement is a no-op when no non-printable characters are present.
19
+ * @param value - The string to sanitize.
20
+ * @returns The sanitized string with non-printable characters removed and whitespace trimmed.
21
+ */
22
+ export function sanitizeString(value) {
23
+ return value.replace(NON_PRINTABLE_PATTERN, "").trim();
24
+ }
25
+ /**
26
+ * Tests whether a string contains any non-printable characters. Used for startup warnings when loading persisted data — the warning alerts users to corruption
27
+ * without modifying the loaded values.
28
+ * @param value - The string to test.
29
+ * @returns True if the string contains at least one non-printable character.
30
+ */
31
+ export function containsNonPrintable(value) {
32
+ // Reset lastIndex before testing — the global flag on NON_PRINTABLE_PATTERN causes .test() to advance lastIndex on each call, which would produce alternating
33
+ // true/false results without this reset.
34
+ NON_PRINTABLE_PATTERN.lastIndex = 0;
35
+ return NON_PRINTABLE_PATTERN.test(value);
36
+ }
37
+ //# sourceMappingURL=sanitize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../../src/utils/sanitize.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;GAMG;AAEH,oKAAoK;AACpK,uIAAuI;AACvI,4CAA4C;AAC5C,MAAM,qBAAqB,GAAG,mEAAmE,CAAC;AAElG;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa;IAE1C,OAAO,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAEhD,8JAA8J;IAC9J,yCAAyC;IACzC,qBAAqB,CAAC,SAAS,GAAG,CAAC,CAAC;IAEpC,OAAO,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC3C,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "prismcast",
3
3
  "displayName": "PrismCast",
4
- "version": "1.4.2",
4
+ "version": "1.5.0",
5
5
  "description": "Browser-based live TV capture server for Channels DVR, Plex, and other streaming clients.",
6
6
  "author": {
7
7
  "name": "HJD",
@@ -63,6 +63,7 @@
63
63
  "watch": "npm run build && nodemon"
64
64
  },
65
65
  "dependencies": {
66
+ "compression": "^1.8.1",
66
67
  "console-stamp": "3.1.2",
67
68
  "express": "5.2.1",
68
69
  "ffmpeg-for-homebridge": "2.2.1",
@@ -72,15 +73,16 @@
72
73
  },
73
74
  "devDependencies": {
74
75
  "@stylistic/eslint-plugin": "5.9.0",
76
+ "@types/compression": "^1.8.1",
75
77
  "@types/dateformat": "5.0.3",
76
78
  "@types/express": "5.0.6",
77
79
  "@types/morgan": "1.9.10",
78
- "@types/node": "25.3.0",
80
+ "@types/node": "25.3.3",
79
81
  "eslint": "9.39.2",
80
82
  "homebridge-plugin-utils": "1.32.0",
81
83
  "nodemon": "3.1.14",
82
84
  "shx": "0.4.0",
83
85
  "typescript": "5.9.3",
84
- "typescript-eslint": "8.56.0"
86
+ "typescript-eslint": "8.56.1"
85
87
  }
86
88
  }