prismcast 1.5.2 → 1.6.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 (158) hide show
  1. package/README.md +44 -28
  2. package/dist/app.d.ts +5 -0
  3. package/dist/app.js +63 -5
  4. package/dist/app.js.map +1 -1
  5. package/dist/browser/channelSelection.js +14 -2
  6. package/dist/browser/channelSelection.js.map +1 -1
  7. package/dist/browser/index.d.ts +23 -9
  8. package/dist/browser/index.js +205 -88
  9. package/dist/browser/index.js.map +1 -1
  10. package/dist/browser/precaching.js +3 -3
  11. package/dist/browser/precaching.js.map +1 -1
  12. package/dist/browser/tuning/directv.js +13 -0
  13. package/dist/browser/tuning/directv.js.map +1 -1
  14. package/dist/browser/tuning/fox.js +12 -0
  15. package/dist/browser/tuning/fox.js.map +1 -1
  16. package/dist/browser/tuning/hbo.js +11 -0
  17. package/dist/browser/tuning/hbo.js.map +1 -1
  18. package/dist/browser/tuning/hulu.js +23 -0
  19. package/dist/browser/tuning/hulu.js.map +1 -1
  20. package/dist/browser/tuning/sling.js +34 -4
  21. package/dist/browser/tuning/sling.js.map +1 -1
  22. package/dist/browser/tuning/spectrum.js +12 -0
  23. package/dist/browser/tuning/spectrum.js.map +1 -1
  24. package/dist/browser/tuning/xfinity.d.ts +2 -0
  25. package/dist/browser/tuning/xfinity.js +607 -0
  26. package/dist/browser/tuning/xfinity.js.map +1 -0
  27. package/dist/browser/tuning/youtubeTv.js +14 -0
  28. package/dist/browser/tuning/youtubeTv.js.map +1 -1
  29. package/dist/browser/video.d.ts +2 -0
  30. package/dist/browser/video.js +99 -17
  31. package/dist/browser/video.js.map +1 -1
  32. package/dist/channels/index.js +173 -56
  33. package/dist/channels/index.js.map +1 -1
  34. package/dist/config/health.d.ts +28 -28
  35. package/dist/config/health.js +52 -52
  36. package/dist/config/health.js.map +1 -1
  37. package/dist/config/paths.d.ts +12 -0
  38. package/dist/config/paths.js +16 -0
  39. package/dist/config/paths.js.map +1 -1
  40. package/dist/config/profiles.d.ts +3 -2
  41. package/dist/config/profiles.js +45 -34
  42. package/dist/config/profiles.js.map +1 -1
  43. package/dist/config/providers.d.ts +7 -0
  44. package/dist/config/providers.js +14 -0
  45. package/dist/config/providers.js.map +1 -1
  46. package/dist/config/sites.d.ts +28 -0
  47. package/dist/config/sites.js +93 -122
  48. package/dist/config/sites.js.map +1 -1
  49. package/dist/config/userChannels.d.ts +3 -0
  50. package/dist/config/userChannels.js +10 -3
  51. package/dist/config/userChannels.js.map +1 -1
  52. package/dist/config/userConfig.d.ts +1 -0
  53. package/dist/config/userConfig.js +4 -0
  54. package/dist/config/userConfig.js.map +1 -1
  55. package/dist/config/userProfiles.d.ts +0 -1
  56. package/dist/config/userProfiles.js +18 -10
  57. package/dist/config/userProfiles.js.map +1 -1
  58. package/dist/index.js +16 -7
  59. package/dist/index.js.map +1 -1
  60. package/dist/native/decrypt.d.ts +32 -0
  61. package/dist/native/decrypt.js +85 -0
  62. package/dist/native/decrypt.js.map +1 -0
  63. package/dist/native/index.d.ts +58 -0
  64. package/dist/native/index.js +341 -0
  65. package/dist/native/index.js.map +1 -0
  66. package/dist/native/intercept.d.ts +35 -0
  67. package/dist/native/intercept.js +184 -0
  68. package/dist/native/intercept.js.map +1 -0
  69. package/dist/native/probe.d.ts +48 -0
  70. package/dist/native/probe.js +270 -0
  71. package/dist/native/probe.js.map +1 -0
  72. package/dist/native/proxy.d.ts +52 -0
  73. package/dist/native/proxy.js +904 -0
  74. package/dist/native/proxy.js.map +1 -0
  75. package/dist/routes/config/channels/table.js +19 -25
  76. package/dist/routes/config/channels/table.js.map +1 -1
  77. package/dist/routes/config/providers.js +4 -4
  78. package/dist/routes/config/providers.js.map +1 -1
  79. package/dist/routes/debug.js +5 -5
  80. package/dist/routes/debug.js.map +1 -1
  81. package/dist/routes/hls.js +12 -2
  82. package/dist/routes/hls.js.map +1 -1
  83. package/dist/routes/index.d.ts +1 -0
  84. package/dist/routes/index.js +3 -0
  85. package/dist/routes/index.js.map +1 -1
  86. package/dist/routes/root/content.js +80 -20
  87. package/dist/routes/root/content.js.map +1 -1
  88. package/dist/routes/root/scripts/status.js +14 -13
  89. package/dist/routes/root/scripts/status.js.map +1 -1
  90. package/dist/routes/root/styles.js +4 -2
  91. package/dist/routes/root/styles.js.map +1 -1
  92. package/dist/streaming/fmp4Segmenter.d.ts +3 -0
  93. package/dist/streaming/fmp4Segmenter.js +143 -42
  94. package/dist/streaming/fmp4Segmenter.js.map +1 -1
  95. package/dist/streaming/hls.d.ts +24 -19
  96. package/dist/streaming/hls.js +486 -261
  97. package/dist/streaming/hls.js.map +1 -1
  98. package/dist/streaming/hlsResume.d.ts +7 -0
  99. package/dist/streaming/hlsResume.js +22 -2
  100. package/dist/streaming/hlsResume.js.map +1 -1
  101. package/dist/streaming/hlsSegments.d.ts +42 -2
  102. package/dist/streaming/hlsSegments.js +109 -8
  103. package/dist/streaming/hlsSegments.js.map +1 -1
  104. package/dist/streaming/lifecycle.js +20 -2
  105. package/dist/streaming/lifecycle.js.map +1 -1
  106. package/dist/streaming/monitor.d.ts +1 -0
  107. package/dist/streaming/monitor.js +369 -21
  108. package/dist/streaming/monitor.js.map +1 -1
  109. package/dist/streaming/mp4Parser.d.ts +52 -6
  110. package/dist/streaming/mp4Parser.js +156 -30
  111. package/dist/streaming/mp4Parser.js.map +1 -1
  112. package/dist/streaming/mpegts.d.ts +2 -2
  113. package/dist/streaming/mpegts.js +156 -109
  114. package/dist/streaming/mpegts.js.map +1 -1
  115. package/dist/streaming/playlistBuilder.d.ts +38 -0
  116. package/dist/streaming/playlistBuilder.js +66 -0
  117. package/dist/streaming/playlistBuilder.js.map +1 -0
  118. package/dist/streaming/preroll.d.ts +106 -0
  119. package/dist/streaming/preroll.js +373 -0
  120. package/dist/streaming/preroll.js.map +1 -0
  121. package/dist/streaming/pretune.d.ts +8 -0
  122. package/dist/streaming/pretune.js +226 -0
  123. package/dist/streaming/pretune.js.map +1 -0
  124. package/dist/streaming/registry.d.ts +24 -3
  125. package/dist/streaming/registry.js +23 -1
  126. package/dist/streaming/registry.js.map +1 -1
  127. package/dist/streaming/setup.d.ts +7 -0
  128. package/dist/streaming/setup.js +27 -6
  129. package/dist/streaming/setup.js.map +1 -1
  130. package/dist/streaming/showInfo.d.ts +24 -0
  131. package/dist/streaming/showInfo.js +55 -3
  132. package/dist/streaming/showInfo.js.map +1 -1
  133. package/dist/streaming/statusEmitter.d.ts +6 -4
  134. package/dist/streaming/statusEmitter.js +9 -3
  135. package/dist/streaming/statusEmitter.js.map +1 -1
  136. package/dist/types/channels.d.ts +1 -0
  137. package/dist/types/index.d.ts +1 -1
  138. package/dist/types/profiles.d.ts +8 -1
  139. package/dist/types/selection.d.ts +4 -1
  140. package/dist/types/streaming.d.ts +7 -0
  141. package/dist/utils/chromeFetch.d.ts +20 -0
  142. package/dist/utils/chromeFetch.js +43 -0
  143. package/dist/utils/chromeFetch.js.map +1 -0
  144. package/dist/utils/debugFilter.js +15 -0
  145. package/dist/utils/debugFilter.js.map +1 -1
  146. package/dist/utils/delay.d.ts +16 -0
  147. package/dist/utils/delay.js +15 -1
  148. package/dist/utils/delay.js.map +1 -1
  149. package/dist/utils/ffmpeg.d.ts +1 -7
  150. package/dist/utils/ffmpeg.js +86 -127
  151. package/dist/utils/ffmpeg.js.map +1 -1
  152. package/dist/utils/index.d.ts +2 -0
  153. package/dist/utils/index.js +2 -0
  154. package/dist/utils/index.js.map +1 -1
  155. package/dist/utils/pid.d.ts +40 -0
  156. package/dist/utils/pid.js +83 -0
  157. package/dist/utils/pid.js.map +1 -0
  158. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"file":"statusEmitter.js","sourceRoot":"","sources":["../../src/streaming/statusEmitter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"statusEmitter.js","sourceRoot":"","sources":["../../src/streaming/statusEmitter.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,7 @@ import type { Nullable } from "./shared.js";
5
5
  export interface Channel {
6
6
  channelNumber?: number;
7
7
  channelSelector?: string;
8
+ dismissSelector?: string;
8
9
  name?: string;
9
10
  pacificStationId?: string;
10
11
  profile?: string;
@@ -3,5 +3,5 @@ export type { Channel, ChannelDelta, ChannelListingEntry, ChannelMap, ProviderGr
3
3
  export type { ChannelSelectionConfig, ChannelSelectionStrategy, DomainConfig, ProfileCategory, ProfileResolutionResult, ProfilesValidationResult, ProviderPack, ResolvedSiteProfile, SiteProfile, UserProfilesFile, UserProfilesLoadResult } from "./profiles.js";
4
4
  export type { ChannelSelectionProfile, ChannelSelectorResult, ChannelStrategyEntry, ChannelStrategyHandler, ClickTarget, DiscoveredChannel, ProviderModule, TuneResult, UiSize } from "./selection.js";
5
5
  export type { ChannelSortField, Nullable, SortDirection } from "./shared.js";
6
- export type { HealthStatus, StreamInfo, StreamListItem, StreamListResponse, UrlValidation, UrlValidationResult, VideoSelectorType, VideoState } from "./streaming.js";
6
+ export type { HealthStatus, StreamInfo, StreamListItem, StreamListResponse, StreamingMode, UrlValidation, UrlValidationResult, VideoSelectorType, VideoState } from "./streaming.js";
7
7
  export { isChannelSelectionProfile } from "./selection.js";
@@ -19,9 +19,11 @@ import type { Nullable } from "./shared.js";
19
19
  * USA Network.
20
20
  * - "tileClick": Find channel element using the profile's matchSelector (defaults to image URL matching), click tile, then optionally click play button if
21
21
  * playSelector is configured. Used by Disney+.
22
+ * - "xfinityDirect": Wait for the Polymer SPA's channelMap to populate, find the target channel by callSign, and call `_watchChannelEventHandler` to switch
23
+ * channels in-page. A fire-and-forget poll dismisses the Watch Now modal if it appears. Used by Xfinity Stream.
22
24
  * - "youtubeGrid": Find channel by aria-label in a non-virtualized EPG grid, extract the watch URL, and navigate directly. Used by YouTube TV.
23
25
  */
24
- export type ChannelSelectionStrategy = "directvGrid" | "foxGrid" | "guideGrid" | "hboGrid" | "none" | "slingGrid" | "spectrumGrid" | "thumbnailRow" | "tileClick" | "youtubeGrid";
26
+ export type ChannelSelectionStrategy = "directvGrid" | "foxGrid" | "guideGrid" | "hboGrid" | "none" | "slingGrid" | "spectrumGrid" | "thumbnailRow" | "tileClick" | "xfinityDirect" | "youtubeGrid";
25
27
  /**
26
28
  * Configuration for channel selection behavior within a site profile.
27
29
  */
@@ -53,6 +55,7 @@ export interface SiteProfile {
53
55
  channelSelector?: Nullable<string>;
54
56
  clickSelector?: Nullable<string>;
55
57
  clickToPlay?: boolean;
58
+ dismissSelector?: Nullable<string>;
56
59
  description?: string;
57
60
  extends?: string;
58
61
  summary?: string;
@@ -75,6 +78,7 @@ export interface ResolvedSiteProfile {
75
78
  channelSelector: Nullable<string>;
76
79
  clickSelector: Nullable<string>;
77
80
  clickToPlay: boolean;
81
+ dismissSelector: Nullable<string>;
78
82
  fullscreenKey: Nullable<string>;
79
83
  fullscreenSelector: Nullable<string>;
80
84
  hideSelector: Nullable<string>;
@@ -84,6 +88,7 @@ export interface ResolvedSiteProfile {
84
88
  noVideo: boolean;
85
89
  selectReadyVideo: boolean;
86
90
  useRequestFullscreen: boolean;
91
+ videoTimeout: Nullable<number>;
87
92
  waitForNetworkIdle: boolean;
88
93
  }
89
94
  /**
@@ -99,11 +104,13 @@ export interface ProfileResolutionResult {
99
104
  * 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.
100
105
  */
101
106
  export interface DomainConfig {
107
+ dismissSelector?: string;
102
108
  loginUrl?: string;
103
109
  maxContinuousPlayback?: number;
104
110
  profile?: string;
105
111
  provider?: string;
106
112
  providerTag?: string;
113
+ videoTimeout?: number;
107
114
  }
108
115
  /**
109
116
  * Storage format for profiles.json. Contains user-defined site profiles and domain mappings that extend or override the built-in configurations.
@@ -1,4 +1,4 @@
1
- import type { ChannelSelectionStrategy, ResolvedSiteProfile } from "./profiles.js";
1
+ import type { ChannelSelectionStrategy, ResolvedSiteProfile, SiteProfile } from "./profiles.js";
2
2
  import type { Frame, Page } from "puppeteer-core";
3
3
  import type { Nullable } from "./shared.js";
4
4
  export interface ChannelSelectionProfile extends ResolvedSiteProfile {
@@ -72,8 +72,11 @@ export interface ProviderModule {
72
72
  guideUrl: string;
73
73
  handlesOwnNavigation?: boolean;
74
74
  label: string;
75
+ profile?: SiteProfile;
76
+ profileName?: string;
75
77
  slug: string;
76
78
  strategy: ChannelStrategyEntry;
79
+ tinySegmentThreshold?: number;
77
80
  strategyName: ChannelSelectionStrategy;
78
81
  validatePrecache?: (channels: DiscoveredChannel[]) => boolean;
79
82
  validateTune?: (channelSelector: string) => boolean;
@@ -24,6 +24,8 @@ export interface VideoState {
24
24
  paused: boolean;
25
25
  readyState: number;
26
26
  time: number;
27
+ videoHeight: number;
28
+ videoWidth: number;
27
29
  volume: number;
28
30
  }
29
31
  /**
@@ -42,6 +44,11 @@ export interface UrlValidationResult {
42
44
  * Alias for UrlValidationResult maintained for backward compatibility with existing code.
43
45
  */
44
46
  export type UrlValidation = UrlValidationResult;
47
+ /**
48
+ * Streaming mode for an active stream. "capture" uses Chrome screen capture via puppeteer-stream. "native" intercepts the provider's HLS stream and consumes it
49
+ * directly in Node, bypassing screen capture entirely.
50
+ */
51
+ export type StreamingMode = "capture" | "native";
45
52
  /**
46
53
  * Health check response structure returned by the /health endpoint.
47
54
  */
@@ -0,0 +1,20 @@
1
+ import type { Nullable } from "../types/index.js";
2
+ /**
3
+ * Sets the Chrome User-Agent string. Called by browser/index.ts when the browser launches (with the UA) and when it disconnects or closes (with null).
4
+ * @param ua - The User-Agent string from the Chrome browser, or null to clear.
5
+ */
6
+ export declare function setChromeUserAgent(ua: Nullable<string>): void;
7
+ /**
8
+ * Returns the current Chrome User-Agent string, or null if the browser is not running.
9
+ * @returns The Chrome User-Agent string or null.
10
+ */
11
+ export declare function getChromeUserAgent(): Nullable<string>;
12
+ /**
13
+ * Fetch wrapper that injects the Chrome User-Agent header into outgoing requests. When the browser is running, the User-Agent header is set to match Chrome's
14
+ * actual UA string so CDN requests appear to originate from the same browser. When the browser is not running, falls through to plain fetch() with no UA
15
+ * injection. Any caller-supplied User-Agent header takes precedence and is not overwritten.
16
+ * @param url - The URL to fetch.
17
+ * @param init - Optional fetch options (method, headers, signal, etc.).
18
+ * @returns The fetch Response.
19
+ */
20
+ export declare function chromeFetch(url: string | URL, init?: RequestInit): Promise<Response>;
@@ -0,0 +1,43 @@
1
+ /* When PrismCast fetches manifests, segments, decryption keys, and other resources from provider CDNs, the requests should appear to originate from the same Chrome
2
+ * browser that is rendering the page. This module stores the Chrome User-Agent string (captured at browser launch) and provides a drop-in fetch() wrapper that
3
+ * injects it into outgoing requests. The setter/getter pattern avoids a circular dependency between utils/ and browser/ — browser/index.ts calls the setter at
4
+ * launch and cleanup, while consumer modules import chromeFetch() without importing from browser/.
5
+ */
6
+ // The Chrome User-Agent string captured at browser launch. Null when the browser is not running.
7
+ let currentUserAgent = null;
8
+ /**
9
+ * Sets the Chrome User-Agent string. Called by browser/index.ts when the browser launches (with the UA) and when it disconnects or closes (with null).
10
+ * @param ua - The User-Agent string from the Chrome browser, or null to clear.
11
+ */
12
+ export function setChromeUserAgent(ua) {
13
+ currentUserAgent = ua;
14
+ }
15
+ /**
16
+ * Returns the current Chrome User-Agent string, or null if the browser is not running.
17
+ * @returns The Chrome User-Agent string or null.
18
+ */
19
+ export function getChromeUserAgent() {
20
+ return currentUserAgent;
21
+ }
22
+ /**
23
+ * Fetch wrapper that injects the Chrome User-Agent header into outgoing requests. When the browser is running, the User-Agent header is set to match Chrome's
24
+ * actual UA string so CDN requests appear to originate from the same browser. When the browser is not running, falls through to plain fetch() with no UA
25
+ * injection. Any caller-supplied User-Agent header takes precedence and is not overwritten.
26
+ * @param url - The URL to fetch.
27
+ * @param init - Optional fetch options (method, headers, signal, etc.).
28
+ * @returns The fetch Response.
29
+ */
30
+ export async function chromeFetch(url, init) {
31
+ const ua = currentUserAgent;
32
+ if (!ua) {
33
+ return fetch(url, init);
34
+ }
35
+ // Merge the Chrome UA into the request headers. We use the Headers constructor to normalize whatever format the caller passed (plain object, Headers instance,
36
+ // or array of key-value pairs), then set User-Agent only if the caller did not already provide one.
37
+ const headers = new Headers(init?.headers);
38
+ if (!headers.has("User-Agent")) {
39
+ headers.set("User-Agent", ua);
40
+ }
41
+ return fetch(url, { ...init, headers });
42
+ }
43
+ //# sourceMappingURL=chromeFetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chromeFetch.js","sourceRoot":"","sources":["../../src/utils/chromeFetch.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AAEH,iGAAiG;AACjG,IAAI,gBAAgB,GAAqB,IAAI,CAAC;AAE9C;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,EAAoB;IAErD,gBAAgB,GAAG,EAAE,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB;IAEhC,OAAO,gBAAgB,CAAC;AAC1B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAiB,EAAE,IAAkB;IAErE,MAAM,EAAE,GAAG,gBAAgB,CAAC;IAE5B,IAAG,CAAC,EAAE,EAAE,CAAC;QAEP,OAAO,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,+JAA+J;IAC/J,oGAAoG;IACpG,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAE3C,IAAG,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC;QAE9B,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IAChC,CAAC;IAED,OAAO,KAAK,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;AAC1C,CAAC"}
@@ -123,20 +123,34 @@ 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: "native:coordinator", description: "Native streaming decisions: interception result, probe result, capture teardown, proxy start." },
127
+ { category: "native:decrypt", description: "AES-128 decryption: key fetch, IV source (explicit vs. sequence), segment sizes." },
128
+ { category: "native:intercept", description: "CDP manifest interception: listener installed, .m3u8 URLs observed, master identified, timeout." },
129
+ { category: "native:manifest", description: "Raw variant manifest body from provider — verbose, use for diagnosing DAI/ad stitching issues." },
130
+ { category: "native:monitor", description: "Native health: segment staleness, error counts, L2/L3 recovery actions, capture fallback." },
131
+ { category: "native:probe", description: "DRM probe: variant count, bandwidths, encryption classification, key accessibility, cache hit/miss." },
132
+ { category: "native:proxy", description: "Manifest polling, segment fetch/store, playlist generation, segment rotation, key rotation." },
133
+ { category: "native:token", description: "Token refresh: expiry parsed, timer scheduled, refresh triggered, manifest acquired." },
126
134
  { category: "precache", description: "Channel lineup precaching: deferred runs, provider filter skips." },
127
135
  { category: "recovery", description: "General recovery: browser re-minimize, monitor abort." },
128
136
  { category: "recovery:context", description: "Video context: frame detachment, re-search." },
129
137
  { category: "recovery:nav", description: "Page navigation recovery: new tab detection, URL validation." },
138
+ { category: "recovery:resolution", description: "Video resolution monitoring: ABR degradation detection, grace periods, recovery escalation." },
130
139
  { category: "recovery:segments", description: "Segment production: self-heal detection." },
131
140
  { category: "recovery:tab", description: "Tab replacement: old tab cleanup, new tab creation, retries." },
141
+ { category: "recovery:tracks", description: "Track composition: video traf presence in below-threshold segments." },
132
142
  { category: "retry", description: "Retry attempts, page-closed aborts." },
133
143
  { category: "streaming:ffmpeg", description: "FFmpeg stderr output, pipe errors." },
134
144
  { category: "streaming:hls", description: "HLS segment storage, page close errors." },
145
+ { category: "streaming:mpegts", description: "MPEG-TS client connections, FFmpeg remuxer spawn and errors." },
146
+ { category: "streaming:preroll", description: "Preroll lifecycle: fMP4 generation, preroll playlist delivery, transition to live content." },
147
+ { category: "streaming:pretune", description: "Predictive channel pretuning: DVR job polling, timer scheduling, tune triggers." },
135
148
  { category: "streaming:segmenter", description: "fMP4 parsing: keyframes, init segments, duration clamping." },
136
149
  { category: "streaming:setup", description: "Stream setup: redirect resolution, profile override, capture init." },
137
150
  { category: "streaming:showinfo", description: "Channels DVR show name lookups, device mapping." },
138
151
  { category: "timing:browser", description: "Browser launch: process spawn, extension init, display detection." },
139
152
  { category: "timing:hls", description: "HLS playlist delivery time." },
153
+ { category: "timing:native", description: "Native streaming: interception latency, probe latency, first segment, refresh duration." },
140
154
  { category: "timing:recovery", description: "Recovery totals: navigation recovery, tab replacement." },
141
155
  { category: "timing:startup", description: "Stream startup: init segment, first playlist, capture ready." },
142
156
  { category: "timing:tab", description: "Tab replacement: old tab cleanup, new page creation." },
@@ -147,6 +161,7 @@ export const DEBUG_CATEGORIES = [
147
161
  { category: "tuning:sling", description: "Sling TV guide grid: binary search, cache, click retries." },
148
162
  { category: "tuning:spectrum", description: "Spectrum TV guide grid: channel discovery, cache, direct URL navigation." },
149
163
  { category: "tuning:tileClick", description: "Tile click strategy: scroll phase, matchSelector poll, play button retries, modal dismiss." },
164
+ { category: "tuning:xfinity", description: "Xfinity Stream: channelmap API interception, SPA channel switching, Watch Now modal dismissal." },
150
165
  { category: "tuning:yttv", description: "YouTube TV EPG grid navigation." }
151
166
  ];
152
167
  /**
@@ -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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"}
@@ -1,3 +1,19 @@
1
+ /**
2
+ * A cancellable timeout that can be used with Promise.race. The promise resolves to false when the timeout fires, and cancel() clears the timer to prevent it
3
+ * from holding an event loop reference after the race is won by another promise.
4
+ */
5
+ export interface CancellableTimeout {
6
+ cancel: () => void;
7
+ promise: Promise<false>;
8
+ }
9
+ /**
10
+ * Creates a cancellable timeout for use with Promise.race. Returns a promise that resolves to false after the specified delay, and a cancel function that clears
11
+ * the timer. This avoids the split declaration-then-assignment pattern that requires definite assignment assertions when managing timer IDs alongside
12
+ * Promise.race.
13
+ * @param ms - The timeout duration in milliseconds.
14
+ * @returns An object with the timeout promise and a cancel function.
15
+ */
16
+ export declare function cancellableTimeout(ms: number): CancellableTimeout;
1
17
  /**
2
18
  * Creates a promise that resolves after the specified delay. This is a convenience wrapper around setTimeout that allows using async/await syntax for delays.
3
19
  * @param ms - The delay duration in milliseconds.
@@ -1,7 +1,21 @@
1
1
  /* Copyright(C) 2024-2026, HJD (https://github.com/hjdhjd). All rights reserved.
2
2
  *
3
- * delay.ts: Async delay utility for PrismCast.
3
+ * delay.ts: Async delay and timeout utilities for PrismCast.
4
4
  */
5
+ /**
6
+ * Creates a cancellable timeout for use with Promise.race. Returns a promise that resolves to false after the specified delay, and a cancel function that clears
7
+ * the timer. This avoids the split declaration-then-assignment pattern that requires definite assignment assertions when managing timer IDs alongside
8
+ * Promise.race.
9
+ * @param ms - The timeout duration in milliseconds.
10
+ * @returns An object with the timeout promise and a cancel function.
11
+ */
12
+ export function cancellableTimeout(ms) {
13
+ let timer;
14
+ const promise = new Promise((resolve) => {
15
+ timer = setTimeout(() => { resolve(false); }, ms);
16
+ });
17
+ return { cancel: () => { clearTimeout(timer); }, promise };
18
+ }
5
19
  /**
6
20
  * Creates a promise that resolves after the specified delay. This is a convenience wrapper around setTimeout that allows using async/await syntax for delays.
7
21
  * @param ms - The delay duration in milliseconds.
@@ -1 +1 @@
1
- {"version":3,"file":"delay.js","sourceRoot":"","sources":["../../src/utils/delay.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,EAAU;IAEpC,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAEnC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"delay.js","sourceRoot":"","sources":["../../src/utils/delay.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAYH;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,EAAU;IAE3C,IAAI,KAAoC,CAAC;IAEzC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAQ,CAAC,OAAO,EAAE,EAAE;QAE7C,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,MAAM,EAAE,GAAS,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;AACnE,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,EAAU;IAEpC,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAEnC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -45,13 +45,7 @@ export declare function spawnFFmpeg(audioBitrate: number, onError: (error: Error
45
45
  * - `-probesize 16384`: Limit input probing to 16KB (fMP4 init segment is ~1.3KB) to minimize startup delay
46
46
  * - `-f mp4 -i pipe:0`: Read fragmented MP4 from stdin
47
47
  * - `-c copy`: Copy both video and audio codecs without transcoding
48
- * - `-f mpegts`: Output MPEG-TS container format
49
- * - `-mpegts_pmt_start_pid 0x0020`: Use ATSC-conventional PMT PID range instead of FFmpeg's default (0x1000). Minimum allowed value is 0x0020 (32).
50
- * - `-mpegts_start_pid 0x0031`: Use ATSC-conventional elementary stream PIDs instead of FFmpeg's defaults (0x100+)
51
- * - `-mpegts_service_type digital_tv`: Label the service as digital TV in the PMT service descriptor
52
- * - `-pat_period 0.1`: Repeat PAT/PMT tables every 100ms, matching ATSC broadcast frequency
53
- * - `-pcr_period 40`: Insert PCR timestamps every 40ms, matching ATSC broadcast convention
54
- * - `-flush_packets 1`: Flush output immediately after each packet to minimize latency
48
+ * - MPEG-TS output flags: See MPEGTS_OUTPUT_FLAGS for ATSC-conventional settings
55
49
  * - `pipe:1`: Write output to stdout
56
50
  * @param onError - Callback invoked when FFmpeg exits unexpectedly or encounters an error.
57
51
  * @param streamId - Optional stream identifier for logging.
@@ -102,98 +102,75 @@ export async function resolveFFmpegPath() {
102
102
  cachedFFmpegPath = undefined;
103
103
  return undefined;
104
104
  }
105
+ // MPEG-TS Output Flags.
106
+ /* MPEG-TS muxer flags for the fMP4-to-MPEG-TS remuxer. Tuned to produce output resembling a real HDHomeRun CONNECT DUO (HDTC-2US) ATSC transport stream.
107
+ * Plex's transcoder may make assumptions about stream structure based on the reported device model (PID assignments, PAT/PMT frequency). Using ATSC-conventional
108
+ * values avoids "Invalid argument" failures when Plex tries to transcode the live session for remote clients. These are pure container metadata changes — the actual
109
+ * A/V data is untouched by -c copy.
110
+ */
111
+ const MPEGTS_OUTPUT_FLAGS = [
112
+ "-f", "mpegts",
113
+ "-mpegts_pmt_start_pid", "0x0020",
114
+ "-mpegts_start_pid", "0x0031",
115
+ "-mpegts_service_type", "digital_tv",
116
+ "-pat_period", "0.1",
117
+ "-pcr_period", "40",
118
+ "-flush_packets", "1"
119
+ ];
120
+ // Internal Helper.
105
121
  /**
106
- * Spawns an FFmpeg process configured to transcode WebM (H264+Opus) to fMP4 (H264+AAC). The process reads from stdin and writes to stdout, allowing it to be
107
- * integrated into a Node.js stream pipeline. Video is passed through unchanged; audio is transcoded from Opus to AAC for HLS compatibility.
108
- *
109
- * FFmpeg arguments:
110
- * - `-hide_banner -loglevel warning`: Reduce noise, only show warnings/errors
111
- * - `-probesize 16384`: Limit input probing to 16KB (Chrome's WebM header fits well under this) to minimize startup delay
112
- * - `-i pipe:0`: Read input from stdin
113
- * - `-c:v copy`: Copy video stream without re-encoding (H264 passthrough)
114
- * - `-c:a aac -b:a <bitrate>`: Transcode audio to AAC at specified bitrate
115
- * - `-f mp4`: Output MP4 container format
116
- * - `-movflags frag_keyframe+empty_moov+default_base_moof`: Streaming-friendly fMP4 flags
117
- * - `-flush_packets 1`: Flush output immediately after each packet to minimize latency
118
- * - `pipe:1`: Write output to stdout
119
- * @param audioBitrate - Audio bitrate in bits per second (e.g., 256000 for 256 kbps).
120
- * @param onError - Callback invoked when FFmpeg exits unexpectedly or encounters an error.
121
- * @param streamId - Stream identifier for logging.
122
- * @param comment - Optional comment metadata (channel name or domain) to embed in the output.
122
+ * Spawns an FFmpeg process with shared lifecycle management. Handles stderr logging with noise filtering, exit and error callbacks, and graceful shutdown for all
123
+ * FFmpeg use cases. Callers provide the FFmpeg arguments and a descriptive label; this helper handles everything else.
124
+ * @param options.args - FFmpeg command-line arguments.
125
+ * @param options.label - Descriptive label for log messages and error strings (e.g., "FFmpeg", "MPEG-TS remuxer").
126
+ * @param options.onError - Callback invoked when FFmpeg exits unexpectedly or encounters an error.
127
+ * @param options.streamId - Optional stream identifier for log prefixing.
123
128
  * @returns FFmpeg process wrapper with stdin, stdout, and kill function.
124
129
  */
125
- export function spawnFFmpeg(audioBitrate, onError, streamId, comment) {
126
- // Use the cached FFmpeg path from resolveFFmpegPath(). This should always be set because isFFmpegAvailable() is called during startup, which populates the cache.
127
- // If somehow not set, fall back to "ffmpeg" and let spawn handle the error.
128
- const ffmpegPath = cachedFFmpegPath ?? "ffmpeg";
129
- // Use Apple's AudioToolbox AAC encoder on macOS for better quality and performance. Fall back to FFmpeg's built-in AAC encoder on other platforms.
130
- const aacEncoder = process.platform === "darwin" ? "aac_at" : "aac";
131
- const ffmpegArgs = [
132
- "-hide_banner",
133
- "-loglevel", "warning",
134
- "-probesize", "16384",
135
- "-i", "pipe:0",
136
- "-c:v", "copy",
137
- "-c:a", aacEncoder,
138
- "-b:a", String(audioBitrate),
139
- "-f", "mp4",
140
- "-movflags", "frag_keyframe+empty_moov+default_base_moof+skip_sidx+skip_trailer",
141
- "-flush_packets", "1"
142
- ];
143
- // Add metadata comment if provided. This embeds "PrismCast - <channel>" in the output for identification.
144
- if (comment) {
145
- ffmpegArgs.push("-metadata", "comment=PrismCast - " + comment);
146
- }
147
- ffmpegArgs.push("pipe:1");
148
- const ffmpeg = spawn(ffmpegPath, ffmpegArgs, {
149
- stdio: ["pipe", "pipe", "pipe"]
150
- });
130
+ function spawnFFmpegProcess({ args, label, onError, streamId }) {
131
+ const ffmpegBin = cachedFFmpegPath ?? "ffmpeg";
132
+ const ffmpeg = spawn(ffmpegBin, args, { stdio: ["pipe", "pipe", "pipe"] });
151
133
  const logPrefix = streamId ? "[" + streamId + "] " : "";
152
134
  // Track whether graceful shutdown has been initiated. When true, we suppress error callbacks because any exit (whether from SIGTERM or stdin close) is expected.
153
135
  let shuttingDown = false;
154
- // Log FFmpeg stderr output (warnings and errors). stderr is guaranteed to be a Readable since we set stdio: ["pipe", "pipe", "pipe"].
136
+ // Log FFmpeg stderr output (warnings and errors). Suppress noise patterns that are not actionable.
155
137
  ffmpeg.stderr.on("data", (data) => {
156
- // Suppress warnings during shutdown - truncated input warnings are expected when the capture stream closes.
157
138
  if (shuttingDown) {
158
139
  return;
159
140
  }
160
141
  const message = data.toString().trim();
161
- // Filter out common noise that isn't actionable.
162
142
  const noisePatterns = ["Press [q] to stop", "frame=", "size=", "time=", "bitrate=", "speed="];
163
143
  if (noisePatterns.some((pattern) => message.includes(pattern))) {
164
144
  return;
165
145
  }
166
146
  if (message.length > 0) {
167
- LOG.debug("streaming:ffmpeg", "%sFFmpeg: %s", logPrefix, message);
147
+ LOG.debug("streaming:ffmpeg", "%s%s: %s", logPrefix, label, message);
168
148
  }
169
149
  });
170
150
  // Handle FFmpeg process exit.
171
151
  ffmpeg.on("exit", (code, signal) => {
172
- // During graceful shutdown, any exit is expected (whether from SIGTERM or stdin closing).
173
152
  if (shuttingDown) {
174
153
  return;
175
154
  }
176
155
  if (signal === "SIGTERM") {
177
- // Normal termination via kill() - don't treat as error.
156
+ // Normal termination via kill() — do not treat as error.
178
157
  return;
179
158
  }
180
159
  if ((code !== null) && (code !== 0)) {
181
- onError(new Error("FFmpeg exited with code " + String(code) + "."));
160
+ onError(new Error(label + " exited with code " + String(code) + "."));
182
161
  }
183
162
  else if (signal) {
184
- onError(new Error("FFmpeg killed by signal " + signal + "."));
163
+ onError(new Error(label + " killed by signal " + signal + "."));
185
164
  }
186
165
  });
187
166
  // Handle spawn errors (e.g., FFmpeg not found).
188
167
  ffmpeg.on("error", (error) => {
189
- // During graceful shutdown, suppress errors from stdin pipe closing.
190
168
  if (shuttingDown) {
191
169
  return;
192
170
  }
193
171
  onError(error);
194
172
  });
195
- // Kill function for graceful shutdown. Sets the shuttingDown flag before sending SIGTERM so that any exit (whether from SIGTERM or stdin closing due to capture
196
- // stream ending) is treated as normal termination.
173
+ // Kill function for graceful shutdown. Sets the shuttingDown flag before sending SIGTERM so that any exit is treated as normal termination.
197
174
  const kill = () => {
198
175
  shuttingDown = true;
199
176
  if (!ffmpeg.killed) {
@@ -207,6 +184,54 @@ export function spawnFFmpeg(audioBitrate, onError, streamId, comment) {
207
184
  stdout: ffmpeg.stdout
208
185
  };
209
186
  }
187
+ // Public FFmpeg Spawners.
188
+ /**
189
+ * Spawns an FFmpeg process configured to transcode WebM (H264+Opus) to fMP4 (H264+AAC). The process reads from stdin and writes to stdout, allowing it to be
190
+ * integrated into a Node.js stream pipeline. Video is passed through unchanged; audio is transcoded from Opus to AAC for HLS compatibility.
191
+ *
192
+ * FFmpeg arguments:
193
+ * - `-hide_banner -loglevel warning`: Reduce noise, only show warnings/errors
194
+ * - `-probesize 16384`: Limit input probing to 16KB (Chrome's WebM header fits well under this) to minimize startup delay
195
+ * - `-i pipe:0`: Read input from stdin
196
+ * - `-c:v copy`: Copy video stream without re-encoding (H264 passthrough)
197
+ * - `-c:a aac -b:a <bitrate>`: Transcode audio to AAC at specified bitrate
198
+ * - `-f mp4`: Output MP4 container format
199
+ * - `-movflags frag_keyframe+empty_moov+default_base_moof`: Streaming-friendly fMP4 flags
200
+ * - `-flush_packets 1`: Flush output immediately after each packet to minimize latency
201
+ * - `pipe:1`: Write output to stdout
202
+ * @param audioBitrate - Audio bitrate in bits per second (e.g., 256000 for 256 kbps).
203
+ * @param onError - Callback invoked when FFmpeg exits unexpectedly or encounters an error.
204
+ * @param streamId - Stream identifier for logging.
205
+ * @param comment - Optional comment metadata (channel name or domain) to embed in the output.
206
+ * @returns FFmpeg process wrapper with stdin, stdout, and kill function.
207
+ */
208
+ export function spawnFFmpeg(audioBitrate, onError, streamId, comment) {
209
+ // Use Apple's AudioToolbox AAC encoder on macOS for better quality and performance. Fall back to FFmpeg's built-in AAC encoder on other platforms.
210
+ const aacEncoder = process.platform === "darwin" ? "aac_at" : "aac";
211
+ const ffmpegArgs = [
212
+ "-hide_banner",
213
+ "-loglevel", "warning",
214
+ "-probesize", "16384",
215
+ "-i", "pipe:0",
216
+ "-c:v", "copy",
217
+ "-c:a", aacEncoder,
218
+ "-b:a", String(audioBitrate),
219
+ "-f", "mp4",
220
+ "-movflags", "frag_keyframe+empty_moov+default_base_moof+skip_sidx+skip_trailer",
221
+ "-flush_packets", "1"
222
+ ];
223
+ // Add metadata comment if provided. This embeds "PrismCast - <channel>" in the output for identification.
224
+ if (comment) {
225
+ ffmpegArgs.push("-metadata", "comment=PrismCast - " + comment);
226
+ }
227
+ ffmpegArgs.push("pipe:1");
228
+ return spawnFFmpegProcess({
229
+ args: ffmpegArgs,
230
+ label: "FFmpeg",
231
+ onError,
232
+ streamId
233
+ });
234
+ }
210
235
  /**
211
236
  * Spawns an FFmpeg process configured to remux fMP4 input to MPEG-TS output with codec copy. The process reads a continuous fMP4 stream (init segment followed by
212
237
  * media segments) from stdin and writes MPEG-TS to stdout. No transcoding occurs — both video (H264) and audio (AAC) are copied unchanged — so CPU usage is minimal.
@@ -216,24 +241,13 @@ export function spawnFFmpeg(audioBitrate, onError, streamId, comment) {
216
241
  * - `-probesize 16384`: Limit input probing to 16KB (fMP4 init segment is ~1.3KB) to minimize startup delay
217
242
  * - `-f mp4 -i pipe:0`: Read fragmented MP4 from stdin
218
243
  * - `-c copy`: Copy both video and audio codecs without transcoding
219
- * - `-f mpegts`: Output MPEG-TS container format
220
- * - `-mpegts_pmt_start_pid 0x0020`: Use ATSC-conventional PMT PID range instead of FFmpeg's default (0x1000). Minimum allowed value is 0x0020 (32).
221
- * - `-mpegts_start_pid 0x0031`: Use ATSC-conventional elementary stream PIDs instead of FFmpeg's defaults (0x100+)
222
- * - `-mpegts_service_type digital_tv`: Label the service as digital TV in the PMT service descriptor
223
- * - `-pat_period 0.1`: Repeat PAT/PMT tables every 100ms, matching ATSC broadcast frequency
224
- * - `-pcr_period 40`: Insert PCR timestamps every 40ms, matching ATSC broadcast convention
225
- * - `-flush_packets 1`: Flush output immediately after each packet to minimize latency
244
+ * - MPEG-TS output flags: See MPEGTS_OUTPUT_FLAGS for ATSC-conventional settings
226
245
  * - `pipe:1`: Write output to stdout
227
246
  * @param onError - Callback invoked when FFmpeg exits unexpectedly or encounters an error.
228
247
  * @param streamId - Optional stream identifier for logging.
229
248
  * @returns FFmpeg process wrapper with stdin, stdout, and kill function.
230
249
  */
231
250
  export function spawnMpegTsRemuxer(onError, streamId) {
232
- const ffmpegBin = cachedFFmpegPath ?? "ffmpeg";
233
- // MPEG-TS muxer flags are tuned to produce output resembling a real HDHomeRun CONNECT DUO (HDTC-2US) ATSC transport stream. Plex's transcoder may make
234
- // assumptions about stream structure based on the reported device model (PID assignments, PAT/PMT frequency). Using ATSC-conventional values avoids "Invalid
235
- // argument" failures when Plex tries to transcode the live session for remote clients. These are pure container metadata changes — the actual A/V data is
236
- // untouched by -c copy.
237
251
  const ffmpegArgs = [
238
252
  "-hide_banner",
239
253
  "-loglevel", "warning",
@@ -241,70 +255,15 @@ export function spawnMpegTsRemuxer(onError, streamId) {
241
255
  "-f", "mp4",
242
256
  "-i", "pipe:0",
243
257
  "-c", "copy",
244
- "-f", "mpegts",
245
- "-mpegts_pmt_start_pid", "0x0020",
246
- "-mpegts_start_pid", "0x0031",
247
- "-mpegts_service_type", "digital_tv",
248
- "-pat_period", "0.1",
249
- "-pcr_period", "40",
250
- "-flush_packets", "1",
258
+ ...MPEGTS_OUTPUT_FLAGS,
251
259
  "pipe:1"
252
260
  ];
253
- const ffmpeg = spawn(ffmpegBin, ffmpegArgs, {
254
- stdio: ["pipe", "pipe", "pipe"]
255
- });
256
- const logPrefix = streamId ? "[" + streamId + "] " : "";
257
- // Track whether graceful shutdown has been initiated. When true, we suppress error callbacks because any exit is expected.
258
- let shuttingDown = false;
259
- // Log FFmpeg stderr output (warnings and errors).
260
- ffmpeg.stderr.on("data", (data) => {
261
- if (shuttingDown) {
262
- return;
263
- }
264
- const message = data.toString().trim();
265
- const noisePatterns = ["Press [q] to stop", "frame=", "size=", "time=", "bitrate=", "speed="];
266
- if (noisePatterns.some((pattern) => message.includes(pattern))) {
267
- return;
268
- }
269
- if (message.length > 0) {
270
- LOG.debug("streaming:ffmpeg", "%sMPEG-TS remuxer: %s", logPrefix, message);
271
- }
261
+ return spawnFFmpegProcess({
262
+ args: ffmpegArgs,
263
+ label: "MPEG-TS remuxer",
264
+ onError,
265
+ streamId
272
266
  });
273
- // Handle FFmpeg process exit.
274
- ffmpeg.on("exit", (code, signal) => {
275
- if (shuttingDown) {
276
- return;
277
- }
278
- if (signal === "SIGTERM") {
279
- return;
280
- }
281
- if ((code !== null) && (code !== 0)) {
282
- onError(new Error("MPEG-TS remuxer exited with code " + String(code) + "."));
283
- }
284
- else if (signal) {
285
- onError(new Error("MPEG-TS remuxer killed by signal " + signal + "."));
286
- }
287
- });
288
- // Handle spawn errors (e.g., FFmpeg not found).
289
- ffmpeg.on("error", (error) => {
290
- if (shuttingDown) {
291
- return;
292
- }
293
- onError(error);
294
- });
295
- // Kill function for graceful shutdown.
296
- const kill = () => {
297
- shuttingDown = true;
298
- if (!ffmpeg.killed) {
299
- ffmpeg.kill("SIGTERM");
300
- }
301
- };
302
- return {
303
- kill,
304
- process: ffmpeg,
305
- stdin: ffmpeg.stdin,
306
- stdout: ffmpeg.stdout
307
- };
308
267
  }
309
268
  /**
310
269
  * Checks if FFmpeg is available on the system. This resolves the FFmpeg path and caches it for use by spawnFFmpeg().