@lightbird/core 0.1.0

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.
@@ -0,0 +1,317 @@
1
+ import { FFmpeg } from '@ffmpeg/ffmpeg';
2
+
3
+ interface LightBirdConfig {
4
+ ffmpegCDN?: string;
5
+ }
6
+ declare function configureLightBird(options: LightBirdConfig): void;
7
+
8
+ interface Chapter {
9
+ index: number;
10
+ title: string;
11
+ startTime: number;
12
+ endTime: number;
13
+ }
14
+ interface PlaylistItem {
15
+ id: string;
16
+ name: string;
17
+ url: string;
18
+ type: 'video' | 'stream';
19
+ file?: File;
20
+ duration?: number;
21
+ }
22
+ interface SubtitleCue {
23
+ startTime: number;
24
+ endTime: number;
25
+ text: string;
26
+ }
27
+ interface Subtitle {
28
+ id: string;
29
+ name: string;
30
+ lang: string;
31
+ url?: string;
32
+ type: 'embedded' | 'external';
33
+ /** Detected source format (vtt, srt, ass, ssa). Defaults to 'vtt'. */
34
+ format?: 'vtt' | 'srt' | 'ass' | 'ssa';
35
+ }
36
+ interface AudioTrack {
37
+ id: string;
38
+ name: string;
39
+ lang: string;
40
+ }
41
+ interface VideoFilters {
42
+ brightness: number;
43
+ contrast: number;
44
+ saturate: number;
45
+ hue: number;
46
+ }
47
+ interface VideoMetadata {
48
+ filename: string;
49
+ fileSize: number | null;
50
+ duration: number;
51
+ container: string;
52
+ width: number;
53
+ height: number;
54
+ frameRate: number | null;
55
+ videoBitrate: number | null;
56
+ videoCodec: string | null;
57
+ colorSpace: string | null;
58
+ audioTracks: AudioTrackMeta[];
59
+ subtitleTracks: SubtitleTrackMeta[];
60
+ }
61
+ interface AudioTrackMeta {
62
+ index: number;
63
+ codec: string | null;
64
+ channels: number | null;
65
+ sampleRate: number | null;
66
+ language: string | null;
67
+ bitrate: number | null;
68
+ }
69
+ interface SubtitleTrackMeta {
70
+ index: number;
71
+ format: string | null;
72
+ language: string | null;
73
+ }
74
+
75
+ interface SimplePlayerFile {
76
+ name: string;
77
+ file: File;
78
+ url: string;
79
+ externalSubtitles?: Subtitle[];
80
+ }
81
+ declare class SimplePlayer {
82
+ private file;
83
+ private videoElement;
84
+ private currentSubtitleTrackIndex;
85
+ constructor(file: File, externalSubtitles?: File[]);
86
+ private processExternalSubtitles;
87
+ initialize(videoElement: HTMLVideoElement): Promise<SimplePlayerFile>;
88
+ getAudioTracks(): AudioTrack[];
89
+ getSubtitles(): Subtitle[];
90
+ switchAudioTrack(trackId: string): Promise<void>;
91
+ switchSubtitle(trackId: string): Promise<void>;
92
+ destroy(): void;
93
+ static isCompatible(file: File): boolean;
94
+ }
95
+
96
+ declare class CancellationError extends Error {
97
+ constructor();
98
+ }
99
+ interface MKVPlayerFile {
100
+ name: string;
101
+ file: File;
102
+ videoUrl?: string;
103
+ audioTracks: AudioTrack[];
104
+ subtitleTracks: Subtitle[];
105
+ activeAudioTrack: string;
106
+ activeSubtitleTrack: string;
107
+ }
108
+ declare class MKVPlayer {
109
+ /**
110
+ * Overrideable in tests to avoid real browser playback probes.
111
+ * @internal
112
+ */
113
+ static _canPlayNatively: (url: string, timeoutMs?: number) => Promise<boolean>;
114
+ /**
115
+ * Overrideable factory for creating the FFmpeg worker. Tests override this
116
+ * to avoid `import.meta.url` (not available in CJS Jest).
117
+ * @internal
118
+ */
119
+ static _workerFactory: (() => Worker) | null;
120
+ private file;
121
+ private playerFile;
122
+ private videoElement;
123
+ private objectUrl;
124
+ private _cancelled;
125
+ private subtitleTrackMap;
126
+ private subtitleBlobUrls;
127
+ private onProgress?;
128
+ private chapters;
129
+ private remuxCache;
130
+ private worker;
131
+ private pendingOperations;
132
+ /**
133
+ * Resolves when track metadata (audio + subtitle) is fully populated.
134
+ * On the FFmpeg path this resolves after initialize() itself. On the native
135
+ * path the probe runs in the background so initialize() returns first —
136
+ * callers that need the real track list should await this promise.
137
+ */
138
+ tracksReady: Promise<void>;
139
+ constructor(file: File, onProgress?: (progress: number) => void);
140
+ private getWorker;
141
+ private _handleWorkerMessage;
142
+ private sendToWorker;
143
+ initialize(videoElement: HTMLVideoElement): Promise<MKVPlayerFile>;
144
+ private _probeForTracks;
145
+ private _remux;
146
+ private _extractSubtitle;
147
+ getAudioTracks(): AudioTrack[];
148
+ getSubtitles(): Subtitle[];
149
+ getChapters(): Chapter[];
150
+ switchAudioTrack(trackId: string): Promise<void>;
151
+ switchSubtitle(trackId: string): Promise<void>;
152
+ getActiveAudioTrack(): string;
153
+ getActiveSubtitleTrack(): string;
154
+ cancel(): void;
155
+ destroy(): void;
156
+ static isCompatible(file: File): boolean;
157
+ }
158
+
159
+ type ProcessedFile = SimplePlayerFile | MKVPlayerFile;
160
+ interface VideoPlayer {
161
+ initialize(videoElement: HTMLVideoElement): Promise<ProcessedFile>;
162
+ getAudioTracks(): AudioTrack[];
163
+ getSubtitles(): Subtitle[];
164
+ getChapters?(): Chapter[];
165
+ switchAudioTrack(trackId: string): Promise<void>;
166
+ switchSubtitle(trackId: string): Promise<void>;
167
+ destroy(): void;
168
+ cancel?(): void;
169
+ /**
170
+ * Resolves when track metadata is fully populated. Only meaningful for
171
+ * MKVPlayer on the native path (where the probe runs after initialize()).
172
+ * For all other players/paths this is already resolved when initialize() returns.
173
+ */
174
+ tracksReady?: Promise<void>;
175
+ }
176
+ declare function createVideoPlayer(file: File, externalSubtitles?: File[], onProgress?: (progress: number) => void): VideoPlayer;
177
+
178
+ declare class UniversalSubtitleManager {
179
+ private records;
180
+ private videoElement;
181
+ private nextId;
182
+ constructor(videoElement?: HTMLVideoElement);
183
+ setVideoElement(videoElement: HTMLVideoElement): void;
184
+ addSubtitleFiles(files: File[]): Promise<Subtitle[]>;
185
+ removeSubtitle(id: string): boolean;
186
+ switchSubtitle(id: string): void;
187
+ /**
188
+ * Sets a time offset (in seconds) for a VTT/SRT subtitle.
189
+ * Regenerates the blob URL with shifted timestamps and updates the track element.
190
+ */
191
+ setOffset(id: string, offsetSeconds: number): Promise<void>;
192
+ /** Returns parsed cue index for a subtitle (empty array for ASS/SSA). */
193
+ getCues(id: string): SubtitleCue[];
194
+ /** Returns cues for all loaded VTT/SRT subtitles merged, for global search. */
195
+ getAllCues(): SubtitleCue[];
196
+ getSubtitles(): Subtitle[];
197
+ clearSubtitles(): void;
198
+ destroy(): void;
199
+ importSubtitles(subtitles: Subtitle[]): void;
200
+ }
201
+
202
+ declare class SubtitleConverter {
203
+ static convertSrtToVtt(srtContent: string): Promise<string>;
204
+ static convertFileToVtt(file: File): Promise<File>;
205
+ }
206
+
207
+ /**
208
+ * Applies a time offset (in seconds) to all cue timestamps in a VTT string.
209
+ * Returns the modified VTT text.
210
+ */
211
+ declare function applyOffsetToVtt(vttText: string, offsetSeconds: number): string;
212
+ /** Re-encodes a VTT blob URL with an applied time offset. Returns a new blob URL. */
213
+ declare function createOffsetVttUrl(originalUrl: string, offsetSeconds: number): Promise<string>;
214
+
215
+ declare class ASSRenderer {
216
+ private canvas;
217
+ private ctx;
218
+ private compiled;
219
+ private animFrame;
220
+ constructor(canvas: HTMLCanvasElement);
221
+ load(assText: string): void;
222
+ start(videoEl: HTMLVideoElement): void;
223
+ stop(): void;
224
+ renderAt(currentTimeSec: number): void;
225
+ private syncCanvasSize;
226
+ private drawDialogue;
227
+ }
228
+
229
+ /**
230
+ * Parses chapter data from an FFmpeg probe/log output.
231
+ *
232
+ * Looks for blocks like:
233
+ * Chapter #0:0: start 0.000000, end 142.500000
234
+ * Metadata:
235
+ * title : Introduction
236
+ *
237
+ * Returns [] (never throws) on malformed or missing input.
238
+ */
239
+ declare function parseChaptersFromFFmpegLog(log: string, totalDuration: number): Chapter[];
240
+ /**
241
+ * Parses chapter data from a WebVTT chapters file.
242
+ *
243
+ * Each cue's identifier is the chapter title; start/end come from the
244
+ * VTT timestamp line "HH:MM:SS.mmm --> HH:MM:SS.mmm".
245
+ *
246
+ * Returns [] (never throws) on malformed or empty input.
247
+ */
248
+ declare function parseChaptersFromVtt(vttText: string): Chapter[];
249
+
250
+ /** Export a playlist as an M3U8 file and trigger a browser download. */
251
+ declare function exportPlaylist(items: PlaylistItem[]): void;
252
+ /** Parse an M3U / M3U8 text file into playlist item descriptors (without id). */
253
+ declare function parseM3U8(text: string): Omit<PlaylistItem, "id">[];
254
+
255
+ type MediaErrorType = 'aborted' | 'network' | 'decode' | 'unsupported' | 'unknown';
256
+ interface ParsedMediaError {
257
+ type: MediaErrorType;
258
+ message: string;
259
+ recoverable: boolean;
260
+ retryable: boolean;
261
+ }
262
+ declare function parseMediaError(error: MediaError | null): ParsedMediaError;
263
+ declare function validateFile(file: File): {
264
+ valid: boolean;
265
+ reason?: string;
266
+ };
267
+
268
+ declare function extractNativeMetadata(videoEl: HTMLVideoElement, file?: File): Partial<VideoMetadata>;
269
+
270
+ declare function captureVideoThumbnail(videoEl: HTMLVideoElement, atSeconds?: number): Promise<string | null>;
271
+
272
+ type ShortcutAction = 'play-pause' | 'seek-forward-5' | 'seek-backward-5' | 'seek-forward-30' | 'seek-backward-30' | 'volume-up' | 'volume-down' | 'mute' | 'fullscreen' | 'next-item' | 'prev-item' | 'screenshot' | 'show-shortcuts' | 'next-chapter' | 'prev-chapter';
273
+ interface ShortcutBinding {
274
+ action: ShortcutAction;
275
+ label: string;
276
+ defaultKey: string;
277
+ key: string;
278
+ modifiers?: {
279
+ ctrl?: boolean;
280
+ shift?: boolean;
281
+ alt?: boolean;
282
+ };
283
+ }
284
+ declare const DEFAULT_SHORTCUTS: ShortcutBinding[];
285
+ declare function loadShortcuts(): ShortcutBinding[];
286
+ declare function saveShortcuts(bindings: ShortcutBinding[]): void;
287
+ declare function matchesShortcut(e: KeyboardEvent, binding: ShortcutBinding): boolean;
288
+ declare function isInteractiveElement(el: EventTarget | null): boolean;
289
+ declare function formatShortcutKey(binding: ShortcutBinding): string;
290
+
291
+ /**
292
+ * Estimates remux speed and ETA from a stream of progress values [0, 1].
293
+ *
294
+ * startTime is initialized lazily on the first update() call with progress > 0
295
+ * to avoid capturing bootstrap latency from the constructor.
296
+ *
297
+ * Usage:
298
+ * const est = new ProgressEstimator(fileSizeBytes);
299
+ * est.update(0.42); // call on each progress event
300
+ * const { speedMBps, etaSeconds } = est.getEstimate();
301
+ */
302
+ declare class ProgressEstimator {
303
+ private readonly fileSizeBytes;
304
+ private startTime;
305
+ private lastProgress;
306
+ constructor(fileSizeBytes: number);
307
+ update(progress: number): void;
308
+ getEstimate(): {
309
+ speedMBps: number;
310
+ etaSeconds: number | null;
311
+ };
312
+ }
313
+
314
+ declare function getFFmpeg(): Promise<FFmpeg>;
315
+ declare function resetFFmpeg(): void;
316
+
317
+ export { ASSRenderer, type AudioTrack, type AudioTrackMeta, CancellationError, type Chapter, DEFAULT_SHORTCUTS, type LightBirdConfig, MKVPlayer, type MKVPlayerFile, type MediaErrorType, type ParsedMediaError, type PlaylistItem, type ProcessedFile, ProgressEstimator, type ShortcutAction, type ShortcutBinding, SimplePlayer, type SimplePlayerFile, type Subtitle, SubtitleConverter, type SubtitleCue, type SubtitleTrackMeta, UniversalSubtitleManager, type VideoFilters, type VideoMetadata, type VideoPlayer, applyOffsetToVtt, captureVideoThumbnail, configureLightBird, createOffsetVttUrl, createVideoPlayer, exportPlaylist, extractNativeMetadata, formatShortcutKey, getFFmpeg, isInteractiveElement, loadShortcuts, matchesShortcut, parseChaptersFromFFmpegLog, parseChaptersFromVtt, parseM3U8, parseMediaError, resetFFmpeg, saveShortcuts, validateFile };
@@ -0,0 +1,317 @@
1
+ import { FFmpeg } from '@ffmpeg/ffmpeg';
2
+
3
+ interface LightBirdConfig {
4
+ ffmpegCDN?: string;
5
+ }
6
+ declare function configureLightBird(options: LightBirdConfig): void;
7
+
8
+ interface Chapter {
9
+ index: number;
10
+ title: string;
11
+ startTime: number;
12
+ endTime: number;
13
+ }
14
+ interface PlaylistItem {
15
+ id: string;
16
+ name: string;
17
+ url: string;
18
+ type: 'video' | 'stream';
19
+ file?: File;
20
+ duration?: number;
21
+ }
22
+ interface SubtitleCue {
23
+ startTime: number;
24
+ endTime: number;
25
+ text: string;
26
+ }
27
+ interface Subtitle {
28
+ id: string;
29
+ name: string;
30
+ lang: string;
31
+ url?: string;
32
+ type: 'embedded' | 'external';
33
+ /** Detected source format (vtt, srt, ass, ssa). Defaults to 'vtt'. */
34
+ format?: 'vtt' | 'srt' | 'ass' | 'ssa';
35
+ }
36
+ interface AudioTrack {
37
+ id: string;
38
+ name: string;
39
+ lang: string;
40
+ }
41
+ interface VideoFilters {
42
+ brightness: number;
43
+ contrast: number;
44
+ saturate: number;
45
+ hue: number;
46
+ }
47
+ interface VideoMetadata {
48
+ filename: string;
49
+ fileSize: number | null;
50
+ duration: number;
51
+ container: string;
52
+ width: number;
53
+ height: number;
54
+ frameRate: number | null;
55
+ videoBitrate: number | null;
56
+ videoCodec: string | null;
57
+ colorSpace: string | null;
58
+ audioTracks: AudioTrackMeta[];
59
+ subtitleTracks: SubtitleTrackMeta[];
60
+ }
61
+ interface AudioTrackMeta {
62
+ index: number;
63
+ codec: string | null;
64
+ channels: number | null;
65
+ sampleRate: number | null;
66
+ language: string | null;
67
+ bitrate: number | null;
68
+ }
69
+ interface SubtitleTrackMeta {
70
+ index: number;
71
+ format: string | null;
72
+ language: string | null;
73
+ }
74
+
75
+ interface SimplePlayerFile {
76
+ name: string;
77
+ file: File;
78
+ url: string;
79
+ externalSubtitles?: Subtitle[];
80
+ }
81
+ declare class SimplePlayer {
82
+ private file;
83
+ private videoElement;
84
+ private currentSubtitleTrackIndex;
85
+ constructor(file: File, externalSubtitles?: File[]);
86
+ private processExternalSubtitles;
87
+ initialize(videoElement: HTMLVideoElement): Promise<SimplePlayerFile>;
88
+ getAudioTracks(): AudioTrack[];
89
+ getSubtitles(): Subtitle[];
90
+ switchAudioTrack(trackId: string): Promise<void>;
91
+ switchSubtitle(trackId: string): Promise<void>;
92
+ destroy(): void;
93
+ static isCompatible(file: File): boolean;
94
+ }
95
+
96
+ declare class CancellationError extends Error {
97
+ constructor();
98
+ }
99
+ interface MKVPlayerFile {
100
+ name: string;
101
+ file: File;
102
+ videoUrl?: string;
103
+ audioTracks: AudioTrack[];
104
+ subtitleTracks: Subtitle[];
105
+ activeAudioTrack: string;
106
+ activeSubtitleTrack: string;
107
+ }
108
+ declare class MKVPlayer {
109
+ /**
110
+ * Overrideable in tests to avoid real browser playback probes.
111
+ * @internal
112
+ */
113
+ static _canPlayNatively: (url: string, timeoutMs?: number) => Promise<boolean>;
114
+ /**
115
+ * Overrideable factory for creating the FFmpeg worker. Tests override this
116
+ * to avoid `import.meta.url` (not available in CJS Jest).
117
+ * @internal
118
+ */
119
+ static _workerFactory: (() => Worker) | null;
120
+ private file;
121
+ private playerFile;
122
+ private videoElement;
123
+ private objectUrl;
124
+ private _cancelled;
125
+ private subtitleTrackMap;
126
+ private subtitleBlobUrls;
127
+ private onProgress?;
128
+ private chapters;
129
+ private remuxCache;
130
+ private worker;
131
+ private pendingOperations;
132
+ /**
133
+ * Resolves when track metadata (audio + subtitle) is fully populated.
134
+ * On the FFmpeg path this resolves after initialize() itself. On the native
135
+ * path the probe runs in the background so initialize() returns first —
136
+ * callers that need the real track list should await this promise.
137
+ */
138
+ tracksReady: Promise<void>;
139
+ constructor(file: File, onProgress?: (progress: number) => void);
140
+ private getWorker;
141
+ private _handleWorkerMessage;
142
+ private sendToWorker;
143
+ initialize(videoElement: HTMLVideoElement): Promise<MKVPlayerFile>;
144
+ private _probeForTracks;
145
+ private _remux;
146
+ private _extractSubtitle;
147
+ getAudioTracks(): AudioTrack[];
148
+ getSubtitles(): Subtitle[];
149
+ getChapters(): Chapter[];
150
+ switchAudioTrack(trackId: string): Promise<void>;
151
+ switchSubtitle(trackId: string): Promise<void>;
152
+ getActiveAudioTrack(): string;
153
+ getActiveSubtitleTrack(): string;
154
+ cancel(): void;
155
+ destroy(): void;
156
+ static isCompatible(file: File): boolean;
157
+ }
158
+
159
+ type ProcessedFile = SimplePlayerFile | MKVPlayerFile;
160
+ interface VideoPlayer {
161
+ initialize(videoElement: HTMLVideoElement): Promise<ProcessedFile>;
162
+ getAudioTracks(): AudioTrack[];
163
+ getSubtitles(): Subtitle[];
164
+ getChapters?(): Chapter[];
165
+ switchAudioTrack(trackId: string): Promise<void>;
166
+ switchSubtitle(trackId: string): Promise<void>;
167
+ destroy(): void;
168
+ cancel?(): void;
169
+ /**
170
+ * Resolves when track metadata is fully populated. Only meaningful for
171
+ * MKVPlayer on the native path (where the probe runs after initialize()).
172
+ * For all other players/paths this is already resolved when initialize() returns.
173
+ */
174
+ tracksReady?: Promise<void>;
175
+ }
176
+ declare function createVideoPlayer(file: File, externalSubtitles?: File[], onProgress?: (progress: number) => void): VideoPlayer;
177
+
178
+ declare class UniversalSubtitleManager {
179
+ private records;
180
+ private videoElement;
181
+ private nextId;
182
+ constructor(videoElement?: HTMLVideoElement);
183
+ setVideoElement(videoElement: HTMLVideoElement): void;
184
+ addSubtitleFiles(files: File[]): Promise<Subtitle[]>;
185
+ removeSubtitle(id: string): boolean;
186
+ switchSubtitle(id: string): void;
187
+ /**
188
+ * Sets a time offset (in seconds) for a VTT/SRT subtitle.
189
+ * Regenerates the blob URL with shifted timestamps and updates the track element.
190
+ */
191
+ setOffset(id: string, offsetSeconds: number): Promise<void>;
192
+ /** Returns parsed cue index for a subtitle (empty array for ASS/SSA). */
193
+ getCues(id: string): SubtitleCue[];
194
+ /** Returns cues for all loaded VTT/SRT subtitles merged, for global search. */
195
+ getAllCues(): SubtitleCue[];
196
+ getSubtitles(): Subtitle[];
197
+ clearSubtitles(): void;
198
+ destroy(): void;
199
+ importSubtitles(subtitles: Subtitle[]): void;
200
+ }
201
+
202
+ declare class SubtitleConverter {
203
+ static convertSrtToVtt(srtContent: string): Promise<string>;
204
+ static convertFileToVtt(file: File): Promise<File>;
205
+ }
206
+
207
+ /**
208
+ * Applies a time offset (in seconds) to all cue timestamps in a VTT string.
209
+ * Returns the modified VTT text.
210
+ */
211
+ declare function applyOffsetToVtt(vttText: string, offsetSeconds: number): string;
212
+ /** Re-encodes a VTT blob URL with an applied time offset. Returns a new blob URL. */
213
+ declare function createOffsetVttUrl(originalUrl: string, offsetSeconds: number): Promise<string>;
214
+
215
+ declare class ASSRenderer {
216
+ private canvas;
217
+ private ctx;
218
+ private compiled;
219
+ private animFrame;
220
+ constructor(canvas: HTMLCanvasElement);
221
+ load(assText: string): void;
222
+ start(videoEl: HTMLVideoElement): void;
223
+ stop(): void;
224
+ renderAt(currentTimeSec: number): void;
225
+ private syncCanvasSize;
226
+ private drawDialogue;
227
+ }
228
+
229
+ /**
230
+ * Parses chapter data from an FFmpeg probe/log output.
231
+ *
232
+ * Looks for blocks like:
233
+ * Chapter #0:0: start 0.000000, end 142.500000
234
+ * Metadata:
235
+ * title : Introduction
236
+ *
237
+ * Returns [] (never throws) on malformed or missing input.
238
+ */
239
+ declare function parseChaptersFromFFmpegLog(log: string, totalDuration: number): Chapter[];
240
+ /**
241
+ * Parses chapter data from a WebVTT chapters file.
242
+ *
243
+ * Each cue's identifier is the chapter title; start/end come from the
244
+ * VTT timestamp line "HH:MM:SS.mmm --> HH:MM:SS.mmm".
245
+ *
246
+ * Returns [] (never throws) on malformed or empty input.
247
+ */
248
+ declare function parseChaptersFromVtt(vttText: string): Chapter[];
249
+
250
+ /** Export a playlist as an M3U8 file and trigger a browser download. */
251
+ declare function exportPlaylist(items: PlaylistItem[]): void;
252
+ /** Parse an M3U / M3U8 text file into playlist item descriptors (without id). */
253
+ declare function parseM3U8(text: string): Omit<PlaylistItem, "id">[];
254
+
255
+ type MediaErrorType = 'aborted' | 'network' | 'decode' | 'unsupported' | 'unknown';
256
+ interface ParsedMediaError {
257
+ type: MediaErrorType;
258
+ message: string;
259
+ recoverable: boolean;
260
+ retryable: boolean;
261
+ }
262
+ declare function parseMediaError(error: MediaError | null): ParsedMediaError;
263
+ declare function validateFile(file: File): {
264
+ valid: boolean;
265
+ reason?: string;
266
+ };
267
+
268
+ declare function extractNativeMetadata(videoEl: HTMLVideoElement, file?: File): Partial<VideoMetadata>;
269
+
270
+ declare function captureVideoThumbnail(videoEl: HTMLVideoElement, atSeconds?: number): Promise<string | null>;
271
+
272
+ type ShortcutAction = 'play-pause' | 'seek-forward-5' | 'seek-backward-5' | 'seek-forward-30' | 'seek-backward-30' | 'volume-up' | 'volume-down' | 'mute' | 'fullscreen' | 'next-item' | 'prev-item' | 'screenshot' | 'show-shortcuts' | 'next-chapter' | 'prev-chapter';
273
+ interface ShortcutBinding {
274
+ action: ShortcutAction;
275
+ label: string;
276
+ defaultKey: string;
277
+ key: string;
278
+ modifiers?: {
279
+ ctrl?: boolean;
280
+ shift?: boolean;
281
+ alt?: boolean;
282
+ };
283
+ }
284
+ declare const DEFAULT_SHORTCUTS: ShortcutBinding[];
285
+ declare function loadShortcuts(): ShortcutBinding[];
286
+ declare function saveShortcuts(bindings: ShortcutBinding[]): void;
287
+ declare function matchesShortcut(e: KeyboardEvent, binding: ShortcutBinding): boolean;
288
+ declare function isInteractiveElement(el: EventTarget | null): boolean;
289
+ declare function formatShortcutKey(binding: ShortcutBinding): string;
290
+
291
+ /**
292
+ * Estimates remux speed and ETA from a stream of progress values [0, 1].
293
+ *
294
+ * startTime is initialized lazily on the first update() call with progress > 0
295
+ * to avoid capturing bootstrap latency from the constructor.
296
+ *
297
+ * Usage:
298
+ * const est = new ProgressEstimator(fileSizeBytes);
299
+ * est.update(0.42); // call on each progress event
300
+ * const { speedMBps, etaSeconds } = est.getEstimate();
301
+ */
302
+ declare class ProgressEstimator {
303
+ private readonly fileSizeBytes;
304
+ private startTime;
305
+ private lastProgress;
306
+ constructor(fileSizeBytes: number);
307
+ update(progress: number): void;
308
+ getEstimate(): {
309
+ speedMBps: number;
310
+ etaSeconds: number | null;
311
+ };
312
+ }
313
+
314
+ declare function getFFmpeg(): Promise<FFmpeg>;
315
+ declare function resetFFmpeg(): void;
316
+
317
+ export { ASSRenderer, type AudioTrack, type AudioTrackMeta, CancellationError, type Chapter, DEFAULT_SHORTCUTS, type LightBirdConfig, MKVPlayer, type MKVPlayerFile, type MediaErrorType, type ParsedMediaError, type PlaylistItem, type ProcessedFile, ProgressEstimator, type ShortcutAction, type ShortcutBinding, SimplePlayer, type SimplePlayerFile, type Subtitle, SubtitleConverter, type SubtitleCue, type SubtitleTrackMeta, UniversalSubtitleManager, type VideoFilters, type VideoMetadata, type VideoPlayer, applyOffsetToVtt, captureVideoThumbnail, configureLightBird, createOffsetVttUrl, createVideoPlayer, exportPlaylist, extractNativeMetadata, formatShortcutKey, getFFmpeg, isInteractiveElement, loadShortcuts, matchesShortcut, parseChaptersFromFFmpegLog, parseChaptersFromVtt, parseM3U8, parseMediaError, resetFFmpeg, saveShortcuts, validateFile };