@livepeer-frameworks/player-core 0.1.0 → 0.1.2

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 (294) hide show
  1. package/README.md +11 -9
  2. package/dist/cjs/core/ABRController.js +456 -0
  3. package/dist/cjs/core/ABRController.js.map +1 -0
  4. package/dist/cjs/core/CodecUtils.js +195 -0
  5. package/dist/cjs/core/CodecUtils.js.map +1 -0
  6. package/dist/cjs/core/ErrorClassifier.js +410 -0
  7. package/dist/cjs/core/ErrorClassifier.js.map +1 -0
  8. package/dist/cjs/core/EventEmitter.js +108 -0
  9. package/dist/cjs/core/EventEmitter.js.map +1 -0
  10. package/dist/cjs/core/GatewayClient.js +342 -0
  11. package/dist/cjs/core/GatewayClient.js.map +1 -0
  12. package/dist/cjs/core/InteractionController.js +606 -0
  13. package/dist/cjs/core/InteractionController.js.map +1 -0
  14. package/dist/cjs/core/LiveDurationProxy.js +186 -0
  15. package/dist/cjs/core/LiveDurationProxy.js.map +1 -0
  16. package/dist/cjs/core/MetaTrackManager.js +624 -0
  17. package/dist/cjs/core/MetaTrackManager.js.map +1 -0
  18. package/dist/cjs/core/MistReporter.js +449 -0
  19. package/dist/cjs/core/MistReporter.js.map +1 -0
  20. package/dist/cjs/core/MistSignaling.js +264 -0
  21. package/dist/cjs/core/MistSignaling.js.map +1 -0
  22. package/dist/cjs/core/PlayerController.js +2658 -0
  23. package/dist/cjs/core/PlayerController.js.map +1 -0
  24. package/dist/cjs/core/PlayerInterface.js +269 -0
  25. package/dist/cjs/core/PlayerInterface.js.map +1 -0
  26. package/dist/cjs/core/PlayerManager.js +806 -0
  27. package/dist/cjs/core/PlayerManager.js.map +1 -0
  28. package/dist/cjs/core/PlayerRegistry.js +270 -0
  29. package/dist/cjs/core/PlayerRegistry.js.map +1 -0
  30. package/dist/cjs/core/QualityMonitor.js +474 -0
  31. package/dist/cjs/core/QualityMonitor.js.map +1 -0
  32. package/dist/cjs/core/SeekingUtils.js +292 -0
  33. package/dist/cjs/core/SeekingUtils.js.map +1 -0
  34. package/dist/cjs/core/StreamStateClient.js +381 -0
  35. package/dist/cjs/core/StreamStateClient.js.map +1 -0
  36. package/dist/cjs/core/SubtitleManager.js +227 -0
  37. package/dist/cjs/core/SubtitleManager.js.map +1 -0
  38. package/dist/cjs/core/TelemetryReporter.js +258 -0
  39. package/dist/cjs/core/TelemetryReporter.js.map +1 -0
  40. package/dist/cjs/core/TimeFormat.js +176 -0
  41. package/dist/cjs/core/TimeFormat.js.map +1 -0
  42. package/dist/cjs/core/TimerManager.js +176 -0
  43. package/dist/cjs/core/TimerManager.js.map +1 -0
  44. package/dist/cjs/core/UrlUtils.js +160 -0
  45. package/dist/cjs/core/UrlUtils.js.map +1 -0
  46. package/dist/cjs/core/detector.js +293 -0
  47. package/dist/cjs/core/detector.js.map +1 -0
  48. package/dist/cjs/core/scorer.js +443 -0
  49. package/dist/cjs/core/scorer.js.map +1 -0
  50. package/dist/cjs/index.js +121 -20134
  51. package/dist/cjs/index.js.map +1 -1
  52. package/dist/cjs/lib/utils.js +11 -0
  53. package/dist/cjs/lib/utils.js.map +1 -0
  54. package/dist/cjs/node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.js +6 -0
  55. package/dist/cjs/node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.js.map +1 -0
  56. package/dist/cjs/node_modules/.pnpm/tailwind-merge@3.4.0/node_modules/tailwind-merge/dist/bundle-mjs.js +3042 -0
  57. package/dist/cjs/node_modules/.pnpm/tailwind-merge@3.4.0/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
  58. package/dist/cjs/players/DashJsPlayer.js +638 -0
  59. package/dist/cjs/players/DashJsPlayer.js.map +1 -0
  60. package/dist/cjs/players/HlsJsPlayer.js +482 -0
  61. package/dist/cjs/players/HlsJsPlayer.js.map +1 -0
  62. package/dist/cjs/players/MewsWsPlayer/SourceBufferManager.js +522 -0
  63. package/dist/cjs/players/MewsWsPlayer/SourceBufferManager.js.map +1 -0
  64. package/dist/cjs/players/MewsWsPlayer/WebSocketManager.js +215 -0
  65. package/dist/cjs/players/MewsWsPlayer/WebSocketManager.js.map +1 -0
  66. package/dist/cjs/players/MewsWsPlayer/index.js +987 -0
  67. package/dist/cjs/players/MewsWsPlayer/index.js.map +1 -0
  68. package/dist/cjs/players/MistPlayer.js +185 -0
  69. package/dist/cjs/players/MistPlayer.js.map +1 -0
  70. package/dist/cjs/players/MistWebRTCPlayer/index.js +635 -0
  71. package/dist/cjs/players/MistWebRTCPlayer/index.js.map +1 -0
  72. package/dist/cjs/players/NativePlayer.js +762 -0
  73. package/dist/cjs/players/NativePlayer.js.map +1 -0
  74. package/dist/cjs/players/VideoJsPlayer.js +585 -0
  75. package/dist/cjs/players/VideoJsPlayer.js.map +1 -0
  76. package/dist/cjs/players/WebCodecsPlayer/JitterBuffer.js +236 -0
  77. package/dist/cjs/players/WebCodecsPlayer/JitterBuffer.js.map +1 -0
  78. package/dist/cjs/players/WebCodecsPlayer/LatencyProfiles.js +143 -0
  79. package/dist/cjs/players/WebCodecsPlayer/LatencyProfiles.js.map +1 -0
  80. package/dist/cjs/players/WebCodecsPlayer/RawChunkParser.js +96 -0
  81. package/dist/cjs/players/WebCodecsPlayer/RawChunkParser.js.map +1 -0
  82. package/dist/cjs/players/WebCodecsPlayer/SyncController.js +359 -0
  83. package/dist/cjs/players/WebCodecsPlayer/SyncController.js.map +1 -0
  84. package/dist/cjs/players/WebCodecsPlayer/WebSocketController.js +460 -0
  85. package/dist/cjs/players/WebCodecsPlayer/WebSocketController.js.map +1 -0
  86. package/dist/cjs/players/WebCodecsPlayer/index.js +1467 -0
  87. package/dist/cjs/players/WebCodecsPlayer/index.js.map +1 -0
  88. package/dist/cjs/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.js +320 -0
  89. package/dist/cjs/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.js.map +1 -0
  90. package/dist/cjs/styles/index.js +57 -0
  91. package/dist/cjs/styles/index.js.map +1 -0
  92. package/dist/cjs/vanilla/FrameWorksPlayer.js +269 -0
  93. package/dist/cjs/vanilla/FrameWorksPlayer.js.map +1 -0
  94. package/dist/cjs/vanilla.js +11 -0
  95. package/dist/cjs/vanilla.js.map +1 -0
  96. package/dist/esm/core/ABRController.js +454 -0
  97. package/dist/esm/core/ABRController.js.map +1 -0
  98. package/dist/esm/core/CodecUtils.js +193 -0
  99. package/dist/esm/core/CodecUtils.js.map +1 -0
  100. package/dist/esm/core/ErrorClassifier.js +408 -0
  101. package/dist/esm/core/ErrorClassifier.js.map +1 -0
  102. package/dist/esm/core/EventEmitter.js +106 -0
  103. package/dist/esm/core/EventEmitter.js.map +1 -0
  104. package/dist/esm/core/GatewayClient.js +340 -0
  105. package/dist/esm/core/GatewayClient.js.map +1 -0
  106. package/dist/esm/core/InteractionController.js +604 -0
  107. package/dist/esm/core/InteractionController.js.map +1 -0
  108. package/dist/esm/core/LiveDurationProxy.js +184 -0
  109. package/dist/esm/core/LiveDurationProxy.js.map +1 -0
  110. package/dist/esm/core/MetaTrackManager.js +622 -0
  111. package/dist/esm/core/MetaTrackManager.js.map +1 -0
  112. package/dist/esm/core/MistReporter.js +447 -0
  113. package/dist/esm/core/MistReporter.js.map +1 -0
  114. package/dist/esm/core/MistSignaling.js +262 -0
  115. package/dist/esm/core/MistSignaling.js.map +1 -0
  116. package/dist/esm/core/PlayerController.js +2651 -0
  117. package/dist/esm/core/PlayerController.js.map +1 -0
  118. package/dist/esm/core/PlayerInterface.js +267 -0
  119. package/dist/esm/core/PlayerInterface.js.map +1 -0
  120. package/dist/esm/core/PlayerManager.js +804 -0
  121. package/dist/esm/core/PlayerManager.js.map +1 -0
  122. package/dist/esm/core/PlayerRegistry.js +264 -0
  123. package/dist/esm/core/PlayerRegistry.js.map +1 -0
  124. package/dist/esm/core/QualityMonitor.js +471 -0
  125. package/dist/esm/core/QualityMonitor.js.map +1 -0
  126. package/dist/esm/core/SeekingUtils.js +280 -0
  127. package/dist/esm/core/SeekingUtils.js.map +1 -0
  128. package/dist/esm/core/StreamStateClient.js +379 -0
  129. package/dist/esm/core/StreamStateClient.js.map +1 -0
  130. package/dist/esm/core/SubtitleManager.js +225 -0
  131. package/dist/esm/core/SubtitleManager.js.map +1 -0
  132. package/dist/esm/core/TelemetryReporter.js +256 -0
  133. package/dist/esm/core/TelemetryReporter.js.map +1 -0
  134. package/dist/esm/core/TimeFormat.js +169 -0
  135. package/dist/esm/core/TimeFormat.js.map +1 -0
  136. package/dist/esm/core/TimerManager.js +174 -0
  137. package/dist/esm/core/TimerManager.js.map +1 -0
  138. package/dist/esm/core/UrlUtils.js +151 -0
  139. package/dist/esm/core/UrlUtils.js.map +1 -0
  140. package/dist/esm/core/detector.js +279 -0
  141. package/dist/esm/core/detector.js.map +1 -0
  142. package/dist/esm/core/scorer.js +422 -0
  143. package/dist/esm/core/scorer.js.map +1 -0
  144. package/dist/esm/index.js +26 -20043
  145. package/dist/esm/index.js.map +1 -1
  146. package/dist/esm/lib/utils.js +9 -0
  147. package/dist/esm/lib/utils.js.map +1 -0
  148. package/dist/esm/node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.js +4 -0
  149. package/dist/esm/node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.js.map +1 -0
  150. package/dist/esm/node_modules/.pnpm/tailwind-merge@3.4.0/node_modules/tailwind-merge/dist/bundle-mjs.js +3036 -0
  151. package/dist/esm/node_modules/.pnpm/tailwind-merge@3.4.0/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
  152. package/dist/esm/players/DashJsPlayer.js +636 -0
  153. package/dist/esm/players/DashJsPlayer.js.map +1 -0
  154. package/dist/esm/players/HlsJsPlayer.js +480 -0
  155. package/dist/esm/players/HlsJsPlayer.js.map +1 -0
  156. package/dist/esm/players/MewsWsPlayer/SourceBufferManager.js +520 -0
  157. package/dist/esm/players/MewsWsPlayer/SourceBufferManager.js.map +1 -0
  158. package/dist/esm/players/MewsWsPlayer/WebSocketManager.js +213 -0
  159. package/dist/esm/players/MewsWsPlayer/WebSocketManager.js.map +1 -0
  160. package/dist/esm/players/MewsWsPlayer/index.js +985 -0
  161. package/dist/esm/players/MewsWsPlayer/index.js.map +1 -0
  162. package/dist/esm/players/MistPlayer.js +183 -0
  163. package/dist/esm/players/MistPlayer.js.map +1 -0
  164. package/dist/esm/players/MistWebRTCPlayer/index.js +633 -0
  165. package/dist/esm/players/MistWebRTCPlayer/index.js.map +1 -0
  166. package/dist/esm/players/NativePlayer.js +759 -0
  167. package/dist/esm/players/NativePlayer.js.map +1 -0
  168. package/dist/esm/players/VideoJsPlayer.js +583 -0
  169. package/dist/esm/players/VideoJsPlayer.js.map +1 -0
  170. package/dist/esm/players/WebCodecsPlayer/JitterBuffer.js +233 -0
  171. package/dist/esm/players/WebCodecsPlayer/JitterBuffer.js.map +1 -0
  172. package/dist/esm/players/WebCodecsPlayer/LatencyProfiles.js +134 -0
  173. package/dist/esm/players/WebCodecsPlayer/LatencyProfiles.js.map +1 -0
  174. package/dist/esm/players/WebCodecsPlayer/RawChunkParser.js +91 -0
  175. package/dist/esm/players/WebCodecsPlayer/RawChunkParser.js.map +1 -0
  176. package/dist/esm/players/WebCodecsPlayer/SyncController.js +357 -0
  177. package/dist/esm/players/WebCodecsPlayer/SyncController.js.map +1 -0
  178. package/dist/esm/players/WebCodecsPlayer/WebSocketController.js +458 -0
  179. package/dist/esm/players/WebCodecsPlayer/WebSocketController.js.map +1 -0
  180. package/dist/esm/players/WebCodecsPlayer/index.js +1458 -0
  181. package/dist/esm/players/WebCodecsPlayer/index.js.map +1 -0
  182. package/dist/esm/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.js +315 -0
  183. package/dist/esm/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.js.map +1 -0
  184. package/dist/esm/styles/index.js +54 -0
  185. package/dist/esm/styles/index.js.map +1 -0
  186. package/dist/esm/vanilla/FrameWorksPlayer.js +264 -0
  187. package/dist/esm/vanilla/FrameWorksPlayer.js.map +1 -0
  188. package/dist/esm/vanilla.js +2 -0
  189. package/dist/esm/vanilla.js.map +1 -0
  190. package/dist/player.css +185 -42
  191. package/dist/types/core/ABRController.d.ts +4 -4
  192. package/dist/types/core/CodecUtils.d.ts +1 -1
  193. package/dist/types/core/ErrorClassifier.d.ts +77 -0
  194. package/dist/types/core/GatewayClient.d.ts +4 -4
  195. package/dist/types/core/MetaTrackManager.d.ts +2 -2
  196. package/dist/types/core/MistReporter.d.ts +3 -3
  197. package/dist/types/core/MistSignaling.d.ts +12 -12
  198. package/dist/types/core/PlayerController.d.ts +19 -14
  199. package/dist/types/core/PlayerInterface.d.ts +100 -2
  200. package/dist/types/core/PlayerManager.d.ts +36 -9
  201. package/dist/types/core/PlayerRegistry.d.ts +11 -11
  202. package/dist/types/core/QualityMonitor.d.ts +2 -2
  203. package/dist/types/core/SeekingUtils.d.ts +2 -2
  204. package/dist/types/core/StreamStateClient.d.ts +2 -2
  205. package/dist/types/core/TelemetryReporter.d.ts +1 -1
  206. package/dist/types/core/TimerManager.d.ts +1 -1
  207. package/dist/types/core/detector.d.ts +1 -1
  208. package/dist/types/core/index.d.ts +44 -44
  209. package/dist/types/core/scorer.d.ts +1 -1
  210. package/dist/types/core/selector.d.ts +2 -2
  211. package/dist/types/index.d.ts +35 -34
  212. package/dist/types/players/DashJsPlayer.d.ts +3 -3
  213. package/dist/types/players/HlsJsPlayer.d.ts +3 -3
  214. package/dist/types/players/MewsWsPlayer/SourceBufferManager.d.ts +1 -1
  215. package/dist/types/players/MewsWsPlayer/WebSocketManager.d.ts +1 -1
  216. package/dist/types/players/MewsWsPlayer/index.d.ts +2 -2
  217. package/dist/types/players/MewsWsPlayer/types.d.ts +15 -15
  218. package/dist/types/players/MistPlayer.d.ts +2 -2
  219. package/dist/types/players/MistWebRTCPlayer/index.d.ts +3 -3
  220. package/dist/types/players/NativePlayer.d.ts +3 -3
  221. package/dist/types/players/VideoJsPlayer.d.ts +3 -3
  222. package/dist/types/players/WebCodecsPlayer/JitterBuffer.d.ts +3 -3
  223. package/dist/types/players/WebCodecsPlayer/LatencyProfiles.d.ts +1 -1
  224. package/dist/types/players/WebCodecsPlayer/RawChunkParser.d.ts +2 -2
  225. package/dist/types/players/WebCodecsPlayer/SyncController.d.ts +2 -2
  226. package/dist/types/players/WebCodecsPlayer/WebSocketController.d.ts +3 -3
  227. package/dist/types/players/WebCodecsPlayer/index.d.ts +9 -9
  228. package/dist/types/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.d.ts +1 -1
  229. package/dist/types/players/WebCodecsPlayer/types.d.ts +49 -49
  230. package/dist/types/players/WebCodecsPlayer/worker/types.d.ts +31 -31
  231. package/dist/types/players/index.d.ts +5 -8
  232. package/dist/types/types.d.ts +15 -15
  233. package/dist/types/vanilla/FrameWorksPlayer.d.ts +2 -2
  234. package/dist/types/vanilla/index.d.ts +4 -4
  235. package/dist/workers/decoder.worker.js +129 -122
  236. package/dist/workers/decoder.worker.js.map +1 -1
  237. package/package.json +31 -15
  238. package/src/core/ABRController.ts +38 -36
  239. package/src/core/CodecUtils.ts +49 -46
  240. package/src/core/Disposable.ts +4 -4
  241. package/src/core/ErrorClassifier.ts +499 -0
  242. package/src/core/EventEmitter.ts +1 -1
  243. package/src/core/GatewayClient.ts +41 -39
  244. package/src/core/InteractionController.ts +89 -82
  245. package/src/core/LiveDurationProxy.ts +14 -15
  246. package/src/core/MetaTrackManager.ts +73 -65
  247. package/src/core/MistReporter.ts +72 -45
  248. package/src/core/MistSignaling.ts +59 -56
  249. package/src/core/PlayerController.ts +542 -384
  250. package/src/core/PlayerInterface.ts +192 -59
  251. package/src/core/PlayerManager.ts +354 -164
  252. package/src/core/PlayerRegistry.ts +238 -87
  253. package/src/core/QualityMonitor.ts +38 -31
  254. package/src/core/ScreenWakeLockManager.ts +8 -9
  255. package/src/core/SeekingUtils.ts +31 -22
  256. package/src/core/StreamStateClient.ts +74 -68
  257. package/src/core/SubtitleManager.ts +24 -22
  258. package/src/core/TelemetryReporter.ts +38 -32
  259. package/src/core/TimeFormat.ts +13 -17
  260. package/src/core/TimerManager.ts +24 -8
  261. package/src/core/UrlUtils.ts +20 -17
  262. package/src/core/detector.ts +44 -44
  263. package/src/core/index.ts +57 -48
  264. package/src/core/scorer.ts +136 -141
  265. package/src/core/selector.ts +2 -6
  266. package/src/global.d.ts +1 -1
  267. package/src/index.ts +56 -36
  268. package/src/players/DashJsPlayer.ts +164 -115
  269. package/src/players/HlsJsPlayer.ts +132 -78
  270. package/src/players/MewsWsPlayer/SourceBufferManager.ts +41 -36
  271. package/src/players/MewsWsPlayer/WebSocketManager.ts +9 -9
  272. package/src/players/MewsWsPlayer/index.ts +192 -152
  273. package/src/players/MewsWsPlayer/types.ts +21 -21
  274. package/src/players/MistPlayer.ts +45 -26
  275. package/src/players/MistWebRTCPlayer/index.ts +175 -129
  276. package/src/players/NativePlayer.ts +203 -143
  277. package/src/players/VideoJsPlayer.ts +170 -118
  278. package/src/players/WebCodecsPlayer/JitterBuffer.ts +6 -7
  279. package/src/players/WebCodecsPlayer/LatencyProfiles.ts +43 -43
  280. package/src/players/WebCodecsPlayer/RawChunkParser.ts +10 -10
  281. package/src/players/WebCodecsPlayer/SyncController.ts +45 -53
  282. package/src/players/WebCodecsPlayer/WebSocketController.ts +66 -68
  283. package/src/players/WebCodecsPlayer/index.ts +265 -223
  284. package/src/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.ts +12 -17
  285. package/src/players/WebCodecsPlayer/types.ts +56 -56
  286. package/src/players/WebCodecsPlayer/worker/decoder.worker.ts +238 -182
  287. package/src/players/WebCodecsPlayer/worker/types.ts +31 -31
  288. package/src/players/index.ts +5 -16
  289. package/src/styles/animations.css +2 -1
  290. package/src/styles/player.css +185 -42
  291. package/src/styles/tailwind.css +473 -159
  292. package/src/types.ts +43 -43
  293. package/src/vanilla/FrameWorksPlayer.ts +26 -14
  294. package/src/vanilla/index.ts +4 -4
