@lightbird/ui 0.7.0 → 0.9.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 +33 -18
- package/dist/index.js +34 -19
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1436,6 +1436,7 @@ function VideoInfoPanel({ metadata, onClose }) {
|
|
|
1436
1436
|
["Frame Rate", metadata.frameRate ? `${metadata.frameRate} fps` : "\u2014"],
|
|
1437
1437
|
["Video Codec", metadata.videoCodec ?? "\u2014"],
|
|
1438
1438
|
["Video Bitrate", formatBitrate(metadata.videoBitrate)],
|
|
1439
|
+
...metadata.streamRenditions ? [["Stream Renditions", `${metadata.streamRenditions} levels`]] : [],
|
|
1439
1440
|
...metadata.audioTracks.map(
|
|
1440
1441
|
(t, i) => [
|
|
1441
1442
|
`Audio ${i + 1}`,
|
|
@@ -1933,7 +1934,6 @@ var MAX_RETRIES = 3;
|
|
|
1933
1934
|
var LightBirdPlayer = () => {
|
|
1934
1935
|
const videoRef = React11.useRef(null);
|
|
1935
1936
|
const containerRef = React11.useRef(null);
|
|
1936
|
-
const canvasRef = React11.useRef(null);
|
|
1937
1937
|
const subtitleInputRef = React11.useRef(null);
|
|
1938
1938
|
const playerRef = React11.useRef(null);
|
|
1939
1939
|
const subtitleFilesMapRef = React11.useRef(/* @__PURE__ */ new Map());
|
|
@@ -1963,7 +1963,7 @@ var LightBirdPlayer = () => {
|
|
|
1963
1963
|
getBrightness: () => filters.filters.brightness / 200,
|
|
1964
1964
|
setBrightness: (v) => filters.setFilters({ ...filters.filters, brightness: Math.round(v * 200) })
|
|
1965
1965
|
});
|
|
1966
|
-
const { metadata: videoMetadata } = react.useVideoInfo(videoRef, playlist.currentItem?.file ?? null);
|
|
1966
|
+
const { metadata: videoMetadata, enrichMetadata } = react.useVideoInfo(videoRef, playlist.currentItem?.file ?? null);
|
|
1967
1967
|
react.useProgressPersistence(videoRef, playlist.currentItem?.name ?? null);
|
|
1968
1968
|
const { chapters, currentChapter, goToChapter } = react.useChapters(videoRef, playerRef);
|
|
1969
1969
|
const magnetLinkEnabled = reactSdk.useBooleanFlagValue(core.FLAG_MAGNET_LINK, true);
|
|
@@ -2177,19 +2177,37 @@ var LightBirdPlayer = () => {
|
|
|
2177
2177
|
if (item.type === "stream") {
|
|
2178
2178
|
playerRef.current?.destroy();
|
|
2179
2179
|
playerRef.current = null;
|
|
2180
|
-
if (videoRef.current) videoRef.current.src = item.url;
|
|
2181
2180
|
subtitles.reset();
|
|
2182
2181
|
setAudioTracks([]);
|
|
2183
2182
|
setActiveAudioTrack("0");
|
|
2184
2183
|
isStreamRef.current = true;
|
|
2185
2184
|
startStallDetection();
|
|
2185
|
+
if (core.isHlsUrl(item.url) && videoRef.current) {
|
|
2186
|
+
const player = core.createVideoPlayer(item.url);
|
|
2187
|
+
playerRef.current = player;
|
|
2188
|
+
player.initialize(videoRef.current).then(() => {
|
|
2189
|
+
const refresh = () => {
|
|
2190
|
+
if (playerRef.current !== player) return;
|
|
2191
|
+
enrichMetadata(player.getMetadata?.() ?? {});
|
|
2192
|
+
const tracks = player.getAudioTracks();
|
|
2193
|
+
setAudioTracks(tracks);
|
|
2194
|
+
setActiveAudioTrack((prev) => prev || tracks[0]?.id || "0");
|
|
2195
|
+
};
|
|
2196
|
+
player.onMetadataChange?.(refresh);
|
|
2197
|
+
refresh();
|
|
2198
|
+
}).catch((error) => {
|
|
2199
|
+
console.error(error);
|
|
2200
|
+
});
|
|
2201
|
+
} else if (videoRef.current) {
|
|
2202
|
+
videoRef.current.src = item.url;
|
|
2203
|
+
}
|
|
2186
2204
|
} else if (item.file) {
|
|
2187
2205
|
isStreamRef.current = false;
|
|
2188
2206
|
stopStallDetection();
|
|
2189
2207
|
const subs = subtitleFilesMapRef.current.get(item.name) ?? [];
|
|
2190
2208
|
processFile(item.file, subs);
|
|
2191
2209
|
}
|
|
2192
|
-
}, [playlist.playlist, playlist.selectItem, subtitles, processFile]);
|
|
2210
|
+
}, [playlist.playlist, playlist.selectItem, subtitles, processFile, enrichMetadata]);
|
|
2193
2211
|
const handleSkipToNext = React11.useCallback(() => {
|
|
2194
2212
|
setPlayerError(null);
|
|
2195
2213
|
clearRetryTimer();
|
|
@@ -2425,19 +2443,17 @@ var LightBirdPlayer = () => {
|
|
|
2425
2443
|
}, [toast2]);
|
|
2426
2444
|
const captureScreenshot = React11.useCallback(() => {
|
|
2427
2445
|
const video = videoRef.current;
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
a.download = `lightbird-screenshot-${(/* @__PURE__ */ new Date()).toISOString()}.png`;
|
|
2440
|
-
a.click();
|
|
2446
|
+
if (!video) return;
|
|
2447
|
+
const dataUrl = core.exportVideoFrame(video, { filter: video.style.filter });
|
|
2448
|
+
if (!dataUrl) {
|
|
2449
|
+
toast2({
|
|
2450
|
+
title: "Screenshot failed",
|
|
2451
|
+
description: "The frame could not be captured (the video may be cross-origin protected).",
|
|
2452
|
+
variant: "destructive"
|
|
2453
|
+
});
|
|
2454
|
+
return;
|
|
2455
|
+
}
|
|
2456
|
+
core.downloadDataUrl(dataUrl, core.frameExportFilename("png"));
|
|
2441
2457
|
toast2({ title: "Screenshot Saved" });
|
|
2442
2458
|
}, [toast2]);
|
|
2443
2459
|
const handleABLoopCycle = React11.useCallback(() => {
|
|
@@ -2516,7 +2532,6 @@ var LightBirdPlayer = () => {
|
|
|
2516
2532
|
crossOrigin: "anonymous"
|
|
2517
2533
|
}
|
|
2518
2534
|
),
|
|
2519
|
-
/* @__PURE__ */ jsxRuntime.jsx("canvas", { ref: canvasRef, className: "hidden" }),
|
|
2520
2535
|
/* @__PURE__ */ jsxRuntime.jsx(SubtitleOverlay, { videoRef, activeSubtitle: subtitles.activeSubtitle }),
|
|
2521
2536
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2522
2537
|
VideoOverlay,
|
package/dist/index.js
CHANGED
|
@@ -18,7 +18,7 @@ import { SortableContext, verticalListSortingStrategy, arrayMove, useSortable }
|
|
|
18
18
|
import { CSS } from '@dnd-kit/utilities';
|
|
19
19
|
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
|
|
20
20
|
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
21
|
-
import { exportPlaylist, formatShortcutKey, FLAG_MAGNET_LINK, loadShortcuts, ProgressEstimator, createVideoPlayer, CancellationError, hasAcceptedDisclaimer, acceptDisclaimer, initFeatureFlags, parseMediaError, captureVideoThumbnail, validateFile, parseM3U8, matchesShortcut, saveShortcuts, DEFAULT_SHORTCUTS } from '@lightbird/core';
|
|
21
|
+
import { exportPlaylist, formatShortcutKey, FLAG_MAGNET_LINK, loadShortcuts, ProgressEstimator, createVideoPlayer, CancellationError, isHlsUrl, hasAcceptedDisclaimer, acceptDisclaimer, exportVideoFrame, downloadDataUrl, frameExportFilename, initFeatureFlags, parseMediaError, captureVideoThumbnail, validateFile, parseM3U8, matchesShortcut, saveShortcuts, DEFAULT_SHORTCUTS } from '@lightbird/core';
|
|
22
22
|
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
23
23
|
import { useBooleanFlagValue, OpenFeatureProvider } from '@openfeature/react-sdk';
|
|
24
24
|
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
|
|
@@ -1405,6 +1405,7 @@ function VideoInfoPanel({ metadata, onClose }) {
|
|
|
1405
1405
|
["Frame Rate", metadata.frameRate ? `${metadata.frameRate} fps` : "\u2014"],
|
|
1406
1406
|
["Video Codec", metadata.videoCodec ?? "\u2014"],
|
|
1407
1407
|
["Video Bitrate", formatBitrate(metadata.videoBitrate)],
|
|
1408
|
+
...metadata.streamRenditions ? [["Stream Renditions", `${metadata.streamRenditions} levels`]] : [],
|
|
1408
1409
|
...metadata.audioTracks.map(
|
|
1409
1410
|
(t, i) => [
|
|
1410
1411
|
`Audio ${i + 1}`,
|
|
@@ -1902,7 +1903,6 @@ var MAX_RETRIES = 3;
|
|
|
1902
1903
|
var LightBirdPlayer = () => {
|
|
1903
1904
|
const videoRef = useRef(null);
|
|
1904
1905
|
const containerRef = useRef(null);
|
|
1905
|
-
const canvasRef = useRef(null);
|
|
1906
1906
|
const subtitleInputRef = useRef(null);
|
|
1907
1907
|
const playerRef = useRef(null);
|
|
1908
1908
|
const subtitleFilesMapRef = useRef(/* @__PURE__ */ new Map());
|
|
@@ -1932,7 +1932,7 @@ var LightBirdPlayer = () => {
|
|
|
1932
1932
|
getBrightness: () => filters.filters.brightness / 200,
|
|
1933
1933
|
setBrightness: (v) => filters.setFilters({ ...filters.filters, brightness: Math.round(v * 200) })
|
|
1934
1934
|
});
|
|
1935
|
-
const { metadata: videoMetadata } = useVideoInfo(videoRef, playlist.currentItem?.file ?? null);
|
|
1935
|
+
const { metadata: videoMetadata, enrichMetadata } = useVideoInfo(videoRef, playlist.currentItem?.file ?? null);
|
|
1936
1936
|
useProgressPersistence(videoRef, playlist.currentItem?.name ?? null);
|
|
1937
1937
|
const { chapters, currentChapter, goToChapter } = useChapters(videoRef, playerRef);
|
|
1938
1938
|
const magnetLinkEnabled = useBooleanFlagValue(FLAG_MAGNET_LINK, true);
|
|
@@ -2146,19 +2146,37 @@ var LightBirdPlayer = () => {
|
|
|
2146
2146
|
if (item.type === "stream") {
|
|
2147
2147
|
playerRef.current?.destroy();
|
|
2148
2148
|
playerRef.current = null;
|
|
2149
|
-
if (videoRef.current) videoRef.current.src = item.url;
|
|
2150
2149
|
subtitles.reset();
|
|
2151
2150
|
setAudioTracks([]);
|
|
2152
2151
|
setActiveAudioTrack("0");
|
|
2153
2152
|
isStreamRef.current = true;
|
|
2154
2153
|
startStallDetection();
|
|
2154
|
+
if (isHlsUrl(item.url) && videoRef.current) {
|
|
2155
|
+
const player = createVideoPlayer(item.url);
|
|
2156
|
+
playerRef.current = player;
|
|
2157
|
+
player.initialize(videoRef.current).then(() => {
|
|
2158
|
+
const refresh = () => {
|
|
2159
|
+
if (playerRef.current !== player) return;
|
|
2160
|
+
enrichMetadata(player.getMetadata?.() ?? {});
|
|
2161
|
+
const tracks = player.getAudioTracks();
|
|
2162
|
+
setAudioTracks(tracks);
|
|
2163
|
+
setActiveAudioTrack((prev) => prev || tracks[0]?.id || "0");
|
|
2164
|
+
};
|
|
2165
|
+
player.onMetadataChange?.(refresh);
|
|
2166
|
+
refresh();
|
|
2167
|
+
}).catch((error) => {
|
|
2168
|
+
console.error(error);
|
|
2169
|
+
});
|
|
2170
|
+
} else if (videoRef.current) {
|
|
2171
|
+
videoRef.current.src = item.url;
|
|
2172
|
+
}
|
|
2155
2173
|
} else if (item.file) {
|
|
2156
2174
|
isStreamRef.current = false;
|
|
2157
2175
|
stopStallDetection();
|
|
2158
2176
|
const subs = subtitleFilesMapRef.current.get(item.name) ?? [];
|
|
2159
2177
|
processFile(item.file, subs);
|
|
2160
2178
|
}
|
|
2161
|
-
}, [playlist.playlist, playlist.selectItem, subtitles, processFile]);
|
|
2179
|
+
}, [playlist.playlist, playlist.selectItem, subtitles, processFile, enrichMetadata]);
|
|
2162
2180
|
const handleSkipToNext = useCallback(() => {
|
|
2163
2181
|
setPlayerError(null);
|
|
2164
2182
|
clearRetryTimer();
|
|
@@ -2394,19 +2412,17 @@ var LightBirdPlayer = () => {
|
|
|
2394
2412
|
}, [toast2]);
|
|
2395
2413
|
const captureScreenshot = useCallback(() => {
|
|
2396
2414
|
const video = videoRef.current;
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
a.download = `lightbird-screenshot-${(/* @__PURE__ */ new Date()).toISOString()}.png`;
|
|
2409
|
-
a.click();
|
|
2415
|
+
if (!video) return;
|
|
2416
|
+
const dataUrl = exportVideoFrame(video, { filter: video.style.filter });
|
|
2417
|
+
if (!dataUrl) {
|
|
2418
|
+
toast2({
|
|
2419
|
+
title: "Screenshot failed",
|
|
2420
|
+
description: "The frame could not be captured (the video may be cross-origin protected).",
|
|
2421
|
+
variant: "destructive"
|
|
2422
|
+
});
|
|
2423
|
+
return;
|
|
2424
|
+
}
|
|
2425
|
+
downloadDataUrl(dataUrl, frameExportFilename("png"));
|
|
2410
2426
|
toast2({ title: "Screenshot Saved" });
|
|
2411
2427
|
}, [toast2]);
|
|
2412
2428
|
const handleABLoopCycle = useCallback(() => {
|
|
@@ -2485,7 +2501,6 @@ var LightBirdPlayer = () => {
|
|
|
2485
2501
|
crossOrigin: "anonymous"
|
|
2486
2502
|
}
|
|
2487
2503
|
),
|
|
2488
|
-
/* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "hidden" }),
|
|
2489
2504
|
/* @__PURE__ */ jsx(SubtitleOverlay, { videoRef, activeSubtitle: subtitles.activeSubtitle }),
|
|
2490
2505
|
/* @__PURE__ */ jsx(
|
|
2491
2506
|
VideoOverlay,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightbird/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Drop-in React video player component powered by LightBird. Full controls, playlist, subtitles, chapters — one import.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Punyam Singh",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"clsx": "^2.1.1",
|
|
66
66
|
"lucide-react": "^0.475.0",
|
|
67
67
|
"tailwind-merge": "^3.0.1",
|
|
68
|
-
"@lightbird/core": "0.
|
|
68
|
+
"@lightbird/core": "0.9.0"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
71
|
"react": "^18.0.0 || ^19.0.0",
|