@lightbird/core 0.4.0 → 0.6.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.
- package/dist/index.cjs +246 -43
- package/dist/index.d.cts +25 -1
- package/dist/index.d.ts +25 -1
- package/dist/index.js +245 -44
- package/dist/react/index.cjs +313 -0
- package/dist/react/index.d.cts +101 -1
- package/dist/react/index.d.ts +101 -1
- package/dist/react/index.js +311 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -239,6 +239,121 @@ function parseVttTimestamp(ts) {
|
|
|
239
239
|
return NaN;
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
+
// src/utils/language-names.ts
|
|
243
|
+
var LANGUAGE_NAMES = {
|
|
244
|
+
// English
|
|
245
|
+
en: "English",
|
|
246
|
+
eng: "English",
|
|
247
|
+
// Japanese
|
|
248
|
+
ja: "Japanese",
|
|
249
|
+
jpn: "Japanese",
|
|
250
|
+
// Chinese
|
|
251
|
+
zh: "Chinese",
|
|
252
|
+
chi: "Chinese",
|
|
253
|
+
zho: "Chinese",
|
|
254
|
+
// Korean
|
|
255
|
+
ko: "Korean",
|
|
256
|
+
kor: "Korean",
|
|
257
|
+
// French
|
|
258
|
+
fr: "French",
|
|
259
|
+
fre: "French",
|
|
260
|
+
fra: "French",
|
|
261
|
+
// German
|
|
262
|
+
de: "German",
|
|
263
|
+
ger: "German",
|
|
264
|
+
deu: "German",
|
|
265
|
+
// Spanish
|
|
266
|
+
es: "Spanish",
|
|
267
|
+
spa: "Spanish",
|
|
268
|
+
// Italian
|
|
269
|
+
it: "Italian",
|
|
270
|
+
ita: "Italian",
|
|
271
|
+
// Portuguese
|
|
272
|
+
pt: "Portuguese",
|
|
273
|
+
por: "Portuguese",
|
|
274
|
+
// Russian
|
|
275
|
+
ru: "Russian",
|
|
276
|
+
rus: "Russian",
|
|
277
|
+
// Dutch
|
|
278
|
+
nl: "Dutch",
|
|
279
|
+
dut: "Dutch",
|
|
280
|
+
nld: "Dutch",
|
|
281
|
+
// Polish
|
|
282
|
+
pl: "Polish",
|
|
283
|
+
pol: "Polish",
|
|
284
|
+
// Arabic
|
|
285
|
+
ar: "Arabic",
|
|
286
|
+
ara: "Arabic",
|
|
287
|
+
// Hindi
|
|
288
|
+
hi: "Hindi",
|
|
289
|
+
hin: "Hindi",
|
|
290
|
+
// Bengali
|
|
291
|
+
bn: "Bengali",
|
|
292
|
+
ben: "Bengali",
|
|
293
|
+
// Turkish
|
|
294
|
+
tr: "Turkish",
|
|
295
|
+
tur: "Turkish",
|
|
296
|
+
// Swedish
|
|
297
|
+
sv: "Swedish",
|
|
298
|
+
swe: "Swedish",
|
|
299
|
+
// Norwegian
|
|
300
|
+
no: "Norwegian",
|
|
301
|
+
nor: "Norwegian",
|
|
302
|
+
// Danish
|
|
303
|
+
da: "Danish",
|
|
304
|
+
dan: "Danish",
|
|
305
|
+
// Finnish
|
|
306
|
+
fi: "Finnish",
|
|
307
|
+
fin: "Finnish",
|
|
308
|
+
// Greek
|
|
309
|
+
el: "Greek",
|
|
310
|
+
gre: "Greek",
|
|
311
|
+
ell: "Greek",
|
|
312
|
+
// Hebrew
|
|
313
|
+
he: "Hebrew",
|
|
314
|
+
heb: "Hebrew",
|
|
315
|
+
// Thai
|
|
316
|
+
th: "Thai",
|
|
317
|
+
tha: "Thai",
|
|
318
|
+
// Vietnamese
|
|
319
|
+
vi: "Vietnamese",
|
|
320
|
+
vie: "Vietnamese",
|
|
321
|
+
// Indonesian
|
|
322
|
+
id: "Indonesian",
|
|
323
|
+
ind: "Indonesian",
|
|
324
|
+
// Malay
|
|
325
|
+
ms: "Malay",
|
|
326
|
+
may: "Malay",
|
|
327
|
+
msa: "Malay",
|
|
328
|
+
// Czech
|
|
329
|
+
cs: "Czech",
|
|
330
|
+
cze: "Czech",
|
|
331
|
+
ces: "Czech",
|
|
332
|
+
// Hungarian
|
|
333
|
+
hu: "Hungarian",
|
|
334
|
+
hun: "Hungarian",
|
|
335
|
+
// Romanian
|
|
336
|
+
ro: "Romanian",
|
|
337
|
+
rum: "Romanian",
|
|
338
|
+
ron: "Romanian",
|
|
339
|
+
// Ukrainian
|
|
340
|
+
uk: "Ukrainian",
|
|
341
|
+
ukr: "Ukrainian",
|
|
342
|
+
// Tamil
|
|
343
|
+
ta: "Tamil",
|
|
344
|
+
tam: "Tamil",
|
|
345
|
+
// Telugu
|
|
346
|
+
te: "Telugu",
|
|
347
|
+
tel: "Telugu"
|
|
348
|
+
};
|
|
349
|
+
var UNDETERMINED = /* @__PURE__ */ new Set(["", "und", "unknown", "mis", "zxx", "mul"]);
|
|
350
|
+
function getLanguageName(code) {
|
|
351
|
+
if (!code) return void 0;
|
|
352
|
+
const normalized = code.trim().toLowerCase();
|
|
353
|
+
if (UNDETERMINED.has(normalized)) return void 0;
|
|
354
|
+
return LANGUAGE_NAMES[normalized] ?? code.trim();
|
|
355
|
+
}
|
|
356
|
+
|
|
242
357
|
// src/players/mkv-player.ts
|
|
243
358
|
async function canPlayNatively(objectUrl, timeoutMs = 3e3) {
|
|
244
359
|
return new Promise((resolve) => {
|
|
@@ -267,23 +382,76 @@ function parseStreamInfo(logs) {
|
|
|
267
382
|
const videoTracks = [];
|
|
268
383
|
const audioTracks = [];
|
|
269
384
|
const subtitleTracks = [];
|
|
385
|
+
let current = null;
|
|
270
386
|
const lines = logs.split("\n");
|
|
271
387
|
for (const line of lines) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
388
|
+
if (/^\s*Stream #\d+:\d+/.test(line)) {
|
|
389
|
+
current = null;
|
|
390
|
+
}
|
|
391
|
+
const streamMatch = line.match(
|
|
392
|
+
/Stream #\d+:\d+(?:\((\w+)\))?: (Video|Audio|Subtitle): (.+)$/i
|
|
393
|
+
);
|
|
394
|
+
if (streamMatch) {
|
|
395
|
+
const [, lang, type, rest] = streamMatch;
|
|
396
|
+
const codec = rest.trim().split(/[\s,]/)[0];
|
|
397
|
+
const forced = /\(forced\)/i.test(rest);
|
|
398
|
+
const isDefault = /\(default\)/i.test(rest);
|
|
399
|
+
const t = type.toLowerCase();
|
|
400
|
+
const bucket = t === "video" ? videoTracks : t === "audio" ? audioTracks : subtitleTracks;
|
|
401
|
+
current = {
|
|
402
|
+
index: bucket.length,
|
|
403
|
+
type: t,
|
|
404
|
+
codec,
|
|
405
|
+
lang,
|
|
406
|
+
forced,
|
|
407
|
+
default: isDefault
|
|
408
|
+
};
|
|
409
|
+
bucket.push(current);
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
if (/^\s*(Chapter #|Program )/i.test(line)) {
|
|
413
|
+
current = null;
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
if (current && current.title === void 0) {
|
|
417
|
+
const titleMatch = line.match(/^\s*title\s*:\s*(.+)$/i);
|
|
418
|
+
if (titleMatch) {
|
|
419
|
+
current.title = titleMatch[1].trim();
|
|
420
|
+
}
|
|
283
421
|
}
|
|
284
422
|
}
|
|
285
423
|
return { videoTracks, audioTracks, subtitleTracks };
|
|
286
424
|
}
|
|
425
|
+
function formatTrackName(index, t) {
|
|
426
|
+
let label = t.title?.trim() || `Track ${index + 1}`;
|
|
427
|
+
const langName = getLanguageName(t.lang);
|
|
428
|
+
if (langName) label += ` - [${langName}]`;
|
|
429
|
+
if (t.forced) label += " [Forced]";
|
|
430
|
+
return label;
|
|
431
|
+
}
|
|
432
|
+
function buildAudioTracks(tracks) {
|
|
433
|
+
if (tracks.length === 0) {
|
|
434
|
+
return [{ id: "0", name: "Default Audio", lang: "unknown" }];
|
|
435
|
+
}
|
|
436
|
+
return tracks.map((t, i) => ({
|
|
437
|
+
id: String(i),
|
|
438
|
+
name: formatTrackName(i, t),
|
|
439
|
+
lang: t.lang ?? "unknown"
|
|
440
|
+
}));
|
|
441
|
+
}
|
|
442
|
+
function buildSubtitleTracks(tracks, trackMap) {
|
|
443
|
+
trackMap.clear();
|
|
444
|
+
return tracks.map((t, i) => {
|
|
445
|
+
const id = String(i);
|
|
446
|
+
trackMap.set(id, i);
|
|
447
|
+
return {
|
|
448
|
+
id,
|
|
449
|
+
name: formatTrackName(i, t),
|
|
450
|
+
lang: t.lang ?? "unknown",
|
|
451
|
+
type: "embedded"
|
|
452
|
+
};
|
|
453
|
+
});
|
|
454
|
+
}
|
|
287
455
|
var _MKVPlayer = class _MKVPlayer {
|
|
288
456
|
constructor(file, onProgress) {
|
|
289
457
|
this.videoElement = null;
|
|
@@ -402,22 +570,11 @@ var _MKVPlayer = class _MKVPlayer {
|
|
|
402
570
|
});
|
|
403
571
|
const { audioTracks, subtitleTracks } = parseStreamInfo(result.logs);
|
|
404
572
|
this.chapters = parseChaptersFromFFmpegLog(result.logs, videoElement.duration || 0);
|
|
405
|
-
this.playerFile.audioTracks = audioTracks
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
this.subtitleTrackMap.clear();
|
|
411
|
-
this.playerFile.subtitleTracks = subtitleTracks.map((t, i) => {
|
|
412
|
-
const id = String(i);
|
|
413
|
-
this.subtitleTrackMap.set(id, i);
|
|
414
|
-
return {
|
|
415
|
-
id,
|
|
416
|
-
name: t.title ?? (t.lang ? `Subtitle ${i + 1} (${t.lang})` : `Subtitle ${i + 1}`),
|
|
417
|
-
lang: t.lang ?? "unknown",
|
|
418
|
-
type: "embedded"
|
|
419
|
-
};
|
|
420
|
-
});
|
|
573
|
+
this.playerFile.audioTracks = buildAudioTracks(audioTracks);
|
|
574
|
+
this.playerFile.subtitleTracks = buildSubtitleTracks(
|
|
575
|
+
subtitleTracks,
|
|
576
|
+
this.subtitleTrackMap
|
|
577
|
+
);
|
|
421
578
|
const blob = new Blob([result.data], { type: "video/mp4" });
|
|
422
579
|
const url = URL.createObjectURL(blob);
|
|
423
580
|
this.remuxCache.set(0, url);
|
|
@@ -448,22 +605,11 @@ var _MKVPlayer = class _MKVPlayer {
|
|
|
448
605
|
});
|
|
449
606
|
if (this._cancelled) return;
|
|
450
607
|
const { audioTracks, subtitleTracks } = parseStreamInfo(probeResult.logs);
|
|
451
|
-
this.playerFile.audioTracks = audioTracks
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
this.subtitleTrackMap.clear();
|
|
457
|
-
this.playerFile.subtitleTracks = subtitleTracks.map((t, i) => {
|
|
458
|
-
const id = String(i);
|
|
459
|
-
this.subtitleTrackMap.set(id, i);
|
|
460
|
-
return {
|
|
461
|
-
id,
|
|
462
|
-
name: t.title ?? (t.lang ? `Subtitle ${i + 1} (${t.lang})` : `Subtitle ${i + 1}`),
|
|
463
|
-
lang: t.lang ?? "unknown",
|
|
464
|
-
type: "embedded"
|
|
465
|
-
};
|
|
466
|
-
});
|
|
608
|
+
this.playerFile.audioTracks = buildAudioTracks(audioTracks);
|
|
609
|
+
this.playerFile.subtitleTracks = buildSubtitleTracks(
|
|
610
|
+
subtitleTracks,
|
|
611
|
+
this.subtitleTrackMap
|
|
612
|
+
);
|
|
467
613
|
}
|
|
468
614
|
async _remux(audioTrackIndex) {
|
|
469
615
|
const cached = this.remuxCache.get(audioTrackIndex);
|
|
@@ -1412,6 +1558,61 @@ async function captureVideoThumbnail(videoEl, atSeconds = 5) {
|
|
|
1412
1558
|
}
|
|
1413
1559
|
});
|
|
1414
1560
|
}
|
|
1561
|
+
async function captureFrameAt(videoEl, timeSeconds, width = 160, height = 90) {
|
|
1562
|
+
return new Promise((resolve) => {
|
|
1563
|
+
const canvas = document.createElement("canvas");
|
|
1564
|
+
canvas.width = width;
|
|
1565
|
+
canvas.height = height;
|
|
1566
|
+
const ctx = canvas.getContext("2d");
|
|
1567
|
+
if (!ctx) {
|
|
1568
|
+
resolve(null);
|
|
1569
|
+
return;
|
|
1570
|
+
}
|
|
1571
|
+
let settled = false;
|
|
1572
|
+
const cleanup = () => {
|
|
1573
|
+
videoEl.removeEventListener("seeked", onSeeked);
|
|
1574
|
+
videoEl.removeEventListener("error", onError);
|
|
1575
|
+
videoEl.removeEventListener("loadedmetadata", onLoadedMetadata);
|
|
1576
|
+
};
|
|
1577
|
+
const finish = (value) => {
|
|
1578
|
+
if (settled) return;
|
|
1579
|
+
settled = true;
|
|
1580
|
+
cleanup();
|
|
1581
|
+
resolve(value);
|
|
1582
|
+
};
|
|
1583
|
+
const draw = () => {
|
|
1584
|
+
try {
|
|
1585
|
+
ctx.drawImage(videoEl, 0, 0, width, height);
|
|
1586
|
+
finish(canvas.toDataURL("image/jpeg", 0.6));
|
|
1587
|
+
} catch {
|
|
1588
|
+
finish(null);
|
|
1589
|
+
}
|
|
1590
|
+
};
|
|
1591
|
+
const seekTo = () => {
|
|
1592
|
+
const duration = videoEl.duration || 0;
|
|
1593
|
+
const target = duration > 0 ? Math.max(0, Math.min(timeSeconds, duration)) : Math.max(0, timeSeconds);
|
|
1594
|
+
if (Math.abs(videoEl.currentTime - target) < 0.05) {
|
|
1595
|
+
draw();
|
|
1596
|
+
return;
|
|
1597
|
+
}
|
|
1598
|
+
try {
|
|
1599
|
+
videoEl.currentTime = target;
|
|
1600
|
+
} catch {
|
|
1601
|
+
finish(null);
|
|
1602
|
+
}
|
|
1603
|
+
};
|
|
1604
|
+
const onSeeked = () => draw();
|
|
1605
|
+
const onError = () => finish(null);
|
|
1606
|
+
const onLoadedMetadata = () => seekTo();
|
|
1607
|
+
videoEl.addEventListener("seeked", onSeeked);
|
|
1608
|
+
videoEl.addEventListener("error", onError);
|
|
1609
|
+
if (videoEl.readyState >= 1) {
|
|
1610
|
+
seekTo();
|
|
1611
|
+
} else {
|
|
1612
|
+
videoEl.addEventListener("loadedmetadata", onLoadedMetadata, { once: true });
|
|
1613
|
+
}
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1415
1616
|
|
|
1416
1617
|
// src/utils/keyboard-shortcuts.ts
|
|
1417
1618
|
var DEFAULT_SHORTCUTS = [
|
|
@@ -1555,6 +1756,7 @@ exports.UniversalSubtitleManager = UniversalSubtitleManager;
|
|
|
1555
1756
|
exports.VIDEO_EXTENSIONS = VIDEO_EXTENSIONS;
|
|
1556
1757
|
exports.acceptDisclaimer = acceptDisclaimer;
|
|
1557
1758
|
exports.applyOffsetToVtt = applyOffsetToVtt;
|
|
1759
|
+
exports.captureFrameAt = captureFrameAt;
|
|
1558
1760
|
exports.captureVideoThumbnail = captureVideoThumbnail;
|
|
1559
1761
|
exports.configureLightBird = configureLightBird;
|
|
1560
1762
|
exports.createOffsetVttUrl = createOffsetVttUrl;
|
|
@@ -1564,6 +1766,7 @@ exports.exportPlaylist = exportPlaylist;
|
|
|
1564
1766
|
exports.extractNativeMetadata = extractNativeMetadata;
|
|
1565
1767
|
exports.formatShortcutKey = formatShortcutKey;
|
|
1566
1768
|
exports.getFFmpeg = getFFmpeg;
|
|
1769
|
+
exports.getLanguageName = getLanguageName;
|
|
1567
1770
|
exports.getVideoFiles = getVideoFiles;
|
|
1568
1771
|
exports.getWebTorrentClient = getWebTorrentClient;
|
|
1569
1772
|
exports.hasAcceptedDisclaimer = hasAcceptedDisclaimer;
|
package/dist/index.d.cts
CHANGED
|
@@ -372,6 +372,21 @@ declare function validateFile(file: File): {
|
|
|
372
372
|
declare function extractNativeMetadata(videoEl: HTMLVideoElement, file?: File): Partial<VideoMetadata>;
|
|
373
373
|
|
|
374
374
|
declare function captureVideoThumbnail(videoEl: HTMLVideoElement, atSeconds?: number): Promise<string | null>;
|
|
375
|
+
/**
|
|
376
|
+
* Capture a single video frame at a specific timestamp.
|
|
377
|
+
*
|
|
378
|
+
* Unlike {@link captureVideoThumbnail}, this does not save/restore
|
|
379
|
+
* `currentTime` — it is built for a dedicated, offscreen preview video so the
|
|
380
|
+
* main playback element is never disturbed. Powers seek-bar hover previews.
|
|
381
|
+
*
|
|
382
|
+
* @param videoEl - A video element (typically offscreen) to seek and capture.
|
|
383
|
+
* @param timeSeconds - Timestamp to capture, clamped to the video duration.
|
|
384
|
+
* @param width - Output thumbnail width in pixels.
|
|
385
|
+
* @param height - Output thumbnail height in pixels.
|
|
386
|
+
* @returns A JPEG data URL, or `null` if capture failed (no 2d context, a
|
|
387
|
+
* tainted canvas, or a media error).
|
|
388
|
+
*/
|
|
389
|
+
declare function captureFrameAt(videoEl: HTMLVideoElement, timeSeconds: number, width?: number, height?: number): Promise<string | null>;
|
|
375
390
|
|
|
376
391
|
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';
|
|
377
392
|
interface ShortcutBinding {
|
|
@@ -426,4 +441,13 @@ declare class ProgressEstimator {
|
|
|
426
441
|
declare function getFFmpeg(): Promise<FFmpeg>;
|
|
427
442
|
declare function resetFFmpeg(): void;
|
|
428
443
|
|
|
429
|
-
|
|
444
|
+
/**
|
|
445
|
+
* Resolve an ISO 639 language code to its English language name, mirroring how
|
|
446
|
+
* VLC labels tracks.
|
|
447
|
+
*
|
|
448
|
+
* @returns The full language name, or the original code if it's unrecognised
|
|
449
|
+
* (VLC behaviour), or `undefined` if the code is absent/undetermined.
|
|
450
|
+
*/
|
|
451
|
+
declare function getLanguageName(code?: string | null): string | undefined;
|
|
452
|
+
|
|
453
|
+
export { ASSRenderer, type AudioTrack, type AudioTrackMeta, CancellationError, type Chapter, DEFAULT_SHORTCUTS, DEFAULT_TRACKERS, DISCLAIMER_KEY, FLAG_MAGNET_LINK, HLSPlayer, type HLSPlayerFile, type LightBirdConfig, MKVPlayer, type MKVPlayerFile, type MediaErrorType, type ParsedMediaError, type PlaylistItem, type ProcessedFile, ProgressEstimator, type QualityLevel, type ShortcutAction, type ShortcutBinding, SimplePlayer, type SimplePlayerFile, type Subtitle, SubtitleConverter, type SubtitleCue, type SubtitleTrackMeta, type TorrentStatus, UniversalSubtitleManager, VIDEO_EXTENSIONS, type VideoFilters, type VideoMetadata, type VideoPlayer, acceptDisclaimer, applyOffsetToVtt, captureFrameAt, captureVideoThumbnail, configureLightBird, createOffsetVttUrl, createVideoPlayer, destroyWebTorrentClient, exportPlaylist, extractNativeMetadata, formatShortcutKey, getFFmpeg, getLanguageName, getVideoFiles, getWebTorrentClient, hasAcceptedDisclaimer, initFeatureFlags, isHlsUrl, isInteractiveElement, isMagnetUri, isVideoFile, loadShortcuts, matchesShortcut, parseChaptersFromFFmpegLog, parseChaptersFromVtt, parseM3U8, parseMediaError, resetFFmpeg, saveShortcuts, validateFile };
|
package/dist/index.d.ts
CHANGED
|
@@ -372,6 +372,21 @@ declare function validateFile(file: File): {
|
|
|
372
372
|
declare function extractNativeMetadata(videoEl: HTMLVideoElement, file?: File): Partial<VideoMetadata>;
|
|
373
373
|
|
|
374
374
|
declare function captureVideoThumbnail(videoEl: HTMLVideoElement, atSeconds?: number): Promise<string | null>;
|
|
375
|
+
/**
|
|
376
|
+
* Capture a single video frame at a specific timestamp.
|
|
377
|
+
*
|
|
378
|
+
* Unlike {@link captureVideoThumbnail}, this does not save/restore
|
|
379
|
+
* `currentTime` — it is built for a dedicated, offscreen preview video so the
|
|
380
|
+
* main playback element is never disturbed. Powers seek-bar hover previews.
|
|
381
|
+
*
|
|
382
|
+
* @param videoEl - A video element (typically offscreen) to seek and capture.
|
|
383
|
+
* @param timeSeconds - Timestamp to capture, clamped to the video duration.
|
|
384
|
+
* @param width - Output thumbnail width in pixels.
|
|
385
|
+
* @param height - Output thumbnail height in pixels.
|
|
386
|
+
* @returns A JPEG data URL, or `null` if capture failed (no 2d context, a
|
|
387
|
+
* tainted canvas, or a media error).
|
|
388
|
+
*/
|
|
389
|
+
declare function captureFrameAt(videoEl: HTMLVideoElement, timeSeconds: number, width?: number, height?: number): Promise<string | null>;
|
|
375
390
|
|
|
376
391
|
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';
|
|
377
392
|
interface ShortcutBinding {
|
|
@@ -426,4 +441,13 @@ declare class ProgressEstimator {
|
|
|
426
441
|
declare function getFFmpeg(): Promise<FFmpeg>;
|
|
427
442
|
declare function resetFFmpeg(): void;
|
|
428
443
|
|
|
429
|
-
|
|
444
|
+
/**
|
|
445
|
+
* Resolve an ISO 639 language code to its English language name, mirroring how
|
|
446
|
+
* VLC labels tracks.
|
|
447
|
+
*
|
|
448
|
+
* @returns The full language name, or the original code if it's unrecognised
|
|
449
|
+
* (VLC behaviour), or `undefined` if the code is absent/undetermined.
|
|
450
|
+
*/
|
|
451
|
+
declare function getLanguageName(code?: string | null): string | undefined;
|
|
452
|
+
|
|
453
|
+
export { ASSRenderer, type AudioTrack, type AudioTrackMeta, CancellationError, type Chapter, DEFAULT_SHORTCUTS, DEFAULT_TRACKERS, DISCLAIMER_KEY, FLAG_MAGNET_LINK, HLSPlayer, type HLSPlayerFile, type LightBirdConfig, MKVPlayer, type MKVPlayerFile, type MediaErrorType, type ParsedMediaError, type PlaylistItem, type ProcessedFile, ProgressEstimator, type QualityLevel, type ShortcutAction, type ShortcutBinding, SimplePlayer, type SimplePlayerFile, type Subtitle, SubtitleConverter, type SubtitleCue, type SubtitleTrackMeta, type TorrentStatus, UniversalSubtitleManager, VIDEO_EXTENSIONS, type VideoFilters, type VideoMetadata, type VideoPlayer, acceptDisclaimer, applyOffsetToVtt, captureFrameAt, captureVideoThumbnail, configureLightBird, createOffsetVttUrl, createVideoPlayer, destroyWebTorrentClient, exportPlaylist, extractNativeMetadata, formatShortcutKey, getFFmpeg, getLanguageName, getVideoFiles, getWebTorrentClient, hasAcceptedDisclaimer, initFeatureFlags, isHlsUrl, isInteractiveElement, isMagnetUri, isVideoFile, loadShortcuts, matchesShortcut, parseChaptersFromFFmpegLog, parseChaptersFromVtt, parseM3U8, parseMediaError, resetFFmpeg, saveShortcuts, validateFile };
|