jvetrau-ds 0.1.33 → 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.
@@ -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((quality) => quality.value === selectedQuality);
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 videoSrc = getVideoSource(src, qualities, selectedQuality);
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(nextValue, 0, duration);
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(nextValue, 0, 1);
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 (!hasAppliedInitialTimeRef.current && initialTime > 0) {
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("div", { className: classes, "aria-label": label, children: [
240
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "video__surface", children: [
241
- cover && !hasStarted ? /* @__PURE__ */ jsxRuntime.jsx(
242
- "img",
243
- {
244
- className: "video__cover",
245
- src: cover,
246
- alt: "",
247
- "aria-hidden": "true"
248
- }
249
- ) : null,
250
- /* @__PURE__ */ jsxRuntime.jsx(
251
- "video",
252
- {
253
- ...props,
254
- ref: videoRef,
255
- className: "video__media",
256
- poster: cover,
257
- autoPlay,
258
- loop,
259
- muted: isMuted,
260
- playsInline,
261
- preload,
262
- controls: false,
263
- onLoadedMetadata: handleLoadedMetadata,
264
- onTimeUpdate: handleTimeUpdate,
265
- onProgress: handleProgress,
266
- onPlay: handlePlay,
267
- onPause: handlePause,
268
- onEnded: handleEnded,
269
- onVolumeChange: handleNativeVolumeChange,
270
- children
271
- }
272
- ),
273
- !hasStarted && /* @__PURE__ */ jsxRuntime.jsx(
274
- ButtonIcon__default.default,
275
- {
276
- className: "video__main-action",
277
- icon: "play",
278
- label: "Play video",
279
- onClick: handlePlayToggle
280
- }
281
- )
282
- ] }),
283
- hasControls && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "video__controls", children: [
284
- /* @__PURE__ */ jsxRuntime.jsx(
285
- ButtonIcon__default.default,
286
- {
287
- icon: isPlaying ? "pause" : "play",
288
- label: isPlaying ? "Pause video" : "Play video",
289
- onClick: handlePlayToggle
290
- }
291
- ),
292
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "video__time", children: formatTime(currentTime) }),
293
- /* @__PURE__ */ jsxRuntime.jsx(
294
- ProgressBar__default.default,
295
- {
296
- className: "video__progress",
297
- mode: "player",
298
- value: currentTime,
299
- buffered,
300
- max: duration || 1,
301
- step: 1,
302
- label: "Video progress",
303
- onValueChange: handleSeek
304
- }
305
- ),
306
- /* @__PURE__ */ jsxRuntime.jsx(ButtonIcon__default.default, { icon: "fullscreen", label: "Fullscreen", onClick: handleFullscreen }),
307
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "video__sound-control", onMouseLeave: () => setIsVolumeOpen(false), children: [
308
- /* @__PURE__ */ jsxRuntime.jsx(
309
- ButtonIcon__default.default,
310
- {
311
- icon: isMuted ? "sound-off" : "sound",
312
- label: isMuted ? "Unmute video" : "Mute video",
313
- onClick: handleSoundToggle,
314
- onMouseEnter: () => setIsVolumeOpen(true),
315
- onFocus: () => setIsVolumeOpen(true)
316
- }
317
- ),
318
- hasVolume && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "video__volume", children: /* @__PURE__ */ jsxRuntime.jsx(
319
- ProgressBar__default.default,
320
- {
321
- mode: "level",
322
- value: isMuted ? 0 : volume,
323
- max: 1,
324
- step: 0.05,
325
- label: "Volume",
326
- onValueChange: handleVolumeChange
327
- }
328
- ) })
329
- ] }),
330
- hasQualityOptions && /* @__PURE__ */ jsxRuntime.jsx(
331
- Dropdown__default.default,
332
- {
333
- align: "top-end",
334
- value: selectedQuality,
335
- options: qualityOptions,
336
- label: "Video quality",
337
- onChange: handleQualityChange,
338
- trigger: /* @__PURE__ */ jsxRuntime.jsx(ButtonIcon__default.default, { icon: "settings", label: "Video quality" })
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((quality) => quality.value === selectedQuality);
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 videoSrc = getVideoSource(src, qualities, selectedQuality);
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(nextValue, 0, duration);
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(nextValue, 0, 1);
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 (!hasAppliedInitialTimeRef.current && initialTime > 0) {
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("div", { className: classes, "aria-label": label, children: [
231
- /* @__PURE__ */ jsxs("div", { className: "video__surface", children: [
232
- cover && !hasStarted ? /* @__PURE__ */ jsx(
233
- "img",
234
- {
235
- className: "video__cover",
236
- src: cover,
237
- alt: "",
238
- "aria-hidden": "true"
239
- }
240
- ) : null,
241
- /* @__PURE__ */ jsx(
242
- "video",
243
- {
244
- ...props,
245
- ref: videoRef,
246
- className: "video__media",
247
- poster: cover,
248
- autoPlay,
249
- loop,
250
- muted: isMuted,
251
- playsInline,
252
- preload,
253
- controls: false,
254
- onLoadedMetadata: handleLoadedMetadata,
255
- onTimeUpdate: handleTimeUpdate,
256
- onProgress: handleProgress,
257
- onPlay: handlePlay,
258
- onPause: handlePause,
259
- onEnded: handleEnded,
260
- onVolumeChange: handleNativeVolumeChange,
261
- children
262
- }
263
- ),
264
- !hasStarted && /* @__PURE__ */ jsx(
265
- ButtonIcon,
266
- {
267
- className: "video__main-action",
268
- icon: "play",
269
- label: "Play video",
270
- onClick: handlePlayToggle
271
- }
272
- )
273
- ] }),
274
- hasControls && /* @__PURE__ */ jsxs("div", { className: "video__controls", children: [
275
- /* @__PURE__ */ jsx(
276
- ButtonIcon,
277
- {
278
- icon: isPlaying ? "pause" : "play",
279
- label: isPlaying ? "Pause video" : "Play video",
280
- onClick: handlePlayToggle
281
- }
282
- ),
283
- /* @__PURE__ */ jsx("div", { className: "video__time", children: formatTime(currentTime) }),
284
- /* @__PURE__ */ jsx(
285
- ProgressBar,
286
- {
287
- className: "video__progress",
288
- mode: "player",
289
- value: currentTime,
290
- buffered,
291
- max: duration || 1,
292
- step: 1,
293
- label: "Video progress",
294
- onValueChange: handleSeek
295
- }
296
- ),
297
- /* @__PURE__ */ jsx(ButtonIcon, { icon: "fullscreen", label: "Fullscreen", onClick: handleFullscreen }),
298
- /* @__PURE__ */ jsxs("div", { className: "video__sound-control", onMouseLeave: () => setIsVolumeOpen(false), children: [
299
- /* @__PURE__ */ jsx(
300
- ButtonIcon,
301
- {
302
- icon: isMuted ? "sound-off" : "sound",
303
- label: isMuted ? "Unmute video" : "Mute video",
304
- onClick: handleSoundToggle,
305
- onMouseEnter: () => setIsVolumeOpen(true),
306
- onFocus: () => setIsVolumeOpen(true)
307
- }
308
- ),
309
- hasVolume && /* @__PURE__ */ jsx("div", { className: "video__volume", children: /* @__PURE__ */ jsx(
310
- ProgressBar,
311
- {
312
- mode: "level",
313
- value: isMuted ? 0 : volume,
314
- max: 1,
315
- step: 0.05,
316
- label: "Volume",
317
- onValueChange: handleVolumeChange
318
- }
319
- ) })
320
- ] }),
321
- hasQualityOptions && /* @__PURE__ */ jsx(
322
- Dropdown,
323
- {
324
- align: "top-end",
325
- value: selectedQuality,
326
- options: qualityOptions,
327
- label: "Video quality",
328
- onChange: handleQualityChange,
329
- trigger: /* @__PURE__ */ jsx(ButtonIcon, { icon: "settings", label: "Video quality" })
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jvetrau-ds",
3
- "version": "0.1.33",
3
+ "version": "0.1.34",
4
4
  "description": "Multi-brand React design system with semantic tokens",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",