@nikelroot/video-player 0.1.11 → 0.2.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 CHANGED
@@ -471,39 +471,39 @@ var TimeBlock = ({ duration, currentTime }) => {
471
471
  formatTime(duration)
472
472
  ] });
473
473
  };
474
+ var ICON_PATHS = {
475
+ back: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
476
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M11 7l-6 5 6 5V7z" }),
477
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M19 7l-6 5 6 5V7z" })
478
+ ] }),
479
+ forward: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
480
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 7l6 5-6 5V7z" }),
481
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M13 7l6 5-6 5V7z" })
482
+ ] }),
483
+ fullscreen: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
484
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 10V5h5" }),
485
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 5h5v5" }),
486
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M19 14v5h-5" }),
487
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10 19H5v-5" })
488
+ ] }),
489
+ mute: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
490
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 10v4h4l5 4V6l-5 4H4z" }),
491
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M17 9l4 6" }),
492
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M21 9l-4 6" })
493
+ ] }),
494
+ pause: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
495
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 6h3v12H8z" }),
496
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M13 6h3v12h-3z" })
497
+ ] }),
498
+ play: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 5v14l11-7L8 5z" }),
499
+ sound: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
500
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 10v4h4l5 4V6l-5 4H4z" }),
501
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M16 9a4 4 0 010 6" }),
502
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M18.5 6.5a8 8 0 010 11" })
503
+ ] })
504
+ };
474
505
  var Icon = ({ name }) => {
475
- const paths = {
476
- back: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
477
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M11 7l-6 5 6 5V7z" }),
478
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M19 7l-6 5 6 5V7z" })
479
- ] }),
480
- forward: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
481
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 7l6 5-6 5V7z" }),
482
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M13 7l6 5-6 5V7z" })
483
- ] }),
484
- fullscreen: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
485
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 10V5h5" }),
486
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 5h5v5" }),
487
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M19 14v5h-5" }),
488
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10 19H5v-5" })
489
- ] }),
490
- mute: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
491
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 10v4h4l5 4V6l-5 4H4z" }),
492
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M17 9l4 6" }),
493
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M21 9l-4 6" })
494
- ] }),
495
- pause: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
496
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 6h3v12H8z" }),
497
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M13 6h3v12h-3z" })
498
- ] }),
499
- play: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 5v14l11-7L8 5z" }),
500
- sound: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
501
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 10v4h4l5 4V6l-5 4H4z" }),
502
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M16 9a4 4 0 010 6" }),
503
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M18.5 6.5a8 8 0 010 11" })
504
- ] })
505
- };
506
- 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] });
506
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { "aria-hidden": "true", className: "video-player-icon", focusable: "false", viewBox: "0 0 24 24", children: ICON_PATHS[name] });
507
507
  };
