@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.
- package/README.md +11 -9
- package/dist/cjs/core/ABRController.js +456 -0
- package/dist/cjs/core/ABRController.js.map +1 -0
- package/dist/cjs/core/CodecUtils.js +195 -0
- package/dist/cjs/core/CodecUtils.js.map +1 -0
- package/dist/cjs/core/ErrorClassifier.js +410 -0
- package/dist/cjs/core/ErrorClassifier.js.map +1 -0
- package/dist/cjs/core/EventEmitter.js +108 -0
- package/dist/cjs/core/EventEmitter.js.map +1 -0
- package/dist/cjs/core/GatewayClient.js +342 -0
- package/dist/cjs/core/GatewayClient.js.map +1 -0
- package/dist/cjs/core/InteractionController.js +606 -0
- package/dist/cjs/core/InteractionController.js.map +1 -0
- package/dist/cjs/core/LiveDurationProxy.js +186 -0
- package/dist/cjs/core/LiveDurationProxy.js.map +1 -0
- package/dist/cjs/core/MetaTrackManager.js +624 -0
- package/dist/cjs/core/MetaTrackManager.js.map +1 -0
- package/dist/cjs/core/MistReporter.js +449 -0
- package/dist/cjs/core/MistReporter.js.map +1 -0
- package/dist/cjs/core/MistSignaling.js +264 -0
- package/dist/cjs/core/MistSignaling.js.map +1 -0
- package/dist/cjs/core/PlayerController.js +2658 -0
- package/dist/cjs/core/PlayerController.js.map +1 -0
- package/dist/cjs/core/PlayerInterface.js +269 -0
- package/dist/cjs/core/PlayerInterface.js.map +1 -0
- package/dist/cjs/core/PlayerManager.js +806 -0
- package/dist/cjs/core/PlayerManager.js.map +1 -0
- package/dist/cjs/core/PlayerRegistry.js +270 -0
- package/dist/cjs/core/PlayerRegistry.js.map +1 -0
- package/dist/cjs/core/QualityMonitor.js +474 -0
- package/dist/cjs/core/QualityMonitor.js.map +1 -0
- package/dist/cjs/core/SeekingUtils.js +292 -0
- package/dist/cjs/core/SeekingUtils.js.map +1 -0
- package/dist/cjs/core/StreamStateClient.js +381 -0
- package/dist/cjs/core/StreamStateClient.js.map +1 -0
- package/dist/cjs/core/SubtitleManager.js +227 -0
- package/dist/cjs/core/SubtitleManager.js.map +1 -0
- package/dist/cjs/core/TelemetryReporter.js +258 -0
- package/dist/cjs/core/TelemetryReporter.js.map +1 -0
- package/dist/cjs/core/TimeFormat.js +176 -0
- package/dist/cjs/core/TimeFormat.js.map +1 -0
- package/dist/cjs/core/TimerManager.js +176 -0
- package/dist/cjs/core/TimerManager.js.map +1 -0
- package/dist/cjs/core/UrlUtils.js +160 -0
- package/dist/cjs/core/UrlUtils.js.map +1 -0
- package/dist/cjs/core/detector.js +293 -0
- package/dist/cjs/core/detector.js.map +1 -0
- package/dist/cjs/core/scorer.js +443 -0
- package/dist/cjs/core/scorer.js.map +1 -0
- package/dist/cjs/index.js +121 -20134
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/lib/utils.js +11 -0
- package/dist/cjs/lib/utils.js.map +1 -0
- package/dist/cjs/node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.js +6 -0
- package/dist/cjs/node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.js.map +1 -0
- package/dist/cjs/node_modules/.pnpm/tailwind-merge@3.4.0/node_modules/tailwind-merge/dist/bundle-mjs.js +3042 -0
- package/dist/cjs/node_modules/.pnpm/tailwind-merge@3.4.0/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
- package/dist/cjs/players/DashJsPlayer.js +638 -0
- package/dist/cjs/players/DashJsPlayer.js.map +1 -0
- package/dist/cjs/players/HlsJsPlayer.js +482 -0
- package/dist/cjs/players/HlsJsPlayer.js.map +1 -0
- package/dist/cjs/players/MewsWsPlayer/SourceBufferManager.js +522 -0
- package/dist/cjs/players/MewsWsPlayer/SourceBufferManager.js.map +1 -0
- package/dist/cjs/players/MewsWsPlayer/WebSocketManager.js +215 -0
- package/dist/cjs/players/MewsWsPlayer/WebSocketManager.js.map +1 -0
- package/dist/cjs/players/MewsWsPlayer/index.js +987 -0
- package/dist/cjs/players/MewsWsPlayer/index.js.map +1 -0
- package/dist/cjs/players/MistPlayer.js +185 -0
- package/dist/cjs/players/MistPlayer.js.map +1 -0
- package/dist/cjs/players/MistWebRTCPlayer/index.js +635 -0
- package/dist/cjs/players/MistWebRTCPlayer/index.js.map +1 -0
- package/dist/cjs/players/NativePlayer.js +762 -0
- package/dist/cjs/players/NativePlayer.js.map +1 -0
- package/dist/cjs/players/VideoJsPlayer.js +585 -0
- package/dist/cjs/players/VideoJsPlayer.js.map +1 -0
- package/dist/cjs/players/WebCodecsPlayer/JitterBuffer.js +236 -0
- package/dist/cjs/players/WebCodecsPlayer/JitterBuffer.js.map +1 -0
- package/dist/cjs/players/WebCodecsPlayer/LatencyProfiles.js +143 -0
- package/dist/cjs/players/WebCodecsPlayer/LatencyProfiles.js.map +1 -0
- package/dist/cjs/players/WebCodecsPlayer/RawChunkParser.js +96 -0
- package/dist/cjs/players/WebCodecsPlayer/RawChunkParser.js.map +1 -0
- package/dist/cjs/players/WebCodecsPlayer/SyncController.js +359 -0
- package/dist/cjs/players/WebCodecsPlayer/SyncController.js.map +1 -0
- package/dist/cjs/players/WebCodecsPlayer/WebSocketController.js +460 -0
- package/dist/cjs/players/WebCodecsPlayer/WebSocketController.js.map +1 -0
- package/dist/cjs/players/WebCodecsPlayer/index.js +1467 -0
- package/dist/cjs/players/WebCodecsPlayer/index.js.map +1 -0
- package/dist/cjs/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.js +320 -0
- package/dist/cjs/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.js.map +1 -0
- package/dist/cjs/styles/index.js +57 -0
- package/dist/cjs/styles/index.js.map +1 -0
- package/dist/cjs/vanilla/FrameWorksPlayer.js +269 -0
- package/dist/cjs/vanilla/FrameWorksPlayer.js.map +1 -0
- package/dist/cjs/vanilla.js +11 -0
- package/dist/cjs/vanilla.js.map +1 -0
- package/dist/esm/core/ABRController.js +454 -0
- package/dist/esm/core/ABRController.js.map +1 -0
- package/dist/esm/core/CodecUtils.js +193 -0
- package/dist/esm/core/CodecUtils.js.map +1 -0
- package/dist/esm/core/ErrorClassifier.js +408 -0
- package/dist/esm/core/ErrorClassifier.js.map +1 -0
- package/dist/esm/core/EventEmitter.js +106 -0
- package/dist/esm/core/EventEmitter.js.map +1 -0
- package/dist/esm/core/GatewayClient.js +340 -0
- package/dist/esm/core/GatewayClient.js.map +1 -0
- package/dist/esm/core/InteractionController.js +604 -0
- package/dist/esm/core/InteractionController.js.map +1 -0
- package/dist/esm/core/LiveDurationProxy.js +184 -0
- package/dist/esm/core/LiveDurationProxy.js.map +1 -0
- package/dist/esm/core/MetaTrackManager.js +622 -0
- package/dist/esm/core/MetaTrackManager.js.map +1 -0
- package/dist/esm/core/MistReporter.js +447 -0
- package/dist/esm/core/MistReporter.js.map +1 -0
- package/dist/esm/core/MistSignaling.js +262 -0
- package/dist/esm/core/MistSignaling.js.map +1 -0
- package/dist/esm/core/PlayerController.js +2651 -0
- package/dist/esm/core/PlayerController.js.map +1 -0
- package/dist/esm/core/PlayerInterface.js +267 -0
- package/dist/esm/core/PlayerInterface.js.map +1 -0
- package/dist/esm/core/PlayerManager.js +804 -0
- package/dist/esm/core/PlayerManager.js.map +1 -0
- package/dist/esm/core/PlayerRegistry.js +264 -0
- package/dist/esm/core/PlayerRegistry.js.map +1 -0
- package/dist/esm/core/QualityMonitor.js +471 -0
- package/dist/esm/core/QualityMonitor.js.map +1 -0
- package/dist/esm/core/SeekingUtils.js +280 -0
- package/dist/esm/core/SeekingUtils.js.map +1 -0
- package/dist/esm/core/StreamStateClient.js +379 -0
- package/dist/esm/core/StreamStateClient.js.map +1 -0
- package/dist/esm/core/SubtitleManager.js +225 -0
- package/dist/esm/core/SubtitleManager.js.map +1 -0
- package/dist/esm/core/TelemetryReporter.js +256 -0
- package/dist/esm/core/TelemetryReporter.js.map +1 -0
- package/dist/esm/core/TimeFormat.js +169 -0
- package/dist/esm/core/TimeFormat.js.map +1 -0
- package/dist/esm/core/TimerManager.js +174 -0
- package/dist/esm/core/TimerManager.js.map +1 -0
- package/dist/esm/core/UrlUtils.js +151 -0
- package/dist/esm/core/UrlUtils.js.map +1 -0
- package/dist/esm/core/detector.js +279 -0
- package/dist/esm/core/detector.js.map +1 -0
- package/dist/esm/core/scorer.js +422 -0
- package/dist/esm/core/scorer.js.map +1 -0
- package/dist/esm/index.js +26 -20043
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/utils.js +9 -0
- package/dist/esm/lib/utils.js.map +1 -0
- package/dist/esm/node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.js +4 -0
- package/dist/esm/node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.js.map +1 -0
- package/dist/esm/node_modules/.pnpm/tailwind-merge@3.4.0/node_modules/tailwind-merge/dist/bundle-mjs.js +3036 -0
- package/dist/esm/node_modules/.pnpm/tailwind-merge@3.4.0/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
- package/dist/esm/players/DashJsPlayer.js +636 -0
- package/dist/esm/players/DashJsPlayer.js.map +1 -0
- package/dist/esm/players/HlsJsPlayer.js +480 -0
- package/dist/esm/players/HlsJsPlayer.js.map +1 -0
- package/dist/esm/players/MewsWsPlayer/SourceBufferManager.js +520 -0
- package/dist/esm/players/MewsWsPlayer/SourceBufferManager.js.map +1 -0
- package/dist/esm/players/MewsWsPlayer/WebSocketManager.js +213 -0
- package/dist/esm/players/MewsWsPlayer/WebSocketManager.js.map +1 -0
- package/dist/esm/players/MewsWsPlayer/index.js +985 -0
- package/dist/esm/players/MewsWsPlayer/index.js.map +1 -0
- package/dist/esm/players/MistPlayer.js +183 -0
- package/dist/esm/players/MistPlayer.js.map +1 -0
- package/dist/esm/players/MistWebRTCPlayer/index.js +633 -0
- package/dist/esm/players/MistWebRTCPlayer/index.js.map +1 -0
- package/dist/esm/players/NativePlayer.js +759 -0
- package/dist/esm/players/NativePlayer.js.map +1 -0
- package/dist/esm/players/VideoJsPlayer.js +583 -0
- package/dist/esm/players/VideoJsPlayer.js.map +1 -0
- package/dist/esm/players/WebCodecsPlayer/JitterBuffer.js +233 -0
- package/dist/esm/players/WebCodecsPlayer/JitterBuffer.js.map +1 -0
- package/dist/esm/players/WebCodecsPlayer/LatencyProfiles.js +134 -0
- package/dist/esm/players/WebCodecsPlayer/LatencyProfiles.js.map +1 -0
- package/dist/esm/players/WebCodecsPlayer/RawChunkParser.js +91 -0
- package/dist/esm/players/WebCodecsPlayer/RawChunkParser.js.map +1 -0
- package/dist/esm/players/WebCodecsPlayer/SyncController.js +357 -0
- package/dist/esm/players/WebCodecsPlayer/SyncController.js.map +1 -0
- package/dist/esm/players/WebCodecsPlayer/WebSocketController.js +458 -0
- package/dist/esm/players/WebCodecsPlayer/WebSocketController.js.map +1 -0
- package/dist/esm/players/WebCodecsPlayer/index.js +1458 -0
- package/dist/esm/players/WebCodecsPlayer/index.js.map +1 -0
- package/dist/esm/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.js +315 -0
- package/dist/esm/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.js.map +1 -0
- package/dist/esm/styles/index.js +54 -0
- package/dist/esm/styles/index.js.map +1 -0
- package/dist/esm/vanilla/FrameWorksPlayer.js +264 -0
- package/dist/esm/vanilla/FrameWorksPlayer.js.map +1 -0
- package/dist/esm/vanilla.js +2 -0
- package/dist/esm/vanilla.js.map +1 -0
- package/dist/player.css +185 -42
- package/dist/types/core/ABRController.d.ts +4 -4
- package/dist/types/core/CodecUtils.d.ts +1 -1
- package/dist/types/core/ErrorClassifier.d.ts +77 -0
- package/dist/types/core/GatewayClient.d.ts +4 -4
- package/dist/types/core/MetaTrackManager.d.ts +2 -2
- package/dist/types/core/MistReporter.d.ts +3 -3
- package/dist/types/core/MistSignaling.d.ts +12 -12
- package/dist/types/core/PlayerController.d.ts +19 -14
- package/dist/types/core/PlayerInterface.d.ts +100 -2
- package/dist/types/core/PlayerManager.d.ts +36 -9
- package/dist/types/core/PlayerRegistry.d.ts +11 -11
- package/dist/types/core/QualityMonitor.d.ts +2 -2
- package/dist/types/core/SeekingUtils.d.ts +2 -2
- package/dist/types/core/StreamStateClient.d.ts +2 -2
- package/dist/types/core/TelemetryReporter.d.ts +1 -1
- package/dist/types/core/TimerManager.d.ts +1 -1
- package/dist/types/core/detector.d.ts +1 -1
- package/dist/types/core/index.d.ts +44 -44
- package/dist/types/core/scorer.d.ts +1 -1
- package/dist/types/core/selector.d.ts +2 -2
- package/dist/types/index.d.ts +35 -34
- package/dist/types/players/DashJsPlayer.d.ts +3 -3
- package/dist/types/players/HlsJsPlayer.d.ts +3 -3
- package/dist/types/players/MewsWsPlayer/SourceBufferManager.d.ts +1 -1
- package/dist/types/players/MewsWsPlayer/WebSocketManager.d.ts +1 -1
- package/dist/types/players/MewsWsPlayer/index.d.ts +2 -2
- package/dist/types/players/MewsWsPlayer/types.d.ts +15 -15
- package/dist/types/players/MistPlayer.d.ts +2 -2
- package/dist/types/players/MistWebRTCPlayer/index.d.ts +3 -3
- package/dist/types/players/NativePlayer.d.ts +3 -3
- package/dist/types/players/VideoJsPlayer.d.ts +3 -3
- package/dist/types/players/WebCodecsPlayer/JitterBuffer.d.ts +3 -3
- package/dist/types/players/WebCodecsPlayer/LatencyProfiles.d.ts +1 -1
- package/dist/types/players/WebCodecsPlayer/RawChunkParser.d.ts +2 -2
- package/dist/types/players/WebCodecsPlayer/SyncController.d.ts +2 -2
- package/dist/types/players/WebCodecsPlayer/WebSocketController.d.ts +3 -3
- package/dist/types/players/WebCodecsPlayer/index.d.ts +9 -9
- package/dist/types/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.d.ts +1 -1
- package/dist/types/players/WebCodecsPlayer/types.d.ts +49 -49
- package/dist/types/players/WebCodecsPlayer/worker/types.d.ts +31 -31
- package/dist/types/players/index.d.ts +5 -8
- package/dist/types/types.d.ts +15 -15
- package/dist/types/vanilla/FrameWorksPlayer.d.ts +2 -2
- package/dist/types/vanilla/index.d.ts +4 -4
- package/dist/workers/decoder.worker.js +129 -122
- package/dist/workers/decoder.worker.js.map +1 -1
- package/package.json +31 -15
- package/src/core/ABRController.ts +38 -36
- package/src/core/CodecUtils.ts +49 -46
- package/src/core/Disposable.ts +4 -4
- package/src/core/ErrorClassifier.ts +499 -0
- package/src/core/EventEmitter.ts +1 -1
- package/src/core/GatewayClient.ts +41 -39
- package/src/core/InteractionController.ts +89 -82
- package/src/core/LiveDurationProxy.ts +14 -15
- package/src/core/MetaTrackManager.ts +73 -65
- package/src/core/MistReporter.ts +72 -45
- package/src/core/MistSignaling.ts +59 -56
- package/src/core/PlayerController.ts +542 -384
- package/src/core/PlayerInterface.ts +192 -59
- package/src/core/PlayerManager.ts +354 -164
- package/src/core/PlayerRegistry.ts +238 -87
- package/src/core/QualityMonitor.ts +38 -31
- package/src/core/ScreenWakeLockManager.ts +8 -9
- package/src/core/SeekingUtils.ts +31 -22
- package/src/core/StreamStateClient.ts +74 -68
- package/src/core/SubtitleManager.ts +24 -22
- package/src/core/TelemetryReporter.ts +38 -32
- package/src/core/TimeFormat.ts +13 -17
- package/src/core/TimerManager.ts +24 -8
- package/src/core/UrlUtils.ts +20 -17
- package/src/core/detector.ts +44 -44
- package/src/core/index.ts +57 -48
- package/src/core/scorer.ts +136 -141
- package/src/core/selector.ts +2 -6
- package/src/global.d.ts +1 -1
- package/src/index.ts +56 -36
- package/src/players/DashJsPlayer.ts +164 -115
- package/src/players/HlsJsPlayer.ts +132 -78
- package/src/players/MewsWsPlayer/SourceBufferManager.ts +41 -36
- package/src/players/MewsWsPlayer/WebSocketManager.ts +9 -9
- package/src/players/MewsWsPlayer/index.ts +192 -152
- package/src/players/MewsWsPlayer/types.ts +21 -21
- package/src/players/MistPlayer.ts +45 -26
- package/src/players/MistWebRTCPlayer/index.ts +175 -129
- package/src/players/NativePlayer.ts +203 -143
- package/src/players/VideoJsPlayer.ts +170 -118
- package/src/players/WebCodecsPlayer/JitterBuffer.ts +6 -7
- package/src/players/WebCodecsPlayer/LatencyProfiles.ts +43 -43
- package/src/players/WebCodecsPlayer/RawChunkParser.ts +10 -10
- package/src/players/WebCodecsPlayer/SyncController.ts +45 -53
- package/src/players/WebCodecsPlayer/WebSocketController.ts +66 -68
- package/src/players/WebCodecsPlayer/index.ts +265 -223
- package/src/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.ts +12 -17
- package/src/players/WebCodecsPlayer/types.ts +56 -56
- package/src/players/WebCodecsPlayer/worker/decoder.worker.ts +238 -182
- package/src/players/WebCodecsPlayer/worker/types.ts +31 -31
- package/src/players/index.ts +5 -16
- package/src/styles/animations.css +2 -1
- package/src/styles/player.css +185 -42
- package/src/styles/tailwind.css +473 -159
- package/src/types.ts +43 -43
- package/src/vanilla/FrameWorksPlayer.ts +26 -14
- 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
|