Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
Sync from GitHub via hub-sync
Browse files
src/components/simple-videos-player.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
"use client";
|
| 2 |
|
| 3 |
-
import React, { useEffect, useRef
|
| 4 |
import { useTime } from "../context/time-context";
|
| 5 |
import { FaExpand, FaCompress, FaTimes, FaEye } from "react-icons/fa";
|
| 6 |
import type { VideoInfo } from "@/types";
|
|
@@ -86,53 +86,84 @@ export const SimpleVideosPlayer = ({
|
|
| 86 |
const timeout = setTimeout(markReady, VIDEO_READY_TIMEOUT_MS);
|
| 87 |
|
| 88 |
videoRefs.current.forEach((video, index) => {
|
| 89 |
-
if (video)
|
| 90 |
-
|
| 91 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
if (info.isSegmented) {
|
| 93 |
-
const
|
| 94 |
-
|
| 95 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
if (
|
| 98 |
-
video.currentTime
|
| 99 |
-
|
| 100 |
) {
|
| 101 |
video.currentTime = segmentStart;
|
| 102 |
-
if (index === firstVisibleIdxRef.current) {
|
| 103 |
-
setCurrentTime(0);
|
| 104 |
-
}
|
| 105 |
}
|
| 106 |
-
}
|
|
|
|
| 107 |
|
| 108 |
-
|
|
|
|
| 109 |
video.currentTime = info.segmentStart || 0;
|
| 110 |
checkReady();
|
| 111 |
-
}
|
| 112 |
-
|
| 113 |
-
video.addEventListener("timeupdate", handleTimeUpdate);
|
| 114 |
-
video.addEventListener("loadeddata", handleLoadedData);
|
| 115 |
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
});
|
| 120 |
-
} else {
|
| 121 |
-
const handleEnded = () => {
|
| 122 |
video.currentTime = 0;
|
| 123 |
if (index === firstVisibleIdxRef.current) {
|
| 124 |
setCurrentTime(0);
|
| 125 |
}
|
| 126 |
};
|
| 127 |
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
}
|
| 135 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
});
|
| 137 |
|
| 138 |
return () => {
|
|
@@ -198,43 +229,6 @@ export const SimpleVideosPlayer = ({
|
|
| 198 |
});
|
| 199 |
}, [externalSeekVersion, currentTime, videosInfo, videosReady, hiddenSet]);
|
| 200 |
|
| 201 |
-
// Stable per-index timeupdate handlers avoid findIndex scan on every event.
|
| 202 |
-
// Tagged "video" so the context doesn't bump externalSeekVersion — the
|
| 203 |
-
// sync effect treats this as a status report, not a seek command.
|
| 204 |
-
const makeTimeUpdateHandler = useCallback(
|
| 205 |
-
(index: number) => {
|
| 206 |
-
return () => {
|
| 207 |
-
const video = videoRefs.current[index];
|
| 208 |
-
const info = videosInfo[index];
|
| 209 |
-
if (!video || !info) return;
|
| 210 |
-
|
| 211 |
-
let globalTime = video.currentTime;
|
| 212 |
-
if (info.isSegmented) {
|
| 213 |
-
globalTime = video.currentTime - (info.segmentStart || 0);
|
| 214 |
-
}
|
| 215 |
-
setCurrentTime(globalTime, "video");
|
| 216 |
-
};
|
| 217 |
-
},
|
| 218 |
-
[videosInfo, setCurrentTime],
|
| 219 |
-
);
|
| 220 |
-
|
| 221 |
-
// Handle play click for segmented videos
|
| 222 |
-
const handlePlay = (video: HTMLVideoElement, info: VideoInfo) => {
|
| 223 |
-
if (info.isSegmented) {
|
| 224 |
-
const segmentStart = info.segmentStart || 0;
|
| 225 |
-
const segmentEnd = info.segmentEnd || video.duration;
|
| 226 |
-
|
| 227 |
-
if (video.currentTime < segmentStart || video.currentTime >= segmentEnd) {
|
| 228 |
-
video.currentTime = segmentStart;
|
| 229 |
-
}
|
| 230 |
-
}
|
| 231 |
-
video.play().catch((e: unknown) => {
|
| 232 |
-
if ((e as Error)?.name !== "AbortError") {
|
| 233 |
-
console.error("Error playing video", e);
|
| 234 |
-
}
|
| 235 |
-
});
|
| 236 |
-
};
|
| 237 |
-
|
| 238 |
return (
|
| 239 |
<>
|
| 240 |
{/* Hidden videos menu */}
|
|
@@ -275,7 +269,6 @@ export const SimpleVideosPlayer = ({
|
|
| 275 |
if (hiddenVideos.includes(info.filename)) return null;
|
| 276 |
|
| 277 |
const isEnlarged = enlargedVideo === info.filename;
|
| 278 |
-
const isFirstVisible = idx === firstVisibleIdx;
|
| 279 |
|
| 280 |
return (
|
| 281 |
<div
|
|
@@ -328,10 +321,6 @@ export const SimpleVideosPlayer = ({
|
|
| 328 |
muted
|
| 329 |
preload="auto"
|
| 330 |
crossOrigin="anonymous"
|
| 331 |
-
onPlay={(e) => handlePlay(e.currentTarget, info)}
|
| 332 |
-
onTimeUpdate={
|
| 333 |
-
isFirstVisible ? makeTimeUpdateHandler(idx) : undefined
|
| 334 |
-
}
|
| 335 |
>
|
| 336 |
<source src={proxyHfUrl(info.url)} type="video/mp4" />
|
| 337 |
Your browser does not support the video tag.
|
|
|
|
| 1 |
"use client";
|
| 2 |
|
| 3 |
+
import React, { useEffect, useRef } from "react";
|
| 4 |
import { useTime } from "../context/time-context";
|
| 5 |
import { FaExpand, FaCompress, FaTimes, FaEye } from "react-icons/fa";
|
| 6 |
import type { VideoInfo } from "@/types";
|
|
|
|
| 86 |
const timeout = setTimeout(markReady, VIDEO_READY_TIMEOUT_MS);
|
| 87 |
|
| 88 |
videoRefs.current.forEach((video, index) => {
|
| 89 |
+
if (!video) return;
|
| 90 |
+
const info = videosInfo[index];
|
| 91 |
|
| 92 |
+
// One timeupdate handler per video covers both jobs:
|
| 93 |
+
// (a) loop-reset on segmented videos at segment-end
|
| 94 |
+
// (b) reporting the primary video's currentTime back to the context
|
| 95 |
+
const handleTimeUpdate = () => {
|
| 96 |
if (info.isSegmented) {
|
| 97 |
+
const segmentEnd = info.segmentEnd || video.duration;
|
| 98 |
+
const segmentStart = info.segmentStart || 0;
|
| 99 |
+
if (
|
| 100 |
+
video.currentTime >=
|
| 101 |
+
segmentEnd - THRESHOLDS.VIDEO_SEGMENT_BOUNDARY
|
| 102 |
+
) {
|
| 103 |
+
video.currentTime = segmentStart;
|
| 104 |
+
if (index === firstVisibleIdxRef.current) {
|
| 105 |
+
setCurrentTime(0);
|
| 106 |
+
}
|
| 107 |
+
return;
|
| 108 |
+
}
|
| 109 |
+
}
|
| 110 |
+
if (index === firstVisibleIdxRef.current) {
|
| 111 |
+
let globalTime = video.currentTime;
|
| 112 |
+
if (info.isSegmented) {
|
| 113 |
+
globalTime = video.currentTime - (info.segmentStart || 0);
|
| 114 |
+
}
|
| 115 |
+
setCurrentTime(globalTime, "video");
|
| 116 |
+
}
|
| 117 |
+
};
|
| 118 |
|
| 119 |
+
// For segmented videos, snap into the segment when play() is called
|
| 120 |
+
// — covers the case where the user paused outside the segment range.
|
| 121 |
+
const handlePlay = info.isSegmented
|
| 122 |
+
? () => {
|
| 123 |
+
const segmentStart = info.segmentStart || 0;
|
| 124 |
+
const segmentEnd = info.segmentEnd || video.duration;
|
| 125 |
if (
|
| 126 |
+
video.currentTime < segmentStart ||
|
| 127 |
+
video.currentTime >= segmentEnd
|
| 128 |
) {
|
| 129 |
video.currentTime = segmentStart;
|
|
|
|
|
|
|
|
|
|
| 130 |
}
|
| 131 |
+
}
|
| 132 |
+
: null;
|
| 133 |
|
| 134 |
+
const handleLoadedData = info.isSegmented
|
| 135 |
+
? () => {
|
| 136 |
video.currentTime = info.segmentStart || 0;
|
| 137 |
checkReady();
|
| 138 |
+
}
|
| 139 |
+
: null;
|
|
|
|
|
|
|
| 140 |
|
| 141 |
+
const handleEnded = info.isSegmented
|
| 142 |
+
? null
|
| 143 |
+
: () => {
|
|
|
|
|
|
|
|
|
|
| 144 |
video.currentTime = 0;
|
| 145 |
if (index === firstVisibleIdxRef.current) {
|
| 146 |
setCurrentTime(0);
|
| 147 |
}
|
| 148 |
};
|
| 149 |
|
| 150 |
+
video.addEventListener("timeupdate", handleTimeUpdate);
|
| 151 |
+
if (handlePlay) video.addEventListener("play", handlePlay);
|
| 152 |
+
if (handleLoadedData)
|
| 153 |
+
video.addEventListener("loadeddata", handleLoadedData);
|
| 154 |
+
if (handleEnded) video.addEventListener("ended", handleEnded);
|
| 155 |
+
if (!info.isSegmented) {
|
| 156 |
+
video.addEventListener("canplaythrough", checkReady, { once: true });
|
| 157 |
}
|
| 158 |
+
|
| 159 |
+
videoEventCleanup.set(video, () => {
|
| 160 |
+
video.removeEventListener("timeupdate", handleTimeUpdate);
|
| 161 |
+
if (handlePlay) video.removeEventListener("play", handlePlay);
|
| 162 |
+
if (handleLoadedData)
|
| 163 |
+
video.removeEventListener("loadeddata", handleLoadedData);
|
| 164 |
+
if (handleEnded) video.removeEventListener("ended", handleEnded);
|
| 165 |
+
video.removeEventListener("canplaythrough", checkReady);
|
| 166 |
+
});
|
| 167 |
});
|
| 168 |
|
| 169 |
return () => {
|
|
|
|
| 229 |
});
|
| 230 |
}, [externalSeekVersion, currentTime, videosInfo, videosReady, hiddenSet]);
|
| 231 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 232 |
return (
|
| 233 |
<>
|
| 234 |
{/* Hidden videos menu */}
|
|
|
|
| 269 |
if (hiddenVideos.includes(info.filename)) return null;
|
| 270 |
|
| 271 |
const isEnlarged = enlargedVideo === info.filename;
|
|
|
|
| 272 |
|
| 273 |
return (
|
| 274 |
<div
|
|
|
|
| 321 |
muted
|
| 322 |
preload="auto"
|
| 323 |
crossOrigin="anonymous"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 324 |
>
|
| 325 |
<source src={proxyHfUrl(info.url)} type="video/mp4" />
|
| 326 |
Your browser does not support the video tag.
|