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