@livepeer-frameworks/player-core 0.0.3
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/dist/cjs/index.js +19493 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/esm/index.js +19398 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/player.css +2140 -0
- package/dist/types/core/ABRController.d.ts +164 -0
- package/dist/types/core/CodecUtils.d.ts +54 -0
- package/dist/types/core/Disposable.d.ts +61 -0
- package/dist/types/core/EventEmitter.d.ts +73 -0
- package/dist/types/core/GatewayClient.d.ts +144 -0
- package/dist/types/core/InteractionController.d.ts +121 -0
- package/dist/types/core/LiveDurationProxy.d.ts +102 -0
- package/dist/types/core/MetaTrackManager.d.ts +220 -0
- package/dist/types/core/MistReporter.d.ts +163 -0
- package/dist/types/core/MistSignaling.d.ts +148 -0
- package/dist/types/core/PlayerController.d.ts +665 -0
- package/dist/types/core/PlayerInterface.d.ts +230 -0
- package/dist/types/core/PlayerManager.d.ts +182 -0
- package/dist/types/core/PlayerRegistry.d.ts +27 -0
- package/dist/types/core/QualityMonitor.d.ts +184 -0
- package/dist/types/core/ScreenWakeLockManager.d.ts +70 -0
- package/dist/types/core/SeekingUtils.d.ts +142 -0
- package/dist/types/core/StreamStateClient.d.ts +108 -0
- package/dist/types/core/SubtitleManager.d.ts +111 -0
- package/dist/types/core/TelemetryReporter.d.ts +79 -0
- package/dist/types/core/TimeFormat.d.ts +97 -0
- package/dist/types/core/TimerManager.d.ts +83 -0
- package/dist/types/core/UrlUtils.d.ts +81 -0
- package/dist/types/core/detector.d.ts +149 -0
- package/dist/types/core/index.d.ts +49 -0
- package/dist/types/core/scorer.d.ts +167 -0
- package/dist/types/core/selector.d.ts +9 -0
- package/dist/types/index.d.ts +45 -0
- package/dist/types/lib/utils.d.ts +2 -0
- package/dist/types/players/DashJsPlayer.d.ts +102 -0
- package/dist/types/players/HlsJsPlayer.d.ts +70 -0
- package/dist/types/players/MewsWsPlayer/SourceBufferManager.d.ts +119 -0
- package/dist/types/players/MewsWsPlayer/WebSocketManager.d.ts +60 -0
- package/dist/types/players/MewsWsPlayer/index.d.ts +220 -0
- package/dist/types/players/MewsWsPlayer/types.d.ts +89 -0
- package/dist/types/players/MistPlayer.d.ts +25 -0
- package/dist/types/players/MistWebRTCPlayer/index.d.ts +133 -0
- package/dist/types/players/NativePlayer.d.ts +143 -0
- package/dist/types/players/VideoJsPlayer.d.ts +59 -0
- package/dist/types/players/WebCodecsPlayer/JitterBuffer.d.ts +118 -0
- package/dist/types/players/WebCodecsPlayer/LatencyProfiles.d.ts +64 -0
- package/dist/types/players/WebCodecsPlayer/RawChunkParser.d.ts +63 -0
- package/dist/types/players/WebCodecsPlayer/SyncController.d.ts +174 -0
- package/dist/types/players/WebCodecsPlayer/WebSocketController.d.ts +164 -0
- package/dist/types/players/WebCodecsPlayer/index.d.ts +149 -0
- package/dist/types/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.d.ts +105 -0
- package/dist/types/players/WebCodecsPlayer/types.d.ts +395 -0
- package/dist/types/players/WebCodecsPlayer/worker/decoder.worker.d.ts +13 -0
- package/dist/types/players/WebCodecsPlayer/worker/types.d.ts +197 -0
- package/dist/types/players/index.d.ts +14 -0
- package/dist/types/styles/index.d.ts +11 -0
- package/dist/types/types.d.ts +363 -0
- package/dist/types/vanilla/FrameWorksPlayer.d.ts +143 -0
- package/dist/types/vanilla/index.d.ts +19 -0
- package/dist/workers/decoder.worker.js +989 -0
- package/dist/workers/decoder.worker.js.map +1 -0
- package/package.json +80 -0
- package/src/core/ABRController.ts +550 -0
- package/src/core/CodecUtils.ts +257 -0
- package/src/core/Disposable.ts +120 -0
- package/src/core/EventEmitter.ts +113 -0
- package/src/core/GatewayClient.ts +439 -0
- package/src/core/InteractionController.ts +712 -0
- package/src/core/LiveDurationProxy.ts +270 -0
- package/src/core/MetaTrackManager.ts +753 -0
- package/src/core/MistReporter.ts +543 -0
- package/src/core/MistSignaling.ts +346 -0
- package/src/core/PlayerController.ts +2829 -0
- package/src/core/PlayerInterface.ts +432 -0
- package/src/core/PlayerManager.ts +900 -0
- package/src/core/PlayerRegistry.ts +149 -0
- package/src/core/QualityMonitor.ts +597 -0
- package/src/core/ScreenWakeLockManager.ts +163 -0
- package/src/core/SeekingUtils.ts +364 -0
- package/src/core/StreamStateClient.ts +457 -0
- package/src/core/SubtitleManager.ts +297 -0
- package/src/core/TelemetryReporter.ts +308 -0
- package/src/core/TimeFormat.ts +205 -0
- package/src/core/TimerManager.ts +209 -0
- package/src/core/UrlUtils.ts +179 -0
- package/src/core/detector.ts +382 -0
- package/src/core/index.ts +140 -0
- package/src/core/scorer.ts +553 -0
- package/src/core/selector.ts +16 -0
- package/src/global.d.ts +11 -0
- package/src/index.ts +75 -0
- package/src/lib/utils.ts +6 -0
- package/src/players/DashJsPlayer.ts +642 -0
- package/src/players/HlsJsPlayer.ts +483 -0
- package/src/players/MewsWsPlayer/SourceBufferManager.ts +572 -0
- package/src/players/MewsWsPlayer/WebSocketManager.ts +241 -0
- package/src/players/MewsWsPlayer/index.ts +1065 -0
- package/src/players/MewsWsPlayer/types.ts +106 -0
- package/src/players/MistPlayer.ts +188 -0
- package/src/players/MistWebRTCPlayer/index.ts +703 -0
- package/src/players/NativePlayer.ts +820 -0
- package/src/players/VideoJsPlayer.ts +643 -0
- package/src/players/WebCodecsPlayer/JitterBuffer.ts +299 -0
- package/src/players/WebCodecsPlayer/LatencyProfiles.ts +151 -0
- package/src/players/WebCodecsPlayer/RawChunkParser.ts +151 -0
- package/src/players/WebCodecsPlayer/SyncController.ts +456 -0
- package/src/players/WebCodecsPlayer/WebSocketController.ts +564 -0
- package/src/players/WebCodecsPlayer/index.ts +1650 -0
- package/src/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.ts +379 -0
- package/src/players/WebCodecsPlayer/types.ts +542 -0
- package/src/players/WebCodecsPlayer/worker/decoder.worker.ts +1360 -0
- package/src/players/WebCodecsPlayer/worker/types.ts +276 -0
- package/src/players/index.ts +22 -0
- package/src/styles/animations.css +21 -0
- package/src/styles/index.ts +52 -0
- package/src/styles/player.css +2126 -0
- package/src/styles/tailwind.css +1015 -0
- package/src/types.ts +421 -0
- package/src/vanilla/FrameWorksPlayer.ts +367 -0
- package/src/vanilla/index.ts +22 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import type { ABRMode, ABROptions, PlaybackQuality, QualityLevel } from '../types';
|
|
2
|
+
export interface ABRControllerConfig {
|
|
3
|
+
/** ABR options */
|
|
4
|
+
options?: Partial<ABROptions>;
|
|
5
|
+
/** Callback to get available qualities */
|
|
6
|
+
getQualities: () => QualityLevel[];
|
|
7
|
+
/** Callback to select a quality */
|
|
8
|
+
selectQuality: (id: string | 'auto') => void;
|
|
9
|
+
/** Callback to get current quality */
|
|
10
|
+
getCurrentQuality?: () => QualityLevel | null;
|
|
11
|
+
/** Callback to get bandwidth estimate (bits per second) - typically from player stats */
|
|
12
|
+
getBandwidthEstimate?: () => Promise<number>;
|
|
13
|
+
/** Debug logging */
|
|
14
|
+
debug?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export type ABRDecision = 'upgrade' | 'downgrade' | 'maintain' | 'none';
|
|
17
|
+
/**
|
|
18
|
+
* ABRController - Adaptive Bitrate Controller
|
|
19
|
+
*
|
|
20
|
+
* Manages automatic quality selection based on:
|
|
21
|
+
* - ABR_resize: Matches video resolution to viewport size
|
|
22
|
+
* - ABR_bitrate: Switches quality based on playback performance
|
|
23
|
+
* - auto: Combines both modes
|
|
24
|
+
* - manual: No automatic switching
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* const abr = new ABRController({
|
|
29
|
+
* options: { mode: 'auto' },
|
|
30
|
+
* getQualities: () => player.getQualities(),
|
|
31
|
+
* selectQuality: (id) => player.selectQuality(id),
|
|
32
|
+
* });
|
|
33
|
+
*
|
|
34
|
+
* abr.start(videoElement);
|
|
35
|
+
* abr.onQualityChange((quality) => console.log('Quality:', quality.score));
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare class ABRController {
|
|
39
|
+
private options;
|
|
40
|
+
private config;
|
|
41
|
+
private videoElement;
|
|
42
|
+
private currentQualityId;
|
|
43
|
+
private lastDecision;
|
|
44
|
+
private lastDecisionTime;
|
|
45
|
+
private resizeObserver;
|
|
46
|
+
private qualityChangeCallbacks;
|
|
47
|
+
private debug;
|
|
48
|
+
private timers;
|
|
49
|
+
private bandwidthHistory;
|
|
50
|
+
private static readonly BANDWIDTH_HISTORY_SIZE;
|
|
51
|
+
private static readonly MONITORING_INTERVAL_MS;
|
|
52
|
+
private static readonly UPGRADE_COOLDOWN_MS;
|
|
53
|
+
private static readonly DOWNGRADE_COOLDOWN_MS;
|
|
54
|
+
private lastUpgradeTime;
|
|
55
|
+
private lastDowngradeTime;
|
|
56
|
+
private static readonly UPGRADE_HEADROOM;
|
|
57
|
+
private static readonly UPGRADE_HOLD_THRESHOLD;
|
|
58
|
+
private static readonly DOWNGRADE_THRESHOLD;
|
|
59
|
+
private currentQualityBitrate;
|
|
60
|
+
constructor(config: ABRControllerConfig);
|
|
61
|
+
/**
|
|
62
|
+
* Start ABR control
|
|
63
|
+
*/
|
|
64
|
+
start(videoElement: HTMLVideoElement): void;
|
|
65
|
+
/**
|
|
66
|
+
* Stop ABR control
|
|
67
|
+
*/
|
|
68
|
+
stop(): void;
|
|
69
|
+
/**
|
|
70
|
+
* Start active bandwidth monitoring loop
|
|
71
|
+
* Continuously monitors bandwidth and proactively switches quality
|
|
72
|
+
*/
|
|
73
|
+
private startActiveMonitoring;
|
|
74
|
+
/**
|
|
75
|
+
* Check current bandwidth and switch quality if needed
|
|
76
|
+
*
|
|
77
|
+
* Uses hysteresis (D2) and separate cooldowns (D3) to prevent oscillation:
|
|
78
|
+
* - Downgrade: immediate response (0ms cooldown), triggers at 0.8x
|
|
79
|
+
* - Upgrade: 5s cooldown, requires 1.5x headroom, holds until 1.2x
|
|
80
|
+
*/
|
|
81
|
+
private checkBandwidthAndSwitch;
|
|
82
|
+
/**
|
|
83
|
+
* Get bandwidth estimate from player stats
|
|
84
|
+
*/
|
|
85
|
+
private getBandwidthEstimate;
|
|
86
|
+
/**
|
|
87
|
+
* Get smoothed bandwidth from history
|
|
88
|
+
*/
|
|
89
|
+
private getSmoothedBandwidth;
|
|
90
|
+
/**
|
|
91
|
+
* Get current bandwidth estimate (for external use)
|
|
92
|
+
*/
|
|
93
|
+
getCurrentBandwidth(): number;
|
|
94
|
+
/**
|
|
95
|
+
* Setup resize observer for viewport-based quality selection
|
|
96
|
+
*/
|
|
97
|
+
private setupResizeObserver;
|
|
98
|
+
/**
|
|
99
|
+
* Handle viewport resize (ABR_resize mode)
|
|
100
|
+
*/
|
|
101
|
+
private handleResize;
|
|
102
|
+
/**
|
|
103
|
+
* Handle quality degradation (ABR_bitrate mode)
|
|
104
|
+
*
|
|
105
|
+
* Called by QualityMonitor when playback quality drops
|
|
106
|
+
*/
|
|
107
|
+
handleQualityDegraded(quality: PlaybackQuality): void;
|
|
108
|
+
/**
|
|
109
|
+
* Handle quality improvement opportunity
|
|
110
|
+
*
|
|
111
|
+
* Called when conditions are good enough to try higher quality
|
|
112
|
+
*/
|
|
113
|
+
handleQualityImproved(quality: PlaybackQuality): void;
|
|
114
|
+
/**
|
|
115
|
+
* Find best quality level for given resolution
|
|
116
|
+
*/
|
|
117
|
+
private findBestQualityForResolution;
|
|
118
|
+
/**
|
|
119
|
+
* Find a lower quality level
|
|
120
|
+
*/
|
|
121
|
+
private findLowerQuality;
|
|
122
|
+
/**
|
|
123
|
+
* Find a higher quality level
|
|
124
|
+
*/
|
|
125
|
+
private findHigherQuality;
|
|
126
|
+
/**
|
|
127
|
+
* Check if quality is within configured constraints
|
|
128
|
+
*/
|
|
129
|
+
private isWithinConstraints;
|
|
130
|
+
/**
|
|
131
|
+
* Select a quality level
|
|
132
|
+
*/
|
|
133
|
+
private selectQuality;
|
|
134
|
+
/**
|
|
135
|
+
* Register callback for quality changes
|
|
136
|
+
*/
|
|
137
|
+
onQualityChange(callback: (level: QualityLevel) => void): () => void;
|
|
138
|
+
/**
|
|
139
|
+
* Manually set quality (switches to manual mode temporarily)
|
|
140
|
+
*/
|
|
141
|
+
setQuality(id: string | 'auto'): void;
|
|
142
|
+
/**
|
|
143
|
+
* Get current ABR mode
|
|
144
|
+
*/
|
|
145
|
+
getMode(): ABRMode;
|
|
146
|
+
/**
|
|
147
|
+
* Set ABR mode at runtime.
|
|
148
|
+
* Restarts monitoring if video element is attached.
|
|
149
|
+
*/
|
|
150
|
+
setMode(mode: ABRMode): void;
|
|
151
|
+
/**
|
|
152
|
+
* Update ABR options
|
|
153
|
+
*/
|
|
154
|
+
updateOptions(options: Partial<ABROptions>): void;
|
|
155
|
+
/**
|
|
156
|
+
* Get last ABR decision
|
|
157
|
+
*/
|
|
158
|
+
getLastDecision(): ABRDecision;
|
|
159
|
+
/**
|
|
160
|
+
* Debug logging
|
|
161
|
+
*/
|
|
162
|
+
private log;
|
|
163
|
+
}
|
|
164
|
+
export default ABRController;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CodecUtils - Codec string translation utilities
|
|
3
|
+
*
|
|
4
|
+
* Based on MistMetaPlayer's MistUtil.tracks.translateCodec functionality.
|
|
5
|
+
* Translates MistServer codec names to browser-compatible codec strings.
|
|
6
|
+
*/
|
|
7
|
+
export interface TrackInfo {
|
|
8
|
+
type: string;
|
|
9
|
+
codec: string;
|
|
10
|
+
init?: string;
|
|
11
|
+
codecstring?: string;
|
|
12
|
+
width?: number;
|
|
13
|
+
height?: number;
|
|
14
|
+
bps?: number;
|
|
15
|
+
fpks?: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Translate a MistServer codec name to a browser-compatible codec string
|
|
19
|
+
*
|
|
20
|
+
* @param track - Track info from MistServer
|
|
21
|
+
* @returns Browser-compatible codec string (e.g., "avc1.64001f")
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* translateCodec({ codec: 'H264', type: 'video' })
|
|
26
|
+
* // => 'avc1.42E01E' (baseline profile, level 3.0 default)
|
|
27
|
+
*
|
|
28
|
+
* translateCodec({ codec: 'AAC', type: 'audio' })
|
|
29
|
+
* // => 'mp4a.40.2'
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function translateCodec(track: TrackInfo): string;
|
|
33
|
+
/**
|
|
34
|
+
* Check if a codec is supported by the browser via MediaSource
|
|
35
|
+
*
|
|
36
|
+
* @param codecString - Codec string to check
|
|
37
|
+
* @param containerType - Container type (default: 'video/mp4')
|
|
38
|
+
* @returns true if supported
|
|
39
|
+
*/
|
|
40
|
+
export declare function isCodecSupported(codecString: string, containerType?: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Get the best supported codec from a list of tracks
|
|
43
|
+
*
|
|
44
|
+
* @param tracks - Array of track info
|
|
45
|
+
* @param type - Track type to filter ('video' or 'audio')
|
|
46
|
+
* @returns Best supported track or null
|
|
47
|
+
*/
|
|
48
|
+
export declare function getBestSupportedTrack(tracks: TrackInfo[], type: 'video' | 'audio'): TrackInfo | null;
|
|
49
|
+
declare const _default: {
|
|
50
|
+
translateCodec: typeof translateCodec;
|
|
51
|
+
isCodecSupported: typeof isCodecSupported;
|
|
52
|
+
getBestSupportedTrack: typeof getBestSupportedTrack;
|
|
53
|
+
};
|
|
54
|
+
export default _default;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Disposable interface for consistent cleanup across all core classes.
|
|
3
|
+
*
|
|
4
|
+
* All classes that manage resources (timers, event listeners, WebSockets, etc.)
|
|
5
|
+
* should implement this interface to ensure proper cleanup.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Interface for objects that need cleanup
|
|
9
|
+
*/
|
|
10
|
+
export interface Disposable {
|
|
11
|
+
/**
|
|
12
|
+
* Clean up all resources held by this object.
|
|
13
|
+
* Safe to call multiple times - subsequent calls should be no-ops.
|
|
14
|
+
*/
|
|
15
|
+
dispose(): void;
|
|
16
|
+
/**
|
|
17
|
+
* Whether this object has been disposed
|
|
18
|
+
*/
|
|
19
|
+
readonly disposed: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Base class for disposable objects that provides:
|
|
23
|
+
* - disposed flag tracking
|
|
24
|
+
* - Double-dispose protection
|
|
25
|
+
* - Template method for subclass cleanup
|
|
26
|
+
*/
|
|
27
|
+
export declare abstract class BaseDisposable implements Disposable {
|
|
28
|
+
private _disposed;
|
|
29
|
+
/**
|
|
30
|
+
* Whether this object has been disposed
|
|
31
|
+
*/
|
|
32
|
+
get disposed(): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Dispose of this object, releasing all resources.
|
|
35
|
+
* Safe to call multiple times.
|
|
36
|
+
*/
|
|
37
|
+
dispose(): void;
|
|
38
|
+
/**
|
|
39
|
+
* Subclasses implement this to clean up their resources.
|
|
40
|
+
* Called exactly once when dispose() is first called.
|
|
41
|
+
*/
|
|
42
|
+
protected abstract onDispose(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Throw if this object has been disposed.
|
|
45
|
+
* Use at the start of methods that shouldn't run after disposal.
|
|
46
|
+
*/
|
|
47
|
+
protected throwIfDisposed(operation?: string): void;
|
|
48
|
+
/**
|
|
49
|
+
* Check if disposed without throwing - useful for conditional guards
|
|
50
|
+
*/
|
|
51
|
+
protected guardDisposed(): boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Utility to dispose multiple disposables at once
|
|
55
|
+
*/
|
|
56
|
+
export declare function disposeAll(...disposables: (Disposable | null | undefined)[]): void;
|
|
57
|
+
/**
|
|
58
|
+
* Create a composite disposable that disposes multiple items
|
|
59
|
+
*/
|
|
60
|
+
export declare function createCompositeDisposable(...disposables: (Disposable | (() => void))[]): Disposable;
|
|
61
|
+
export default BaseDisposable;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EventEmitter.ts
|
|
3
|
+
*
|
|
4
|
+
* A lightweight, typed event emitter for framework-agnostic components.
|
|
5
|
+
* Used by GatewayClient, StreamStateClient, and PlayerController.
|
|
6
|
+
*/
|
|
7
|
+
type Listener<T> = (data: T) => void;
|
|
8
|
+
/**
|
|
9
|
+
* Typed event emitter that provides type-safe event handling.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* interface MyEvents {
|
|
14
|
+
* stateChange: { state: string };
|
|
15
|
+
* error: { message: string };
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* class MyClass extends TypedEventEmitter<MyEvents> {
|
|
19
|
+
* doSomething() {
|
|
20
|
+
* this.emit('stateChange', { state: 'ready' });
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* const instance = new MyClass();
|
|
25
|
+
* const unsub = instance.on('stateChange', ({ state }) => console.log(state));
|
|
26
|
+
* unsub(); // unsubscribe
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare class TypedEventEmitter<Events extends Record<string, any>> {
|
|
30
|
+
private listeners;
|
|
31
|
+
/**
|
|
32
|
+
* Subscribe to an event.
|
|
33
|
+
* @param event - The event name to listen for
|
|
34
|
+
* @param listener - Callback function invoked when the event is emitted
|
|
35
|
+
* @returns Unsubscribe function
|
|
36
|
+
*/
|
|
37
|
+
on<K extends keyof Events>(event: K, listener: Listener<Events[K]>): () => void;
|
|
38
|
+
/**
|
|
39
|
+
* Subscribe to an event only once.
|
|
40
|
+
* The listener is automatically removed after the first invocation.
|
|
41
|
+
* @param event - The event name to listen for
|
|
42
|
+
* @param listener - Callback function invoked when the event is emitted
|
|
43
|
+
* @returns Unsubscribe function
|
|
44
|
+
*/
|
|
45
|
+
once<K extends keyof Events>(event: K, listener: Listener<Events[K]>): () => void;
|
|
46
|
+
/**
|
|
47
|
+
* Unsubscribe from an event.
|
|
48
|
+
* @param event - The event name
|
|
49
|
+
* @param listener - The callback to remove
|
|
50
|
+
*/
|
|
51
|
+
off<K extends keyof Events>(event: K, listener: Listener<Events[K]>): void;
|
|
52
|
+
/**
|
|
53
|
+
* Emit an event to all subscribers.
|
|
54
|
+
* @param event - The event name
|
|
55
|
+
* @param data - The event payload
|
|
56
|
+
*/
|
|
57
|
+
protected emit<K extends keyof Events>(event: K, data: Events[K]): void;
|
|
58
|
+
/**
|
|
59
|
+
* Remove all listeners for all events.
|
|
60
|
+
*/
|
|
61
|
+
removeAllListeners(): void;
|
|
62
|
+
/**
|
|
63
|
+
* Remove all listeners for a specific event.
|
|
64
|
+
* @param event - The event name
|
|
65
|
+
*/
|
|
66
|
+
removeListeners<K extends keyof Events>(event: K): void;
|
|
67
|
+
/**
|
|
68
|
+
* Check if there are any listeners for an event.
|
|
69
|
+
* @param event - The event name
|
|
70
|
+
*/
|
|
71
|
+
hasListeners<K extends keyof Events>(event: K): boolean;
|
|
72
|
+
}
|
|
73
|
+
export default TypedEventEmitter;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GatewayClient.ts
|
|
3
|
+
*
|
|
4
|
+
* Framework-agnostic client for resolving viewer endpoints from the Gateway GraphQL API.
|
|
5
|
+
* Extracted from useViewerEndpoints.ts for use in headless core.
|
|
6
|
+
*/
|
|
7
|
+
import { TypedEventEmitter } from './EventEmitter';
|
|
8
|
+
import type { ContentEndpoints, ContentType } from '../types';
|
|
9
|
+
export type GatewayStatus = 'idle' | 'loading' | 'ready' | 'error';
|
|
10
|
+
export interface GatewayClientConfig {
|
|
11
|
+
/** Gateway GraphQL endpoint URL */
|
|
12
|
+
gatewayUrl: string;
|
|
13
|
+
/** Content type to resolve */
|
|
14
|
+
contentType: ContentType;
|
|
15
|
+
/** Content identifier (stream name) */
|
|
16
|
+
contentId: string;
|
|
17
|
+
/** Optional auth token for private streams */
|
|
18
|
+
authToken?: string;
|
|
19
|
+
/** Maximum retry attempts (default: 3) */
|
|
20
|
+
maxRetries?: number;
|
|
21
|
+
/** Initial retry delay in ms (default: 500) */
|
|
22
|
+
initialDelayMs?: number;
|
|
23
|
+
}
|
|
24
|
+
export interface GatewayClientEvents {
|
|
25
|
+
/** Emitted when status changes */
|
|
26
|
+
statusChange: {
|
|
27
|
+
status: GatewayStatus;
|
|
28
|
+
error?: string;
|
|
29
|
+
};
|
|
30
|
+
/** Emitted when endpoints are successfully resolved */
|
|
31
|
+
endpointsResolved: {
|
|
32
|
+
endpoints: ContentEndpoints;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
type CircuitBreakerState = 'closed' | 'open' | 'half-open';
|
|
36
|
+
/**
|
|
37
|
+
* Client for resolving viewer endpoints from the Gateway GraphQL API.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* const client = new GatewayClient({
|
|
42
|
+
* gatewayUrl: 'https://gateway.example.com/graphql',
|
|
43
|
+
* contentType: 'live',
|
|
44
|
+
* contentId: 'my-stream',
|
|
45
|
+
* });
|
|
46
|
+
*
|
|
47
|
+
* client.on('statusChange', ({ status }) => console.log('Status:', status));
|
|
48
|
+
* client.on('endpointsResolved', ({ endpoints }) => console.log('Endpoints:', endpoints));
|
|
49
|
+
*
|
|
50
|
+
* const endpoints = await client.resolve();
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare class GatewayClient extends TypedEventEmitter<GatewayClientEvents> {
|
|
54
|
+
private config;
|
|
55
|
+
private status;
|
|
56
|
+
private endpoints;
|
|
57
|
+
private error;
|
|
58
|
+
private abortController;
|
|
59
|
+
private inFlightRequest;
|
|
60
|
+
private cacheTimestamp;
|
|
61
|
+
private cacheTtlMs;
|
|
62
|
+
private circuitState;
|
|
63
|
+
private consecutiveFailures;
|
|
64
|
+
private circuitOpenedAt;
|
|
65
|
+
constructor(config: GatewayClientConfig);
|
|
66
|
+
/**
|
|
67
|
+
* Resolve endpoints from the gateway.
|
|
68
|
+
* F2: Returns cached result if still valid, deduplicates concurrent requests.
|
|
69
|
+
* F3: Respects circuit breaker state.
|
|
70
|
+
* @param forceRefresh - If true, bypasses cache and fetches fresh data
|
|
71
|
+
* @returns Promise resolving to ContentEndpoints
|
|
72
|
+
* @throws Error if resolution fails after retries or circuit is open
|
|
73
|
+
*/
|
|
74
|
+
resolve(forceRefresh?: boolean): Promise<ContentEndpoints>;
|
|
75
|
+
/**
|
|
76
|
+
* F2: Check if cache is still valid
|
|
77
|
+
*/
|
|
78
|
+
private isCacheValid;
|
|
79
|
+
/**
|
|
80
|
+
* F2: Set cache TTL (for testing or custom requirements)
|
|
81
|
+
*/
|
|
82
|
+
setCacheTtl(ttlMs: number): void;
|
|
83
|
+
/**
|
|
84
|
+
* F2: Invalidate the cache manually
|
|
85
|
+
*/
|
|
86
|
+
invalidateCache(): void;
|
|
87
|
+
/**
|
|
88
|
+
* F3: Check if a request can be attempted based on circuit state
|
|
89
|
+
*/
|
|
90
|
+
private canAttemptRequest;
|
|
91
|
+
/**
|
|
92
|
+
* F3: Record a successful request
|
|
93
|
+
*/
|
|
94
|
+
private onSuccess;
|
|
95
|
+
/**
|
|
96
|
+
* F3: Record a failed request
|
|
97
|
+
*/
|
|
98
|
+
private onFailure;
|
|
99
|
+
/**
|
|
100
|
+
* F3: Get current circuit breaker state (for monitoring/debugging)
|
|
101
|
+
*/
|
|
102
|
+
getCircuitState(): {
|
|
103
|
+
state: CircuitBreakerState;
|
|
104
|
+
failures: number;
|
|
105
|
+
openedAt: number | null;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* F3: Manually reset the circuit breaker
|
|
109
|
+
*/
|
|
110
|
+
resetCircuitBreaker(): void;
|
|
111
|
+
/**
|
|
112
|
+
* Internal method to perform the actual resolution.
|
|
113
|
+
* @returns Promise resolving to ContentEndpoints
|
|
114
|
+
*/
|
|
115
|
+
private doResolve;
|
|
116
|
+
/**
|
|
117
|
+
* Abort any in-flight request.
|
|
118
|
+
*/
|
|
119
|
+
abort(): void;
|
|
120
|
+
/**
|
|
121
|
+
* Get current status.
|
|
122
|
+
*/
|
|
123
|
+
getStatus(): GatewayStatus;
|
|
124
|
+
/**
|
|
125
|
+
* Get resolved endpoints (null if not yet resolved).
|
|
126
|
+
*/
|
|
127
|
+
getEndpoints(): ContentEndpoints | null;
|
|
128
|
+
/**
|
|
129
|
+
* Get error message (null if no error).
|
|
130
|
+
*/
|
|
131
|
+
getError(): string | null;
|
|
132
|
+
/**
|
|
133
|
+
* Update configuration and reset state.
|
|
134
|
+
* F2: Also clears cache and in-flight request
|
|
135
|
+
* F3: Resets circuit breaker (new config = fresh start)
|
|
136
|
+
*/
|
|
137
|
+
updateConfig(config: Partial<GatewayClientConfig>): void;
|
|
138
|
+
/**
|
|
139
|
+
* Clean up resources.
|
|
140
|
+
*/
|
|
141
|
+
destroy(): void;
|
|
142
|
+
private setStatus;
|
|
143
|
+
}
|
|
144
|
+
export default GatewayClient;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InteractionController - Unified keyboard and gesture handling for video players
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Hold space for 2x speed (VOD/clips only, tap = play/pause)
|
|
6
|
+
* - Click/touch and hold for 2x speed
|
|
7
|
+
* - Comprehensive keyboard shortcuts
|
|
8
|
+
* - Double-tap to skip on mobile
|
|
9
|
+
* - All interactions disabled for live streams (where applicable)
|
|
10
|
+
*/
|
|
11
|
+
export interface InteractionControllerConfig {
|
|
12
|
+
container: HTMLElement;
|
|
13
|
+
videoElement: HTMLVideoElement;
|
|
14
|
+
isLive: boolean;
|
|
15
|
+
onPlayPause: () => void;
|
|
16
|
+
onSeek: (delta: number) => void;
|
|
17
|
+
onVolumeChange: (delta: number) => void;
|
|
18
|
+
onMuteToggle: () => void;
|
|
19
|
+
onFullscreenToggle: () => void;
|
|
20
|
+
onCaptionsToggle?: () => void;
|
|
21
|
+
onLoopToggle?: () => void;
|
|
22
|
+
onSpeedChange: (speed: number, isHolding: boolean) => void;
|
|
23
|
+
onSeekPercent?: (percent: number) => void;
|
|
24
|
+
speedHoldValue?: number;
|
|
25
|
+
/** Idle timeout in ms (default 5000). Set to 0 to disable. */
|
|
26
|
+
idleTimeout?: number;
|
|
27
|
+
/** Callback fired when user becomes idle */
|
|
28
|
+
onIdle?: () => void;
|
|
29
|
+
/** Callback fired when user becomes active after being idle */
|
|
30
|
+
onActive?: () => void;
|
|
31
|
+
}
|
|
32
|
+
export interface InteractionState {
|
|
33
|
+
isHoldingSpeed: boolean;
|
|
34
|
+
previousSpeed: number;
|
|
35
|
+
holdSpeed: number;
|
|
36
|
+
/** Whether the user is currently idle (no interaction for idleTimeout) */
|
|
37
|
+
isIdle: boolean;
|
|
38
|
+
}
|
|
39
|
+
export declare class InteractionController {
|
|
40
|
+
private config;
|
|
41
|
+
private state;
|
|
42
|
+
private isAttached;
|
|
43
|
+
private spaceKeyDownTime;
|
|
44
|
+
private spaceIsHeld;
|
|
45
|
+
private holdCheckTimeout;
|
|
46
|
+
private pointerDownTime;
|
|
47
|
+
private pointerIsHeld;
|
|
48
|
+
private pointerHoldTimeout;
|
|
49
|
+
private lastTapTime;
|
|
50
|
+
private lastTapX;
|
|
51
|
+
private pendingTapTimeout;
|
|
52
|
+
private idleTimeout;
|
|
53
|
+
private lastInteractionTime;
|
|
54
|
+
private boundKeyDown;
|
|
55
|
+
private boundKeyUp;
|
|
56
|
+
private boundPointerDown;
|
|
57
|
+
private boundPointerUp;
|
|
58
|
+
private boundPointerCancel;
|
|
59
|
+
private boundContextMenu;
|
|
60
|
+
private boundMouseMove;
|
|
61
|
+
constructor(config: InteractionControllerConfig);
|
|
62
|
+
/**
|
|
63
|
+
* Attach event listeners to container
|
|
64
|
+
*/
|
|
65
|
+
attach(): void;
|
|
66
|
+
/**
|
|
67
|
+
* Detach event listeners and cleanup
|
|
68
|
+
*/
|
|
69
|
+
detach(): void;
|
|
70
|
+
/**
|
|
71
|
+
* Check if currently holding for speed boost
|
|
72
|
+
*/
|
|
73
|
+
isHoldingSpeed(): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Check if user is currently idle (no interaction for idleTimeout)
|
|
76
|
+
*/
|
|
77
|
+
isIdle(): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Get current interaction state
|
|
80
|
+
*/
|
|
81
|
+
getState(): InteractionState;
|
|
82
|
+
/**
|
|
83
|
+
* Update config (e.g., when isLive changes)
|
|
84
|
+
*/
|
|
85
|
+
updateConfig(updates: Partial<InteractionControllerConfig>): void;
|
|
86
|
+
private handleKeyDown;
|
|
87
|
+
private handleKeyUp;
|
|
88
|
+
private handleSpaceDown;
|
|
89
|
+
private handleSpaceUp;
|
|
90
|
+
private handlePointerDown;
|
|
91
|
+
private handlePointerUp;
|
|
92
|
+
private handlePointerCancel;
|
|
93
|
+
private cancelPointerHold;
|
|
94
|
+
private handleContextMenu;
|
|
95
|
+
private engageSpeedHold;
|
|
96
|
+
private releaseSpeedHold;
|
|
97
|
+
private adjustPlaybackSpeed;
|
|
98
|
+
private handleMouseMove;
|
|
99
|
+
/**
|
|
100
|
+
* Record that an interaction occurred and reset idle timer
|
|
101
|
+
*/
|
|
102
|
+
recordInteraction(): void;
|
|
103
|
+
/**
|
|
104
|
+
* Reset the idle timer
|
|
105
|
+
*/
|
|
106
|
+
private resetIdleTimer;
|
|
107
|
+
/**
|
|
108
|
+
* Manually mark as active (e.g., when controls become visible)
|
|
109
|
+
*/
|
|
110
|
+
markActive(): void;
|
|
111
|
+
/**
|
|
112
|
+
* Pause idle tracking (e.g., when controls are visible)
|
|
113
|
+
*/
|
|
114
|
+
pauseIdleTracking(): void;
|
|
115
|
+
/**
|
|
116
|
+
* Resume idle tracking
|
|
117
|
+
*/
|
|
118
|
+
resumeIdleTracking(): void;
|
|
119
|
+
private isInputElement;
|
|
120
|
+
private isControlElement;
|
|
121
|
+
}
|