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