508
508
  var ControlButton = ({
509
509
  disabled,
@@ -828,7 +828,6 @@ var VideoPlayerBase = (props, ref) => {
828
828
  scrollTo = true,
829
829
  muted: mutedProp,
830
830
  defaultMuted = false,
831
- currentTime: currentTimeProp,
832
831
  duration: durationProp,
833
832
  hlsConfig,
834
833
  vodHlsConfig,
@@ -854,6 +853,7 @@ var VideoPlayerBase = (props, ref) => {
854
853
  const liveStabilityMode = (0, import_react3.useRef)(false);
855
854
  const liveStallEvents = (0, import_react3.useRef)([]);
856
855
  const controlTime = (0, import_react3.useRef)(null);
856
+ const sourceLoadId = (0, import_react3.useRef)(0);
857
857
  const [playingState, setPlayingState] = (0, import_react3.useState)(!!defaultPlaying);
858
858
  const [mutedState, setMutedState] = (0, import_react3.useState)(!!defaultMuted);
859
859
  const [durationState, setDurationState] = (0, import_react3.useState)(0);
@@ -862,17 +862,19 @@ var VideoPlayerBase = (props, ref) => {
862
862
  const [showControl, setControlVisible] = (0, import_react3.useState)(true);
863
863
  const [errorState, setErrorState] = (0, import_react3.useState)(null);
864
864
  const [reloadToken, setReloadToken] = (0, import_react3.useState)(0);
865
+ const showControlRef = (0, import_react3.useRef)(showControl);
865
866
  const playing = playingProp != null ? playingProp : playingState;
866
867
  const muted = mutedProp != null ? mutedProp : mutedState;
867
868
  const isDurationControlled = durationProp !== void 0;
868
- const isCurrentTimeControlled = currentTimeProp !== void 0;
869
869
  const duration = durationProp != null ? durationProp : durationState;
870
- const currentTime = currentTimeProp != null ? currentTimeProp : currentTimeState;
870
+ const currentTime = currentTimeState;
871
871
  const fullscreenAllowed = active != null ? active : true;
872
872
  const onTimeChangeRef = (0, import_react3.useRef)(onTimeChange);
873
873
  const onDurationChangeRef = (0, import_react3.useRef)(onDurationChange);
874
874
  onTimeChangeRef.current = onTimeChange;
875
875
  onDurationChangeRef.current = onDurationChange;
876
+ showControlRef.current = showControl;
877
+ const { currentTime: _ignoredCurrentTime, ...domVideoProps } = videoProps;
876
878
  const setTimeD = (0, import_react3.useRef)(
877
879
  createThrottledNumberFn((value) => {
878
880
  const v = videoRef.current;
@@ -880,6 +882,7 @@ var VideoPlayerBase = (props, ref) => {
880
882
  v.currentTime = value;
881
883
  }, 30)
882
884
  );
885
+ const setCurrentTimeUi = (0, import_react3.useRef)(createThrottledNumberFn((value) => setCurrentTimeState(value), 250));
883
886
  const clearTimers = (0, import_react3.useCallback)(() => {
884
887
  if (reloadTimer.current) {
885
888
  clearTimeout(reloadTimer.current);
@@ -894,6 +897,18 @@ var VideoPlayerBase = (props, ref) => {
894
897
  controlTime.current = null;
895
898
  }
896
899
  }, []);
900
+ const resetMediaForSourceChange = (0, import_react3.useCallback)(() => {
901
+ clearTimers();
902
+ if (hlsRef.current) {
903
+ hlsRef.current.destroy();
904
+ hlsRef.current = null;
905
+ }
906
+ const video = videoRef.current;
907
+ if (!video) return;
908
+ video.pause();
909
+ video.removeAttribute("src");
910
+ video.load();
911
+ }, [clearTimers]);
897
912
  const setNextPlaying = (0, import_react3.useCallback)(
898
913
  (value) => {
899
914
  if (playingProp === void 0) setPlayingState(value);
@@ -941,9 +956,9 @@ var VideoPlayerBase = (props, ref) => {
941
956
  const el = videoRef.current;
942
957
  if (!el) return;
943
958
  const nextTime = Number.isFinite(el.currentTime) ? el.currentTime : 0;
944
- if (!isCurrentTimeControlled) setCurrentTimeState(nextTime);
959
+ setCurrentTimeUi.current(nextTime);
945
960
  (_a = onTimeChangeRef.current) == null ? void 0 : _a.call(onTimeChangeRef, nextTime, el);
946
- }, [isCurrentTimeControlled]);
961
+ }, []);
947
962
  const onDur = (0, import_react3.useCallback)(() => {
948
963
  var _a;
949
964
  const el = videoRef.current;
@@ -1024,10 +1039,11 @@ var VideoPlayerBase = (props, ref) => {
1024
1039
  if (!el) return;
1025
1040
  const next = clampTime(value, duration);
1026
1041
  setTimeD.current(next);
1027
- if (currentTimeProp === void 0) setCurrentTimeState(next);
1042
+ setCurrentTimeUi.current.cancel();
1043
+ setCurrentTimeState(next);
1028
1044
  notifyActiveChange(el, "seek");
1029
1045
  },
1030
- [currentTimeProp, duration, notifyActiveChange]
1046
+ [duration, notifyActiveChange]
1031
1047
  );
1032
1048
  const seekPrevAction = (0, import_react3.useCallback)(
1033
1049
  (sec = 15) => {
@@ -1084,7 +1100,7 @@ var VideoPlayerBase = (props, ref) => {
1084
1100
  );
1085
1101
  }, [hlsConfig, hlsCredentials, live, liveHlsConfig, vodHlsConfig]);
1086
1102
  const loadVideo = (0, import_react3.useCallback)(
1087
- async (startAt = initialTime) => {
1103
+ async (startAt = initialTime, loadId = sourceLoadId.current) => {
1088
1104
  const startVideoRef = videoRef.current;
1089
1105
  if (!startVideoRef) return;
1090
1106
  let HlsCtor;
@@ -1094,13 +1110,16 @@ var VideoPlayerBase = (props, ref) => {
1094
1110
  reportError(error, { type: "hls-loader", src: videoSrc }, messages.streamLoadFailed);
1095
1111
  return;
1096
1112
  }
1113
+ if (loadId !== sourceLoadId.current) return;
1097
1114
  if (!videoRef.current || videoRef.current !== startVideoRef) return;
1098
1115
  const video = videoRef.current;
1099
1116
  if (!video) return;
1100
1117
  const h = new HlsCtor(resolvedHlsConfig());
1101
1118
  hlsRef.current = h;
1119
+ const isStaleLoad = () => loadId !== sourceLoadId.current || hlsRef.current !== h || videoRef.current !== video;
1102
1120
  h.on(HlsCtor.Events.ERROR, (_evt, data) => {
1103
1121
  var _a, _b;
1122
+ if (isStaleLoad()) return;
1104
1123
  const { type: errorType, details, fatal } = data;
1105
1124
  if (details === HlsCtor.ErrorDetails.BUFFER_APPEND_ERROR || details === "bufferAppendingError") return;
1106
1125
  if (!fatal) {
@@ -1121,7 +1140,7 @@ var VideoPlayerBase = (props, ref) => {
1121
1140
  hlsRef.current.destroy();
1122
1141
  hlsRef.current = null;
1123
1142
  }
1124
- void loadVideo(resumeTime2);
1143
+ void loadVideo(resumeTime2, loadId);
1125
1144
  }, 250);
1126
1145
  }
1127
1146
  return;
@@ -1141,7 +1160,7 @@ var VideoPlayerBase = (props, ref) => {
1141
1160
  hlsRef.current.destroy();
1142
1161
  hlsRef.current = null;
1143
1162
  }
1144
- void loadVideo(resumeTime);
1163
+ void loadVideo(resumeTime, loadId);
1145
1164
  }, backoffMs);
1146
1165
  return;
1147
1166
  }
@@ -1185,12 +1204,14 @@ var VideoPlayerBase = (props, ref) => {
1185
1204
  hlsRef.current.destroy();
1186
1205
  hlsRef.current = null;
1187
1206
  }
1188
- void loadVideo(resumeTime);
1207
+ void loadVideo(resumeTime, loadId);
1189
1208
  });
1190
1209
  h.on(HlsCtor.Events.MEDIA_ATTACHED, () => {
1210
+ if (isStaleLoad()) return;
1191
1211
  h.loadSource(videoSrc);
1192
1212
  });
1193
1213
  h.on(HlsCtor.Events.MANIFEST_PARSED, () => {
1214
+ if (isStaleLoad()) return;
1194
1215
  fatalReloadCount.current = 0;
1195
1216
  netRetryCount.current = 0;
1196
1217
  mediaRecoverCount.current = 0;
@@ -1220,14 +1241,15 @@ var VideoPlayerBase = (props, ref) => {
1220
1241
  videoSrc
1221
1242
  ]
1222
1243
  );
1223
- const loadVideoNative = (0, import_react3.useCallback)(() => {
1244
+ const loadVideoNative = (0, import_react3.useCallback)((loadId = sourceLoadId.current) => {
1245
+ if (loadId !== sourceLoadId.current) return;
1224
1246
  const video = videoRef.current;
1225
1247
  if (!video) return;
1226
1248
  video.src = videoSrc;
1227
1249
  video.currentTime = initialTime;
1228
1250
  if (autoPlay) void playAction();
1229
1251
  }, [autoPlay, initialTime, playAction, videoSrc]);
1230
- const loadVideoHls = (0, import_react3.useCallback)(async () => {
1252
+ const loadVideoHls = (0, import_react3.useCallback)(async (loadId = sourceLoadId.current) => {
1231
1253
  const video = videoRef.current;
1232
1254
  const canNativeHls = !!(video == null ? void 0 : video.canPlayType) && video.canPlayType("application/vnd.apple.mpegurl") !== "";
1233
1255
  let HlsCtor;
@@ -1237,13 +1259,14 @@ var VideoPlayerBase = (props, ref) => {
1237
1259
  reportError(error, { type: "hls-loader", src: videoSrc }, messages.streamLoadFailed);
1238
1260
  return;
1239
1261
  }
1262
+ if (loadId !== sourceLoadId.current) return;
1240
1263
  if (!videoRef.current || videoRef.current !== video) return;
1241
1264
  if (HlsCtor.isSupported()) {
1242
- void loadVideo(initialTime);
1265
+ void loadVideo(initialTime, loadId);
1243
1266
  return;
1244
1267
  }
1245
1268
  if (canNativeHls) {
1246
- loadVideoNative();
1269
+ loadVideoNative(loadId);
1247
1270
  return;
1248
1271
  }
1249
1272
  reportError(null, { type: "hls", src: videoSrc, reason: "HLS is not supported in this browser" }, messages.unsupported);
@@ -1268,10 +1291,10 @@ var VideoPlayerBase = (props, ref) => {
1268
1291
  notifyActiveChange(null, "destroy");
1269
1292
  setNextPlaying(false);
1270
1293
  if (durationProp === void 0) setDurationState(0);
1271
- if (currentTimeProp === void 0) setCurrentTimeState(0);
1294
+ setCurrentTimeUi.current.cancel();
1295
+ setCurrentTimeState(0);
1272
1296
  }, [
1273
1297
  clearTimers,
1274
- currentTimeProp,
1275
1298
  durationProp,
1276
1299
  notifyActiveChange,
1277
1300
  onDur,
@@ -1333,8 +1356,12 @@ var VideoPlayerBase = (props, ref) => {
1333
1356
  }, [clearErrorState]);
1334
1357
  const mouseMoveHandler = (0, import_react3.useCallback)(() => {
1335
1358
  if (controlTime.current) clearTimeout(controlTime.current);
1336
- setControlVisible(true);
1359
+ if (!showControlRef.current) {
1360
+ showControlRef.current = true;
1361
+ setControlVisible(true);
1362
+ }
1337
1363
  controlTime.current = setTimeout(() => {
1364
+ showControlRef.current = false;
1338
1365
  setControlVisible(false);
1339
1366
  }, 1e3 * 10);
1340
1367
  }, []);
@@ -1360,42 +1387,32 @@ var VideoPlayerBase = (props, ref) => {
1360
1387
  if (playingProp) void playAction();
1361
1388
  else pauseAction();
1362
1389
  }, [pauseAction, playAction, playingProp]);
1363
- (0, import_react3.useEffect)(() => {
1364
- const el = videoRef.current;
1365
- if (!el || currentTimeProp === void 0) return;
1366
- if (Math.abs(el.currentTime - currentTimeProp) < 0.25) return;
1367
- el.currentTime = clampTime(currentTimeProp, duration);
1368
- }, [currentTimeProp, duration]);
1369
1390
  (0, import_react3.useEffect)(() => {
1370
1391
  if (!videoRef.current) return;
1392
+ const loadId = sourceLoadId.current + 1;
1393
+ sourceLoadId.current = loadId;
1371
1394
  clearErrorState();
1372
1395
  fatalReloadCount.current = 0;
1373
1396
  netRetryCount.current = 0;
1374
1397
  mediaRecoverCount.current = 0;
1375
1398
  liveStabilityMode.current = false;
1376
1399
  liveStallEvents.current = [];
1377
- if (hlsRef.current) {
1378
- hlsRef.current.destroy();
1379
- hlsRef.current = null;
1380
- }
1381
- if (sourceType === "native" || type === "video") loadVideoNative();
1382
- else if (sourceType === "hls") void loadVideo(initialTime);
1383
- else if (isLikelyHlsSource(videoSrc)) void loadVideoHls();
1384
- else loadVideoNative();
1400
+ resetMediaForSourceChange();
1401
+ if (sourceType === "native" || type === "video") loadVideoNative(loadId);
1402
+ else if (sourceType === "hls") void loadVideo(initialTime, loadId);
1403
+ else if (isLikelyHlsSource(videoSrc)) void loadVideoHls(loadId);
1404
+ else loadVideoNative(loadId);
1385
1405
  return () => {
1386
- clearTimers();
1387
- if (hlsRef.current) {
1388
- hlsRef.current.destroy();
1389
- hlsRef.current = null;
1390
- }
1406
+ sourceLoadId.current += 1;
1407
+ resetMediaForSourceChange();
1391
1408
  };
1392
1409
  }, [
1393
1410
  clearErrorState,
1394
- clearTimers,
1395
1411
  loadVideo,
1396
1412
  loadVideoHls,
1397
1413
  loadVideoNative,
1398
1414
  initialTime,
1415
+ resetMediaForSourceChange,
1399
1416
  reloadKey,
1400
1417
  reloadToken,
1401
1418
  sourceType,
@@ -1443,6 +1460,7 @@ var VideoPlayerBase = (props, ref) => {
1443
1460
  (0, import_react3.useEffect)(() => {
1444
1461
  return () => {
1445
1462
  setTimeD.current.cancel();
1463
+ setCurrentTimeUi.current.cancel();
1446
1464
  destroy();
1447
1465
  };
1448
1466
  }, [destroy]);
@@ -1484,7 +1502,7 @@ var VideoPlayerBase = (props, ref) => {
1484
1502
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1485
1503
  "video",
1486
1504
  {
1487
- ...videoProps,
1505
+ ...domVideoProps,
1488
1506
  controls: false,
1489
1507
  ref: setVideoEl,
1490
1508
  crossOrigin,
package/dist/index.d.cts CHANGED
@@ -58,7 +58,6 @@ interface VideoPlayerProps extends Omit<React.VideoHTMLAttributes<HTMLVideoEleme
58
58
  defaultPlaying?: boolean;
59
59
  playing?: boolean;
60
60
  onPlayingChange?: (playing: boolean) => void;
61
- currentTime?: number;
62
61
  duration?: number;
63
62
  onTimeChange?: (time: number, video: HTMLVideoElement) => void;
64
63
  onDurationChange?: (duration: number, video: HTMLVideoElement) => void;
package/dist/index.d.ts CHANGED
@@ -58,7 +58,6 @@ interface VideoPlayerProps extends Omit<React.VideoHTMLAttributes<HTMLVideoEleme
58
58
  defaultPlaying?: boolean;
59
59
  playing?: boolean;
60
60
  onPlayingChange?: (playing: boolean) => void;
61
- currentTime?: number;
62
61
  duration?: number;
63
62
  onTimeChange?: (time: number, video: HTMLVideoElement) => void;
64
63
  onDurationChange?: (duration: number, video: HTMLVideoElement) => void;
package/dist/index.js CHANGED
@@ -442,39 +442,39 @@ var TimeBlock = ({ duration, currentTime }) => {
442
442
  formatTime(duration)
443
443
  ] });
444
444
  };
445
+ var ICON_PATHS = {
446
+ back: /* @__PURE__ */ jsxs(Fragment, { children: [
447
+ /* @__PURE__ */ jsx("path", { d: "M11 7l-6 5 6 5V7z" }),
448
+ /* @__PURE__ */ jsx("path", { d: "M19 7l-6 5 6 5V7z" })
449
+ ] }),
450
+ forward: /* @__PURE__ */ jsxs(Fragment, { children: [
451
+ /* @__PURE__ */ jsx("path", { d: "M5 7l6 5-6 5V7z" }),
452
+ /* @__PURE__ */ jsx("path", { d: "M13 7l6 5-6 5V7z" })
453
+ ] }),
454
+ fullscreen: /* @__PURE__ */ jsxs(Fragment, { children: [
455
+ /* @__PURE__ */ jsx("path", { d: "M5 10V5h5" }),
456
+ /* @__PURE__ */ jsx("path", { d: "M14 5h5v5" }),
457
+ /* @__PURE__ */ jsx("path", { d: "M19 14v5h-5" }),
458
+ /* @__PURE__ */ jsx("path", { d: "M10 19H5v-5" })
459
+ ] }),
460
+ mute: /* @__PURE__ */ jsxs(Fragment, { children: [
461
+ /* @__PURE__ */ jsx("path", { d: "M4 10v4h4l5 4V6l-5 4H4z" }),
462
+ /* @__PURE__ */ jsx("path", { d: "M17 9l4 6" }),
463
+ /* @__PURE__ */ jsx("path", { d: "M21 9l-4 6" })
464
+ ] }),
465
+ pause: /* @__PURE__ */ jsxs(Fragment, { children: [
466
+ /* @__PURE__ */ jsx("path", { d: "M8 6h3v12H8z" }),
467
+ /* @__PURE__ */ jsx("path", { d: "M13 6h3v12h-3z" })
468
+ ] }),
469
+ play: /* @__PURE__ */ jsx("path", { d: "M8 5v14l11-7L8 5z" }),
470
+ sound: /* @__PURE__ */ jsxs(Fragment, { children: [
471
+ /* @__PURE__ */ jsx("path", { d: "M4 10v4h4l5 4V6l-5 4H4z" }),
472
+ /* @__PURE__ */ jsx("path", { d: "M16 9a4 4 0 010 6" }),
473
+ /* @__PURE__ */ jsx("path", { d: "M18.5 6.5a8 8 0 010 11" })
474
+ ] })
475
+ };
445
476
  var Icon = ({ name }) => {
446
- const paths = {
447
- back: /* @__PURE__ */ jsxs(Fragment, { children: [
448
- /* @__PURE__ */ jsx("path", { d: "M11 7l-6 5 6 5V7z" }),
449
- /* @__PURE__ */ jsx("path", { d: "M19 7l-6 5 6 5V7z" })
450
- ] }),
451
- forward: /* @__PURE__ */ jsxs(Fragment, { children: [
452
- /* @__PURE__ */ jsx("path", { d: "M5 7l6 5-6 5V7z" }),
453
- /* @__PURE__ */ jsx("path", { d: "M13 7l6 5-6 5V7z" })
454
- ] }),
455
- fullscreen: /* @__PURE__ */ jsxs(Fragment, { children: [
456
- /* @__PURE__ */ jsx("path", { d: "M5 10V5h5" }),
457
- /* @__PURE__ */ jsx("path", { d: "M14 5h5v5" }),
458
- /* @__PURE__ */ jsx("path", { d: "M19 14v5h-5" }),
459
- /* @__PURE__ */ jsx("path", { d: "M10 19H5v-5" })
460
- ] }),
461
- mute: /* @__PURE__ */ jsxs(Fragment, { children: [
462
- /* @__PURE__ */ jsx("path", { d: "M4 10v4h4l5 4V6l-5 4H4z" }),
463
- /* @__PURE__ */ jsx("path", { d: "M17 9l4 6" }),
464
- /* @__PURE__ */ jsx("path", { d: "M21 9l-4 6" })
465
- ] }),
466
- pause: /* @__PURE__ */ jsxs(Fragment, { children: [
467
- /* @__PURE__ */ jsx("path", { d: "M8 6h3v12H8z" }),
468
- /* @__PURE__ */ jsx("path", { d: "M13 6h3v12h-3z" })
469
- ] }),
470
- play: /* @__PURE__ */ jsx("path", { d: "M8 5v14l11-7L8 5z" }),
471
- sound: /* @__PURE__ */ jsxs(Fragment, { children: [
472
- /* @__PURE__ */ jsx("path", { d: "M4 10v4h4l5 4V6l-5 4H4z" }),
473
- /* @__PURE__ */ jsx("path", { d: "M16 9a4 4 0 010 6" }),
474
- /* @__PURE__ */ jsx("path", { d: "M18.5 6.5a8 8 0 010 11" })
475
- ] })
476
- };
477
- return /* @__PURE__ */ jsx("svg", { "aria-hidden": "true", className: "video-player-icon", focusable: "false", viewBox: "0 0 24 24", children: paths[name] });
477
+ return /* @__PURE__ */ jsx("svg", { "aria-hidden": "true", className: "video-player-icon", focusable: "false", viewBox: "0 0 24 24", children: ICON_PATHS[name] });
478
478
  };
479
479
  var ControlButton = ({
480
480
  disabled,
@@ -799,7 +799,6 @@ var VideoPlayerBase = (props, ref) => {
799
799
  scrollTo = true,
800
800
  muted: mutedProp,
801
801
  defaultMuted = false,
802
- currentTime: currentTimeProp,
803
802
  duration: durationProp,
804
803
  hlsConfig,
805
804
  vodHlsConfig,
@@ -825,6 +824,7 @@ var VideoPlayerBase = (props, ref) => {
825
824
  const liveStabilityMode = useRef2(false);
826
825
  const liveStallEvents = useRef2([]);
827
826
  const controlTime = useRef2(null);
827
+ const sourceLoadId = useRef2(0);
828
828
  const [playingState, setPlayingState] = useState(!!defaultPlaying);
829
829
  const [mutedState, setMutedState] = useState(!!defaultMuted);
830
830
  const [durationState, setDurationState] = useState(0);
@@ -833,17 +833,19 @@ var VideoPlayerBase = (props, ref) => {
833
833
  const [showControl, setControlVisible] = useState(true);
834
834
  const [errorState, setErrorState] = useState(null);
835
835
  const [reloadToken, setReloadToken] = useState(0);
836
+ const showControlRef = useRef2(showControl);
836
837
  const playing = playingProp != null ? playingProp : playingState;
837
838
  const muted = mutedProp != null ? mutedProp : mutedState;
838
839
  const isDurationControlled = durationProp !== void 0;
839
- const isCurrentTimeControlled = currentTimeProp !== void 0;
840
840
  const duration = durationProp != null ? durationProp : durationState;
841
- const currentTime = currentTimeProp != null ? currentTimeProp : currentTimeState;
841
+ const currentTime = currentTimeState;
842
842
  const fullscreenAllowed = active != null ? active : true;
843
843
  const onTimeChangeRef = useRef2(onTimeChange);
844
844
  const onDurationChangeRef = useRef2(onDurationChange);
845
845
  onTimeChangeRef.current = onTimeChange;
846
846
  onDurationChangeRef.current = onDurationChange;
847
+ showControlRef.current = showControl;
848
+ const { currentTime: _ignoredCurrentTime, ...domVideoProps } = videoProps;
847
849
  const setTimeD = useRef2(
848
850
  createThrottledNumberFn((value) => {
849
851
  const v = videoRef.current;
@@ -851,6 +853,7 @@ var VideoPlayerBase = (props, ref) => {
851
853
  v.currentTime = value;
852
854
  }, 30)
853
855
  );
856
+ const setCurrentTimeUi = useRef2(createThrottledNumberFn((value) => setCurrentTimeState(value), 250));
854
857
  const clearTimers = useCallback(() => {
855
858
  if (reloadTimer.current) {
856
859
  clearTimeout(reloadTimer.current);
@@ -865,6 +868,18 @@ var VideoPlayerBase = (props, ref) => {
865
868
  controlTime.current = null;
866
869
  }
867
870
  }, []);
871
+ const resetMediaForSourceChange = useCallback(() => {
872
+ clearTimers();
873
+ if (hlsRef.current) {
874
+ hlsRef.current.destroy();
875
+ hlsRef.current = null;
876
+ }
877
+ const video = videoRef.current;
878
+ if (!video) return;
879
+ video.pause();
880
+ video.removeAttribute("src");
881
+ video.load();
882
+ }, [clearTimers]);
868
883
  const setNextPlaying = useCallback(
869
884
  (value) => {
870
885
  if (playingProp === void 0) setPlayingState(value);
@@ -912,9 +927,9 @@ var VideoPlayerBase = (props, ref) => {
912
927
  const el = videoRef.current;
913
928
  if (!el) return;
914
929
  const nextTime = Number.isFinite(el.currentTime) ? el.currentTime : 0;
915
- if (!isCurrentTimeControlled) setCurrentTimeState(nextTime);
930
+ setCurrentTimeUi.current(nextTime);
916
931
  (_a = onTimeChangeRef.current) == null ? void 0 : _a.call(onTimeChangeRef, nextTime, el);
917
- }, [isCurrentTimeControlled]);
932
+ }, []);
918
933
  const onDur = useCallback(() => {
919
934
  var _a;
920
935
  const el = videoRef.current;
@@ -995,10 +1010,11 @@ var VideoPlayerBase = (props, ref) => {
995
1010
  if (!el) return;
996
1011
  const next = clampTime(value, duration);
997
1012
  setTimeD.current(next);
998
- if (currentTimeProp === void 0) setCurrentTimeState(next);
1013
+ setCurrentTimeUi.current.cancel();
1014
+ setCurrentTimeState(next);
999
1015
  notifyActiveChange(el, "seek");
1000
1016
  },
1001
- [currentTimeProp, duration, notifyActiveChange]
1017
+ [duration, notifyActiveChange]
1002
1018
  );
1003
1019
  const seekPrevAction = useCallback(
1004
1020
  (sec = 15) => {
@@ -1055,7 +1071,7 @@ var VideoPlayerBase = (props, ref) => {
1055
1071
  );
1056
1072
  }, [hlsConfig, hlsCredentials, live, liveHlsConfig, vodHlsConfig]);
1057
1073
  const loadVideo = useCallback(
1058
- async (startAt = initialTime) => {
1074
+ async (startAt = initialTime, loadId = sourceLoadId.current) => {
1059
1075
  const startVideoRef = videoRef.current;
1060
1076
  if (!startVideoRef) return;
1061
1077
  let HlsCtor;
@@ -1065,13 +1081,16 @@ var VideoPlayerBase = (props, ref) => {
1065
1081
  reportError(error, { type: "hls-loader", src: videoSrc }, messages.streamLoadFailed);
1066
1082
  return;
1067
1083
  }
1084
+ if (loadId !== sourceLoadId.current) return;
1068
1085
  if (!videoRef.current || videoRef.current !== startVideoRef) return;
1069
1086
  const video = videoRef.current;
1070
1087
  if (!video) return;
1071
1088
  const h = new HlsCtor(resolvedHlsConfig());
1072
1089
  hlsRef.current = h;
1090
+ const isStaleLoad = () => loadId !== sourceLoadId.current || hlsRef.current !== h || videoRef.current !== video;
1073
1091
  h.on(HlsCtor.Events.ERROR, (_evt, data) => {
1074
1092
  var _a, _b;
1093
+ if (isStaleLoad()) return;
1075
1094
  const { type: errorType, details, fatal } = data;
1076
1095
  if (details === HlsCtor.ErrorDetails.BUFFER_APPEND_ERROR || details === "bufferAppendingError") return;
1077
1096
  if (!fatal) {
@@ -1092,7 +1111,7 @@ var VideoPlayerBase = (props, ref) => {
1092
1111
  hlsRef.current.destroy();
1093
1112
  hlsRef.current = null;
1094
1113
  }
1095
- void loadVideo(resumeTime2);
1114
+ void loadVideo(resumeTime2, loadId);
1096
1115
  }, 250);
1097
1116
  }
1098
1117
  return;
@@ -1112,7 +1131,7 @@ var VideoPlayerBase = (props, ref) => {
1112
1131
  hlsRef.current.destroy();
1113
1132
  hlsRef.current = null;
1114
1133
  }
1115
- void loadVideo(resumeTime);
1134
+ void loadVideo(resumeTime, loadId);
1116
1135
  }, backoffMs);
1117
1136
  return;
1118
1137
  }
@@ -1156,12 +1175,14 @@ var VideoPlayerBase = (props, ref) => {
1156
1175
  hlsRef.current.destroy();
1157
1176
  hlsRef.current = null;
1158
1177
  }
1159
- void loadVideo(resumeTime);
1178
+ void loadVideo(resumeTime, loadId);
1160
1179
  });
1161
1180
  h.on(HlsCtor.Events.MEDIA_ATTACHED, () => {
1181
+ if (isStaleLoad()) return;
1162
1182
  h.loadSource(videoSrc);
1163
1183
  });
1164
1184
  h.on(HlsCtor.Events.MANIFEST_PARSED, () => {
1185
+ if (isStaleLoad()) return;
1165
1186
  fatalReloadCount.current = 0;
1166
1187
  netRetryCount.current = 0;
1167
1188
  mediaRecoverCount.current = 0;
@@ -1191,14 +1212,15 @@ var VideoPlayerBase = (props, ref) => {
1191
1212
  videoSrc
1192
1213
  ]
1193
1214
  );
1194
- const loadVideoNative = useCallback(() => {
1215
+ const loadVideoNative = useCallback((loadId = sourceLoadId.current) => {
1216
+ if (loadId !== sourceLoadId.current) return;
1195
1217
  const video = videoRef.current;
1196
1218
  if (!video) return;
1197
1219
  video.src = videoSrc;
1198
1220
  video.currentTime = initialTime;
1199
1221
  if (autoPlay) void playAction();
1200
1222
  }, [autoPlay, initialTime, playAction, videoSrc]);
1201
- const loadVideoHls = useCallback(async () => {
1223
+ const loadVideoHls = useCallback(async (loadId = sourceLoadId.current) => {
1202
1224
  const video = videoRef.current;
1203
1225
  const canNativeHls = !!(video == null ? void 0 : video.canPlayType) && video.canPlayType("application/vnd.apple.mpegurl") !== "";
1204
1226
  let HlsCtor;
@@ -1208,13 +1230,14 @@ var VideoPlayerBase = (props, ref) => {
1208
1230
  reportError(error, { type: "hls-loader", src: videoSrc }, messages.streamLoadFailed);
1209
1231
  return;
1210
1232
  }
1233
+ if (loadId !== sourceLoadId.current) return;
1211
1234
  if (!videoRef.current || videoRef.current !== video) return;
1212
1235
  if (HlsCtor.isSupported()) {
1213
- void loadVideo(initialTime);
1236
+ void loadVideo(initialTime, loadId);
1214
1237
  return;
1215
1238
  }
1216
1239
  if (canNativeHls) {
1217
- loadVideoNative();
1240
+ loadVideoNative(loadId);
1218
1241
  return;
1219
1242
  }
1220
1243
  reportError(null, { type: "hls", src: videoSrc, reason: "HLS is not supported in this browser" }, messages.unsupported);
@@ -1239,10 +1262,10 @@ var VideoPlayerBase = (props, ref) => {
1239
1262
  notifyActiveChange(null, "destroy");
1240
1263
  setNextPlaying(false);
1241
1264
  if (durationProp === void 0) setDurationState(0);
1242
- if (currentTimeProp === void 0) setCurrentTimeState(0);
1265
+ setCurrentTimeUi.current.cancel();
1266
+ setCurrentTimeState(0);
1243
1267
  }, [
1244
1268
  clearTimers,
1245
- currentTimeProp,
1246
1269
  durationProp,
1247
1270
  notifyActiveChange,
1248
1271
  onDur,
@@ -1304,8 +1327,12 @@ var VideoPlayerBase = (props, ref) => {
1304
1327
  }, [clearErrorState]);
1305
1328
  const mouseMoveHandler = useCallback(() => {
1306
1329
  if (controlTime.current) clearTimeout(controlTime.current);
1307
- setControlVisible(true);
1330
+ if (!showControlRef.current) {
1331
+ showControlRef.current = true;
1332
+ setControlVisible(true);
1333
+ }
1308
1334
  controlTime.current = setTimeout(() => {
1335
+ showControlRef.current = false;
1309
1336
  setControlVisible(false);
1310
1337
  }, 1e3 * 10);
1311
1338
  }, []);
@@ -1331,42 +1358,32 @@ var VideoPlayerBase = (props, ref) => {
1331
1358
  if (playingProp) void playAction();
1332
1359
  else pauseAction();
1333
1360
  }, [pauseAction, playAction, playingProp]);
1334
- useEffect(() => {
1335
- const el = videoRef.current;
1336
- if (!el || currentTimeProp === void 0) return;
1337
- if (Math.abs(el.currentTime - currentTimeProp) < 0.25) return;
1338
- el.currentTime = clampTime(currentTimeProp, duration);
1339
- }, [currentTimeProp, duration]);
1340
1361
  useEffect(() => {
1341
1362
  if (!videoRef.current) return;
1363
+ const loadId = sourceLoadId.current + 1;
1364
+ sourceLoadId.current = loadId;
1342
1365
  clearErrorState();
1343
1366
  fatalReloadCount.current = 0;
1344
1367
  netRetryCount.current = 0;
1345
1368
  mediaRecoverCount.current = 0;
1346
1369
  liveStabilityMode.current = false;
1347
1370
  liveStallEvents.current = [];
1348
- if (hlsRef.current) {
1349
- hlsRef.current.destroy();
1350
- hlsRef.current = null;
1351
- }
1352
- if (sourceType === "native" || type === "video") loadVideoNative();
1353
- else if (sourceType === "hls") void loadVideo(initialTime);
1354
- else if (isLikelyHlsSource(videoSrc)) void loadVideoHls();
1355
- else loadVideoNative();
1371
+ resetMediaForSourceChange();
1372
+ if (sourceType === "native" || type === "video") loadVideoNative(loadId);
1373
+ else if (sourceType === "hls") void loadVideo(initialTime, loadId);
1374
+ else if (isLikelyHlsSource(videoSrc)) void loadVideoHls(loadId);
1375
+ else loadVideoNative(loadId);
1356
1376
  return () => {
1357
- clearTimers();
1358
- if (hlsRef.current) {
1359
- hlsRef.current.destroy();
1360
- hlsRef.current = null;
1361
- }
1377
+ sourceLoadId.current += 1;
1378
+ resetMediaForSourceChange();
1362
1379
  };
1363
1380
  }, [
1364
1381
  clearErrorState,
1365
- clearTimers,
1366
1382
  loadVideo,
1367
1383
  loadVideoHls,
1368
1384
  loadVideoNative,
1369
1385
  initialTime,
1386
+ resetMediaForSourceChange,
1370
1387
  reloadKey,
1371
1388
  reloadToken,
1372
1389
  sourceType,
@@ -1414,6 +1431,7 @@ var VideoPlayerBase = (props, ref) => {
1414
1431
  useEffect(() => {
1415
1432
  return () => {
1416
1433
  setTimeD.current.cancel();
1434
+ setCurrentTimeUi.current.cancel();
1417
1435
  destroy();
1418
1436
  };
1419
1437
  }, [destroy]);
@@ -1455,7 +1473,7 @@ var VideoPlayerBase = (props, ref) => {
1455
1473
  /* @__PURE__ */ jsx2(
1456
1474
  "video",
1457
1475
  {
1458
- ...videoProps,
1476
+ ...domVideoProps,
1459
1477
  controls: false,
1460
1478
  ref: setVideoEl,
1461
1479
  crossOrigin,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nikelroot/video-player",
3
- "version": "0.1.11",
3
+ "version": "0.2.0",
4
4
  "description": "Standalone React video player with HLS support.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",