@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
@@ -0,0 +1,622 @@
1
+ import { TimerManager } from './TimerManager.js';
2
+
3
+ /**
4
+ * MetaTrackManager - Handles real-time metadata subscriptions via MistServer WebSocket
5
+ *
6
+ * Uses native MistServer WebSocket protocol (from embed/player.js):
7
+ * - Connect: ws://{baseUrl}/json_{streamName}.js?rate=1
8
+ * - Set tracks: {type:"tracks", meta:"1,2,3"} (comma-separated indices)
9
+ * - Seek: {type:"seek", seek_time:<ms>, ff_to:<ms>}
10
+ * - Receive: {time:<ms>, track:<index>, data:{...}}
11
+ * - Control: {type:"hold"}, {type:"play"}, {type:"fast_forward", ff_to:<ms>}
12
+ *
13
+ * Features:
14
+ * - Automatic reconnection with exponential backoff
15
+ * - Message buffering during reconnection
16
+ * - Type detection for subtitle/score/event/chapter data
17
+ * - Stay-ahead buffering for smooth playback
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const manager = new MetaTrackManager({
22
+ * mistBaseUrl: 'https://mist.example.com',
23
+ * streamName: 'pk_...', // playbackId (view key)
24
+ * });
25
+ *
26
+ * manager.subscribe('1', (event) => {
27
+ * if (event.type === 'subtitle') {
28
+ * console.log('Subtitle:', event.data);
29
+ * }
30
+ * });
31
+ *
32
+ * manager.connect();
33
+ * ```
34
+ */
35
+ class MetaTrackManager {
36
+ constructor(config) {
37
+ this.ws = null;
38
+ this.state = "disconnected";
39
+ this.subscriptions = new Map();
40
+ this.pendingSubscriptions = new Set();
41
+ this.reconnectAttempt = 0;
42
+ this.timers = new TimerManager();
43
+ this.messageBuffer = [];
44
+ this.connectionId = 0; // Track connection attempts to prevent stale callbacks
45
+ // Buffer management (MistMetaPlayer feature backport)
46
+ this.currentPlaybackTime = 0;
47
+ this.lastFastForwardTime = 0;
48
+ this.timedEventBuffer = new Map(); // trackId -> events sorted by time
49
+ this.config = config;
50
+ this.debug = config.debug ?? false;
51
+ // Buffer management settings (MistMetaPlayer defaults)
52
+ this.bufferAhead = config.bufferAhead ?? 5;
53
+ this.maxMessageAge = config.maxMessageAge ?? 5;
54
+ this.fastForwardInterval = config.fastForwardInterval ?? 5;
55
+ // Add initial subscriptions
56
+ if (config.subscriptions) {
57
+ for (const sub of config.subscriptions) {
58
+ this.subscribe(sub.trackId, sub.callback);
59
+ }
60
+ }
61
+ }
62
+ /**
63
+ * Connect to MistServer WebSocket
64
+ * Debounced to prevent orphaned connections during rapid mount/unmount cycles.
65
+ */
66
+ connect() {
67
+ if (this.state === "connecting" || this.state === "connected") {
68
+ return;
69
+ }
70
+ this.state = "connecting";
71
+ this.log("Connecting...");
72
+ // Increment connection ID to invalidate any pending callbacks
73
+ const currentConnectionId = ++this.connectionId;
74
+ // Debounce connection
75
+ this.timers.start(() => {
76
+ // Check if this connection attempt is still valid
77
+ if (this.state !== "connecting" || this.connectionId !== currentConnectionId) {
78
+ return;
79
+ }
80
+ this.createWebSocket(currentConnectionId);
81
+ }, MetaTrackManager.CONNECTION_DEBOUNCE_MS, "connect");
82
+ }
83
+ /**
84
+ * Internal method to create WebSocket after debounce
85
+ */
86
+ createWebSocket(connectionId) {
87
+ try {
88
+ const wsUrl = this.buildWsUrl();
89
+ this.ws = new WebSocket(wsUrl);
90
+ this.ws.onopen = () => {
91
+ // Verify still valid
92
+ if (this.connectionId !== connectionId) {
93
+ this.ws?.close();
94
+ return;
95
+ }
96
+ this.log("Connected");
97
+ this.state = "connected";
98
+ this.reconnectAttempt = 0;
99
+ // Merge pending subscriptions into existing
100
+ for (const trackId of this.pendingSubscriptions) {
101
+ if (!this.subscriptions.has(trackId)) {
102
+ this.subscriptions.set(trackId, new Set());
103
+ }
104
+ }
105
+ this.pendingSubscriptions.clear();
106
+ // Send all subscribed tracks at once (MistServer protocol)
107
+ this.sendTracksUpdate();
108
+ // Send initial seek to current playback position
109
+ this.sendSeek(this.currentPlaybackTime);
110
+ // Flush message buffer
111
+ this.flushMessageBuffer();
112
+ };
113
+ this.ws.onmessage = (event) => {
114
+ this.handleMessage(event.data);
115
+ };
116
+ this.ws.onerror = (event) => {
117
+ this.log("WebSocket error");
118
+ console.warn("[MetaTrackManager] WebSocket error:", event);
119
+ };
120
+ this.ws.onclose = () => {
121
+ this.log("Disconnected");
122
+ this.ws = null;
123
+ if (this.state !== "disconnected") {
124
+ this.scheduleReconnect();
125
+ }
126
+ };
127
+ }
128
+ catch (error) {
129
+ this.log(`Connection error: ${error}`);
130
+ this.scheduleReconnect();
131
+ }
132
+ }
133
+ /**
134
+ * Disconnect from MistServer
135
+ */
136
+ disconnect() {
137
+ this.state = "disconnected";
138
+ // Clear all timers
139
+ this.timers.destroy();
140
+ if (this.ws) {
141
+ this.ws.close();
142
+ this.ws = null;
143
+ }
144
+ }
145
+ /**
146
+ * Subscribe to a meta track
147
+ * @param trackId Track index (number as string) or "all" for all meta tracks
148
+ */
149
+ subscribe(trackId, callback) {
150
+ const isNewTrack = !this.subscriptions.has(trackId);
151
+ if (isNewTrack) {
152
+ this.subscriptions.set(trackId, new Set());
153
+ }
154
+ this.subscriptions.get(trackId).add(callback);
155
+ // Send updated track list if connected and this is a new track
156
+ if (this.state === "connected" && this.ws && isNewTrack) {
157
+ this.sendTracksUpdate();
158
+ }
159
+ else if (isNewTrack) {
160
+ this.pendingSubscriptions.add(trackId);
161
+ }
162
+ // Return unsubscribe function
163
+ return () => this.unsubscribe(trackId, callback);
164
+ }
165
+ /**
166
+ * Unsubscribe from a meta track
167
+ */
168
+ unsubscribe(trackId, callback) {
169
+ const callbacks = this.subscriptions.get(trackId);
170
+ if (callbacks) {
171
+ callbacks.delete(callback);
172
+ // If no more callbacks, remove subscription and update MistServer
173
+ if (callbacks.size === 0) {
174
+ this.subscriptions.delete(trackId);
175
+ // Send updated track list (MistServer doesn't have explicit unsubscribe)
176
+ if (this.state === "connected" && this.ws) {
177
+ this.sendTracksUpdate();
178
+ }
179
+ }
180
+ }
181
+ }
182
+ /**
183
+ * Get list of subscribed track IDs
184
+ */
185
+ getSubscribedTracks() {
186
+ return Array.from(this.subscriptions.keys());
187
+ }
188
+ /**
189
+ * Get connection state
190
+ */
191
+ getState() {
192
+ return this.state;
193
+ }
194
+ /**
195
+ * Check if connected
196
+ */
197
+ isConnected() {
198
+ return this.state === "connected";
199
+ }
200
+ // ========================================
201
+ // Buffer Management (MistMetaPlayer backport)
202
+ // ========================================
203
+ /**
204
+ * Update current playback time
205
+ * Call this on video timeupdate events to keep buffer in sync
206
+ */
207
+ setPlaybackTime(timeInSeconds) {
208
+ this.currentPlaybackTime = timeInSeconds;
209
+ // Process any buffered events that are now due
210
+ this.processTimedEvents();
211
+ }
212
+ /**
213
+ * Get current playback time
214
+ */
215
+ getPlaybackTime() {
216
+ return this.currentPlaybackTime;
217
+ }
218
+ /**
219
+ * Handle seek event - clears buffer and sends seek command to MistServer
220
+ * Call this when video seeks to a new position
221
+ */
222
+ onSeek(newTimeInSeconds) {
223
+ this.log(`Seek to ${newTimeInSeconds}s - clearing buffer and notifying server`);
224
+ this.currentPlaybackTime = newTimeInSeconds;
225
+ // Clear all timed event buffers
226
+ this.timedEventBuffer.clear();
227
+ // Reset fast-forward tracking
228
+ this.lastFastForwardTime = 0;
229
+ // Tell MistServer to seek its metadata stream
230
+ this.sendSeek(newTimeInSeconds);
231
+ }
232
+ /**
233
+ * Process buffered events up to current playback time
234
+ * Dispatches events that are ready to be shown
235
+ */
236
+ processTimedEvents() {
237
+ const now = this.currentPlaybackTime * 1000; // Convert to ms
238
+ for (const [trackId, events] of this.timedEventBuffer) {
239
+ // Find events that should be dispatched
240
+ const dueEvents = [];
241
+ const remainingEvents = [];
242
+ for (const event of events) {
243
+ if (event.timestamp <= now) {
244
+ // Check if event is too old (filter stale events)
245
+ const ageSeconds = (now - event.timestamp) / 1000;
246
+ if (ageSeconds <= this.maxMessageAge) {
247
+ dueEvents.push(event);
248
+ }
249
+ else {
250
+ this.log(`Filtering stale event (${ageSeconds.toFixed(1)}s old)`);
251
+ }
252
+ }
253
+ else {
254
+ remainingEvents.push(event);
255
+ }
256
+ }
257
+ // Dispatch due events
258
+ for (const event of dueEvents) {
259
+ this.dispatchEvent(event);
260
+ }
261
+ // Update buffer with remaining events
262
+ if (remainingEvents.length > 0) {
263
+ this.timedEventBuffer.set(trackId, remainingEvents);
264
+ }
265
+ else {
266
+ this.timedEventBuffer.delete(trackId);
267
+ }
268
+ }
269
+ }
270
+ /**
271
+ * Add event to timed buffer (sorted by timestamp)
272
+ * Used for events that should be dispatched at specific playback times
273
+ */
274
+ addToTimedBuffer(event) {
275
+ const trackId = event.trackId;
276
+ if (!this.timedEventBuffer.has(trackId)) {
277
+ this.timedEventBuffer.set(trackId, []);
278
+ }
279
+ const buffer = this.timedEventBuffer.get(trackId);
280
+ // Insert in sorted order by timestamp
281
+ let insertIndex = buffer.length;
282
+ for (let i = 0; i < buffer.length; i++) {
283
+ if (buffer[i].timestamp > event.timestamp) {
284
+ insertIndex = i;
285
+ break;
286
+ }
287
+ }
288
+ buffer.splice(insertIndex, 0, event);
289
+ // Limit buffer size per track
290
+ while (buffer.length > MetaTrackManager.MESSAGE_BUFFER_SIZE) {
291
+ buffer.shift();
292
+ }
293
+ }
294
+ /**
295
+ * Check if we need to request more data (stay bufferAhead seconds ahead)
296
+ * Returns true if buffer is running low
297
+ */
298
+ needsMoreData(trackId) {
299
+ const buffer = this.timedEventBuffer.get(trackId);
300
+ if (!buffer || buffer.length === 0)
301
+ return true;
302
+ const lastEventTime = buffer[buffer.length - 1].timestamp / 1000;
303
+ const currentTime = this.currentPlaybackTime;
304
+ const bufferedAhead = lastEventTime - currentTime;
305
+ return bufferedAhead < this.bufferAhead;
306
+ }
307
+ /**
308
+ * Fast-forward through buffered events (rate-limited)
309
+ * Used when playback jumps ahead and needs to catch up
310
+ * Also notifies MistServer to fast-forward its metadata stream
311
+ */
312
+ fastForward() {
313
+ const now = Date.now();
314
+ // Rate limit fast-forward (once per fastForwardInterval seconds)
315
+ if (now - this.lastFastForwardTime < this.fastForwardInterval * 1000) {
316
+ return;
317
+ }
318
+ this.lastFastForwardTime = now;
319
+ this.log("Fast-forwarding through buffered events");
320
+ // Process all events up to current time + bufferAhead
321
+ const targetTime = (this.currentPlaybackTime + this.bufferAhead) * 1000;
322
+ for (const [trackId, events] of this.timedEventBuffer) {
323
+ const processEvents = [];
324
+ const remainingEvents = [];
325
+ for (const event of events) {
326
+ if (event.timestamp <= targetTime) {
327
+ // Only dispatch if not too old
328
+ const ageSeconds = (this.currentPlaybackTime * 1000 - event.timestamp) / 1000;
329
+ if (ageSeconds <= this.maxMessageAge) {
330
+ processEvents.push(event);
331
+ }
332
+ }
333
+ else {
334
+ remainingEvents.push(event);
335
+ }
336
+ }
337
+ // Dispatch events
338
+ for (const event of processEvents) {
339
+ this.dispatchEvent(event);
340
+ }
341
+ // Update buffer
342
+ if (remainingEvents.length > 0) {
343
+ this.timedEventBuffer.set(trackId, remainingEvents);
344
+ }
345
+ else {
346
+ this.timedEventBuffer.delete(trackId);
347
+ }
348
+ }
349
+ // Tell MistServer to fast-forward as well
350
+ this.sendFastForward(this.currentPlaybackTime + this.bufferAhead);
351
+ }
352
+ /**
353
+ * Get buffer status for debugging
354
+ */
355
+ getBufferStatus() {
356
+ const status = {};
357
+ for (const [trackId, events] of this.timedEventBuffer) {
358
+ if (events.length > 0) {
359
+ status[trackId] = {
360
+ count: events.length,
361
+ oldestMs: events[0].timestamp,
362
+ newestMs: events[events.length - 1].timestamp,
363
+ };
364
+ }
365
+ }
366
+ return status;
367
+ }
368
+ /**
369
+ * Build WebSocket URL for MistServer meta track subscription
370
+ * Uses the same endpoint as JSON info polling, just over WebSocket
371
+ */
372
+ buildWsUrl() {
373
+ const baseUrl = this.config.mistBaseUrl
374
+ .replace(/^http:/, "ws:")
375
+ .replace(/^https:/, "wss:")
376
+ .replace(/\/$/, "");
377
+ // MistServer meta track WebSocket uses /json_<streamname>.js endpoint
378
+ // The rate=1 param tells MistServer to stream metadata in real-time
379
+ return `${baseUrl}/json_${this.config.streamName}.js?rate=1`;
380
+ }
381
+ /**
382
+ * Send tracks update to MistServer
383
+ * MistServer protocol: {type:"tracks", meta:"1,2,3"} (comma-separated track indices)
384
+ */
385
+ sendTracksUpdate() {
386
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
387
+ const trackIds = Array.from(this.subscriptions.keys());
388
+ // Support "all" as special track ID to subscribe to all meta tracks
389
+ const metaValue = trackIds.includes("all") ? "all" : trackIds.join(",");
390
+ const message = JSON.stringify({
391
+ type: "tracks",
392
+ meta: metaValue,
393
+ });
394
+ this.ws.send(message);
395
+ this.log(`Set tracks: ${metaValue}`);
396
+ }
397
+ }
398
+ /**
399
+ * Send seek command to MistServer
400
+ * MistServer protocol: {type:"seek", seek_time:<ms>, ff_to:<ms>}
401
+ */
402
+ sendSeek(timeInSeconds) {
403
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
404
+ const seekTimeMs = Math.round(timeInSeconds * 1000);
405
+ const ffToMs = Math.round((timeInSeconds + this.bufferAhead) * 1000);
406
+ const message = JSON.stringify({
407
+ type: "seek",
408
+ seek_time: seekTimeMs,
409
+ ff_to: ffToMs,
410
+ });
411
+ this.ws.send(message);
412
+ this.log(`Seek to ${timeInSeconds}s, buffer ahead to ${timeInSeconds + this.bufferAhead}s`);
413
+ }
414
+ }
415
+ /**
416
+ * Send hold command (pause metadata delivery)
417
+ */
418
+ sendHold() {
419
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
420
+ this.ws.send(JSON.stringify({ type: "hold" }));
421
+ this.log("Sent hold");
422
+ }
423
+ }
424
+ /**
425
+ * Send play command (resume metadata delivery)
426
+ */
427
+ sendPlay() {
428
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
429
+ this.ws.send(JSON.stringify({ type: "play" }));
430
+ this.log("Sent play");
431
+ }
432
+ }
433
+ /**
434
+ * Send fast-forward command
435
+ */
436
+ sendFastForward(targetTimeSeconds) {
437
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
438
+ const message = JSON.stringify({
439
+ type: "fast_forward",
440
+ ff_to: Math.round(targetTimeSeconds * 1000),
441
+ });
442
+ this.ws.send(message);
443
+ this.log(`Fast-forward to ${targetTimeSeconds}s`);
444
+ }
445
+ }
446
+ /**
447
+ * Handle incoming WebSocket message
448
+ * MistServer format:
449
+ * - Metadata: {time:<ms>, track:<index>, data:{...}}
450
+ * - Status: {type:"on_time", data:{current:<ms>}}
451
+ * - Seek complete: {type:"seek", ...}
452
+ */
453
+ handleMessage(data) {
454
+ try {
455
+ const parsed = JSON.parse(data);
456
+ // Handle metadata event: {time, track, data}
457
+ if ("time" in parsed && "track" in parsed && "data" in parsed) {
458
+ const event = this.parseMetaTrackEvent(parsed);
459
+ // Check if we're subscribed to this track (or "all")
460
+ const trackId = String(parsed.track);
461
+ if (this.subscriptions.has(trackId) || this.subscriptions.has("all")) {
462
+ // Subtitles and chapters should be buffered for timed playback
463
+ // Other events (scores, generic events) dispatch immediately
464
+ if (event.type === "subtitle" || event.type === "chapter") {
465
+ this.addToTimedBuffer(event);
466
+ // Also process immediately in case we're already past this time
467
+ this.processTimedEvents();
468
+ }
469
+ else {
470
+ // Dispatch immediately for non-timed events
471
+ this.dispatchEvent(event);
472
+ }
473
+ }
474
+ return;
475
+ }
476
+ // Handle server status messages: {type:..., ...}
477
+ if ("type" in parsed) {
478
+ switch (parsed.type) {
479
+ case "on_time":
480
+ // Server time update - can be used for buffer management
481
+ if (parsed.data?.current) {
482
+ const serverTimeMs = parsed.data.current;
483
+ const playerTimeMs = this.currentPlaybackTime * 1000;
484
+ const aheadMs = serverTimeMs - playerTimeMs;
485
+ // If server is too far ahead, pause and wait
486
+ if (aheadMs > this.bufferAhead * 6 * 1000) {
487
+ this.log(`Server ${aheadMs}ms ahead, sending hold`);
488
+ this.sendHold();
489
+ }
490
+ }
491
+ break;
492
+ case "seek":
493
+ // Seek completed - clear buffers
494
+ this.log("Server confirmed seek, clearing buffers");
495
+ this.timedEventBuffer.clear();
496
+ break;
497
+ default:
498
+ this.log(`Unknown message type: ${parsed.type}`);
499
+ }
500
+ }
501
+ }
502
+ catch (error) {
503
+ this.log(`Failed to parse message: ${error}`);
504
+ }
505
+ }
506
+ /**
507
+ * Parse meta track event from MistServer message
508
+ * MistServer format: {time:<ms>, track:<index>, data:{...}}
509
+ */
510
+ parseMetaTrackEvent(message) {
511
+ const trackId = String(message.track);
512
+ const timestamp = Number(message.time);
513
+ const data = message.data ?? message;
514
+ // Detect event type from data shape
515
+ const type = this.detectEventType(data);
516
+ return {
517
+ type,
518
+ timestamp,
519
+ trackId,
520
+ data,
521
+ };
522
+ }
523
+ /**
524
+ * Detect event type from data shape
525
+ */
526
+ detectEventType(data) {
527
+ if (typeof data !== "object" || data === null) {
528
+ return "unknown";
529
+ }
530
+ const obj = data;
531
+ // Subtitle: has text, startTime/endTime
532
+ if ("text" in obj && ("startTime" in obj || "start" in obj)) {
533
+ return "subtitle";
534
+ }
535
+ // Score: has key and value
536
+ if ("key" in obj && "value" in obj) {
537
+ return "score";
538
+ }
539
+ // Chapter: has title and startTime
540
+ if ("title" in obj && "startTime" in obj) {
541
+ return "chapter";
542
+ }
543
+ // Event: has name
544
+ if ("name" in obj) {
545
+ return "event";
546
+ }
547
+ return "unknown";
548
+ }
549
+ /**
550
+ * Dispatch event to subscribers
551
+ */
552
+ dispatchEvent(event) {
553
+ const callbacks = this.subscriptions.get(event.trackId);
554
+ if (callbacks) {
555
+ for (const callback of callbacks) {
556
+ try {
557
+ callback(event);
558
+ }
559
+ catch (error) {
560
+ console.error("[MetaTrackManager] Callback error:", error);
561
+ }
562
+ }
563
+ }
564
+ }
565
+ /**
566
+ * Schedule reconnection attempt
567
+ */
568
+ scheduleReconnect() {
569
+ if (this.state === "disconnected")
570
+ return;
571
+ if (this.reconnectAttempt >= MetaTrackManager.MAX_RECONNECT_ATTEMPTS) {
572
+ this.log("Max reconnect attempts reached");
573
+ this.state = "disconnected";
574
+ return;
575
+ }
576
+ this.state = "reconnecting";
577
+ this.reconnectAttempt++;
578
+ const delay = Math.min(MetaTrackManager.INITIAL_RECONNECT_DELAY * Math.pow(2, this.reconnectAttempt - 1), MetaTrackManager.MAX_RECONNECT_DELAY);
579
+ this.log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempt})`);
580
+ this.timers.start(() => {
581
+ this.connect();
582
+ }, delay, "reconnect");
583
+ }
584
+ /**
585
+ * Buffer message for later delivery
586
+ */
587
+ bufferMessage(event) {
588
+ this.messageBuffer.push(event);
589
+ // Limit buffer size
590
+ while (this.messageBuffer.length > MetaTrackManager.MESSAGE_BUFFER_SIZE) {
591
+ this.messageBuffer.shift();
592
+ }
593
+ }
594
+ /**
595
+ * Flush buffered messages to subscribers
596
+ */
597
+ flushMessageBuffer() {
598
+ const buffered = [...this.messageBuffer];
599
+ this.messageBuffer = [];
600
+ for (const event of buffered) {
601
+ this.dispatchEvent(event);
602
+ }
603
+ }
604
+ /**
605
+ * Debug logging
606
+ */
607
+ log(message) {
608
+ if (this.debug) {
609
+ console.debug(`[MetaTrackManager] ${message}`);
610
+ }
611
+ }
612
+ }
613
+ // Debounce time for rapid mount/unmount cycles (ms)
614
+ MetaTrackManager.CONNECTION_DEBOUNCE_MS = 100;
615
+ // Reconnection settings
616
+ MetaTrackManager.MAX_RECONNECT_ATTEMPTS = 5;
617
+ MetaTrackManager.INITIAL_RECONNECT_DELAY = 1000;
618
+ MetaTrackManager.MAX_RECONNECT_DELAY = 30000;
619
+ MetaTrackManager.MESSAGE_BUFFER_SIZE = 100;
620
+
621
+ export { MetaTrackManager };
622
+ //# sourceMappingURL=MetaTrackManager.js.map