jvetrau-ds 0.1.32 → 0.1.34
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/components/attach/attach.css +44 -2
- package/components/video/video.cjs +194 -112
- package/components/video/video.cjs.map +1 -1
- package/components/video/video.d.cts +8 -1
- package/components/video/video.d.ts +8 -1
- package/components/video/video.js +194 -112
- package/components/video/video.js.map +1 -1
- package/package.json +1 -1
- package/styles.css +44 -2
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
/* color */
|
|
3
3
|
--attachment-color: var(--color-text);
|
|
4
4
|
--attachment-item-bg: var(--color-control);
|
|
5
|
+
--attachment-item-color: var(--color-text-control);
|
|
5
6
|
--attachment-border-color: var(--color-line);
|
|
6
|
-
--attachment-meta-color: var(--color-text-note);
|
|
7
|
+
--attachment-item-meta-color: var(--color-text-note);
|
|
7
8
|
|
|
8
9
|
color: var(--attachment-color);
|
|
9
10
|
|
|
@@ -37,6 +38,10 @@
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
.attach__list {
|
|
41
|
+
/* color */
|
|
42
|
+
|
|
43
|
+
/* typography */
|
|
44
|
+
|
|
40
45
|
/* size */
|
|
41
46
|
display: flex;
|
|
42
47
|
flex-direction: column;
|
|
@@ -46,11 +51,18 @@
|
|
|
46
51
|
|
|
47
52
|
/* border */
|
|
48
53
|
list-style: none;
|
|
54
|
+
|
|
55
|
+
/* depth */
|
|
56
|
+
|
|
57
|
+
/* motion */
|
|
49
58
|
}
|
|
50
59
|
|
|
51
60
|
.attach__item {
|
|
52
61
|
/* color */
|
|
53
62
|
background: var(--attachment-item-bg);
|
|
63
|
+
color: var(--attachment-item-color);
|
|
64
|
+
|
|
65
|
+
/* typography */
|
|
54
66
|
|
|
55
67
|
/* size */
|
|
56
68
|
display: flex;
|
|
@@ -89,11 +101,25 @@
|
|
|
89
101
|
}
|
|
90
102
|
|
|
91
103
|
.attach__link:hover .attach__name {
|
|
104
|
+
/* color */
|
|
105
|
+
|
|
92
106
|
/* typography */
|
|
93
107
|
text-decoration: underline;
|
|
108
|
+
|
|
109
|
+
/* size */
|
|
110
|
+
|
|
111
|
+
/* border */
|
|
112
|
+
|
|
113
|
+
/* depth */
|
|
114
|
+
|
|
115
|
+
/* motion */
|
|
94
116
|
}
|
|
95
117
|
|
|
96
118
|
.attach__preview {
|
|
119
|
+
/* color */
|
|
120
|
+
|
|
121
|
+
/* typography */
|
|
122
|
+
|
|
97
123
|
/* size */
|
|
98
124
|
display: block;
|
|
99
125
|
max-width: var(--attachment-preview-max-width);
|
|
@@ -109,6 +135,10 @@
|
|
|
109
135
|
}
|
|
110
136
|
|
|
111
137
|
.attach__file {
|
|
138
|
+
/* color */
|
|
139
|
+
|
|
140
|
+
/* typography */
|
|
141
|
+
|
|
112
142
|
/* size */
|
|
113
143
|
display: flex;
|
|
114
144
|
flex-direction: column;
|
|
@@ -122,6 +152,8 @@
|
|
|
122
152
|
}
|
|
123
153
|
|
|
124
154
|
.attach__name {
|
|
155
|
+
/* color */
|
|
156
|
+
|
|
125
157
|
/* typography */
|
|
126
158
|
text-overflow: ellipsis;
|
|
127
159
|
white-space: nowrap;
|
|
@@ -138,7 +170,7 @@
|
|
|
138
170
|
|
|
139
171
|
.attach__meta {
|
|
140
172
|
/* color */
|
|
141
|
-
color: var(--attachment-meta-color);
|
|
173
|
+
color: var(--attachment-item-meta-color);
|
|
142
174
|
|
|
143
175
|
/* typography */
|
|
144
176
|
font-size: 0.85em;
|
|
@@ -174,6 +206,16 @@
|
|
|
174
206
|
}
|
|
175
207
|
|
|
176
208
|
.attach__remove:hover {
|
|
209
|
+
/* color */
|
|
210
|
+
|
|
177
211
|
/* typography */
|
|
178
212
|
text-decoration: underline;
|
|
213
|
+
|
|
214
|
+
/* size */
|
|
215
|
+
|
|
216
|
+
/* border */
|
|
217
|
+
|
|
218
|
+
/* depth */
|
|
219
|
+
|
|
220
|
+
/* motion */
|
|
179
221
|
}
|
|
@@ -30,7 +30,9 @@ function getBufferedEnd(video) {
|
|
|
30
30
|
return video.buffered.end(video.buffered.length - 1);
|
|
31
31
|
}
|
|
32
32
|
function getVideoSource(src, qualities, selectedQuality) {
|
|
33
|
-
const selectedQualityItem = qualities.find(
|
|
33
|
+
const selectedQualityItem = qualities.find(
|
|
34
|
+
(quality) => quality.value === selectedQuality
|
|
35
|
+
);
|
|
34
36
|
return selectedQualityItem?.src ?? src;
|
|
35
37
|
}
|
|
36
38
|
function getQualityOptions(qualities) {
|
|
@@ -52,6 +54,7 @@ function getVideoProgressState(video) {
|
|
|
52
54
|
const defaultQualities = [];
|
|
53
55
|
const Video = ({
|
|
54
56
|
src,
|
|
57
|
+
mediaKey,
|
|
55
58
|
cover,
|
|
56
59
|
qualities = defaultQualities,
|
|
57
60
|
defaultQuality = "",
|
|
@@ -67,9 +70,11 @@ const Video = ({
|
|
|
67
70
|
playsInline = true,
|
|
68
71
|
preload = "metadata",
|
|
69
72
|
onQualityChange,
|
|
73
|
+
onPlaybackError,
|
|
70
74
|
onPlay,
|
|
71
75
|
onPause,
|
|
72
76
|
onEnded,
|
|
77
|
+
onError,
|
|
73
78
|
onLoadedMetadata,
|
|
74
79
|
onTimeUpdate,
|
|
75
80
|
onProgress,
|
|
@@ -89,14 +94,36 @@ const Video = ({
|
|
|
89
94
|
const [isMuted, setIsMuted] = react.useState(muted ?? initialMuted);
|
|
90
95
|
const hasAppliedInitialTimeRef = react.useRef(false);
|
|
91
96
|
const lastProgressCommitAtRef = react.useRef(0);
|
|
92
|
-
const
|
|
97
|
+
const loadedMediaKeyRef = react.useRef(void 0);
|
|
98
|
+
const pendingRestoreTimeRef = react.useRef(null);
|
|
99
|
+
const onPlaybackErrorRef = react.useRef(onPlaybackError);
|
|
100
|
+
const videoSrc = getVideoSource(
|
|
101
|
+
src,
|
|
102
|
+
qualities,
|
|
103
|
+
selectedQuality
|
|
104
|
+
);
|
|
105
|
+
const effectiveMediaKey = mediaKey ?? videoSrc;
|
|
93
106
|
const qualityOptions = getQualityOptions(qualities);
|
|
94
107
|
const hasQualityOptions = qualityOptions.length > 0;
|
|
108
|
+
react.useEffect(() => {
|
|
109
|
+
onPlaybackErrorRef.current = onPlaybackError;
|
|
110
|
+
}, [onPlaybackError]);
|
|
95
111
|
react.useEffect(() => {
|
|
96
112
|
const video = videoRef.current;
|
|
97
|
-
hasAppliedInitialTimeRef.current = false;
|
|
98
|
-
lastProgressCommitAtRef.current = 0;
|
|
99
113
|
if (!video || !videoSrc) return;
|
|
114
|
+
const isSameMedia = loadedMediaKeyRef.current !== void 0 && loadedMediaKeyRef.current === effectiveMediaKey;
|
|
115
|
+
if (isSameMedia) {
|
|
116
|
+
pendingRestoreTimeRef.current = video.currentTime;
|
|
117
|
+
} else {
|
|
118
|
+
pendingRestoreTimeRef.current = null;
|
|
119
|
+
hasAppliedInitialTimeRef.current = false;
|
|
120
|
+
lastProgressCommitAtRef.current = 0;
|
|
121
|
+
setCurrentTime(0);
|
|
122
|
+
setDuration(0);
|
|
123
|
+
setBuffered(0);
|
|
124
|
+
setIsPlaying(false);
|
|
125
|
+
}
|
|
126
|
+
loadedMediaKeyRef.current = effectiveMediaKey;
|
|
100
127
|
video.removeAttribute("src");
|
|
101
128
|
video.load();
|
|
102
129
|
if (video.canPlayType("application/vnd.apple.mpegurl")) {
|
|
@@ -110,13 +137,18 @@ const Video = ({
|
|
|
110
137
|
const hls = new Hls__default.default();
|
|
111
138
|
hls.on(Hls__default.default.Events.ERROR, (_, data) => {
|
|
112
139
|
console.error("HLS error", data);
|
|
140
|
+
onPlaybackErrorRef.current?.({
|
|
141
|
+
type: "hls",
|
|
142
|
+
fatal: data.fatal,
|
|
143
|
+
error: data
|
|
144
|
+
});
|
|
113
145
|
});
|
|
114
146
|
hls.loadSource(videoSrc);
|
|
115
147
|
hls.attachMedia(video);
|
|
116
148
|
return () => {
|
|
117
149
|
hls.destroy();
|
|
118
150
|
};
|
|
119
|
-
}, [videoSrc]);
|
|
151
|
+
}, [videoSrc, effectiveMediaKey]);
|
|
120
152
|
const hasStarted = currentTime > 0 || isPlaying;
|
|
121
153
|
const hasControls = hasStarted;
|
|
122
154
|
const hasVolume = isVolumeOpen;
|
|
@@ -166,13 +198,21 @@ const Video = ({
|
|
|
166
198
|
const handleSeek = (nextValue) => {
|
|
167
199
|
const video = videoRef.current;
|
|
168
200
|
if (!video) return;
|
|
169
|
-
video.currentTime = getClampedValue(
|
|
201
|
+
video.currentTime = getClampedValue(
|
|
202
|
+
nextValue,
|
|
203
|
+
0,
|
|
204
|
+
duration
|
|
205
|
+
);
|
|
170
206
|
setCurrentTime(video.currentTime);
|
|
171
207
|
};
|
|
172
208
|
const handleVolumeChange = (nextValue) => {
|
|
173
209
|
const video = videoRef.current;
|
|
174
210
|
if (!video) return;
|
|
175
|
-
const nextVolume = getClampedValue(
|
|
211
|
+
const nextVolume = getClampedValue(
|
|
212
|
+
nextValue,
|
|
213
|
+
0,
|
|
214
|
+
1
|
|
215
|
+
);
|
|
176
216
|
video.volume = nextVolume;
|
|
177
217
|
video.muted = nextVolume === 0;
|
|
178
218
|
setVolume(nextVolume);
|
|
@@ -195,7 +235,13 @@ const Video = ({
|
|
|
195
235
|
};
|
|
196
236
|
const handleLoadedMetadata = (event) => {
|
|
197
237
|
const video = event.currentTarget;
|
|
198
|
-
if (
|
|
238
|
+
if (pendingRestoreTimeRef.current !== null) {
|
|
239
|
+
video.currentTime = Math.min(
|
|
240
|
+
pendingRestoreTimeRef.current,
|
|
241
|
+
video.duration || pendingRestoreTimeRef.current
|
|
242
|
+
);
|
|
243
|
+
pendingRestoreTimeRef.current = null;
|
|
244
|
+
} else if (!hasAppliedInitialTimeRef.current && initialTime > 0) {
|
|
199
245
|
video.currentTime = Math.min(
|
|
200
246
|
initialTime,
|
|
201
247
|
video.duration || initialTime
|
|
@@ -232,114 +278,150 @@ const Video = ({
|
|
|
232
278
|
commitProgress(event.currentTarget);
|
|
233
279
|
onEnded?.(event);
|
|
234
280
|
};
|
|
281
|
+
const handleNativeError = (event) => {
|
|
282
|
+
onPlaybackError?.({
|
|
283
|
+
type: "media",
|
|
284
|
+
fatal: true,
|
|
285
|
+
error: event.currentTarget.error ?? event
|
|
286
|
+
});
|
|
287
|
+
onError?.(event);
|
|
288
|
+
};
|
|
235
289
|
const handleNativeVolumeChange = (event) => {
|
|
236
290
|
syncMediaState();
|
|
237
291
|
onVolumeChange?.(event);
|
|
238
292
|
};
|
|
239
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
293
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
294
|
+
"div",
|
|
295
|
+
{
|
|
296
|
+
className: classes,
|
|
297
|
+
"aria-label": label,
|
|
298
|
+
children: [
|
|
299
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "video__surface", children: [
|
|
300
|
+
cover && !hasStarted ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
301
|
+
"img",
|
|
302
|
+
{
|
|
303
|
+
className: "video__cover",
|
|
304
|
+
src: cover,
|
|
305
|
+
alt: "",
|
|
306
|
+
"aria-hidden": "true"
|
|
307
|
+
}
|
|
308
|
+
) : null,
|
|
309
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
310
|
+
"video",
|
|
311
|
+
{
|
|
312
|
+
...props,
|
|
313
|
+
ref: videoRef,
|
|
314
|
+
className: "video__media",
|
|
315
|
+
poster: cover,
|
|
316
|
+
autoPlay,
|
|
317
|
+
loop,
|
|
318
|
+
muted: isMuted,
|
|
319
|
+
playsInline,
|
|
320
|
+
preload,
|
|
321
|
+
controls: false,
|
|
322
|
+
onLoadedMetadata: handleLoadedMetadata,
|
|
323
|
+
onTimeUpdate: handleTimeUpdate,
|
|
324
|
+
onProgress: handleProgress,
|
|
325
|
+
onPlay: handlePlay,
|
|
326
|
+
onPause: handlePause,
|
|
327
|
+
onEnded: handleEnded,
|
|
328
|
+
onError: handleNativeError,
|
|
329
|
+
onVolumeChange: handleNativeVolumeChange,
|
|
330
|
+
children
|
|
331
|
+
}
|
|
332
|
+
),
|
|
333
|
+
!hasStarted && /* @__PURE__ */ jsxRuntime.jsx(
|
|
334
|
+
ButtonIcon__default.default,
|
|
335
|
+
{
|
|
336
|
+
className: "video__main-action",
|
|
337
|
+
icon: "play",
|
|
338
|
+
label: "Play video",
|
|
339
|
+
onClick: handlePlayToggle
|
|
340
|
+
}
|
|
341
|
+
)
|
|
342
|
+
] }),
|
|
343
|
+
hasControls && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "video__controls", children: [
|
|
344
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
345
|
+
ButtonIcon__default.default,
|
|
346
|
+
{
|
|
347
|
+
icon: isPlaying ? "pause" : "play",
|
|
348
|
+
label: isPlaying ? "Pause video" : "Play video",
|
|
349
|
+
onClick: handlePlayToggle
|
|
350
|
+
}
|
|
351
|
+
),
|
|
352
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "video__time", children: formatTime(currentTime) }),
|
|
353
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
354
|
+
ProgressBar__default.default,
|
|
355
|
+
{
|
|
356
|
+
className: "video__progress",
|
|
357
|
+
mode: "player",
|
|
358
|
+
value: currentTime,
|
|
359
|
+
buffered,
|
|
360
|
+
max: duration || 1,
|
|
361
|
+
step: 1,
|
|
362
|
+
label: "Video progress",
|
|
363
|
+
onValueChange: handleSeek
|
|
364
|
+
}
|
|
365
|
+
),
|
|
366
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
367
|
+
ButtonIcon__default.default,
|
|
368
|
+
{
|
|
369
|
+
icon: "fullscreen",
|
|
370
|
+
label: "Fullscreen",
|
|
371
|
+
onClick: handleFullscreen
|
|
372
|
+
}
|
|
373
|
+
),
|
|
374
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
375
|
+
"div",
|
|
376
|
+
{
|
|
377
|
+
className: "video__sound-control",
|
|
378
|
+
onMouseLeave: () => setIsVolumeOpen(false),
|
|
379
|
+
children: [
|
|
380
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
381
|
+
ButtonIcon__default.default,
|
|
382
|
+
{
|
|
383
|
+
icon: isMuted ? "sound-off" : "sound",
|
|
384
|
+
label: isMuted ? "Unmute video" : "Mute video",
|
|
385
|
+
onClick: handleSoundToggle,
|
|
386
|
+
onMouseEnter: () => setIsVolumeOpen(true),
|
|
387
|
+
onFocus: () => setIsVolumeOpen(true)
|
|
388
|
+
}
|
|
389
|
+
),
|
|
390
|
+
hasVolume && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "video__volume", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
391
|
+
ProgressBar__default.default,
|
|
392
|
+
{
|
|
393
|
+
mode: "level",
|
|
394
|
+
value: isMuted ? 0 : volume,
|
|
395
|
+
max: 1,
|
|
396
|
+
step: 0.05,
|
|
397
|
+
label: "Volume",
|
|
398
|
+
onValueChange: handleVolumeChange
|
|
399
|
+
}
|
|
400
|
+
) })
|
|
401
|
+
]
|
|
402
|
+
}
|
|
403
|
+
),
|
|
404
|
+
hasQualityOptions && /* @__PURE__ */ jsxRuntime.jsx(
|
|
405
|
+
Dropdown__default.default,
|
|
406
|
+
{
|
|
407
|
+
align: "top-end",
|
|
408
|
+
value: selectedQuality,
|
|
409
|
+
options: qualityOptions,
|
|
410
|
+
label: "Video quality",
|
|
411
|
+
onChange: handleQualityChange,
|
|
412
|
+
trigger: /* @__PURE__ */ jsxRuntime.jsx(
|
|
413
|
+
ButtonIcon__default.default,
|
|
414
|
+
{
|
|
415
|
+
icon: "settings",
|
|
416
|
+
label: "Video quality"
|
|
417
|
+
}
|
|
418
|
+
)
|
|
419
|
+
}
|
|
420
|
+
)
|
|
421
|
+
] })
|
|
422
|
+
]
|
|
423
|
+
}
|
|
424
|
+
);
|
|
343
425
|
};
|
|
344
426
|
var video_default = Video;
|
|
345
427
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/video/video.tsx"],"names":["useRef","useState","useEffect","Hls","classnames","jsxs","jsx","ButtonIcon","ProgressBar","Dropdown"],"mappings":"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","file":"video.cjs","sourcesContent":["import React, { useEffect, useRef, useState } from 'react';\nimport Hls from 'hls.js';\n\nimport './video.css';\nimport { classnames } from '../helpers/classnames';\nimport ButtonIcon from '../button-icon';\nimport ProgressBar from '../progressbar';\nimport Dropdown, { type DropdownOption } from '../dropdown';\n\nexport interface VideoQuality {\n value: string;\n label: string;\n src?: string;\n disabled?: boolean;\n}\n\nexport interface VideoProgressState {\n currentTime: number;\n duration: number;\n completed: boolean;\n}\n\nexport interface VideoProps\n extends Omit<React.VideoHTMLAttributes<HTMLVideoElement>, 'controls' | 'src' | 'poster'> {\n src?: string;\n cover?: string;\n qualities?: VideoQuality[];\n defaultQuality?: string;\n label?: string;\n initialVolume?: number;\n initialMuted?: boolean;\n initialTime?: number;\n progressCommitIntervalSec?: number;\n onProgressCommit?: (state: VideoProgressState) => void;\n onQualityChange?: (quality: string) => void;\n}\n\nfunction getClampedValue(value: number, min: number, max: number) {\n return Math.min(Math.max(value, min), max);\n}\n\nfunction formatTime(value: number) {\n if (!Number.isFinite(value)) return '0:00';\n\n const minutes = Math.floor(value / 60);\n const seconds = Math.floor(value % 60);\n\n return `${minutes}:${String(seconds).padStart(2, '0')}`;\n}\n\nfunction getBufferedEnd(video: HTMLVideoElement) {\n if (video.buffered.length === 0) return 0;\n\n return video.buffered.end(video.buffered.length - 1);\n}\n\nfunction getVideoSource(src: string | undefined, qualities: VideoQuality[], selectedQuality: string) {\n const selectedQualityItem = qualities.find((quality) => quality.value === selectedQuality);\n\n return selectedQualityItem?.src ?? src;\n}\n\nfunction getQualityOptions(qualities: VideoQuality[]): DropdownOption[] {\n return qualities.map((quality) => ({\n value: quality.value,\n label: quality.label,\n disabled: quality.disabled,\n }));\n}\n\nfunction getVideoProgressState(\n video: HTMLVideoElement,\n): VideoProgressState {\n const duration = video.duration || 0;\n const currentTime = video.currentTime || 0;\n\n return {\n currentTime,\n duration,\n\n completed:\n video.ended ||\n (\n duration > 0 &&\n currentTime >= duration * 0.9\n ),\n };\n}\n\nconst defaultQualities: VideoQuality[] = [];\n\nconst Video: React.FC<VideoProps> = ({\n src,\n cover,\n qualities = defaultQualities,\n defaultQuality = '',\n label = 'Video player',\n initialVolume = 1,\n initialMuted = false,\n initialTime = 0,\n progressCommitIntervalSec = 15,\n className = '',\n autoPlay,\n loop,\n muted,\n playsInline = true,\n preload = 'metadata',\n onQualityChange,\n onPlay,\n onPause,\n onEnded,\n onLoadedMetadata,\n onTimeUpdate,\n onProgress,\n onVolumeChange,\n onProgressCommit,\n children,\n ...props\n}) => {\n const videoRef = useRef<HTMLVideoElement>(null);\n\n const [isPlaying, setIsPlaying] = useState(false);\n const [isVolumeOpen, setIsVolumeOpen] = useState(false);\n const [selectedQuality, setSelectedQuality] = useState(defaultQuality);\n const [currentTime, setCurrentTime] = useState(0);\n const [duration, setDuration] = useState(0);\n const [buffered, setBuffered] = useState(0);\n const [volume, setVolume] = useState(initialVolume);\n const [isMuted, setIsMuted] = useState(muted ?? initialMuted);\n\n const hasAppliedInitialTimeRef = useRef(false);\n const lastProgressCommitAtRef = useRef(0);\n\n const videoSrc = getVideoSource(src, qualities, selectedQuality);\n const qualityOptions = getQualityOptions(qualities);\n const hasQualityOptions = qualityOptions.length > 0;\n\n useEffect(() => {\n const video = videoRef.current;\n\n hasAppliedInitialTimeRef.current = false;\n lastProgressCommitAtRef.current = 0;\n\n if (!video || !videoSrc) return;\n\n video.removeAttribute('src');\n video.load();\n\n if (video.canPlayType('application/vnd.apple.mpegurl')) {\n video.src = videoSrc;\n return;\n }\n\n if (!Hls.isSupported()) {\n video.src = videoSrc;\n return;\n }\n\n const hls = new Hls();\n\n hls.on(Hls.Events.ERROR, (_, data) => {\n console.error('HLS error', data);\n });\n\n hls.loadSource(videoSrc);\n hls.attachMedia(video);\n\n return () => {\n hls.destroy();\n };\n }, [videoSrc]);\n\n const hasStarted = currentTime > 0 || isPlaying;\n const hasControls = hasStarted;\n const hasVolume = isVolumeOpen;\n\n const classes = classnames('video', className, {\n 'video--with-controls': hasControls,\n 'video--with-volume': hasVolume,\n });\n\n const syncMediaState = () => {\n const video = videoRef.current;\n\n if (!video) return;\n\n setCurrentTime(video.currentTime);\n setDuration(video.duration || 0);\n setBuffered(getBufferedEnd(video));\n setVolume(video.volume);\n setIsMuted(video.muted);\n setIsPlaying(!video.paused);\n };\n\n const commitProgress = (\n video: HTMLVideoElement,\n ) => {\n if (!onProgressCommit) {\n return;\n }\n\n onProgressCommit(\n getVideoProgressState(video),\n );\n };\n\n const handlePlayToggle = async () => {\n const video = videoRef.current;\n\n if (!video) return;\n\n console.log('play click', {\n paused: video.paused,\n readyState: video.readyState,\n src: video.currentSrc,\n networkState: video.networkState,\n error: video.error,\n });\n\n if (video.paused) {\n try {\n await video.play();\n console.log('play success');\n } catch (error) {\n console.error('play failed', error);\n }\n\n return;\n }\n\n video.pause();\n };\n\n const handleSeek = (nextValue: number) => {\n const video = videoRef.current;\n\n if (!video) return;\n\n video.currentTime = getClampedValue(nextValue, 0, duration);\n setCurrentTime(video.currentTime);\n };\n\n const handleVolumeChange = (nextValue: number) => {\n const video = videoRef.current;\n\n if (!video) return;\n\n const nextVolume = getClampedValue(nextValue, 0, 1);\n\n video.volume = nextVolume;\n video.muted = nextVolume === 0;\n\n setVolume(nextVolume);\n setIsMuted(video.muted);\n };\n\n const handleSoundToggle = () => {\n const video = videoRef.current;\n\n if (!video) return;\n\n video.muted = !video.muted;\n setIsMuted(video.muted);\n };\n\n const handleFullscreen = () => {\n const video = videoRef.current;\n\n if (!video) return;\n\n video.requestFullscreen?.();\n };\n\n const handleQualityChange = (quality: string) => {\n setSelectedQuality(quality);\n onQualityChange?.(quality);\n };\n\n const handleLoadedMetadata = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n const video = event.currentTarget;\n\n if (\n !hasAppliedInitialTimeRef.current &&\n initialTime > 0\n ) {\n video.currentTime = Math.min(\n initialTime,\n video.duration || initialTime,\n );\n\n hasAppliedInitialTimeRef.current = true;\n }\n\n syncMediaState();\n\n onLoadedMetadata?.(event);\n };\n\n const handleTimeUpdate = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n const video = event.currentTarget;\n\n syncMediaState();\n\n if (\n onProgressCommit &&\n (\n video.currentTime -\n lastProgressCommitAtRef.current\n ) >= progressCommitIntervalSec\n ) {\n lastProgressCommitAtRef.current =\n video.currentTime;\n\n commitProgress(video);\n }\n\n onTimeUpdate?.(event);\n };\n\n const handleProgress = (event: React.SyntheticEvent<HTMLVideoElement>) => {\n syncMediaState();\n onProgress?.(event);\n };\n\n const handlePlay = (event: React.SyntheticEvent<HTMLVideoElement>) => {\n setIsPlaying(true);\n onPlay?.(event);\n };\n\n const handlePause = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n setIsPlaying(false);\n\n commitProgress(event.currentTarget);\n\n onPause?.(event);\n };\n\n const handleEnded = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n syncMediaState();\n\n commitProgress(event.currentTarget);\n\n onEnded?.(event);\n };\n\n const handleNativeVolumeChange = (event: React.SyntheticEvent<HTMLVideoElement>) => {\n syncMediaState();\n onVolumeChange?.(event);\n };\n\n return (\n <div className={classes} aria-label={label}>\n <div className=\"video__surface\">\n {cover && !hasStarted ? (\n <img\n className=\"video__cover\"\n src={cover}\n alt=\"\"\n aria-hidden=\"true\"\n />\n ) : null}\n\n <video\n {...props}\n ref={videoRef}\n className=\"video__media\"\n poster={cover}\n autoPlay={autoPlay}\n loop={loop}\n muted={isMuted}\n playsInline={playsInline}\n preload={preload}\n controls={false}\n onLoadedMetadata={handleLoadedMetadata}\n onTimeUpdate={handleTimeUpdate}\n onProgress={handleProgress}\n onPlay={handlePlay}\n onPause={handlePause}\n onEnded={handleEnded}\n onVolumeChange={handleNativeVolumeChange}\n >\n {children}\n </video>\n\n {!hasStarted && (\n <ButtonIcon\n className=\"video__main-action\"\n icon=\"play\"\n label=\"Play video\"\n onClick={handlePlayToggle}\n />\n )}\n </div>\n\n {hasControls && (\n <div className=\"video__controls\">\n <ButtonIcon\n icon={isPlaying ? 'pause' : 'play'}\n label={isPlaying ? 'Pause video' : 'Play video'}\n onClick={handlePlayToggle}\n />\n\n <div className=\"video__time\">{formatTime(currentTime)}</div>\n\n <ProgressBar\n className=\"video__progress\"\n mode=\"player\"\n value={currentTime}\n buffered={buffered}\n max={duration || 1}\n step={1}\n label=\"Video progress\"\n onValueChange={handleSeek}\n />\n\n <ButtonIcon icon=\"fullscreen\" label=\"Fullscreen\" onClick={handleFullscreen} />\n\n <div className=\"video__sound-control\" onMouseLeave={() => setIsVolumeOpen(false)}>\n <ButtonIcon\n icon={isMuted ? 'sound-off' : 'sound'}\n label={isMuted ? 'Unmute video' : 'Mute video'}\n onClick={handleSoundToggle}\n onMouseEnter={() => setIsVolumeOpen(true)}\n onFocus={() => setIsVolumeOpen(true)}\n />\n\n {hasVolume && (\n <div className=\"video__volume\">\n <ProgressBar\n mode=\"level\"\n value={isMuted ? 0 : volume}\n max={1}\n step={0.05}\n label=\"Volume\"\n onValueChange={handleVolumeChange}\n />\n </div>\n )}\n </div>\n\n {hasQualityOptions && (\n <Dropdown\n align=\"top-end\"\n value={selectedQuality}\n options={qualityOptions}\n label=\"Video quality\"\n onChange={handleQualityChange}\n trigger={<ButtonIcon icon=\"settings\" label=\"Video quality\" />}\n />\n )}\n </div>\n )}\n </div>\n );\n};\n\nexport default Video;"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/video/video.tsx"],"names":["useRef","useState","useEffect","Hls","classnames","jsxs","jsx","ButtonIcon","ProgressBar","Dropdown"],"mappings":"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","file":"video.cjs","sourcesContent":["import React, { useEffect, useRef, useState } from 'react';\nimport Hls from 'hls.js';\n\nimport './video.css';\nimport { classnames } from '../helpers/classnames';\nimport ButtonIcon from '../button-icon';\nimport ProgressBar from '../progressbar';\nimport Dropdown, { type DropdownOption } from '../dropdown';\n\nexport interface VideoQuality {\n value: string;\n label: string;\n src?: string;\n disabled?: boolean;\n}\n\nexport interface VideoProgressState {\n currentTime: number;\n duration: number;\n completed: boolean;\n}\n\nexport interface VideoPlaybackError {\n type: 'media' | 'hls';\n fatal: boolean;\n error: unknown;\n}\n\nexport interface VideoProps\n extends Omit<React.VideoHTMLAttributes<HTMLVideoElement>, 'controls' | 'src' | 'poster'> {\n src?: string;\n mediaKey?: string;\n cover?: string;\n qualities?: VideoQuality[];\n defaultQuality?: string;\n label?: string;\n initialVolume?: number;\n initialMuted?: boolean;\n initialTime?: number;\n progressCommitIntervalSec?: number;\n onProgressCommit?: (state: VideoProgressState) => void;\n onQualityChange?: (quality: string) => void;\n onPlaybackError?: (error: VideoPlaybackError) => void;\n}\n\nfunction getClampedValue(value: number, min: number, max: number) {\n return Math.min(Math.max(value, min), max);\n}\n\nfunction formatTime(value: number) {\n if (!Number.isFinite(value)) return '0:00';\n\n const minutes = Math.floor(value / 60);\n const seconds = Math.floor(value % 60);\n\n return `${minutes}:${String(seconds).padStart(2, '0')}`;\n}\n\nfunction getBufferedEnd(video: HTMLVideoElement) {\n if (video.buffered.length === 0) return 0;\n\n return video.buffered.end(video.buffered.length - 1);\n}\n\nfunction getVideoSource(\n src: string | undefined,\n qualities: VideoQuality[],\n selectedQuality: string,\n) {\n const selectedQualityItem = qualities.find(\n (quality) => quality.value === selectedQuality,\n );\n\n return selectedQualityItem?.src ?? src;\n}\n\nfunction getQualityOptions(qualities: VideoQuality[]): DropdownOption[] {\n return qualities.map((quality) => ({\n value: quality.value,\n label: quality.label,\n disabled: quality.disabled,\n }));\n}\n\nfunction getVideoProgressState(\n video: HTMLVideoElement,\n): VideoProgressState {\n const duration = video.duration || 0;\n const currentTime = video.currentTime || 0;\n\n return {\n currentTime,\n duration,\n\n completed:\n video.ended ||\n (\n duration > 0 &&\n currentTime >= duration * 0.9\n ),\n };\n}\n\nconst defaultQualities: VideoQuality[] = [];\n\nconst Video: React.FC<VideoProps> = ({\n src,\n mediaKey,\n cover,\n qualities = defaultQualities,\n defaultQuality = '',\n label = 'Video player',\n initialVolume = 1,\n initialMuted = false,\n initialTime = 0,\n progressCommitIntervalSec = 15,\n className = '',\n autoPlay,\n loop,\n muted,\n playsInline = true,\n preload = 'metadata',\n onQualityChange,\n onPlaybackError,\n onPlay,\n onPause,\n onEnded,\n onError,\n onLoadedMetadata,\n onTimeUpdate,\n onProgress,\n onVolumeChange,\n onProgressCommit,\n children,\n ...props\n}) => {\n const videoRef = useRef<HTMLVideoElement>(null);\n\n const [isPlaying, setIsPlaying] = useState(false);\n const [isVolumeOpen, setIsVolumeOpen] = useState(false);\n const [selectedQuality, setSelectedQuality] = useState(defaultQuality);\n const [currentTime, setCurrentTime] = useState(0);\n const [duration, setDuration] = useState(0);\n const [buffered, setBuffered] = useState(0);\n const [volume, setVolume] = useState(initialVolume);\n const [isMuted, setIsMuted] = useState(muted ?? initialMuted);\n\n const hasAppliedInitialTimeRef = useRef(false);\n const lastProgressCommitAtRef = useRef(0);\n const loadedMediaKeyRef = useRef<string | undefined>(undefined);\n const pendingRestoreTimeRef = useRef<number | null>(null);\n const onPlaybackErrorRef = useRef(onPlaybackError);\n\n const videoSrc = getVideoSource(\n src,\n qualities,\n selectedQuality,\n );\n\n const effectiveMediaKey = mediaKey ?? videoSrc;\n\n const qualityOptions = getQualityOptions(qualities);\n const hasQualityOptions = qualityOptions.length > 0;\n\n useEffect(() => {\n onPlaybackErrorRef.current = onPlaybackError;\n }, [onPlaybackError]);\n\n useEffect(() => {\n const video = videoRef.current;\n\n if (!video || !videoSrc) return;\n\n const isSameMedia =\n loadedMediaKeyRef.current !== undefined &&\n loadedMediaKeyRef.current === effectiveMediaKey;\n\n if (isSameMedia) {\n pendingRestoreTimeRef.current = video.currentTime;\n } else {\n pendingRestoreTimeRef.current = null;\n hasAppliedInitialTimeRef.current = false;\n lastProgressCommitAtRef.current = 0;\n\n setCurrentTime(0);\n setDuration(0);\n setBuffered(0);\n setIsPlaying(false);\n }\n\n loadedMediaKeyRef.current = effectiveMediaKey;\n\n video.removeAttribute('src');\n video.load();\n\n if (video.canPlayType('application/vnd.apple.mpegurl')) {\n video.src = videoSrc;\n return;\n }\n\n if (!Hls.isSupported()) {\n video.src = videoSrc;\n return;\n }\n\n const hls = new Hls();\n\n hls.on(Hls.Events.ERROR, (_, data) => {\n console.error('HLS error', data);\n\n onPlaybackErrorRef.current?.({\n type: 'hls',\n fatal: data.fatal,\n error: data,\n });\n });\n\n hls.loadSource(videoSrc);\n hls.attachMedia(video);\n\n return () => {\n hls.destroy();\n };\n }, [videoSrc, effectiveMediaKey]);\n\n const hasStarted = currentTime > 0 || isPlaying;\n const hasControls = hasStarted;\n const hasVolume = isVolumeOpen;\n\n const classes = classnames('video', className, {\n 'video--with-controls': hasControls,\n 'video--with-volume': hasVolume,\n });\n\n const syncMediaState = () => {\n const video = videoRef.current;\n\n if (!video) return;\n\n setCurrentTime(video.currentTime);\n setDuration(video.duration || 0);\n setBuffered(getBufferedEnd(video));\n setVolume(video.volume);\n setIsMuted(video.muted);\n setIsPlaying(!video.paused);\n };\n\n const commitProgress = (\n video: HTMLVideoElement,\n ) => {\n if (!onProgressCommit) {\n return;\n }\n\n onProgressCommit(\n getVideoProgressState(video),\n );\n };\n\n const handlePlayToggle = async () => {\n const video = videoRef.current;\n\n if (!video) return;\n\n console.log('play click', {\n paused: video.paused,\n readyState: video.readyState,\n src: video.currentSrc,\n networkState: video.networkState,\n error: video.error,\n });\n\n if (video.paused) {\n try {\n await video.play();\n console.log('play success');\n } catch (error) {\n console.error('play failed', error);\n }\n\n return;\n }\n\n video.pause();\n };\n\n const handleSeek = (nextValue: number) => {\n const video = videoRef.current;\n\n if (!video) return;\n\n video.currentTime = getClampedValue(\n nextValue,\n 0,\n duration,\n );\n\n setCurrentTime(video.currentTime);\n };\n\n const handleVolumeChange = (nextValue: number) => {\n const video = videoRef.current;\n\n if (!video) return;\n\n const nextVolume = getClampedValue(\n nextValue,\n 0,\n 1,\n );\n\n video.volume = nextVolume;\n video.muted = nextVolume === 0;\n\n setVolume(nextVolume);\n setIsMuted(video.muted);\n };\n\n const handleSoundToggle = () => {\n const video = videoRef.current;\n\n if (!video) return;\n\n video.muted = !video.muted;\n setIsMuted(video.muted);\n };\n\n const handleFullscreen = () => {\n const video = videoRef.current;\n\n if (!video) return;\n\n video.requestFullscreen?.();\n };\n\n const handleQualityChange = (quality: string) => {\n setSelectedQuality(quality);\n onQualityChange?.(quality);\n };\n\n const handleLoadedMetadata = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n const video = event.currentTarget;\n\n if (pendingRestoreTimeRef.current !== null) {\n video.currentTime = Math.min(\n pendingRestoreTimeRef.current,\n video.duration || pendingRestoreTimeRef.current,\n );\n\n pendingRestoreTimeRef.current = null;\n } else if (\n !hasAppliedInitialTimeRef.current &&\n initialTime > 0\n ) {\n video.currentTime = Math.min(\n initialTime,\n video.duration || initialTime,\n );\n\n hasAppliedInitialTimeRef.current = true;\n }\n\n syncMediaState();\n\n onLoadedMetadata?.(event);\n };\n\n const handleTimeUpdate = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n const video = event.currentTarget;\n\n syncMediaState();\n\n if (\n onProgressCommit &&\n (\n video.currentTime -\n lastProgressCommitAtRef.current\n ) >= progressCommitIntervalSec\n ) {\n lastProgressCommitAtRef.current =\n video.currentTime;\n\n commitProgress(video);\n }\n\n onTimeUpdate?.(event);\n };\n\n const handleProgress = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n syncMediaState();\n onProgress?.(event);\n };\n\n const handlePlay = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n setIsPlaying(true);\n onPlay?.(event);\n };\n\n const handlePause = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n setIsPlaying(false);\n\n commitProgress(event.currentTarget);\n\n onPause?.(event);\n };\n\n const handleEnded = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n syncMediaState();\n\n commitProgress(event.currentTarget);\n\n onEnded?.(event);\n };\n\n const handleNativeError = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n onPlaybackError?.({\n type: 'media',\n fatal: true,\n error: event.currentTarget.error ?? event,\n });\n\n onError?.(event);\n };\n\n const handleNativeVolumeChange = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n syncMediaState();\n onVolumeChange?.(event);\n };\n\n return (\n <div\n className={classes}\n aria-label={label}\n >\n <div className=\"video__surface\">\n {cover && !hasStarted ? (\n <img\n className=\"video__cover\"\n src={cover}\n alt=\"\"\n aria-hidden=\"true\"\n />\n ) : null}\n\n <video\n {...props}\n ref={videoRef}\n className=\"video__media\"\n poster={cover}\n autoPlay={autoPlay}\n loop={loop}\n muted={isMuted}\n playsInline={playsInline}\n preload={preload}\n controls={false}\n onLoadedMetadata={handleLoadedMetadata}\n onTimeUpdate={handleTimeUpdate}\n onProgress={handleProgress}\n onPlay={handlePlay}\n onPause={handlePause}\n onEnded={handleEnded}\n onError={handleNativeError}\n onVolumeChange={handleNativeVolumeChange}\n >\n {children}\n </video>\n\n {!hasStarted && (\n <ButtonIcon\n className=\"video__main-action\"\n icon=\"play\"\n label=\"Play video\"\n onClick={handlePlayToggle}\n />\n )}\n </div>\n\n {hasControls && (\n <div className=\"video__controls\">\n <ButtonIcon\n icon={isPlaying ? 'pause' : 'play'}\n label={isPlaying ? 'Pause video' : 'Play video'}\n onClick={handlePlayToggle}\n />\n\n <div className=\"video__time\">\n {formatTime(currentTime)}\n </div>\n\n <ProgressBar\n className=\"video__progress\"\n mode=\"player\"\n value={currentTime}\n buffered={buffered}\n max={duration || 1}\n step={1}\n label=\"Video progress\"\n onValueChange={handleSeek}\n />\n\n <ButtonIcon\n icon=\"fullscreen\"\n label=\"Fullscreen\"\n onClick={handleFullscreen}\n />\n\n <div\n className=\"video__sound-control\"\n onMouseLeave={() => setIsVolumeOpen(false)}\n >\n <ButtonIcon\n icon={isMuted ? 'sound-off' : 'sound'}\n label={isMuted ? 'Unmute video' : 'Mute video'}\n onClick={handleSoundToggle}\n onMouseEnter={() => setIsVolumeOpen(true)}\n onFocus={() => setIsVolumeOpen(true)}\n />\n\n {hasVolume && (\n <div className=\"video__volume\">\n <ProgressBar\n mode=\"level\"\n value={isMuted ? 0 : volume}\n max={1}\n step={0.05}\n label=\"Volume\"\n onValueChange={handleVolumeChange}\n />\n </div>\n )}\n </div>\n\n {hasQualityOptions && (\n <Dropdown\n align=\"top-end\"\n value={selectedQuality}\n options={qualityOptions}\n label=\"Video quality\"\n onChange={handleQualityChange}\n trigger={\n <ButtonIcon\n icon=\"settings\"\n label=\"Video quality\"\n />\n }\n />\n )}\n </div>\n )}\n </div>\n );\n};\n\nexport default Video;"]}
|
|
@@ -11,8 +11,14 @@ interface VideoProgressState {
|
|
|
11
11
|
duration: number;
|
|
12
12
|
completed: boolean;
|
|
13
13
|
}
|
|
14
|
+
interface VideoPlaybackError {
|
|
15
|
+
type: 'media' | 'hls';
|
|
16
|
+
fatal: boolean;
|
|
17
|
+
error: unknown;
|
|
18
|
+
}
|
|
14
19
|
interface VideoProps extends Omit<React.VideoHTMLAttributes<HTMLVideoElement>, 'controls' | 'src' | 'poster'> {
|
|
15
20
|
src?: string;
|
|
21
|
+
mediaKey?: string;
|
|
16
22
|
cover?: string;
|
|
17
23
|
qualities?: VideoQuality[];
|
|
18
24
|
defaultQuality?: string;
|
|
@@ -23,7 +29,8 @@ interface VideoProps extends Omit<React.VideoHTMLAttributes<HTMLVideoElement>, '
|
|
|
23
29
|
progressCommitIntervalSec?: number;
|
|
24
30
|
onProgressCommit?: (state: VideoProgressState) => void;
|
|
25
31
|
onQualityChange?: (quality: string) => void;
|
|
32
|
+
onPlaybackError?: (error: VideoPlaybackError) => void;
|
|
26
33
|
}
|
|
27
34
|
declare const Video: React.FC<VideoProps>;
|
|
28
35
|
|
|
29
|
-
export { type VideoProgressState, type VideoProps, type VideoQuality, Video as default };
|
|
36
|
+
export { type VideoPlaybackError, type VideoProgressState, type VideoProps, type VideoQuality, Video as default };
|
|
@@ -11,8 +11,14 @@ interface VideoProgressState {
|
|
|
11
11
|
duration: number;
|
|
12
12
|
completed: boolean;
|
|
13
13
|
}
|
|
14
|
+
interface VideoPlaybackError {
|
|
15
|
+
type: 'media' | 'hls';
|
|
16
|
+
fatal: boolean;
|
|
17
|
+
error: unknown;
|
|
18
|
+
}
|
|
14
19
|
interface VideoProps extends Omit<React.VideoHTMLAttributes<HTMLVideoElement>, 'controls' | 'src' | 'poster'> {
|
|
15
20
|
src?: string;
|
|
21
|
+
mediaKey?: string;
|
|
16
22
|
cover?: string;
|
|
17
23
|
qualities?: VideoQuality[];
|
|
18
24
|
defaultQuality?: string;
|
|
@@ -23,7 +29,8 @@ interface VideoProps extends Omit<React.VideoHTMLAttributes<HTMLVideoElement>, '
|
|
|
23
29
|
progressCommitIntervalSec?: number;
|
|
24
30
|
onProgressCommit?: (state: VideoProgressState) => void;
|
|
25
31
|
onQualityChange?: (quality: string) => void;
|
|
32
|
+
onPlaybackError?: (error: VideoPlaybackError) => void;
|
|
26
33
|
}
|
|
27
34
|
declare const Video: React.FC<VideoProps>;
|
|
28
35
|
|
|
29
|
-
export { type VideoProgressState, type VideoProps, type VideoQuality, Video as default };
|
|
36
|
+
export { type VideoPlaybackError, type VideoProgressState, type VideoProps, type VideoQuality, Video as default };
|
|
@@ -21,7 +21,9 @@ function getBufferedEnd(video) {
|
|
|
21
21
|
return video.buffered.end(video.buffered.length - 1);
|
|
22
22
|
}
|
|
23
23
|
function getVideoSource(src, qualities, selectedQuality) {
|
|
24
|
-
const selectedQualityItem = qualities.find(
|
|
24
|
+
const selectedQualityItem = qualities.find(
|
|
25
|
+
(quality) => quality.value === selectedQuality
|
|
26
|
+
);
|
|
25
27
|
return selectedQualityItem?.src ?? src;
|
|
26
28
|
}
|
|
27
29
|
function getQualityOptions(qualities) {
|
|
@@ -43,6 +45,7 @@ function getVideoProgressState(video) {
|
|
|
43
45
|
const defaultQualities = [];
|
|
44
46
|
const Video = ({
|
|
45
47
|
src,
|
|
48
|
+
mediaKey,
|
|
46
49
|
cover,
|
|
47
50
|
qualities = defaultQualities,
|
|
48
51
|
defaultQuality = "",
|
|
@@ -58,9 +61,11 @@ const Video = ({
|
|
|
58
61
|
playsInline = true,
|
|
59
62
|
preload = "metadata",
|
|
60
63
|
onQualityChange,
|
|
64
|
+
onPlaybackError,
|
|
61
65
|
onPlay,
|
|
62
66
|
onPause,
|
|
63
67
|
onEnded,
|
|
68
|
+
onError,
|
|
64
69
|
onLoadedMetadata,
|
|
65
70
|
onTimeUpdate,
|
|
66
71
|
onProgress,
|
|
@@ -80,14 +85,36 @@ const Video = ({
|
|
|
80
85
|
const [isMuted, setIsMuted] = useState(muted ?? initialMuted);
|
|
81
86
|
const hasAppliedInitialTimeRef = useRef(false);
|
|
82
87
|
const lastProgressCommitAtRef = useRef(0);
|
|
83
|
-
const
|
|
88
|
+
const loadedMediaKeyRef = useRef(void 0);
|
|
89
|
+
const pendingRestoreTimeRef = useRef(null);
|
|
90
|
+
const onPlaybackErrorRef = useRef(onPlaybackError);
|
|
91
|
+
const videoSrc = getVideoSource(
|
|
92
|
+
src,
|
|
93
|
+
qualities,
|
|
94
|
+
selectedQuality
|
|
95
|
+
);
|
|
96
|
+
const effectiveMediaKey = mediaKey ?? videoSrc;
|
|
84
97
|
const qualityOptions = getQualityOptions(qualities);
|
|
85
98
|
const hasQualityOptions = qualityOptions.length > 0;
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
onPlaybackErrorRef.current = onPlaybackError;
|
|
101
|
+
}, [onPlaybackError]);
|
|
86
102
|
useEffect(() => {
|
|
87
103
|
const video = videoRef.current;
|
|
88
|
-
hasAppliedInitialTimeRef.current = false;
|
|
89
|
-
lastProgressCommitAtRef.current = 0;
|
|
90
104
|
if (!video || !videoSrc) return;
|
|
105
|
+
const isSameMedia = loadedMediaKeyRef.current !== void 0 && loadedMediaKeyRef.current === effectiveMediaKey;
|
|
106
|
+
if (isSameMedia) {
|
|
107
|
+
pendingRestoreTimeRef.current = video.currentTime;
|
|
108
|
+
} else {
|
|
109
|
+
pendingRestoreTimeRef.current = null;
|
|
110
|
+
hasAppliedInitialTimeRef.current = false;
|
|
111
|
+
lastProgressCommitAtRef.current = 0;
|
|
112
|
+
setCurrentTime(0);
|
|
113
|
+
setDuration(0);
|
|
114
|
+
setBuffered(0);
|
|
115
|
+
setIsPlaying(false);
|
|
116
|
+
}
|
|
117
|
+
loadedMediaKeyRef.current = effectiveMediaKey;
|
|
91
118
|
video.removeAttribute("src");
|
|
92
119
|
video.load();
|
|
93
120
|
if (video.canPlayType("application/vnd.apple.mpegurl")) {
|
|
@@ -101,13 +128,18 @@ const Video = ({
|
|
|
101
128
|
const hls = new Hls();
|
|
102
129
|
hls.on(Hls.Events.ERROR, (_, data) => {
|
|
103
130
|
console.error("HLS error", data);
|
|
131
|
+
onPlaybackErrorRef.current?.({
|
|
132
|
+
type: "hls",
|
|
133
|
+
fatal: data.fatal,
|
|
134
|
+
error: data
|
|
135
|
+
});
|
|
104
136
|
});
|
|
105
137
|
hls.loadSource(videoSrc);
|
|
106
138
|
hls.attachMedia(video);
|
|
107
139
|
return () => {
|
|
108
140
|
hls.destroy();
|
|
109
141
|
};
|
|
110
|
-
}, [videoSrc]);
|
|
142
|
+
}, [videoSrc, effectiveMediaKey]);
|
|
111
143
|
const hasStarted = currentTime > 0 || isPlaying;
|
|
112
144
|
const hasControls = hasStarted;
|
|
113
145
|
const hasVolume = isVolumeOpen;
|
|
@@ -157,13 +189,21 @@ const Video = ({
|
|
|
157
189
|
const handleSeek = (nextValue) => {
|
|
158
190
|
const video = videoRef.current;
|
|
159
191
|
if (!video) return;
|
|
160
|
-
video.currentTime = getClampedValue(
|
|
192
|
+
video.currentTime = getClampedValue(
|
|
193
|
+
nextValue,
|
|
194
|
+
0,
|
|
195
|
+
duration
|
|
196
|
+
);
|
|
161
197
|
setCurrentTime(video.currentTime);
|
|
162
198
|
};
|
|
163
199
|
const handleVolumeChange = (nextValue) => {
|
|
164
200
|
const video = videoRef.current;
|
|
165
201
|
if (!video) return;
|
|
166
|
-
const nextVolume = getClampedValue(
|
|
202
|
+
const nextVolume = getClampedValue(
|
|
203
|
+
nextValue,
|
|
204
|
+
0,
|
|
205
|
+
1
|
|
206
|
+
);
|
|
167
207
|
video.volume = nextVolume;
|
|
168
208
|
video.muted = nextVolume === 0;
|
|
169
209
|
setVolume(nextVolume);
|
|
@@ -186,7 +226,13 @@ const Video = ({
|
|
|
186
226
|
};
|
|
187
227
|
const handleLoadedMetadata = (event) => {
|
|
188
228
|
const video = event.currentTarget;
|
|
189
|
-
if (
|
|
229
|
+
if (pendingRestoreTimeRef.current !== null) {
|
|
230
|
+
video.currentTime = Math.min(
|
|
231
|
+
pendingRestoreTimeRef.current,
|
|
232
|
+
video.duration || pendingRestoreTimeRef.current
|
|
233
|
+
);
|
|
234
|
+
pendingRestoreTimeRef.current = null;
|
|
235
|
+
} else if (!hasAppliedInitialTimeRef.current && initialTime > 0) {
|
|
190
236
|
video.currentTime = Math.min(
|
|
191
237
|
initialTime,
|
|
192
238
|
video.duration || initialTime
|
|
@@ -223,114 +269,150 @@ const Video = ({
|
|
|
223
269
|
commitProgress(event.currentTarget);
|
|
224
270
|
onEnded?.(event);
|
|
225
271
|
};
|
|
272
|
+
const handleNativeError = (event) => {
|
|
273
|
+
onPlaybackError?.({
|
|
274
|
+
type: "media",
|
|
275
|
+
fatal: true,
|
|
276
|
+
error: event.currentTarget.error ?? event
|
|
277
|
+
});
|
|
278
|
+
onError?.(event);
|
|
279
|
+
};
|
|
226
280
|
const handleNativeVolumeChange = (event) => {
|
|
227
281
|
syncMediaState();
|
|
228
282
|
onVolumeChange?.(event);
|
|
229
283
|
};
|
|
230
|
-
return /* @__PURE__ */ jsxs(
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
284
|
+
return /* @__PURE__ */ jsxs(
|
|
285
|
+
"div",
|
|
286
|
+
{
|
|
287
|
+
className: classes,
|
|
288
|
+
"aria-label": label,
|
|
289
|
+
children: [
|
|
290
|
+
/* @__PURE__ */ jsxs("div", { className: "video__surface", children: [
|
|
291
|
+
cover && !hasStarted ? /* @__PURE__ */ jsx(
|
|
292
|
+
"img",
|
|
293
|
+
{
|
|
294
|
+
className: "video__cover",
|
|
295
|
+
src: cover,
|
|
296
|
+
alt: "",
|
|
297
|
+
"aria-hidden": "true"
|
|
298
|
+
}
|
|
299
|
+
) : null,
|
|
300
|
+
/* @__PURE__ */ jsx(
|
|
301
|
+
"video",
|
|
302
|
+
{
|
|
303
|
+
...props,
|
|
304
|
+
ref: videoRef,
|
|
305
|
+
className: "video__media",
|
|
306
|
+
poster: cover,
|
|
307
|
+
autoPlay,
|
|
308
|
+
loop,
|
|
309
|
+
muted: isMuted,
|
|
310
|
+
playsInline,
|
|
311
|
+
preload,
|
|
312
|
+
controls: false,
|
|
313
|
+
onLoadedMetadata: handleLoadedMetadata,
|
|
314
|
+
onTimeUpdate: handleTimeUpdate,
|
|
315
|
+
onProgress: handleProgress,
|
|
316
|
+
onPlay: handlePlay,
|
|
317
|
+
onPause: handlePause,
|
|
318
|
+
onEnded: handleEnded,
|
|
319
|
+
onError: handleNativeError,
|
|
320
|
+
onVolumeChange: handleNativeVolumeChange,
|
|
321
|
+
children
|
|
322
|
+
}
|
|
323
|
+
),
|
|
324
|
+
!hasStarted && /* @__PURE__ */ jsx(
|
|
325
|
+
ButtonIcon,
|
|
326
|
+
{
|
|
327
|
+
className: "video__main-action",
|
|
328
|
+
icon: "play",
|
|
329
|
+
label: "Play video",
|
|
330
|
+
onClick: handlePlayToggle
|
|
331
|
+
}
|
|
332
|
+
)
|
|
333
|
+
] }),
|
|
334
|
+
hasControls && /* @__PURE__ */ jsxs("div", { className: "video__controls", children: [
|
|
335
|
+
/* @__PURE__ */ jsx(
|
|
336
|
+
ButtonIcon,
|
|
337
|
+
{
|
|
338
|
+
icon: isPlaying ? "pause" : "play",
|
|
339
|
+
label: isPlaying ? "Pause video" : "Play video",
|
|
340
|
+
onClick: handlePlayToggle
|
|
341
|
+
}
|
|
342
|
+
),
|
|
343
|
+
/* @__PURE__ */ jsx("div", { className: "video__time", children: formatTime(currentTime) }),
|
|
344
|
+
/* @__PURE__ */ jsx(
|
|
345
|
+
ProgressBar,
|
|
346
|
+
{
|
|
347
|
+
className: "video__progress",
|
|
348
|
+
mode: "player",
|
|
349
|
+
value: currentTime,
|
|
350
|
+
buffered,
|
|
351
|
+
max: duration || 1,
|
|
352
|
+
step: 1,
|
|
353
|
+
label: "Video progress",
|
|
354
|
+
onValueChange: handleSeek
|
|
355
|
+
}
|
|
356
|
+
),
|
|
357
|
+
/* @__PURE__ */ jsx(
|
|
358
|
+
ButtonIcon,
|
|
359
|
+
{
|
|
360
|
+
icon: "fullscreen",
|
|
361
|
+
label: "Fullscreen",
|
|
362
|
+
onClick: handleFullscreen
|
|
363
|
+
}
|
|
364
|
+
),
|
|
365
|
+
/* @__PURE__ */ jsxs(
|
|
366
|
+
"div",
|
|
367
|
+
{
|
|
368
|
+
className: "video__sound-control",
|
|
369
|
+
onMouseLeave: () => setIsVolumeOpen(false),
|
|
370
|
+
children: [
|
|
371
|
+
/* @__PURE__ */ jsx(
|
|
372
|
+
ButtonIcon,
|
|
373
|
+
{
|
|
374
|
+
icon: isMuted ? "sound-off" : "sound",
|
|
375
|
+
label: isMuted ? "Unmute video" : "Mute video",
|
|
376
|
+
onClick: handleSoundToggle,
|
|
377
|
+
onMouseEnter: () => setIsVolumeOpen(true),
|
|
378
|
+
onFocus: () => setIsVolumeOpen(true)
|
|
379
|
+
}
|
|
380
|
+
),
|
|
381
|
+
hasVolume && /* @__PURE__ */ jsx("div", { className: "video__volume", children: /* @__PURE__ */ jsx(
|
|
382
|
+
ProgressBar,
|
|
383
|
+
{
|
|
384
|
+
mode: "level",
|
|
385
|
+
value: isMuted ? 0 : volume,
|
|
386
|
+
max: 1,
|
|
387
|
+
step: 0.05,
|
|
388
|
+
label: "Volume",
|
|
389
|
+
onValueChange: handleVolumeChange
|
|
390
|
+
}
|
|
391
|
+
) })
|
|
392
|
+
]
|
|
393
|
+
}
|
|
394
|
+
),
|
|
395
|
+
hasQualityOptions && /* @__PURE__ */ jsx(
|
|
396
|
+
Dropdown,
|
|
397
|
+
{
|
|
398
|
+
align: "top-end",
|
|
399
|
+
value: selectedQuality,
|
|
400
|
+
options: qualityOptions,
|
|
401
|
+
label: "Video quality",
|
|
402
|
+
onChange: handleQualityChange,
|
|
403
|
+
trigger: /* @__PURE__ */ jsx(
|
|
404
|
+
ButtonIcon,
|
|
405
|
+
{
|
|
406
|
+
icon: "settings",
|
|
407
|
+
label: "Video quality"
|
|
408
|
+
}
|
|
409
|
+
)
|
|
410
|
+
}
|
|
411
|
+
)
|
|
412
|
+
] })
|
|
413
|
+
]
|
|
414
|
+
}
|
|
415
|
+
);
|
|
334
416
|
};
|
|
335
417
|
var video_default = Video;
|
|
336
418
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/video/video.tsx"],"names":[],"mappings":"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","file":"video.js","sourcesContent":["import React, { useEffect, useRef, useState } from 'react';\nimport Hls from 'hls.js';\n\nimport './video.css';\nimport { classnames } from '../helpers/classnames';\nimport ButtonIcon from '../button-icon';\nimport ProgressBar from '../progressbar';\nimport Dropdown, { type DropdownOption } from '../dropdown';\n\nexport interface VideoQuality {\n value: string;\n label: string;\n src?: string;\n disabled?: boolean;\n}\n\nexport interface VideoProgressState {\n currentTime: number;\n duration: number;\n completed: boolean;\n}\n\nexport interface VideoProps\n extends Omit<React.VideoHTMLAttributes<HTMLVideoElement>, 'controls' | 'src' | 'poster'> {\n src?: string;\n cover?: string;\n qualities?: VideoQuality[];\n defaultQuality?: string;\n label?: string;\n initialVolume?: number;\n initialMuted?: boolean;\n initialTime?: number;\n progressCommitIntervalSec?: number;\n onProgressCommit?: (state: VideoProgressState) => void;\n onQualityChange?: (quality: string) => void;\n}\n\nfunction getClampedValue(value: number, min: number, max: number) {\n return Math.min(Math.max(value, min), max);\n}\n\nfunction formatTime(value: number) {\n if (!Number.isFinite(value)) return '0:00';\n\n const minutes = Math.floor(value / 60);\n const seconds = Math.floor(value % 60);\n\n return `${minutes}:${String(seconds).padStart(2, '0')}`;\n}\n\nfunction getBufferedEnd(video: HTMLVideoElement) {\n if (video.buffered.length === 0) return 0;\n\n return video.buffered.end(video.buffered.length - 1);\n}\n\nfunction getVideoSource(src: string | undefined, qualities: VideoQuality[], selectedQuality: string) {\n const selectedQualityItem = qualities.find((quality) => quality.value === selectedQuality);\n\n return selectedQualityItem?.src ?? src;\n}\n\nfunction getQualityOptions(qualities: VideoQuality[]): DropdownOption[] {\n return qualities.map((quality) => ({\n value: quality.value,\n label: quality.label,\n disabled: quality.disabled,\n }));\n}\n\nfunction getVideoProgressState(\n video: HTMLVideoElement,\n): VideoProgressState {\n const duration = video.duration || 0;\n const currentTime = video.currentTime || 0;\n\n return {\n currentTime,\n duration,\n\n completed:\n video.ended ||\n (\n duration > 0 &&\n currentTime >= duration * 0.9\n ),\n };\n}\n\nconst defaultQualities: VideoQuality[] = [];\n\nconst Video: React.FC<VideoProps> = ({\n src,\n cover,\n qualities = defaultQualities,\n defaultQuality = '',\n label = 'Video player',\n initialVolume = 1,\n initialMuted = false,\n initialTime = 0,\n progressCommitIntervalSec = 15,\n className = '',\n autoPlay,\n loop,\n muted,\n playsInline = true,\n preload = 'metadata',\n onQualityChange,\n onPlay,\n onPause,\n onEnded,\n onLoadedMetadata,\n onTimeUpdate,\n onProgress,\n onVolumeChange,\n onProgressCommit,\n children,\n ...props\n}) => {\n const videoRef = useRef<HTMLVideoElement>(null);\n\n const [isPlaying, setIsPlaying] = useState(false);\n const [isVolumeOpen, setIsVolumeOpen] = useState(false);\n const [selectedQuality, setSelectedQuality] = useState(defaultQuality);\n const [currentTime, setCurrentTime] = useState(0);\n const [duration, setDuration] = useState(0);\n const [buffered, setBuffered] = useState(0);\n const [volume, setVolume] = useState(initialVolume);\n const [isMuted, setIsMuted] = useState(muted ?? initialMuted);\n\n const hasAppliedInitialTimeRef = useRef(false);\n const lastProgressCommitAtRef = useRef(0);\n\n const videoSrc = getVideoSource(src, qualities, selectedQuality);\n const qualityOptions = getQualityOptions(qualities);\n const hasQualityOptions = qualityOptions.length > 0;\n\n useEffect(() => {\n const video = videoRef.current;\n\n hasAppliedInitialTimeRef.current = false;\n lastProgressCommitAtRef.current = 0;\n\n if (!video || !videoSrc) return;\n\n video.removeAttribute('src');\n video.load();\n\n if (video.canPlayType('application/vnd.apple.mpegurl')) {\n video.src = videoSrc;\n return;\n }\n\n if (!Hls.isSupported()) {\n video.src = videoSrc;\n return;\n }\n\n const hls = new Hls();\n\n hls.on(Hls.Events.ERROR, (_, data) => {\n console.error('HLS error', data);\n });\n\n hls.loadSource(videoSrc);\n hls.attachMedia(video);\n\n return () => {\n hls.destroy();\n };\n }, [videoSrc]);\n\n const hasStarted = currentTime > 0 || isPlaying;\n const hasControls = hasStarted;\n const hasVolume = isVolumeOpen;\n\n const classes = classnames('video', className, {\n 'video--with-controls': hasControls,\n 'video--with-volume': hasVolume,\n });\n\n const syncMediaState = () => {\n const video = videoRef.current;\n\n if (!video) return;\n\n setCurrentTime(video.currentTime);\n setDuration(video.duration || 0);\n setBuffered(getBufferedEnd(video));\n setVolume(video.volume);\n setIsMuted(video.muted);\n setIsPlaying(!video.paused);\n };\n\n const commitProgress = (\n video: HTMLVideoElement,\n ) => {\n if (!onProgressCommit) {\n return;\n }\n\n onProgressCommit(\n getVideoProgressState(video),\n );\n };\n\n const handlePlayToggle = async () => {\n const video = videoRef.current;\n\n if (!video) return;\n\n console.log('play click', {\n paused: video.paused,\n readyState: video.readyState,\n src: video.currentSrc,\n networkState: video.networkState,\n error: video.error,\n });\n\n if (video.paused) {\n try {\n await video.play();\n console.log('play success');\n } catch (error) {\n console.error('play failed', error);\n }\n\n return;\n }\n\n video.pause();\n };\n\n const handleSeek = (nextValue: number) => {\n const video = videoRef.current;\n\n if (!video) return;\n\n video.currentTime = getClampedValue(nextValue, 0, duration);\n setCurrentTime(video.currentTime);\n };\n\n const handleVolumeChange = (nextValue: number) => {\n const video = videoRef.current;\n\n if (!video) return;\n\n const nextVolume = getClampedValue(nextValue, 0, 1);\n\n video.volume = nextVolume;\n video.muted = nextVolume === 0;\n\n setVolume(nextVolume);\n setIsMuted(video.muted);\n };\n\n const handleSoundToggle = () => {\n const video = videoRef.current;\n\n if (!video) return;\n\n video.muted = !video.muted;\n setIsMuted(video.muted);\n };\n\n const handleFullscreen = () => {\n const video = videoRef.current;\n\n if (!video) return;\n\n video.requestFullscreen?.();\n };\n\n const handleQualityChange = (quality: string) => {\n setSelectedQuality(quality);\n onQualityChange?.(quality);\n };\n\n const handleLoadedMetadata = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n const video = event.currentTarget;\n\n if (\n !hasAppliedInitialTimeRef.current &&\n initialTime > 0\n ) {\n video.currentTime = Math.min(\n initialTime,\n video.duration || initialTime,\n );\n\n hasAppliedInitialTimeRef.current = true;\n }\n\n syncMediaState();\n\n onLoadedMetadata?.(event);\n };\n\n const handleTimeUpdate = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n const video = event.currentTarget;\n\n syncMediaState();\n\n if (\n onProgressCommit &&\n (\n video.currentTime -\n lastProgressCommitAtRef.current\n ) >= progressCommitIntervalSec\n ) {\n lastProgressCommitAtRef.current =\n video.currentTime;\n\n commitProgress(video);\n }\n\n onTimeUpdate?.(event);\n };\n\n const handleProgress = (event: React.SyntheticEvent<HTMLVideoElement>) => {\n syncMediaState();\n onProgress?.(event);\n };\n\n const handlePlay = (event: React.SyntheticEvent<HTMLVideoElement>) => {\n setIsPlaying(true);\n onPlay?.(event);\n };\n\n const handlePause = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n setIsPlaying(false);\n\n commitProgress(event.currentTarget);\n\n onPause?.(event);\n };\n\n const handleEnded = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n syncMediaState();\n\n commitProgress(event.currentTarget);\n\n onEnded?.(event);\n };\n\n const handleNativeVolumeChange = (event: React.SyntheticEvent<HTMLVideoElement>) => {\n syncMediaState();\n onVolumeChange?.(event);\n };\n\n return (\n <div className={classes} aria-label={label}>\n <div className=\"video__surface\">\n {cover && !hasStarted ? (\n <img\n className=\"video__cover\"\n src={cover}\n alt=\"\"\n aria-hidden=\"true\"\n />\n ) : null}\n\n <video\n {...props}\n ref={videoRef}\n className=\"video__media\"\n poster={cover}\n autoPlay={autoPlay}\n loop={loop}\n muted={isMuted}\n playsInline={playsInline}\n preload={preload}\n controls={false}\n onLoadedMetadata={handleLoadedMetadata}\n onTimeUpdate={handleTimeUpdate}\n onProgress={handleProgress}\n onPlay={handlePlay}\n onPause={handlePause}\n onEnded={handleEnded}\n onVolumeChange={handleNativeVolumeChange}\n >\n {children}\n </video>\n\n {!hasStarted && (\n <ButtonIcon\n className=\"video__main-action\"\n icon=\"play\"\n label=\"Play video\"\n onClick={handlePlayToggle}\n />\n )}\n </div>\n\n {hasControls && (\n <div className=\"video__controls\">\n <ButtonIcon\n icon={isPlaying ? 'pause' : 'play'}\n label={isPlaying ? 'Pause video' : 'Play video'}\n onClick={handlePlayToggle}\n />\n\n <div className=\"video__time\">{formatTime(currentTime)}</div>\n\n <ProgressBar\n className=\"video__progress\"\n mode=\"player\"\n value={currentTime}\n buffered={buffered}\n max={duration || 1}\n step={1}\n label=\"Video progress\"\n onValueChange={handleSeek}\n />\n\n <ButtonIcon icon=\"fullscreen\" label=\"Fullscreen\" onClick={handleFullscreen} />\n\n <div className=\"video__sound-control\" onMouseLeave={() => setIsVolumeOpen(false)}>\n <ButtonIcon\n icon={isMuted ? 'sound-off' : 'sound'}\n label={isMuted ? 'Unmute video' : 'Mute video'}\n onClick={handleSoundToggle}\n onMouseEnter={() => setIsVolumeOpen(true)}\n onFocus={() => setIsVolumeOpen(true)}\n />\n\n {hasVolume && (\n <div className=\"video__volume\">\n <ProgressBar\n mode=\"level\"\n value={isMuted ? 0 : volume}\n max={1}\n step={0.05}\n label=\"Volume\"\n onValueChange={handleVolumeChange}\n />\n </div>\n )}\n </div>\n\n {hasQualityOptions && (\n <Dropdown\n align=\"top-end\"\n value={selectedQuality}\n options={qualityOptions}\n label=\"Video quality\"\n onChange={handleQualityChange}\n trigger={<ButtonIcon icon=\"settings\" label=\"Video quality\" />}\n />\n )}\n </div>\n )}\n </div>\n );\n};\n\nexport default Video;"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/video/video.tsx"],"names":[],"mappings":"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","file":"video.js","sourcesContent":["import React, { useEffect, useRef, useState } from 'react';\nimport Hls from 'hls.js';\n\nimport './video.css';\nimport { classnames } from '../helpers/classnames';\nimport ButtonIcon from '../button-icon';\nimport ProgressBar from '../progressbar';\nimport Dropdown, { type DropdownOption } from '../dropdown';\n\nexport interface VideoQuality {\n value: string;\n label: string;\n src?: string;\n disabled?: boolean;\n}\n\nexport interface VideoProgressState {\n currentTime: number;\n duration: number;\n completed: boolean;\n}\n\nexport interface VideoPlaybackError {\n type: 'media' | 'hls';\n fatal: boolean;\n error: unknown;\n}\n\nexport interface VideoProps\n extends Omit<React.VideoHTMLAttributes<HTMLVideoElement>, 'controls' | 'src' | 'poster'> {\n src?: string;\n mediaKey?: string;\n cover?: string;\n qualities?: VideoQuality[];\n defaultQuality?: string;\n label?: string;\n initialVolume?: number;\n initialMuted?: boolean;\n initialTime?: number;\n progressCommitIntervalSec?: number;\n onProgressCommit?: (state: VideoProgressState) => void;\n onQualityChange?: (quality: string) => void;\n onPlaybackError?: (error: VideoPlaybackError) => void;\n}\n\nfunction getClampedValue(value: number, min: number, max: number) {\n return Math.min(Math.max(value, min), max);\n}\n\nfunction formatTime(value: number) {\n if (!Number.isFinite(value)) return '0:00';\n\n const minutes = Math.floor(value / 60);\n const seconds = Math.floor(value % 60);\n\n return `${minutes}:${String(seconds).padStart(2, '0')}`;\n}\n\nfunction getBufferedEnd(video: HTMLVideoElement) {\n if (video.buffered.length === 0) return 0;\n\n return video.buffered.end(video.buffered.length - 1);\n}\n\nfunction getVideoSource(\n src: string | undefined,\n qualities: VideoQuality[],\n selectedQuality: string,\n) {\n const selectedQualityItem = qualities.find(\n (quality) => quality.value === selectedQuality,\n );\n\n return selectedQualityItem?.src ?? src;\n}\n\nfunction getQualityOptions(qualities: VideoQuality[]): DropdownOption[] {\n return qualities.map((quality) => ({\n value: quality.value,\n label: quality.label,\n disabled: quality.disabled,\n }));\n}\n\nfunction getVideoProgressState(\n video: HTMLVideoElement,\n): VideoProgressState {\n const duration = video.duration || 0;\n const currentTime = video.currentTime || 0;\n\n return {\n currentTime,\n duration,\n\n completed:\n video.ended ||\n (\n duration > 0 &&\n currentTime >= duration * 0.9\n ),\n };\n}\n\nconst defaultQualities: VideoQuality[] = [];\n\nconst Video: React.FC<VideoProps> = ({\n src,\n mediaKey,\n cover,\n qualities = defaultQualities,\n defaultQuality = '',\n label = 'Video player',\n initialVolume = 1,\n initialMuted = false,\n initialTime = 0,\n progressCommitIntervalSec = 15,\n className = '',\n autoPlay,\n loop,\n muted,\n playsInline = true,\n preload = 'metadata',\n onQualityChange,\n onPlaybackError,\n onPlay,\n onPause,\n onEnded,\n onError,\n onLoadedMetadata,\n onTimeUpdate,\n onProgress,\n onVolumeChange,\n onProgressCommit,\n children,\n ...props\n}) => {\n const videoRef = useRef<HTMLVideoElement>(null);\n\n const [isPlaying, setIsPlaying] = useState(false);\n const [isVolumeOpen, setIsVolumeOpen] = useState(false);\n const [selectedQuality, setSelectedQuality] = useState(defaultQuality);\n const [currentTime, setCurrentTime] = useState(0);\n const [duration, setDuration] = useState(0);\n const [buffered, setBuffered] = useState(0);\n const [volume, setVolume] = useState(initialVolume);\n const [isMuted, setIsMuted] = useState(muted ?? initialMuted);\n\n const hasAppliedInitialTimeRef = useRef(false);\n const lastProgressCommitAtRef = useRef(0);\n const loadedMediaKeyRef = useRef<string | undefined>(undefined);\n const pendingRestoreTimeRef = useRef<number | null>(null);\n const onPlaybackErrorRef = useRef(onPlaybackError);\n\n const videoSrc = getVideoSource(\n src,\n qualities,\n selectedQuality,\n );\n\n const effectiveMediaKey = mediaKey ?? videoSrc;\n\n const qualityOptions = getQualityOptions(qualities);\n const hasQualityOptions = qualityOptions.length > 0;\n\n useEffect(() => {\n onPlaybackErrorRef.current = onPlaybackError;\n }, [onPlaybackError]);\n\n useEffect(() => {\n const video = videoRef.current;\n\n if (!video || !videoSrc) return;\n\n const isSameMedia =\n loadedMediaKeyRef.current !== undefined &&\n loadedMediaKeyRef.current === effectiveMediaKey;\n\n if (isSameMedia) {\n pendingRestoreTimeRef.current = video.currentTime;\n } else {\n pendingRestoreTimeRef.current = null;\n hasAppliedInitialTimeRef.current = false;\n lastProgressCommitAtRef.current = 0;\n\n setCurrentTime(0);\n setDuration(0);\n setBuffered(0);\n setIsPlaying(false);\n }\n\n loadedMediaKeyRef.current = effectiveMediaKey;\n\n video.removeAttribute('src');\n video.load();\n\n if (video.canPlayType('application/vnd.apple.mpegurl')) {\n video.src = videoSrc;\n return;\n }\n\n if (!Hls.isSupported()) {\n video.src = videoSrc;\n return;\n }\n\n const hls = new Hls();\n\n hls.on(Hls.Events.ERROR, (_, data) => {\n console.error('HLS error', data);\n\n onPlaybackErrorRef.current?.({\n type: 'hls',\n fatal: data.fatal,\n error: data,\n });\n });\n\n hls.loadSource(videoSrc);\n hls.attachMedia(video);\n\n return () => {\n hls.destroy();\n };\n }, [videoSrc, effectiveMediaKey]);\n\n const hasStarted = currentTime > 0 || isPlaying;\n const hasControls = hasStarted;\n const hasVolume = isVolumeOpen;\n\n const classes = classnames('video', className, {\n 'video--with-controls': hasControls,\n 'video--with-volume': hasVolume,\n });\n\n const syncMediaState = () => {\n const video = videoRef.current;\n\n if (!video) return;\n\n setCurrentTime(video.currentTime);\n setDuration(video.duration || 0);\n setBuffered(getBufferedEnd(video));\n setVolume(video.volume);\n setIsMuted(video.muted);\n setIsPlaying(!video.paused);\n };\n\n const commitProgress = (\n video: HTMLVideoElement,\n ) => {\n if (!onProgressCommit) {\n return;\n }\n\n onProgressCommit(\n getVideoProgressState(video),\n );\n };\n\n const handlePlayToggle = async () => {\n const video = videoRef.current;\n\n if (!video) return;\n\n console.log('play click', {\n paused: video.paused,\n readyState: video.readyState,\n src: video.currentSrc,\n networkState: video.networkState,\n error: video.error,\n });\n\n if (video.paused) {\n try {\n await video.play();\n console.log('play success');\n } catch (error) {\n console.error('play failed', error);\n }\n\n return;\n }\n\n video.pause();\n };\n\n const handleSeek = (nextValue: number) => {\n const video = videoRef.current;\n\n if (!video) return;\n\n video.currentTime = getClampedValue(\n nextValue,\n 0,\n duration,\n );\n\n setCurrentTime(video.currentTime);\n };\n\n const handleVolumeChange = (nextValue: number) => {\n const video = videoRef.current;\n\n if (!video) return;\n\n const nextVolume = getClampedValue(\n nextValue,\n 0,\n 1,\n );\n\n video.volume = nextVolume;\n video.muted = nextVolume === 0;\n\n setVolume(nextVolume);\n setIsMuted(video.muted);\n };\n\n const handleSoundToggle = () => {\n const video = videoRef.current;\n\n if (!video) return;\n\n video.muted = !video.muted;\n setIsMuted(video.muted);\n };\n\n const handleFullscreen = () => {\n const video = videoRef.current;\n\n if (!video) return;\n\n video.requestFullscreen?.();\n };\n\n const handleQualityChange = (quality: string) => {\n setSelectedQuality(quality);\n onQualityChange?.(quality);\n };\n\n const handleLoadedMetadata = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n const video = event.currentTarget;\n\n if (pendingRestoreTimeRef.current !== null) {\n video.currentTime = Math.min(\n pendingRestoreTimeRef.current,\n video.duration || pendingRestoreTimeRef.current,\n );\n\n pendingRestoreTimeRef.current = null;\n } else if (\n !hasAppliedInitialTimeRef.current &&\n initialTime > 0\n ) {\n video.currentTime = Math.min(\n initialTime,\n video.duration || initialTime,\n );\n\n hasAppliedInitialTimeRef.current = true;\n }\n\n syncMediaState();\n\n onLoadedMetadata?.(event);\n };\n\n const handleTimeUpdate = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n const video = event.currentTarget;\n\n syncMediaState();\n\n if (\n onProgressCommit &&\n (\n video.currentTime -\n lastProgressCommitAtRef.current\n ) >= progressCommitIntervalSec\n ) {\n lastProgressCommitAtRef.current =\n video.currentTime;\n\n commitProgress(video);\n }\n\n onTimeUpdate?.(event);\n };\n\n const handleProgress = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n syncMediaState();\n onProgress?.(event);\n };\n\n const handlePlay = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n setIsPlaying(true);\n onPlay?.(event);\n };\n\n const handlePause = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n setIsPlaying(false);\n\n commitProgress(event.currentTarget);\n\n onPause?.(event);\n };\n\n const handleEnded = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n syncMediaState();\n\n commitProgress(event.currentTarget);\n\n onEnded?.(event);\n };\n\n const handleNativeError = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n onPlaybackError?.({\n type: 'media',\n fatal: true,\n error: event.currentTarget.error ?? event,\n });\n\n onError?.(event);\n };\n\n const handleNativeVolumeChange = (\n event: React.SyntheticEvent<HTMLVideoElement>,\n ) => {\n syncMediaState();\n onVolumeChange?.(event);\n };\n\n return (\n <div\n className={classes}\n aria-label={label}\n >\n <div className=\"video__surface\">\n {cover && !hasStarted ? (\n <img\n className=\"video__cover\"\n src={cover}\n alt=\"\"\n aria-hidden=\"true\"\n />\n ) : null}\n\n <video\n {...props}\n ref={videoRef}\n className=\"video__media\"\n poster={cover}\n autoPlay={autoPlay}\n loop={loop}\n muted={isMuted}\n playsInline={playsInline}\n preload={preload}\n controls={false}\n onLoadedMetadata={handleLoadedMetadata}\n onTimeUpdate={handleTimeUpdate}\n onProgress={handleProgress}\n onPlay={handlePlay}\n onPause={handlePause}\n onEnded={handleEnded}\n onError={handleNativeError}\n onVolumeChange={handleNativeVolumeChange}\n >\n {children}\n </video>\n\n {!hasStarted && (\n <ButtonIcon\n className=\"video__main-action\"\n icon=\"play\"\n label=\"Play video\"\n onClick={handlePlayToggle}\n />\n )}\n </div>\n\n {hasControls && (\n <div className=\"video__controls\">\n <ButtonIcon\n icon={isPlaying ? 'pause' : 'play'}\n label={isPlaying ? 'Pause video' : 'Play video'}\n onClick={handlePlayToggle}\n />\n\n <div className=\"video__time\">\n {formatTime(currentTime)}\n </div>\n\n <ProgressBar\n className=\"video__progress\"\n mode=\"player\"\n value={currentTime}\n buffered={buffered}\n max={duration || 1}\n step={1}\n label=\"Video progress\"\n onValueChange={handleSeek}\n />\n\n <ButtonIcon\n icon=\"fullscreen\"\n label=\"Fullscreen\"\n onClick={handleFullscreen}\n />\n\n <div\n className=\"video__sound-control\"\n onMouseLeave={() => setIsVolumeOpen(false)}\n >\n <ButtonIcon\n icon={isMuted ? 'sound-off' : 'sound'}\n label={isMuted ? 'Unmute video' : 'Mute video'}\n onClick={handleSoundToggle}\n onMouseEnter={() => setIsVolumeOpen(true)}\n onFocus={() => setIsVolumeOpen(true)}\n />\n\n {hasVolume && (\n <div className=\"video__volume\">\n <ProgressBar\n mode=\"level\"\n value={isMuted ? 0 : volume}\n max={1}\n step={0.05}\n label=\"Volume\"\n onValueChange={handleVolumeChange}\n />\n </div>\n )}\n </div>\n\n {hasQualityOptions && (\n <Dropdown\n align=\"top-end\"\n value={selectedQuality}\n options={qualityOptions}\n label=\"Video quality\"\n onChange={handleQualityChange}\n trigger={\n <ButtonIcon\n icon=\"settings\"\n label=\"Video quality\"\n />\n }\n />\n )}\n </div>\n )}\n </div>\n );\n};\n\nexport default Video;"]}
|
package/package.json
CHANGED
package/styles.css
CHANGED
|
@@ -31,8 +31,9 @@
|
|
|
31
31
|
/* color */
|
|
32
32
|
--attachment-color: var(--color-text);
|
|
33
33
|
--attachment-item-bg: var(--color-control);
|
|
34
|
+
--attachment-item-color: var(--color-text-control);
|
|
34
35
|
--attachment-border-color: var(--color-line);
|
|
35
|
-
--attachment-meta-color: var(--color-text-note);
|
|
36
|
+
--attachment-item-meta-color: var(--color-text-note);
|
|
36
37
|
|
|
37
38
|
color: var(--attachment-color);
|
|
38
39
|
|
|
@@ -66,6 +67,10 @@
|
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
.attach__list {
|
|
70
|
+
/* color */
|
|
71
|
+
|
|
72
|
+
/* typography */
|
|
73
|
+
|
|
69
74
|
/* size */
|
|
70
75
|
display: flex;
|
|
71
76
|
flex-direction: column;
|
|
@@ -75,11 +80,18 @@
|
|
|
75
80
|
|
|
76
81
|
/* border */
|
|
77
82
|
list-style: none;
|
|
83
|
+
|
|
84
|
+
/* depth */
|
|
85
|
+
|
|
86
|
+
/* motion */
|
|
78
87
|
}
|
|
79
88
|
|
|
80
89
|
.attach__item {
|
|
81
90
|
/* color */
|
|
82
91
|
background: var(--attachment-item-bg);
|
|
92
|
+
color: var(--attachment-item-color);
|
|
93
|
+
|
|
94
|
+
/* typography */
|
|
83
95
|
|
|
84
96
|
/* size */
|
|
85
97
|
display: flex;
|
|
@@ -118,11 +130,25 @@
|
|
|
118
130
|
}
|
|
119
131
|
|
|
120
132
|
.attach__link:hover .attach__name {
|
|
133
|
+
/* color */
|
|
134
|
+
|
|
121
135
|
/* typography */
|
|
122
136
|
text-decoration: underline;
|
|
137
|
+
|
|
138
|
+
/* size */
|
|
139
|
+
|
|
140
|
+
/* border */
|
|
141
|
+
|
|
142
|
+
/* depth */
|
|
143
|
+
|
|
144
|
+
/* motion */
|
|
123
145
|
}
|
|
124
146
|
|
|
125
147
|
.attach__preview {
|
|
148
|
+
/* color */
|
|
149
|
+
|
|
150
|
+
/* typography */
|
|
151
|
+
|
|
126
152
|
/* size */
|
|
127
153
|
display: block;
|
|
128
154
|
max-width: var(--attachment-preview-max-width);
|
|
@@ -138,6 +164,10 @@
|
|
|
138
164
|
}
|
|
139
165
|
|
|
140
166
|
.attach__file {
|
|
167
|
+
/* color */
|
|
168
|
+
|
|
169
|
+
/* typography */
|
|
170
|
+
|
|
141
171
|
/* size */
|
|
142
172
|
display: flex;
|
|
143
173
|
flex-direction: column;
|
|
@@ -151,6 +181,8 @@
|
|
|
151
181
|
}
|
|
152
182
|
|
|
153
183
|
.attach__name {
|
|
184
|
+
/* color */
|
|
185
|
+
|
|
154
186
|
/* typography */
|
|
155
187
|
text-overflow: ellipsis;
|
|
156
188
|
white-space: nowrap;
|
|
@@ -167,7 +199,7 @@
|
|
|
167
199
|
|
|
168
200
|
.attach__meta {
|
|
169
201
|
/* color */
|
|
170
|
-
color: var(--attachment-meta-color);
|
|
202
|
+
color: var(--attachment-item-meta-color);
|
|
171
203
|
|
|
172
204
|
/* typography */
|
|
173
205
|
font-size: 0.85em;
|
|
@@ -203,8 +235,18 @@
|
|
|
203
235
|
}
|
|
204
236
|
|
|
205
237
|
.attach__remove:hover {
|
|
238
|
+
/* color */
|
|
239
|
+
|
|
206
240
|
/* typography */
|
|
207
241
|
text-decoration: underline;
|
|
242
|
+
|
|
243
|
+
/* size */
|
|
244
|
+
|
|
245
|
+
/* border */
|
|
246
|
+
|
|
247
|
+
/* depth */
|
|
248
|
+
|
|
249
|
+
/* motion */
|
|
208
250
|
}
|
|
209
251
|
|
|
210
252
|
/* src/components/avatar/avatar.css */
|