@nikelroot/video-player 0.1.1 → 0.1.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/README.md +1 -1
- package/dist/index.cjs +165 -70
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +163 -76
- package/package.json +1 -5
package/README.md
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -39,13 +39,9 @@ module.exports = __toCommonJS(index_exports);
|
|
|
39
39
|
// src/VideoPlayer.tsx
|
|
40
40
|
var import_react2 = require("react");
|
|
41
41
|
|
|
42
|
-
// src/controls.tsx
|
|
43
|
-
var import_antd = require("antd");
|
|
44
|
-
var import_icons = require("@ant-design/icons");
|
|
45
|
-
|
|
46
42
|
// src/styles.ts
|
|
47
|
-
var import_styled_components =
|
|
48
|
-
var VideoPlayerControlStyles = import_styled_components.
|
|
43
|
+
var import_styled_components = require("styled-components");
|
|
44
|
+
var VideoPlayerControlStyles = import_styled_components.styled.div`
|
|
49
45
|
position: absolute;
|
|
50
46
|
left: 0;
|
|
51
47
|
right: 0;
|
|
@@ -62,13 +58,61 @@ var VideoPlayerControlStyles = import_styled_components.default.div`
|
|
|
62
58
|
flex: 1 auto;
|
|
63
59
|
}
|
|
64
60
|
|
|
61
|
+
.video-player-control-row {
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: center;
|
|
64
|
+
justify-content: space-between;
|
|
65
|
+
gap: 8px;
|
|
66
|
+
min-height: 30px;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.video-player-control-button {
|
|
70
|
+
display: inline-flex;
|
|
71
|
+
align-items: center;
|
|
72
|
+
justify-content: center;
|
|
73
|
+
width: 28px;
|
|
74
|
+
height: 28px;
|
|
75
|
+
flex: 0 0 28px;
|
|
76
|
+
border: 1px solid rgba(255, 255, 255, 0.24);
|
|
77
|
+
border-radius: 4px;
|
|
78
|
+
background: rgba(255, 255, 255, 0.12);
|
|
79
|
+
color: #fff;
|
|
80
|
+
cursor: pointer;
|
|
81
|
+
padding: 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.video-player-control-button:hover,
|
|
85
|
+
.video-player-control-button:focus-visible {
|
|
86
|
+
background: rgba(255, 255, 255, 0.22);
|
|
87
|
+
outline: none;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.video-player-icon {
|
|
91
|
+
width: 17px;
|
|
92
|
+
height: 17px;
|
|
93
|
+
fill: currentColor;
|
|
94
|
+
stroke: currentColor;
|
|
95
|
+
stroke-width: 1.8;
|
|
96
|
+
stroke-linecap: round;
|
|
97
|
+
stroke-linejoin: round;
|
|
98
|
+
}
|
|
99
|
+
|
|
65
100
|
.video-player-control-title {
|
|
66
101
|
color: #fff;
|
|
67
102
|
max-width: 40%;
|
|
103
|
+
overflow: hidden;
|
|
104
|
+
text-overflow: ellipsis;
|
|
105
|
+
white-space: nowrap;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.video-player-time {
|
|
109
|
+
color: #fff;
|
|
110
|
+
white-space: nowrap;
|
|
111
|
+
font-size: 14px;
|
|
68
112
|
}
|
|
69
113
|
|
|
70
114
|
${(props) => !props.$showControl && import_styled_components.css`
|
|
71
|
-
.
|
|
115
|
+
.video-player-control-row {
|
|
72
116
|
> * {
|
|
73
117
|
opacity: 0;
|
|
74
118
|
display: none;
|
|
@@ -85,7 +129,7 @@ var VideoPlayerControlStyles = import_styled_components.default.div`
|
|
|
85
129
|
padding: 0;
|
|
86
130
|
`};
|
|
87
131
|
`;
|
|
88
|
-
var VideoPlayerStyles = import_styled_components.
|
|
132
|
+
var VideoPlayerStyles = import_styled_components.styled.div`
|
|
89
133
|
position: relative;
|
|
90
134
|
aspect-ratio: 16 / 9;
|
|
91
135
|
max-height: 100%;
|
|
@@ -240,42 +284,25 @@ var VideoPlayerStyles = import_styled_components.default.div`
|
|
|
240
284
|
}
|
|
241
285
|
`};
|
|
242
286
|
`;
|
|
243
|
-
var SeekSliderStyled = import_styled_components.
|
|
287
|
+
var SeekSliderStyled = import_styled_components.styled.div`
|
|
244
288
|
position: absolute;
|
|
245
289
|
left: 0;
|
|
246
290
|
right: 0;
|
|
247
291
|
top: -12px;
|
|
248
292
|
|
|
249
|
-
.
|
|
293
|
+
.seek-range {
|
|
294
|
+
display: block;
|
|
295
|
+
width: 100%;
|
|
250
296
|
height: 20px;
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
background: transparent
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
.ant-slider {
|
|
257
|
-
&:hover {
|
|
258
|
-
.ant-slider-rail {
|
|
259
|
-
background: transparent !important;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
.ant-slider-track {
|
|
265
|
-
top: 6px;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
.ant-slider-handle {
|
|
269
|
-
top: 3px;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
.ant-slider-step {
|
|
273
|
-
height: 8px;
|
|
274
|
-
background: rgba(255, 255, 255, 0.1);
|
|
297
|
+
margin: 0;
|
|
298
|
+
accent-color: #fff;
|
|
299
|
+
background: transparent;
|
|
300
|
+
cursor: pointer;
|
|
275
301
|
}
|
|
276
302
|
|
|
277
|
-
.
|
|
278
|
-
|
|
303
|
+
.seek-range:disabled {
|
|
304
|
+
cursor: default;
|
|
305
|
+
opacity: 0.65;
|
|
279
306
|
}
|
|
280
307
|
`;
|
|
281
308
|
|
|
@@ -384,36 +411,83 @@ var createThrottledNumberFn = (fn, waitMs) => {
|
|
|
384
411
|
// src/controls.tsx
|
|
385
412
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
386
413
|
var SeekSlider = ({ playAction, duration, currentTime, seekSetTime }) => {
|
|
387
|
-
const formatter = (value) => {
|
|
388
|
-
return formatTime(Number(value != null ? value : 0));
|
|
389
|
-
};
|
|
390
414
|
const clickHandler = (e) => {
|
|
391
415
|
e.preventDefault();
|
|
392
416
|
e.stopPropagation();
|
|
393
417
|
};
|
|
394
418
|
const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
|
|
395
419
|
const safeCurrentTime = Math.max(0, Math.min(Number.isFinite(currentTime) ? currentTime : 0, safeDuration || Infinity));
|
|
420
|
+
const completeChange = () => {
|
|
421
|
+
void playAction();
|
|
422
|
+
};
|
|
396
423
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SeekSliderStyled, { onClick: clickHandler, className: "seek-slider", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
397
|
-
|
|
424
|
+
"input",
|
|
398
425
|
{
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
426
|
+
"aria-label": "Seek",
|
|
427
|
+
className: "seek-range",
|
|
428
|
+
disabled: safeDuration <= 0,
|
|
429
|
+
max: safeDuration,
|
|
430
|
+
min: 0,
|
|
431
|
+
onChange: (e) => seekSetTime(Number(e.currentTarget.value)),
|
|
432
|
+
onKeyUp: (e) => {
|
|
433
|
+
if (e.key === "Enter" || e.key === " ") completeChange();
|
|
402
434
|
},
|
|
435
|
+
onPointerUp: completeChange,
|
|
436
|
+
step: "any",
|
|
437
|
+
type: "range",
|
|
403
438
|
value: safeCurrentTime,
|
|
404
|
-
|
|
405
|
-
max: safeDuration,
|
|
406
|
-
keyboard: false
|
|
439
|
+
title: formatTime(safeCurrentTime)
|
|
407
440
|
}
|
|
408
441
|
) });
|
|
409
442
|
};
|
|
410
443
|
var TimeBlock = ({ duration, currentTime }) => {
|
|
411
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
444
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "video-player-time", children: [
|
|
412
445
|
formatTime(currentTime),
|
|
413
446
|
" / ",
|
|
414
447
|
formatTime(duration)
|
|
415
448
|
] });
|
|
416
449
|
};
|
|
450
|
+
var Icon = ({ name }) => {
|
|
451
|
+
const paths = {
|
|
452
|
+
back: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
453
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M11 7l-6 5 6 5V7z" }),
|
|
454
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M19 7l-6 5 6 5V7z" })
|
|
455
|
+
] }),
|
|
456
|
+
forward: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
457
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 7l6 5-6 5V7z" }),
|
|
458
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M13 7l6 5-6 5V7z" })
|
|
459
|
+
] }),
|
|
460
|
+
fullscreen: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
461
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 10V5h5" }),
|
|
462
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 5h5v5" }),
|
|
463
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M19 14v5h-5" }),
|
|
464
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10 19H5v-5" })
|
|
465
|
+
] }),
|
|
466
|
+
mute: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
467
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 10v4h4l5 4V6l-5 4H4z" }),
|
|
468
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M17 9l4 6" }),
|
|
469
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M21 9l-4 6" })
|
|
470
|
+
] }),
|
|
471
|
+
pause: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
472
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 6h3v12H8z" }),
|
|
473
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M13 6h3v12h-3z" })
|
|
474
|
+
] }),
|
|
475
|
+
play: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 5v14l11-7L8 5z" }),
|
|
476
|
+
sound: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
477
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 10v4h4l5 4V6l-5 4H4z" }),
|
|
478
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M16 9a4 4 0 010 6" }),
|
|
479
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M18.5 6.5a8 8 0 010 11" })
|
|
480
|
+
] })
|
|
481
|
+
};
|
|
482
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { "aria-hidden": "true", className: "video-player-icon", focusable: "false", viewBox: "0 0 24 24", children: paths[name] });
|
|
483
|
+
};
|
|
484
|
+
var ControlButton = ({
|
|
485
|
+
icon,
|
|
486
|
+
label,
|
|
487
|
+
onClick
|
|
488
|
+
}) => {
|
|
489
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { "aria-label": label, className: "video-player-control-button", onClick: () => void onClick(), title: label, type: "button", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { name: icon }) });
|
|
490
|
+
};
|
|
417
491
|
var stopControlClick = (e) => {
|
|
418
492
|
e.preventDefault();
|
|
419
493
|
e.stopPropagation();
|
|
@@ -423,7 +497,6 @@ var VideoPlayerControls = (props) => {
|
|
|
423
497
|
variant,
|
|
424
498
|
fullScreenAction,
|
|
425
499
|
showControl,
|
|
426
|
-
videoRef,
|
|
427
500
|
playAction,
|
|
428
501
|
pauseAction,
|
|
429
502
|
seekSetTime,
|
|
@@ -438,30 +511,29 @@ var VideoPlayerControls = (props) => {
|
|
|
438
511
|
} = props;
|
|
439
512
|
const [, isMobile] = useWindowWidth();
|
|
440
513
|
if (variant === "none") return null;
|
|
441
|
-
if (!videoRef.current) return null;
|
|
442
514
|
if (variant === "tiny") {
|
|
443
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VideoPlayerControlStyles, { $tiny: true, $isMobile: isMobile, onClick: stopControlClick, $showControl: showControl, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
515
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VideoPlayerControlStyles, { $tiny: true, $isMobile: isMobile, onClick: stopControlClick, $showControl: showControl, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "video-player-control-row", children: [
|
|
444
516
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "full" }),
|
|
445
517
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SeekSlider, { playAction, duration, currentTime, seekSetTime })
|
|
446
518
|
] }) });
|
|
447
519
|
}
|
|
448
520
|
if (variant === "fullscreen-only") {
|
|
449
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VideoPlayerControlStyles, { $isMobile: isMobile, onClick: stopControlClick, $showControl: showControl, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
521
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VideoPlayerControlStyles, { $isMobile: isMobile, onClick: stopControlClick, $showControl: showControl, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "video-player-control-row", children: [
|
|
450
522
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "full" }),
|
|
451
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
452
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
523
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ControlButton, { icon: muted ? "mute" : "sound", label: muted ? "Unmute" : "Mute", onClick: () => onMutedChange(!muted) }),
|
|
524
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ControlButton, { icon: "fullscreen", label: "Fullscreen", onClick: fullScreenAction })
|
|
453
525
|
] }) });
|
|
454
526
|
}
|
|
455
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VideoPlayerControlStyles, { $isMobile: isMobile, onClick: stopControlClick, $showControl: showControl, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
456
|
-
playing ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
457
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
458
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
459
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
527
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VideoPlayerControlStyles, { $isMobile: isMobile, onClick: stopControlClick, $showControl: showControl, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "video-player-control-row", children: [
|
|
528
|
+
playing ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ControlButton, { icon: "pause", label: "Pause", onClick: pauseAction }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ControlButton, { icon: "play", label: "Play", onClick: playAction }),
|
|
529
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ControlButton, { icon: "back", label: "Back 15 seconds", onClick: () => seekPrevAction() }),
|
|
530
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ControlButton, { icon: "forward", label: "Forward 15 seconds", onClick: () => seekNextAction() }),
|
|
531
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "video-player-control-title", title: typeof title === "string" ? title : void 0, children: title }),
|
|
460
532
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "full" }),
|
|
461
533
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SeekSlider, { playAction, duration, currentTime, seekSetTime }),
|
|
462
534
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TimeBlock, { duration, currentTime }),
|
|
463
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
464
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
535
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ControlButton, { icon: muted ? "mute" : "sound", label: muted ? "Unmute" : "Mute", onClick: () => onMutedChange(!muted) }),
|
|
536
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ControlButton, { icon: "fullscreen", label: "Fullscreen", onClick: fullScreenAction })
|
|
465
537
|
] }) });
|
|
466
538
|
};
|
|
467
539
|
|
|
@@ -660,6 +732,14 @@ var clampTime = (time, duration) => {
|
|
|
660
732
|
const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
|
|
661
733
|
return safeDuration > 0 ? Math.max(0, Math.min(safeTime, safeDuration)) : Math.max(0, safeTime);
|
|
662
734
|
};
|
|
735
|
+
var isLikelyHlsSource = (src) => {
|
|
736
|
+
try {
|
|
737
|
+
const url = new URL(src, "http://video-player.local");
|
|
738
|
+
return /\.m3u8$/i.test(url.pathname) || /\.m3u8($|[?#])/i.test(src);
|
|
739
|
+
} catch {
|
|
740
|
+
return /\.m3u8($|[?#])/i.test(src);
|
|
741
|
+
}
|
|
742
|
+
};
|
|
663
743
|
var VideoPlayerBase = (props, ref) => {
|
|
664
744
|
const {
|
|
665
745
|
controlsVariant = "none",
|
|
@@ -943,10 +1023,16 @@ var VideoPlayerBase = (props, ref) => {
|
|
|
943
1023
|
);
|
|
944
1024
|
}, [hlsConfig, hlsCredentials, live, liveHlsConfig, vodHlsConfig]);
|
|
945
1025
|
const loadVideo = (0, import_react2.useCallback)(
|
|
946
|
-
async (startAt =
|
|
1026
|
+
async (startAt = initialTime) => {
|
|
947
1027
|
const startVideoRef = videoRef.current;
|
|
948
1028
|
if (!startVideoRef) return;
|
|
949
|
-
|
|
1029
|
+
let HlsCtor;
|
|
1030
|
+
try {
|
|
1031
|
+
HlsCtor = await loadHlsCtor();
|
|
1032
|
+
} catch (error) {
|
|
1033
|
+
reportError(error, { type: "hls-loader", src: videoSrc }, messages.streamLoadFailed);
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
950
1036
|
if (!videoRef.current || videoRef.current !== startVideoRef) return;
|
|
951
1037
|
const video = videoRef.current;
|
|
952
1038
|
if (!video) return;
|
|
@@ -1040,7 +1126,6 @@ var VideoPlayerBase = (props, ref) => {
|
|
|
1040
1126
|
}
|
|
1041
1127
|
void loadVideo(resumeTime);
|
|
1042
1128
|
});
|
|
1043
|
-
h.attachMedia(video);
|
|
1044
1129
|
h.on(HlsCtor.Events.MEDIA_ATTACHED, () => {
|
|
1045
1130
|
h.loadSource(videoSrc);
|
|
1046
1131
|
});
|
|
@@ -1056,11 +1141,13 @@ var VideoPlayerBase = (props, ref) => {
|
|
|
1056
1141
|
}
|
|
1057
1142
|
if (autoPlay) void playAction();
|
|
1058
1143
|
});
|
|
1144
|
+
h.attachMedia(video);
|
|
1059
1145
|
},
|
|
1060
1146
|
[
|
|
1061
1147
|
autoPlay,
|
|
1062
1148
|
clearErrorState,
|
|
1063
1149
|
clearTimers,
|
|
1150
|
+
initialTime,
|
|
1064
1151
|
live,
|
|
1065
1152
|
messages.streamDecodeFailed,
|
|
1066
1153
|
messages.streamLoadFailed,
|
|
@@ -1082,18 +1169,24 @@ var VideoPlayerBase = (props, ref) => {
|
|
|
1082
1169
|
const loadVideoHls = (0, import_react2.useCallback)(async () => {
|
|
1083
1170
|
const video = videoRef.current;
|
|
1084
1171
|
const canNativeHls = !!(video == null ? void 0 : video.canPlayType) && video.canPlayType("application/vnd.apple.mpegurl") !== "";
|
|
1085
|
-
|
|
1172
|
+
let HlsCtor;
|
|
1173
|
+
try {
|
|
1174
|
+
HlsCtor = await loadHlsCtor();
|
|
1175
|
+
} catch (error) {
|
|
1176
|
+
reportError(error, { type: "hls-loader", src: videoSrc }, messages.streamLoadFailed);
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1086
1179
|
if (!videoRef.current || videoRef.current !== video) return;
|
|
1087
1180
|
if (HlsCtor.isSupported()) {
|
|
1088
|
-
void loadVideo();
|
|
1181
|
+
void loadVideo(initialTime);
|
|
1089
1182
|
return;
|
|
1090
1183
|
}
|
|
1091
1184
|
if (canNativeHls) {
|
|
1092
1185
|
loadVideoNative();
|
|
1093
1186
|
return;
|
|
1094
1187
|
}
|
|
1095
|
-
|
|
1096
|
-
}, [loadVideo, loadVideoNative]);
|
|
1188
|
+
reportError(null, { type: "hls", src: videoSrc, reason: "HLS is not supported in this browser" }, messages.unsupported);
|
|
1189
|
+
}, [initialTime, loadVideo, loadVideoNative, messages.streamLoadFailed, messages.unsupported, reportError, videoSrc]);
|
|
1097
1190
|
const destroy = (0, import_react2.useCallback)(() => {
|
|
1098
1191
|
clearTimers();
|
|
1099
1192
|
if (hlsRef.current) {
|
|
@@ -1224,8 +1317,9 @@ var VideoPlayerBase = (props, ref) => {
|
|
|
1224
1317
|
hlsRef.current = null;
|
|
1225
1318
|
}
|
|
1226
1319
|
if (sourceType === "native" || type === "video") loadVideoNative();
|
|
1227
|
-
else if (sourceType === "hls") void loadVideo();
|
|
1228
|
-
else void loadVideoHls();
|
|
1320
|
+
else if (sourceType === "hls") void loadVideo(initialTime);
|
|
1321
|
+
else if (isLikelyHlsSource(videoSrc)) void loadVideoHls();
|
|
1322
|
+
else loadVideoNative();
|
|
1229
1323
|
return () => {
|
|
1230
1324
|
clearTimers();
|
|
1231
1325
|
if (hlsRef.current) {
|
|
@@ -1239,6 +1333,7 @@ var VideoPlayerBase = (props, ref) => {
|
|
|
1239
1333
|
loadVideo,
|
|
1240
1334
|
loadVideoHls,
|
|
1241
1335
|
loadVideoNative,
|
|
1336
|
+
initialTime,
|
|
1242
1337
|
reloadKey,
|
|
1243
1338
|
reloadToken,
|
|
1244
1339
|
sourceType,
|
|
@@ -1305,6 +1400,7 @@ var VideoPlayerBase = (props, ref) => {
|
|
|
1305
1400
|
"video",
|
|
1306
1401
|
{
|
|
1307
1402
|
...videoProps,
|
|
1403
|
+
controls: false,
|
|
1308
1404
|
ref: setVideoEl,
|
|
1309
1405
|
crossOrigin,
|
|
1310
1406
|
playsInline: true,
|
|
@@ -1334,7 +1430,6 @@ var VideoPlayerBase = (props, ref) => {
|
|
|
1334
1430
|
variant: controlsVariant,
|
|
1335
1431
|
fullScreenAction,
|
|
1336
1432
|
showControl,
|
|
1337
|
-
videoRef,
|
|
1338
1433
|
playAction,
|
|
1339
1434
|
pauseAction,
|
|
1340
1435
|
seekSetTime,
|
package/dist/index.d.cts
CHANGED
|
@@ -36,7 +36,7 @@ interface VideoPlayerProps extends Omit<React.VideoHTMLAttributes<HTMLVideoEleme
|
|
|
36
36
|
videoSrc: string;
|
|
37
37
|
sourceType?: 'auto' | 'hls' | 'native';
|
|
38
38
|
type?: string;
|
|
39
|
-
controlsVariant
|
|
39
|
+
controlsVariant: ControlsVariant;
|
|
40
40
|
autoPlay?: boolean;
|
|
41
41
|
preload?: '' | 'none' | 'auto' | 'metadata';
|
|
42
42
|
loop?: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -36,7 +36,7 @@ interface VideoPlayerProps extends Omit<React.VideoHTMLAttributes<HTMLVideoEleme
|
|
|
36
36
|
videoSrc: string;
|
|
37
37
|
sourceType?: 'auto' | 'hls' | 'native';
|
|
38
38
|
type?: string;
|
|
39
|
-
controlsVariant
|
|
39
|
+
controlsVariant: ControlsVariant;
|
|
40
40
|
autoPlay?: boolean;
|
|
41
41
|
preload?: '' | 'none' | 'auto' | 'metadata';
|
|
42
42
|
loop?: boolean;
|
package/dist/index.js
CHANGED
|
@@ -10,20 +10,8 @@ import {
|
|
|
10
10
|
useState
|
|
11
11
|
} from "react";
|
|
12
12
|
|
|
13
|
-
// src/controls.tsx
|
|
14
|
-
import { Flex, Button, Slider, Typography } from "antd";
|
|
15
|
-
import {
|
|
16
|
-
BackwardFilled,
|
|
17
|
-
FastForwardFilled,
|
|
18
|
-
FullscreenOutlined,
|
|
19
|
-
MutedFilled,
|
|
20
|
-
PauseOutlined,
|
|
21
|
-
PlaySquareFilled,
|
|
22
|
-
SoundFilled
|
|
23
|
-
} from "@ant-design/icons";
|
|
24
|
-
|
|
25
13
|
// src/styles.ts
|
|
26
|
-
import
|
|
14
|
+
import { css, styled } from "styled-components";
|
|
27
15
|
var VideoPlayerControlStyles = styled.div`
|
|
28
16
|
position: absolute;
|
|
29
17
|
left: 0;
|
|
@@ -41,13 +29,61 @@ var VideoPlayerControlStyles = styled.div`
|
|
|
41
29
|
flex: 1 auto;
|
|
42
30
|
}
|
|
43
31
|
|
|
32
|
+
.video-player-control-row {
|
|
33
|
+
display: flex;
|
|
34
|
+
align-items: center;
|
|
35
|
+
justify-content: space-between;
|
|
36
|
+
gap: 8px;
|
|
37
|
+
min-height: 30px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.video-player-control-button {
|
|
41
|
+
display: inline-flex;
|
|
42
|
+
align-items: center;
|
|
43
|
+
justify-content: center;
|
|
44
|
+
width: 28px;
|
|
45
|
+
height: 28px;
|
|
46
|
+
flex: 0 0 28px;
|
|
47
|
+
border: 1px solid rgba(255, 255, 255, 0.24);
|
|
48
|
+
border-radius: 4px;
|
|
49
|
+
background: rgba(255, 255, 255, 0.12);
|
|
50
|
+
color: #fff;
|
|
51
|
+
cursor: pointer;
|
|
52
|
+
padding: 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.video-player-control-button:hover,
|
|
56
|
+
.video-player-control-button:focus-visible {
|
|
57
|
+
background: rgba(255, 255, 255, 0.22);
|
|
58
|
+
outline: none;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.video-player-icon {
|
|
62
|
+
width: 17px;
|
|
63
|
+
height: 17px;
|
|
64
|
+
fill: currentColor;
|
|
65
|
+
stroke: currentColor;
|
|
66
|
+
stroke-width: 1.8;
|
|
67
|
+
stroke-linecap: round;
|
|
68
|
+
stroke-linejoin: round;
|
|
69
|
+
}
|
|
70
|
+
|
|
44
71
|
.video-player-control-title {
|
|
45
72
|
color: #fff;
|
|
46
73
|
max-width: 40%;
|
|
74
|
+
overflow: hidden;
|
|
75
|
+
text-overflow: ellipsis;
|
|
76
|
+
white-space: nowrap;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.video-player-time {
|
|
80
|
+
color: #fff;
|
|
81
|
+
white-space: nowrap;
|
|
82
|
+
font-size: 14px;
|
|
47
83
|
}
|
|
48
84
|
|
|
49
85
|
${(props) => !props.$showControl && css`
|
|
50
|
-
.
|
|
86
|
+
.video-player-control-row {
|
|
51
87
|
> * {
|
|
52
88
|
opacity: 0;
|
|
53
89
|
display: none;
|
|
@@ -225,36 +261,19 @@ var SeekSliderStyled = styled.div`
|
|
|
225
261
|
right: 0;
|
|
226
262
|
top: -12px;
|
|
227
263
|
|
|
228
|
-
.
|
|
264
|
+
.seek-range {
|
|
265
|
+
display: block;
|
|
266
|
+
width: 100%;
|
|
229
267
|
height: 20px;
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
background: transparent
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
.ant-slider {
|
|
236
|
-
&:hover {
|
|
237
|
-
.ant-slider-rail {
|
|
238
|
-
background: transparent !important;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
.ant-slider-track {
|
|
244
|
-
top: 6px;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
.ant-slider-handle {
|
|
248
|
-
top: 3px;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
.ant-slider-step {
|
|
252
|
-
height: 8px;
|
|
253
|
-
background: rgba(255, 255, 255, 0.1);
|
|
268
|
+
margin: 0;
|
|
269
|
+
accent-color: #fff;
|
|
270
|
+
background: transparent;
|
|
271
|
+
cursor: pointer;
|
|
254
272
|
}
|
|
255
273
|
|
|
256
|
-
.
|
|
257
|
-
|
|
274
|
+
.seek-range:disabled {
|
|
275
|
+
cursor: default;
|
|
276
|
+
opacity: 0.65;
|
|
258
277
|
}
|
|
259
278
|
`;
|
|
260
279
|
|
|
@@ -361,38 +380,85 @@ var createThrottledNumberFn = (fn, waitMs) => {
|
|
|
361
380
|
};
|
|
362
381
|
|
|
363
382
|
// src/controls.tsx
|
|
364
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
383
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
365
384
|
var SeekSlider = ({ playAction, duration, currentTime, seekSetTime }) => {
|
|
366
|
-
const formatter = (value) => {
|
|
367
|
-
return formatTime(Number(value != null ? value : 0));
|
|
368
|
-
};
|
|
369
385
|
const clickHandler = (e) => {
|
|
370
386
|
e.preventDefault();
|
|
371
387
|
e.stopPropagation();
|
|
372
388
|
};
|
|
373
389
|
const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
|
|
374
390
|
const safeCurrentTime = Math.max(0, Math.min(Number.isFinite(currentTime) ? currentTime : 0, safeDuration || Infinity));
|
|
391
|
+
const completeChange = () => {
|
|
392
|
+
void playAction();
|
|
393
|
+
};
|
|
375
394
|
return /* @__PURE__ */ jsx(SeekSliderStyled, { onClick: clickHandler, className: "seek-slider", children: /* @__PURE__ */ jsx(
|
|
376
|
-
|
|
395
|
+
"input",
|
|
377
396
|
{
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
397
|
+
"aria-label": "Seek",
|
|
398
|
+
className: "seek-range",
|
|
399
|
+
disabled: safeDuration <= 0,
|
|
400
|
+
max: safeDuration,
|
|
401
|
+
min: 0,
|
|
402
|
+
onChange: (e) => seekSetTime(Number(e.currentTarget.value)),
|
|
403
|
+
onKeyUp: (e) => {
|
|
404
|
+
if (e.key === "Enter" || e.key === " ") completeChange();
|
|
381
405
|
},
|
|
406
|
+
onPointerUp: completeChange,
|
|
407
|
+
step: "any",
|
|
408
|
+
type: "range",
|
|
382
409
|
value: safeCurrentTime,
|
|
383
|
-
|
|
384
|
-
max: safeDuration,
|
|
385
|
-
keyboard: false
|
|
410
|
+
title: formatTime(safeCurrentTime)
|
|
386
411
|
}
|
|
387
412
|
) });
|
|
388
413
|
};
|
|
389
414
|
var TimeBlock = ({ duration, currentTime }) => {
|
|
390
|
-
return /* @__PURE__ */ jsxs(
|
|
415
|
+
return /* @__PURE__ */ jsxs("span", { className: "video-player-time", children: [
|
|
391
416
|
formatTime(currentTime),
|
|
392
417
|
" / ",
|
|
393
418
|
formatTime(duration)
|
|
394
419
|
] });
|
|
395
420
|
};
|
|
421
|
+
var Icon = ({ name }) => {
|
|
422
|
+
const paths = {
|
|
423
|
+
back: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
424
|
+
/* @__PURE__ */ jsx("path", { d: "M11 7l-6 5 6 5V7z" }),
|
|
425
|
+
/* @__PURE__ */ jsx("path", { d: "M19 7l-6 5 6 5V7z" })
|
|
426
|
+
] }),
|
|
427
|
+
forward: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
428
|
+
/* @__PURE__ */ jsx("path", { d: "M5 7l6 5-6 5V7z" }),
|
|
429
|
+
/* @__PURE__ */ jsx("path", { d: "M13 7l6 5-6 5V7z" })
|
|
430
|
+
] }),
|
|
431
|
+
fullscreen: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
432
|
+
/* @__PURE__ */ jsx("path", { d: "M5 10V5h5" }),
|
|
433
|
+
/* @__PURE__ */ jsx("path", { d: "M14 5h5v5" }),
|
|
434
|
+
/* @__PURE__ */ jsx("path", { d: "M19 14v5h-5" }),
|
|
435
|
+
/* @__PURE__ */ jsx("path", { d: "M10 19H5v-5" })
|
|
436
|
+
] }),
|
|
437
|
+
mute: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
438
|
+
/* @__PURE__ */ jsx("path", { d: "M4 10v4h4l5 4V6l-5 4H4z" }),
|
|
439
|
+
/* @__PURE__ */ jsx("path", { d: "M17 9l4 6" }),
|
|
440
|
+
/* @__PURE__ */ jsx("path", { d: "M21 9l-4 6" })
|
|
441
|
+
] }),
|
|
442
|
+
pause: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
443
|
+
/* @__PURE__ */ jsx("path", { d: "M8 6h3v12H8z" }),
|
|
444
|
+
/* @__PURE__ */ jsx("path", { d: "M13 6h3v12h-3z" })
|
|
445
|
+
] }),
|
|
446
|
+
play: /* @__PURE__ */ jsx("path", { d: "M8 5v14l11-7L8 5z" }),
|
|
447
|
+
sound: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
448
|
+
/* @__PURE__ */ jsx("path", { d: "M4 10v4h4l5 4V6l-5 4H4z" }),
|
|
449
|
+
/* @__PURE__ */ jsx("path", { d: "M16 9a4 4 0 010 6" }),
|
|
450
|
+
/* @__PURE__ */ jsx("path", { d: "M18.5 6.5a8 8 0 010 11" })
|
|
451
|
+
] })
|
|
452
|
+
};
|
|
453
|
+
return /* @__PURE__ */ jsx("svg", { "aria-hidden": "true", className: "video-player-icon", focusable: "false", viewBox: "0 0 24 24", children: paths[name] });
|
|
454
|
+
};
|
|
455
|
+
var ControlButton = ({
|
|
456
|
+
icon,
|
|
457
|
+
label,
|
|
458
|
+
onClick
|
|
459
|
+
}) => {
|
|
460
|
+
return /* @__PURE__ */ jsx("button", { "aria-label": label, className: "video-player-control-button", onClick: () => void onClick(), title: label, type: "button", children: /* @__PURE__ */ jsx(Icon, { name: icon }) });
|
|
461
|
+
};
|
|
396
462
|
var stopControlClick = (e) => {
|
|
397
463
|
e.preventDefault();
|
|
398
464
|
e.stopPropagation();
|
|
@@ -402,7 +468,6 @@ var VideoPlayerControls = (props) => {
|
|
|
402
468
|
variant,
|
|
403
469
|
fullScreenAction,
|
|
404
470
|
showControl,
|
|
405
|
-
videoRef,
|
|
406
471
|
playAction,
|
|
407
472
|
pauseAction,
|
|
408
473
|
seekSetTime,
|
|
@@ -417,30 +482,29 @@ var VideoPlayerControls = (props) => {
|
|
|
417
482
|
} = props;
|
|
418
483
|
const [, isMobile] = useWindowWidth();
|
|
419
484
|
if (variant === "none") return null;
|
|
420
|
-
if (!videoRef.current) return null;
|
|
421
485
|
if (variant === "tiny") {
|
|
422
|
-
return /* @__PURE__ */ jsx(VideoPlayerControlStyles, { $tiny: true, $isMobile: isMobile, onClick: stopControlClick, $showControl: showControl, children: /* @__PURE__ */ jsxs(
|
|
486
|
+
return /* @__PURE__ */ jsx(VideoPlayerControlStyles, { $tiny: true, $isMobile: isMobile, onClick: stopControlClick, $showControl: showControl, children: /* @__PURE__ */ jsxs("div", { className: "video-player-control-row", children: [
|
|
423
487
|
/* @__PURE__ */ jsx("div", { className: "full" }),
|
|
424
488
|
/* @__PURE__ */ jsx(SeekSlider, { playAction, duration, currentTime, seekSetTime })
|
|
425
489
|
] }) });
|
|
426
490
|
}
|
|
427
491
|
if (variant === "fullscreen-only") {
|
|
428
|
-
return /* @__PURE__ */ jsx(VideoPlayerControlStyles, { $isMobile: isMobile, onClick: stopControlClick, $showControl: showControl, children: /* @__PURE__ */ jsxs(
|
|
492
|
+
return /* @__PURE__ */ jsx(VideoPlayerControlStyles, { $isMobile: isMobile, onClick: stopControlClick, $showControl: showControl, children: /* @__PURE__ */ jsxs("div", { className: "video-player-control-row", children: [
|
|
429
493
|
/* @__PURE__ */ jsx("div", { className: "full" }),
|
|
430
|
-
/* @__PURE__ */ jsx(
|
|
431
|
-
/* @__PURE__ */ jsx(
|
|
494
|
+
/* @__PURE__ */ jsx(ControlButton, { icon: muted ? "mute" : "sound", label: muted ? "Unmute" : "Mute", onClick: () => onMutedChange(!muted) }),
|
|
495
|
+
/* @__PURE__ */ jsx(ControlButton, { icon: "fullscreen", label: "Fullscreen", onClick: fullScreenAction })
|
|
432
496
|
] }) });
|
|
433
497
|
}
|
|
434
|
-
return /* @__PURE__ */ jsx(VideoPlayerControlStyles, { $isMobile: isMobile, onClick: stopControlClick, $showControl: showControl, children: /* @__PURE__ */ jsxs(
|
|
435
|
-
playing ? /* @__PURE__ */ jsx(
|
|
436
|
-
/* @__PURE__ */ jsx(
|
|
437
|
-
/* @__PURE__ */ jsx(
|
|
438
|
-
/* @__PURE__ */ jsx(
|
|
498
|
+
return /* @__PURE__ */ jsx(VideoPlayerControlStyles, { $isMobile: isMobile, onClick: stopControlClick, $showControl: showControl, children: /* @__PURE__ */ jsxs("div", { className: "video-player-control-row", children: [
|
|
499
|
+
playing ? /* @__PURE__ */ jsx(ControlButton, { icon: "pause", label: "Pause", onClick: pauseAction }) : /* @__PURE__ */ jsx(ControlButton, { icon: "play", label: "Play", onClick: playAction }),
|
|
500
|
+
/* @__PURE__ */ jsx(ControlButton, { icon: "back", label: "Back 15 seconds", onClick: () => seekPrevAction() }),
|
|
501
|
+
/* @__PURE__ */ jsx(ControlButton, { icon: "forward", label: "Forward 15 seconds", onClick: () => seekNextAction() }),
|
|
502
|
+
/* @__PURE__ */ jsx("span", { className: "video-player-control-title", title: typeof title === "string" ? title : void 0, children: title }),
|
|
439
503
|
/* @__PURE__ */ jsx("div", { className: "full" }),
|
|
440
504
|
/* @__PURE__ */ jsx(SeekSlider, { playAction, duration, currentTime, seekSetTime }),
|
|
441
505
|
/* @__PURE__ */ jsx(TimeBlock, { duration, currentTime }),
|
|
442
|
-
/* @__PURE__ */ jsx(
|
|
443
|
-
/* @__PURE__ */ jsx(
|
|
506
|
+
/* @__PURE__ */ jsx(ControlButton, { icon: muted ? "mute" : "sound", label: muted ? "Unmute" : "Mute", onClick: () => onMutedChange(!muted) }),
|
|
507
|
+
/* @__PURE__ */ jsx(ControlButton, { icon: "fullscreen", label: "Fullscreen", onClick: fullScreenAction })
|
|
444
508
|
] }) });
|
|
445
509
|
};
|
|
446
510
|
|
|
@@ -639,6 +703,14 @@ var clampTime = (time, duration) => {
|
|
|
639
703
|
const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
|
|
640
704
|
return safeDuration > 0 ? Math.max(0, Math.min(safeTime, safeDuration)) : Math.max(0, safeTime);
|
|
641
705
|
};
|
|
706
|
+
var isLikelyHlsSource = (src) => {
|
|
707
|
+
try {
|
|
708
|
+
const url = new URL(src, "http://video-player.local");
|
|
709
|
+
return /\.m3u8$/i.test(url.pathname) || /\.m3u8($|[?#])/i.test(src);
|
|
710
|
+
} catch {
|
|
711
|
+
return /\.m3u8($|[?#])/i.test(src);
|
|
712
|
+
}
|
|
713
|
+
};
|
|
642
714
|
var VideoPlayerBase = (props, ref) => {
|
|
643
715
|
const {
|
|
644
716
|
controlsVariant = "none",
|
|
@@ -922,10 +994,16 @@ var VideoPlayerBase = (props, ref) => {
|
|
|
922
994
|
);
|
|
923
995
|
}, [hlsConfig, hlsCredentials, live, liveHlsConfig, vodHlsConfig]);
|
|
924
996
|
const loadVideo = useCallback(
|
|
925
|
-
async (startAt =
|
|
997
|
+
async (startAt = initialTime) => {
|
|
926
998
|
const startVideoRef = videoRef.current;
|
|
927
999
|
if (!startVideoRef) return;
|
|
928
|
-
|
|
1000
|
+
let HlsCtor;
|
|
1001
|
+
try {
|
|
1002
|
+
HlsCtor = await loadHlsCtor();
|
|
1003
|
+
} catch (error) {
|
|
1004
|
+
reportError(error, { type: "hls-loader", src: videoSrc }, messages.streamLoadFailed);
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
929
1007
|
if (!videoRef.current || videoRef.current !== startVideoRef) return;
|
|
930
1008
|
const video = videoRef.current;
|
|
931
1009
|
if (!video) return;
|
|
@@ -1019,7 +1097,6 @@ var VideoPlayerBase = (props, ref) => {
|
|
|
1019
1097
|
}
|
|
1020
1098
|
void loadVideo(resumeTime);
|
|
1021
1099
|
});
|
|
1022
|
-
h.attachMedia(video);
|
|
1023
1100
|
h.on(HlsCtor.Events.MEDIA_ATTACHED, () => {
|
|
1024
1101
|
h.loadSource(videoSrc);
|
|
1025
1102
|
});
|
|
@@ -1035,11 +1112,13 @@ var VideoPlayerBase = (props, ref) => {
|
|
|
1035
1112
|
}
|
|
1036
1113
|
if (autoPlay) void playAction();
|
|
1037
1114
|
});
|
|
1115
|
+
h.attachMedia(video);
|
|
1038
1116
|
},
|
|
1039
1117
|
[
|
|
1040
1118
|
autoPlay,
|
|
1041
1119
|
clearErrorState,
|
|
1042
1120
|
clearTimers,
|
|
1121
|
+
initialTime,
|
|
1043
1122
|
live,
|
|
1044
1123
|
messages.streamDecodeFailed,
|
|
1045
1124
|
messages.streamLoadFailed,
|
|
@@ -1061,18 +1140,24 @@ var VideoPlayerBase = (props, ref) => {
|
|
|
1061
1140
|
const loadVideoHls = useCallback(async () => {
|
|
1062
1141
|
const video = videoRef.current;
|
|
1063
1142
|
const canNativeHls = !!(video == null ? void 0 : video.canPlayType) && video.canPlayType("application/vnd.apple.mpegurl") !== "";
|
|
1064
|
-
|
|
1143
|
+
let HlsCtor;
|
|
1144
|
+
try {
|
|
1145
|
+
HlsCtor = await loadHlsCtor();
|
|
1146
|
+
} catch (error) {
|
|
1147
|
+
reportError(error, { type: "hls-loader", src: videoSrc }, messages.streamLoadFailed);
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1065
1150
|
if (!videoRef.current || videoRef.current !== video) return;
|
|
1066
1151
|
if (HlsCtor.isSupported()) {
|
|
1067
|
-
void loadVideo();
|
|
1152
|
+
void loadVideo(initialTime);
|
|
1068
1153
|
return;
|
|
1069
1154
|
}
|
|
1070
1155
|
if (canNativeHls) {
|
|
1071
1156
|
loadVideoNative();
|
|
1072
1157
|
return;
|
|
1073
1158
|
}
|
|
1074
|
-
|
|
1075
|
-
}, [loadVideo, loadVideoNative]);
|
|
1159
|
+
reportError(null, { type: "hls", src: videoSrc, reason: "HLS is not supported in this browser" }, messages.unsupported);
|
|
1160
|
+
}, [initialTime, loadVideo, loadVideoNative, messages.streamLoadFailed, messages.unsupported, reportError, videoSrc]);
|
|
1076
1161
|
const destroy = useCallback(() => {
|
|
1077
1162
|
clearTimers();
|
|
1078
1163
|
if (hlsRef.current) {
|
|
@@ -1203,8 +1288,9 @@ var VideoPlayerBase = (props, ref) => {
|
|
|
1203
1288
|
hlsRef.current = null;
|
|
1204
1289
|
}
|
|
1205
1290
|
if (sourceType === "native" || type === "video") loadVideoNative();
|
|
1206
|
-
else if (sourceType === "hls") void loadVideo();
|
|
1207
|
-
else void loadVideoHls();
|
|
1291
|
+
else if (sourceType === "hls") void loadVideo(initialTime);
|
|
1292
|
+
else if (isLikelyHlsSource(videoSrc)) void loadVideoHls();
|
|
1293
|
+
else loadVideoNative();
|
|
1208
1294
|
return () => {
|
|
1209
1295
|
clearTimers();
|
|
1210
1296
|
if (hlsRef.current) {
|
|
@@ -1218,6 +1304,7 @@ var VideoPlayerBase = (props, ref) => {
|
|
|
1218
1304
|
loadVideo,
|
|
1219
1305
|
loadVideoHls,
|
|
1220
1306
|
loadVideoNative,
|
|
1307
|
+
initialTime,
|
|
1221
1308
|
reloadKey,
|
|
1222
1309
|
reloadToken,
|
|
1223
1310
|
sourceType,
|
|
@@ -1284,6 +1371,7 @@ var VideoPlayerBase = (props, ref) => {
|
|
|
1284
1371
|
"video",
|
|
1285
1372
|
{
|
|
1286
1373
|
...videoProps,
|
|
1374
|
+
controls: false,
|
|
1287
1375
|
ref: setVideoEl,
|
|
1288
1376
|
crossOrigin,
|
|
1289
1377
|
playsInline: true,
|
|
@@ -1313,7 +1401,6 @@ var VideoPlayerBase = (props, ref) => {
|
|
|
1313
1401
|
variant: controlsVariant,
|
|
1314
1402
|
fullScreenAction,
|
|
1315
1403
|
showControl,
|
|
1316
|
-
videoRef,
|
|
1317
1404
|
playAction,
|
|
1318
1405
|
pauseAction,
|
|
1319
1406
|
seekSetTime,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nikelroot/video-player",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Standalone React video player with HLS support.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -26,18 +26,14 @@
|
|
|
26
26
|
"hls.js": "^1.6.15"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
|
-
"@ant-design/icons": "^6.1.0",
|
|
30
|
-
"antd": "^6.3.1",
|
|
31
29
|
"react": ">=18",
|
|
32
30
|
"react-dom": ">=18",
|
|
33
31
|
"styled-components": "^6.0.0"
|
|
34
32
|
},
|
|
35
33
|
"devDependencies": {
|
|
36
|
-
"@ant-design/icons": "^6.1.0",
|
|
37
34
|
"@types/node": "^25.3.5",
|
|
38
35
|
"@types/react": "^19.2.14",
|
|
39
36
|
"@types/react-dom": "^19.2.3",
|
|
40
|
-
"antd": "^6.3.1",
|
|
41
37
|
"react": "^19.2.4",
|
|
42
38
|
"react-dom": "^19.2.4",
|
|
43
39
|
"styled-components": "^6.3.11",
|