@@ -11,13 +11,13 @@
11
11
  * - Latency tier: Protocol-based classification affecting live detection thresholds
12
12
  */
13
13
 
14
- import type { MistStreamInfo, MistTrackInfo } from '../types';
14
+ import type { MistStreamInfo, MistTrackInfo } from "../types";
15
15
 
16
16
  // ============================================================================
17
17
  // Types
18
18
  // ============================================================================
19
19
 
20
- export type LatencyTier = 'ultra-low' | 'low' | 'medium' | 'high';
20
+ export type LatencyTier = "ultra-low" | "low" | "medium" | "high";
21
21
 
22
22
  export interface LiveThresholds {
23
23
  /** Seconds behind live edge to exit "LIVE" state (become clickable) */
@@ -66,13 +66,13 @@ export interface CanSeekParams {
66
66
  */
67
67
  export const LATENCY_TIERS: Record<LatencyTier, LiveThresholds> = {
68
68
  // WebRTC/WHEP: sub-second latency
69
- 'ultra-low': { exitLive: 2, enterLive: 0.5 },
69
+ "ultra-low": { exitLive: 2, enterLive: 0.5 },
70
70
  // MEWS (WebSocket MP4): 2-5s latency
71
- 'low': { exitLive: 5, enterLive: 1.5 },
71
+ low: { exitLive: 5, enterLive: 1.5 },
72
72
  // HLS/DASH: 10-30s latency (segment-based)
73
- 'medium': { exitLive: 15, enterLive: 5 },
73
+ medium: { exitLive: 15, enterLive: 5 },
74
74
  // Fallback for unknown protocols
75
- 'high': { exitLive: 30, enterLive: 10 },
75
+ high: { exitLive: 30, enterLive: 10 },
76
76
  };
77
77
 
78
78
  /**
@@ -97,30 +97,30 @@ export const DEFAULT_BUFFER_WINDOW_SEC = 60;
97
97
  * @returns Latency tier classification
98
98
  */
99
99
  export function getLatencyTier(sourceType?: string): LatencyTier {
100
- if (!sourceType) return 'medium';
100
+ if (!sourceType) return "medium";
101
101
  const t = sourceType.toLowerCase();
102
102
 
103
103
  // Ultra-low: WebRTC protocols (sub-second latency)
104
- if (t === 'whep' || t === 'webrtc' || t.includes('mist/webrtc')) {
105
- return 'ultra-low';
104
+ if (t === "whep" || t === "webrtc" || t.includes("mist/webrtc")) {
105
+ return "ultra-low";
106
106
  }
107
107
 
108
108
  // Low: WebSocket-based streaming (2-5s latency)
109
- if (t.startsWith('ws/') || t.startsWith('wss/')) {
110
- return 'low';
109
+ if (t.startsWith("ws/") || t.startsWith("wss/")) {
110
+ return "low";
111
111
  }
112
112
 
113
113
  // Medium: HLS/DASH (segment-based, 10-30s latency)
114
- if (t.includes('mpegurl') || t.includes('dash')) {
115
- return 'medium';
114
+ if (t.includes("mpegurl") || t.includes("dash")) {
115
+ return "medium";
116
116
  }
117
117
 
118
118
  // Progressive MP4/WebM - use medium defaults
119
- if (t.includes('video/mp4') || t.includes('video/webm')) {
120
- return 'medium';
119
+ if (t.includes("video/mp4") || t.includes("video/webm")) {
120
+ return "medium";
121
121
  }
122
122
 
123
- return 'medium';
123
+ return "medium";
124
124
  }
125
125
 
126
126
  /**
@@ -160,7 +160,14 @@ export function supportsPlaybackRate(video: HTMLVideoElement | null): boolean {
160
160
  * @returns Seekable range with start and live edge
161
161
  */
162
162
  export function calculateSeekableRange(params: SeekableRangeParams): SeekableRange {
163
- const { isLive, video, mistStreamInfo, currentTime, duration, allowMediaStreamDvr = false } = params;
163
+ const {
164
+ isLive,
165
+ video,
166
+ mistStreamInfo,
167
+ currentTime,
168
+ duration,
169
+ allowMediaStreamDvr = false,
170
+ } = params;
164
171
 
165
172
  // VOD: full duration is seekable
166
173
  if (!isLive) {
@@ -183,8 +190,10 @@ export function calculateSeekableRange(params: SeekableRangeParams): SeekableRan
183
190
  if ((allowMediaStreamDvr || !isMediaStream) && mistStreamInfo?.meta?.tracks) {
184
191
  const tracks = Object.values(mistStreamInfo.meta.tracks) as MistTrackInfo[];
185
192
  if (tracks.length > 0) {
186
- const firstmsValues = tracks.map(t => t.firstms).filter((v): v is number => v !== undefined);
187
- const lastmsValues = tracks.map(t => t.lastms).filter((v): v is number => v !== undefined);
193
+ const firstmsValues = tracks
194
+ .map((t) => t.firstms)
195
+ .filter((v): v is number => v !== undefined);
196
+ const lastmsValues = tracks.map((t) => t.lastms).filter((v): v is number => v !== undefined);
188
197
 
189
198
  if (firstmsValues.length > 0 && lastmsValues.length > 0) {
190
199
  const firstms = Math.max(...firstmsValues);
@@ -273,11 +282,11 @@ export function calculateLiveThresholds(
273
282
  bufferWindowMs?: number
274
283
  ): LiveThresholds {
275
284
  // Determine tier from source type, or use ultra-low for WebRTC
276
- const tier = sourceType ? getLatencyTier(sourceType) : (isWebRTC ? 'ultra-low' : 'medium');
285
+ const tier = sourceType ? getLatencyTier(sourceType) : isWebRTC ? "ultra-low" : "medium";
277
286
  const tierThresholds = LATENCY_TIERS[tier];
278
287
 
279
288
  // For medium/high tiers, scale thresholds based on buffer_window
280
- if ((tier === 'medium' || tier === 'high') && bufferWindowMs && bufferWindowMs > 0) {
289
+ if ((tier === "medium" || tier === "high") && bufferWindowMs && bufferWindowMs > 0) {
281
290
  const bufferWindowSec = bufferWindowMs / 1000;
282
291
  // Scale thresholds proportionally to buffer, with reasonable bounds
283
292
  return {
@@ -356,7 +365,7 @@ export function isLiveContent(
356
365
 
357
366
  // MistServer type
358
367
  if (mistStreamInfo?.type) {
359
- return mistStreamInfo.type === 'live';
368
+ return mistStreamInfo.type === "live";
360
369
  }
361
370
 
362
371
  // Fallback: non-finite duration indicates live
@@ -5,9 +5,9 @@
5
5
  * Extracted from useStreamState.ts for use in headless core.
6
6
  */
7
7
 
8
- import { TypedEventEmitter } from './EventEmitter';
9
- import { TimerManager } from './TimerManager';
10
- import type { StreamState, StreamStatus, MistStreamInfo } from '../types';
8
+ import { TypedEventEmitter } from "./EventEmitter";
9
+ import { TimerManager } from "./TimerManager";
10
+ import type { StreamState, StreamStatus, MistStreamInfo } from "../types";
11
11
 
12
12
  // ============================================================================
13
13
  // Types
@@ -26,7 +26,7 @@ export interface StreamStateClientConfig {
26
26
 
27
27
  type StreamStateClientResolvedConfig = Omit<
28
28
  StreamStateClientConfig,
29
- 'pollInterval' | 'useWebSocket'
29
+ "pollInterval" | "useWebSocket"
30
30
  > & {
31
31
  pollInterval: number;
32
32
  useWebSocket: boolean;
@@ -50,9 +50,9 @@ export interface StreamStateClientEvents {
50
50
  const DEFAULT_POLL_INTERVAL = 3000;
51
51
 
52
52
  const initialState: StreamState = {
53
- status: 'OFFLINE',
53
+ status: "OFFLINE",
54
54
  isOnline: false,
55
- message: 'Connecting...',
55
+ message: "Connecting...",
56
56
  lastUpdate: 0,
57
57
  };
58
58
 
@@ -66,14 +66,14 @@ const initialState: StreamState = {
66
66
  function parseErrorToStatus(error: string): StreamStatus {
67
67
  const lowerError = error.toLowerCase();
68
68
 
69
- if (lowerError.includes('offline')) return 'OFFLINE';
70
- if (lowerError.includes('initializing')) return 'INITIALIZING';
71
- if (lowerError.includes('booting')) return 'BOOTING';
72
- if (lowerError.includes('waiting for data')) return 'WAITING_FOR_DATA';
73
- if (lowerError.includes('shutting down')) return 'SHUTTING_DOWN';
74
- if (lowerError.includes('invalid')) return 'INVALID';
69
+ if (lowerError.includes("offline")) return "OFFLINE";
70
+ if (lowerError.includes("initializing")) return "INITIALIZING";
71
+ if (lowerError.includes("booting")) return "BOOTING";
72
+ if (lowerError.includes("waiting for data")) return "WAITING_FOR_DATA";
73
+ if (lowerError.includes("shutting down")) return "SHUTTING_DOWN";
74
+ if (lowerError.includes("invalid")) return "INVALID";
75
75
 
76
- return 'ERROR';
76
+ return "ERROR";
77
77
  }
78
78
 
79
79
  /**
@@ -81,25 +81,25 @@ function parseErrorToStatus(error: string): StreamStatus {
81
81
  */
82
82
  function getStatusMessage(status: StreamStatus, percentage?: number): string {
83
83
  switch (status) {
84
- case 'ONLINE':
85
- return 'Stream is online';
86
- case 'OFFLINE':
87
- return 'Stream is offline';
88
- case 'INITIALIZING':
84
+ case "ONLINE":
85
+ return "Stream is online";
86
+ case "OFFLINE":
87
+ return "Stream is offline";
88
+ case "INITIALIZING":
89
89
  return percentage !== undefined
90
90
  ? `Initializing... ${Math.round(percentage * 10) / 10}%`
91
- : 'Stream is initializing';
92
- case 'BOOTING':
93
- return 'Stream is starting up';
94
- case 'WAITING_FOR_DATA':
95
- return 'Waiting for stream data';
96
- case 'SHUTTING_DOWN':
97
- return 'Stream is shutting down';
98
- case 'INVALID':
99
- return 'Stream status is invalid';
100
- case 'ERROR':
91
+ : "Stream is initializing";
92
+ case "BOOTING":
93
+ return "Stream is starting up";
94
+ case "WAITING_FOR_DATA":
95
+ return "Waiting for stream data";
96
+ case "SHUTTING_DOWN":
97
+ return "Stream is shutting down";
98
+ case "INVALID":
99
+ return "Stream status is invalid";
100
+ case "ERROR":
101
101
  default:
102
- return 'Stream error';
102
+ return "Stream error";
103
103
  }
104
104
  }
105
105
 
@@ -161,14 +161,14 @@ export class StreamStateClient extends TypedEventEmitter<StreamStateClientEvents
161
161
  const { mistBaseUrl, streamName, useWebSocket } = this.config;
162
162
 
163
163
  if (!mistBaseUrl || !streamName) {
164
- console.warn('[StreamStateClient] Missing mistBaseUrl or streamName');
164
+ console.warn("[StreamStateClient] Missing mistBaseUrl or streamName");
165
165
  return;
166
166
  }
167
167
 
168
168
  // Reset state
169
169
  this.setState({
170
170
  ...initialState,
171
- message: 'Connecting...',
171
+ message: "Connecting...",
172
172
  lastUpdate: Date.now(),
173
173
  });
174
174
 
@@ -176,21 +176,25 @@ export class StreamStateClient extends TypedEventEmitter<StreamStateClientEvents
176
176
  const currentConnectionId = ++this.connectionId;
177
177
 
178
178
  // Debounce connection to prevent rapid reconnects during mount/unmount cycles
179
- this.timers.start(() => {
180
- // Check if this connection attempt is still valid
181
- if (!this.isRunning || this.connectionId !== currentConnectionId) {
182
- return;
183
- }
184
-
185
- // Always do initial HTTP poll to get full data (including sources)
186
- // Then connect WebSocket for real-time updates
187
- this.pollHttp().then(() => {
188
- // Verify still valid before WebSocket connection
189
- if (useWebSocket && this.isRunning && this.connectionId === currentConnectionId) {
190
- this.connectWebSocket();
179
+ this.timers.start(
180
+ () => {
181
+ // Check if this connection attempt is still valid
182
+ if (!this.isRunning || this.connectionId !== currentConnectionId) {
183
+ return;
191
184
  }
192
- });
193
- }, StreamStateClient.CONNECTION_DEBOUNCE_MS, 'connect');
185
+
186
+ // Always do initial HTTP poll to get full data (including sources)
187
+ // Then connect WebSocket for real-time updates
188
+ this.pollHttp().then(() => {
189
+ // Verify still valid before WebSocket connection
190
+ if (useWebSocket && this.isRunning && this.connectionId === currentConnectionId) {
191
+ this.connectWebSocket();
192
+ }
193
+ });
194
+ },
195
+ StreamStateClient.CONNECTION_DEBOUNCE_MS,
196
+ "connect"
197
+ );
194
198
  }
195
199
 
196
200
  /**
@@ -287,15 +291,17 @@ export class StreamStateClient extends TypedEventEmitter<StreamStateClientEvents
287
291
  try {
288
292
  // Convert http(s) to ws(s)
289
293
  const wsUrl = mistBaseUrl
290
- .replace(/^http:/, 'ws:')
291
- .replace(/^https:/, 'wss:')
292
- .replace(/\/$/, '');
294
+ .replace(/^http:/, "ws:")
295
+ .replace(/^https:/, "wss:")
296
+ .replace(/\/$/, "");
293
297
 
294
- const ws = new WebSocket(`${wsUrl}/json_${encodeURIComponent(streamName)}.js?metaeverywhere=1&inclzero=1`);
298
+ const ws = new WebSocket(
299
+ `${wsUrl}/json_${encodeURIComponent(streamName)}.js?metaeverywhere=1&inclzero=1`
300
+ );
295
301
  this.ws = ws;
296
302
 
297
303
  ws.onopen = () => {
298
- console.debug('[StreamStateClient] WebSocket connected');
304
+ console.debug("[StreamStateClient] WebSocket connected");
299
305
  };
300
306
 
301
307
  ws.onmessage = (event) => {
@@ -303,12 +309,12 @@ export class StreamStateClient extends TypedEventEmitter<StreamStateClientEvents
303
309
  const data = JSON.parse(event.data) as MistStreamInfo;
304
310
  this.processStreamInfo(data);
305
311
  } catch (e) {
306
- console.warn('[StreamStateClient] Failed to parse WebSocket message:', e);
312
+ console.warn("[StreamStateClient] Failed to parse WebSocket message:", e);
307
313
  }
308
314
  };
309
315
 
310
316
  ws.onerror = () => {
311
- console.warn('[StreamStateClient] WebSocket error, falling back to HTTP polling');
317
+ console.warn("[StreamStateClient] WebSocket error, falling back to HTTP polling");
312
318
  ws.close();
313
319
  };
314
320
 
@@ -320,11 +326,11 @@ export class StreamStateClient extends TypedEventEmitter<StreamStateClientEvents
320
326
  // Disable WebSocket and switch to HTTP polling
321
327
  // This ensures pollHttp() schedules repeat polls (see line 365 condition)
322
328
  this.config.useWebSocket = false;
323
- console.debug('[StreamStateClient] WebSocket closed, switching to HTTP polling');
329
+ console.debug("[StreamStateClient] WebSocket closed, switching to HTTP polling");
324
330
  this.pollHttp();
325
331
  };
326
332
  } catch (error) {
327
- console.warn('[StreamStateClient] WebSocket connection failed:', error);
333
+ console.warn("[StreamStateClient] WebSocket connection failed:", error);
328
334
  // Disable WebSocket and switch to HTTP polling
329
335
  this.config.useWebSocket = false;
330
336
  this.pollHttp();
@@ -337,10 +343,10 @@ export class StreamStateClient extends TypedEventEmitter<StreamStateClientEvents
337
343
  const { mistBaseUrl, streamName, pollInterval } = this.config;
338
344
 
339
345
  try {
340
- const url = `${mistBaseUrl.replace(/\/$/, '')}/json_${encodeURIComponent(streamName)}.js?metaeverywhere=1&inclzero=1`;
346
+ const url = `${mistBaseUrl.replace(/\/$/, "")}/json_${encodeURIComponent(streamName)}.js?metaeverywhere=1&inclzero=1`;
341
347
  const response = await fetch(url, {
342
- method: 'GET',
343
- headers: { 'Accept': 'application/json' },
348
+ method: "GET",
349
+ headers: { Accept: "application/json" },
344
350
  });
345
351
 
346
352
  if (!response.ok) {
@@ -360,21 +366,21 @@ export class StreamStateClient extends TypedEventEmitter<StreamStateClientEvents
360
366
  } catch (error) {
361
367
  if (!this.isRunning) return;
362
368
 
363
- const errorMessage = error instanceof Error ? error.message : 'Connection failed';
369
+ const errorMessage = error instanceof Error ? error.message : "Connection failed";
364
370
  this.setState({
365
371
  ...this.state,
366
- status: 'ERROR',
372
+ status: "ERROR",
367
373
  isOnline: false,
368
374
  message: errorMessage,
369
375
  lastUpdate: Date.now(),
370
376
  error: errorMessage,
371
377
  });
372
- this.emit('error', { error: errorMessage });
378
+ this.emit("error", { error: errorMessage });
373
379
  }
374
380
 
375
381
  // Schedule next poll
376
382
  if (this.isRunning && !this.config.useWebSocket) {
377
- this.timers.start(() => this.pollHttp(), pollInterval, 'poll');
383
+ this.timers.start(() => this.pollHttp(), pollInterval, "poll");
378
384
  }
379
385
  }
380
386
 
@@ -402,8 +408,8 @@ export class StreamStateClient extends TypedEventEmitter<StreamStateClientEvents
402
408
  // Merge new data with existing streamInfo to preserve source/tracks from initial fetch
403
409
  // WebSocket updates may not include source array
404
410
  const mergedStreamInfo: MistStreamInfo = {
405
- ...this.state.streamInfo, // Keep existing source/meta if present
406
- ...data, // Override with new data
411
+ ...this.state.streamInfo, // Keep existing source/meta if present
412
+ ...data, // Override with new data
407
413
  // Explicitly preserve source if not in new data
408
414
  source: data.source || this.state.streamInfo?.source,
409
415
  // Merge meta to preserve tracks
@@ -416,9 +422,9 @@ export class StreamStateClient extends TypedEventEmitter<StreamStateClientEvents
416
422
  };
417
423
 
418
424
  newState = {
419
- status: 'ONLINE',
425
+ status: "ONLINE",
420
426
  isOnline: true,
421
- message: 'Stream is online',
427
+ message: "Stream is online",
422
428
  lastUpdate: Date.now(),
423
429
  streamInfo: mergedStreamInfo,
424
430
  };
@@ -428,9 +434,9 @@ export class StreamStateClient extends TypedEventEmitter<StreamStateClientEvents
428
434
 
429
435
  // Emit online/offline events on state transitions
430
436
  if (newState.isOnline && !this.wasOnline) {
431
- this.emit('online', undefined as never);
437
+ this.emit("online", undefined as never);
432
438
  } else if (!newState.isOnline && this.wasOnline) {
433
- this.emit('offline', undefined as never);
439
+ this.emit("offline", undefined as never);
434
440
  }
435
441
  this.wasOnline = newState.isOnline;
436
442
  }
@@ -449,7 +455,7 @@ export class StreamStateClient extends TypedEventEmitter<StreamStateClientEvents
449
455
  prevState.lastUpdate !== state.lastUpdate;
450
456
 
451
457
  if (hasChanged) {
452
- this.emit('stateChange', { state });
458
+ this.emit("stateChange", { state });
453
459
  }
454
460
  }
455
461
  }
@@ -59,12 +59,12 @@ export class SubtitleManager {
59
59
  const onLoadedData = () => this.correctSubtitleSync();
60
60
  const onSeeked = () => this.correctSubtitleSync();
61
61
 
62
- video.addEventListener('loadeddata', onLoadedData);
63
- video.addEventListener('seeked', onSeeked);
62
+ video.addEventListener("loadeddata", onLoadedData);
63
+ video.addEventListener("seeked", onSeeked);
64
64
 
65
65
  this.listeners = [
66
- () => video.removeEventListener('loadeddata', onLoadedData),
67
- () => video.removeEventListener('seeked', onSeeked),
66
+ () => video.removeEventListener("loadeddata", onLoadedData),
67
+ () => video.removeEventListener("seeked", onSeeked),
68
68
  ];
69
69
  }
70
70
 
@@ -72,7 +72,7 @@ export class SubtitleManager {
72
72
  * Detach from video element
73
73
  */
74
74
  detach(): void {
75
- this.listeners.forEach(fn => fn());
75
+ this.listeners.forEach((fn) => fn());
76
76
  this.listeners = [];
77
77
  this.removeAllTracks();
78
78
  this.video = null;
@@ -92,7 +92,7 @@ export class SubtitleManager {
92
92
  */
93
93
  getTrackElements(): HTMLTrackElement[] {
94
94
  if (!this.video) return [];
95
- return Array.from(this.video.querySelectorAll('track'));
95
+ return Array.from(this.video.querySelectorAll("track"));
96
96
  }
97
97
 
98
98
  /**
@@ -101,7 +101,7 @@ export class SubtitleManager {
101
101
  */
102
102
  setSubtitle(track: SubtitleTrackInfo | null): void {
103
103
  if (!this.video) {
104
- this.log('Cannot set subtitle: no video element attached');
104
+ this.log("Cannot set subtitle: no video element attached");
105
105
  return;
106
106
  }
107
107
 
@@ -110,20 +110,20 @@ export class SubtitleManager {
110
110
 
111
111
  if (!track) {
112
112
  this.currentTrackId = null;
113
- this.log('Subtitles disabled');
113
+ this.log("Subtitles disabled");
114
114
  return;
115
115
  }
116
116
 
117
117
  // Create new track element
118
- const trackElement = document.createElement('track');
119
- trackElement.kind = 'subtitles';
118
+ const trackElement = document.createElement("track");
119
+ trackElement.kind = "subtitles";
120
120
  trackElement.label = track.label;
121
121
  trackElement.srclang = track.lang;
122
122
  trackElement.src = this.buildTrackUrl(track.src);
123
123
  trackElement.default = true;
124
124
 
125
125
  // Set up load handler for sync correction
126
- trackElement.addEventListener('load', () => {
126
+ trackElement.addEventListener("load", () => {
127
127
  this.correctSubtitleSync();
128
128
  });
129
129
 
@@ -133,7 +133,7 @@ export class SubtitleManager {
133
133
  // Enable the track
134
134
  const textTrack = this.video.textTracks[this.video.textTracks.length - 1];
135
135
  if (textTrack) {
136
- textTrack.mode = 'showing';
136
+ textTrack.mode = "showing";
137
137
  }
138
138
 
139
139
  this.log(`Subtitle track set: ${track.label} (${track.lang})`);
@@ -146,14 +146,14 @@ export class SubtitleManager {
146
146
  let url = src;
147
147
 
148
148
  // If relative URL and base URL provided, construct full URL
149
- if (!url.startsWith('http') && this.config.mistBaseUrl) {
150
- const base = this.config.mistBaseUrl.replace(/\/$/, '');
151
- url = url.startsWith('/') ? `${base}${url}` : `${base}/${url}`;
149
+ if (!url.startsWith("http") && this.config.mistBaseUrl) {
150
+ const base = this.config.mistBaseUrl.replace(/\/$/, "");
151
+ url = url.startsWith("/") ? `${base}${url}` : `${base}/${url}`;
152
152
  }
153
153
 
154
154
  // Append URL params if configured
155
155
  if (this.config.urlAppend) {
156
- const separator = url.includes('?') ? '&' : '?';
156
+ const separator = url.includes("?") ? "&" : "?";
157
157
  url = `${url}${separator}${this.config.urlAppend}`;
158
158
  }
159
159
 
@@ -181,8 +181,8 @@ export class SubtitleManager {
181
181
  removeAllTracks(): void {
182
182
  if (!this.video) return;
183
183
 
184
- const tracks = this.video.querySelectorAll('track');
185
- tracks.forEach(track => track.remove());
184
+ const tracks = this.video.querySelectorAll("track");
185
+ tracks.forEach((track) => track.remove());
186
186
  }
187
187
 
188
188
  /**
@@ -258,7 +258,9 @@ export class SubtitleManager {
258
258
  * Parse subtitle tracks from MistServer stream info
259
259
  */
260
260
  static parseTracksFromStreamInfo(
261
- streamInfo: { meta?: { tracks?: Record<string, { type: string; codec: string; lang?: string }> } },
261
+ streamInfo: {
262
+ meta?: { tracks?: Record<string, { type: string; codec: string; lang?: string }> };
263
+ },
262
264
  baseUrl: string,
263
265
  streamName: string
264
266
  ): SubtitleTrackInfo[] {
@@ -267,9 +269,9 @@ export class SubtitleManager {
267
269
  if (!streamInfo.meta?.tracks) return tracks;
268
270
 
269
271
  for (const [trackId, trackData] of Object.entries(streamInfo.meta.tracks)) {
270
- if (trackData.type === 'meta' && trackData.codec === 'subtitle') {
271
- const lang = trackData.lang || 'und';
272
- const label = lang === 'und' ? `Subtitles ${trackId}` : lang.toUpperCase();
272
+ if (trackData.type === "meta" && trackData.codec === "subtitle") {
273
+ const lang = trackData.lang || "und";
274
+ const label = lang === "und" ? `Subtitles ${trackId}` : lang.toUpperCase();
273
275
  tracks.push(SubtitleManager.createTrackInfo(trackId, label, lang, baseUrl, streamName));
274
276
  }
275
277
  }