@rxdrag/website-lib-core 0.0.108 → 0.0.110
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/package.json
CHANGED
|
@@ -31,6 +31,7 @@ export const ReactVideoPlayer = forwardRef<
|
|
|
31
31
|
endTitle?: string;
|
|
32
32
|
media: Media;
|
|
33
33
|
posterUrl?: string;
|
|
34
|
+
eagerLoad?: boolean;
|
|
34
35
|
classNames?: VideoPlayerClassNames;
|
|
35
36
|
callToAction?: string;
|
|
36
37
|
onToggleSelect?: (id: ID) => void;
|
|
@@ -42,6 +43,7 @@ export const ReactVideoPlayer = forwardRef<
|
|
|
42
43
|
media,
|
|
43
44
|
onToggleSelect,
|
|
44
45
|
posterUrl,
|
|
46
|
+
eagerLoad,
|
|
45
47
|
classNames,
|
|
46
48
|
callToAction,
|
|
47
49
|
designMode,
|
|
@@ -51,6 +53,57 @@ export const ReactVideoPlayer = forwardRef<
|
|
|
51
53
|
const hlsRef = useRef<Hls | null>(null);
|
|
52
54
|
const [isPlaying, setIsPlaying] = useState(false);
|
|
53
55
|
const [isEnded, setIsEnded] = useState(false);
|
|
56
|
+
const [isSourceReady, setIsSourceReady] = useState(false);
|
|
57
|
+
const sourceReadyRef = useRef(false);
|
|
58
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
59
|
+
const isEndedRef = useRef(false);
|
|
60
|
+
|
|
61
|
+
const ensureSourceReady = useCallback(() => {
|
|
62
|
+
const video = videoRef.current;
|
|
63
|
+
const url = media.file?.original;
|
|
64
|
+
if (!video || !url) return false;
|
|
65
|
+
if (sourceReadyRef.current) return true;
|
|
66
|
+
|
|
67
|
+
if (media.storageType === "cloudflare_stream") {
|
|
68
|
+
if (!Hls.isSupported()) {
|
|
69
|
+
if (video.canPlayType("application/vnd.apple.mpegurl")) {
|
|
70
|
+
video.src = url;
|
|
71
|
+
sourceReadyRef.current = true;
|
|
72
|
+
setIsSourceReady(true);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const hls = new Hls({ enableWorker: true });
|
|
79
|
+
hls.on(Hls.Events.ERROR, (_event, data) => {
|
|
80
|
+
if (data.fatal) {
|
|
81
|
+
switch (data.type) {
|
|
82
|
+
case Hls.ErrorTypes.NETWORK_ERROR:
|
|
83
|
+
hls.startLoad();
|
|
84
|
+
break;
|
|
85
|
+
case Hls.ErrorTypes.MEDIA_ERROR:
|
|
86
|
+
hls.recoverMediaError();
|
|
87
|
+
break;
|
|
88
|
+
default:
|
|
89
|
+
hls.destroy();
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
hls.loadSource(url);
|
|
95
|
+
hls.attachMedia(video);
|
|
96
|
+
hlsRef.current = hls;
|
|
97
|
+
sourceReadyRef.current = true;
|
|
98
|
+
setIsSourceReady(true);
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
video.src = url;
|
|
103
|
+
sourceReadyRef.current = true;
|
|
104
|
+
setIsSourceReady(true);
|
|
105
|
+
return true;
|
|
106
|
+
}, [media.file?.original, media.storageType]);
|
|
54
107
|
|
|
55
108
|
const handleContainerClick = useCallback(
|
|
56
109
|
(e: React.MouseEvent) => {
|
|
@@ -73,80 +126,83 @@ export const ReactVideoPlayer = forwardRef<
|
|
|
73
126
|
e.stopPropagation();
|
|
74
127
|
const v = videoRef.current;
|
|
75
128
|
if (!v) return;
|
|
76
|
-
if (isPlaying)
|
|
77
|
-
|
|
129
|
+
if (isPlaying) {
|
|
130
|
+
v.pause();
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const ok = ensureSourceReady();
|
|
135
|
+
if (!ok) return;
|
|
136
|
+
|
|
137
|
+
setIsLoading(true);
|
|
138
|
+
|
|
139
|
+
v.play().catch((error) => {
|
|
140
|
+
console.log("ReactVideoPlayer play() failed:", error);
|
|
141
|
+
setIsLoading(false);
|
|
142
|
+
});
|
|
78
143
|
},
|
|
79
|
-
[isPlaying, designMode]
|
|
144
|
+
[isPlaying, designMode, ensureSourceReady]
|
|
80
145
|
);
|
|
81
146
|
|
|
82
147
|
useEffect(() => {
|
|
83
148
|
const video = videoRef.current;
|
|
84
|
-
|
|
85
|
-
|
|
149
|
+
if (!video) return;
|
|
150
|
+
|
|
151
|
+
video.pause();
|
|
152
|
+
if (hlsRef.current) {
|
|
153
|
+
hlsRef.current.destroy();
|
|
154
|
+
hlsRef.current = null;
|
|
155
|
+
}
|
|
156
|
+
video.removeAttribute("src");
|
|
157
|
+
video.load();
|
|
158
|
+
setIsSourceReady(false);
|
|
159
|
+
sourceReadyRef.current = false;
|
|
86
160
|
|
|
87
161
|
const onPlay = () => {
|
|
88
162
|
setIsPlaying(true);
|
|
89
163
|
setIsEnded(false);
|
|
164
|
+
isEndedRef.current = false;
|
|
165
|
+
setIsLoading(false);
|
|
90
166
|
};
|
|
91
167
|
const onPause = () => setIsPlaying(false);
|
|
92
168
|
const onEnded = () => {
|
|
93
169
|
setIsPlaying(false);
|
|
94
170
|
setIsEnded(true);
|
|
171
|
+
isEndedRef.current = true;
|
|
172
|
+
setIsLoading(false);
|
|
173
|
+
};
|
|
174
|
+
const onWaiting = () => {
|
|
175
|
+
if (!isEndedRef.current) setIsLoading(true);
|
|
176
|
+
};
|
|
177
|
+
const onCanPlay = () => {
|
|
178
|
+
setIsLoading(false);
|
|
95
179
|
};
|
|
96
180
|
|
|
97
181
|
video.addEventListener("play", onPlay);
|
|
98
182
|
video.addEventListener("pause", onPause);
|
|
99
183
|
video.addEventListener("ended", onEnded);
|
|
184
|
+
video.addEventListener("waiting", onWaiting);
|
|
185
|
+
video.addEventListener("canplay", onCanPlay);
|
|
186
|
+
video.addEventListener("playing", onCanPlay);
|
|
100
187
|
|
|
101
|
-
if (
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
video.src = url;
|
|
105
|
-
}
|
|
106
|
-
return () => {
|
|
107
|
-
video.removeEventListener("play", onPlay);
|
|
108
|
-
video.removeEventListener("pause", onPause);
|
|
109
|
-
video.removeEventListener("ended", onEnded);
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const hls = new Hls({ enableWorker: true });
|
|
114
|
-
hls.on(Hls.Events.ERROR, (_event, data) => {
|
|
115
|
-
if (data.fatal) {
|
|
116
|
-
switch (data.type) {
|
|
117
|
-
case Hls.ErrorTypes.NETWORK_ERROR:
|
|
118
|
-
hls.startLoad();
|
|
119
|
-
break;
|
|
120
|
-
case Hls.ErrorTypes.MEDIA_ERROR:
|
|
121
|
-
hls.recoverMediaError();
|
|
122
|
-
break;
|
|
123
|
-
default:
|
|
124
|
-
hls.destroy();
|
|
125
|
-
break;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
});
|
|
188
|
+
if (eagerLoad) {
|
|
189
|
+
ensureSourceReady();
|
|
190
|
+
}
|
|
129
191
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
192
|
+
return () => {
|
|
193
|
+
video.removeEventListener("play", onPlay);
|
|
194
|
+
video.removeEventListener("pause", onPause);
|
|
195
|
+
video.removeEventListener("ended", onEnded);
|
|
196
|
+
video.removeEventListener("waiting", onWaiting);
|
|
197
|
+
video.removeEventListener("canplay", onCanPlay);
|
|
198
|
+
video.removeEventListener("playing", onCanPlay);
|
|
133
199
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
video.removeEventListener("pause", onPause);
|
|
137
|
-
video.removeEventListener("ended", onEnded);
|
|
138
|
-
hls.destroy();
|
|
200
|
+
if (hlsRef.current) {
|
|
201
|
+
hlsRef.current.destroy();
|
|
139
202
|
hlsRef.current = null;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return () => {
|
|
144
|
-
video.removeEventListener("play", onPlay);
|
|
145
|
-
video.removeEventListener("pause", onPause);
|
|
146
|
-
video.removeEventListener("ended", onEnded);
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
}, [media.file?.original, media.storageType]);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
}, [eagerLoad, ensureSourceReady, media.file?.original, media.storageType]);
|
|
150
206
|
|
|
151
207
|
return (
|
|
152
208
|
<div
|
|
@@ -161,7 +217,7 @@ export const ReactVideoPlayer = forwardRef<
|
|
|
161
217
|
<video
|
|
162
218
|
ref={videoRef}
|
|
163
219
|
onClick={(e) => !designMode && e.stopPropagation()}
|
|
164
|
-
preload="metadata"
|
|
220
|
+
preload={eagerLoad ? "metadata" : "none"}
|
|
165
221
|
poster={posterUrl ?? media.file?.thumbnail}
|
|
166
222
|
className={clsx(
|
|
167
223
|
"w-full h-full rounded-lg object-cover",
|
|
@@ -169,13 +225,13 @@ export const ReactVideoPlayer = forwardRef<
|
|
|
169
225
|
)}
|
|
170
226
|
controls={!designMode}
|
|
171
227
|
>
|
|
172
|
-
{!media.storageType && media.file?.original ? (
|
|
228
|
+
{!media.storageType && media.file?.original && (eagerLoad || isSourceReady) ? (
|
|
173
229
|
<source src={media.file.original} />
|
|
174
230
|
) : null}
|
|
175
231
|
Your browser does not support video playback.
|
|
176
232
|
</video>
|
|
177
233
|
|
|
178
|
-
{!isPlaying && !isEnded && (
|
|
234
|
+
{!isPlaying && !isEnded && !isLoading && (
|
|
179
235
|
<button
|
|
180
236
|
type="button"
|
|
181
237
|
onClick={handlePlayClick}
|
|
@@ -216,6 +272,21 @@ export const ReactVideoPlayer = forwardRef<
|
|
|
216
272
|
</button>
|
|
217
273
|
)}
|
|
218
274
|
|
|
275
|
+
{!isPlaying && !isEnded && isLoading && (
|
|
276
|
+
<div
|
|
277
|
+
className={clsx(
|
|
278
|
+
"absolute inset-0 flex items-center justify-center w-full h-full rounded-lg z-10 bg-black/20",
|
|
279
|
+
classNames?.playButton
|
|
280
|
+
)}
|
|
281
|
+
onClick={(e) => e.stopPropagation()}
|
|
282
|
+
>
|
|
283
|
+
<div className="flex items-center gap-3 rounded-full bg-black/50 px-4 py-2 text-white">
|
|
284
|
+
<div className="h-4 w-4 animate-spin rounded-full border-2 border-white/30 border-t-white" />
|
|
285
|
+
<span className="text-sm">Loading...</span>
|
|
286
|
+
</div>
|
|
287
|
+
</div>
|
|
288
|
+
)}
|
|
289
|
+
|
|
219
290
|
<div
|
|
220
291
|
className={clsx(
|
|
221
292
|
"absolute inset-0 bg-black/50 flex-col gap-6 items-center justify-center text-center text-white hidden z-10 rounded-2xl",
|
|
@@ -248,9 +319,15 @@ export const ReactVideoPlayer = forwardRef<
|
|
|
248
319
|
e.stopPropagation();
|
|
249
320
|
const v = videoRef.current;
|
|
250
321
|
if (!v) return;
|
|
322
|
+
const ok = ensureSourceReady();
|
|
323
|
+
if (!ok) return;
|
|
324
|
+
setIsLoading(true);
|
|
251
325
|
v.currentTime = 0;
|
|
252
326
|
setIsEnded(false);
|
|
253
|
-
v.play()
|
|
327
|
+
v.play().catch((error) => {
|
|
328
|
+
console.log("ReactVideoPlayer play() failed:", error);
|
|
329
|
+
setIsLoading(false);
|
|
330
|
+
});
|
|
254
331
|
}}
|
|
255
332
|
>
|
|
256
333
|
Replay
|