@geekapps/silo-elements-nextjs 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/FileUploader.d.ts +7 -3
- package/dist/FileUploader.js +279 -38
- package/dist/FileUploader.js.map +1 -1
- package/dist/ImageUploader.d.ts +7 -3
- package/dist/ImageUploader.js +329 -66
- package/dist/ImageUploader.js.map +1 -1
- package/dist/MediaUploader.d.ts +7 -3
- package/dist/MediaUploader.js +654 -43
- package/dist/MediaUploader.js.map +1 -1
- package/dist/VideoPlayer.d.ts +20 -18
- package/dist/VideoPlayer.js +980 -675
- package/dist/VideoPlayer.js.map +1 -1
- package/dist/VideoUploader.d.ts +7 -3
- package/dist/VideoUploader.js +295 -43
- package/dist/VideoUploader.js.map +1 -1
- package/dist/components/DropZone.d.ts +8 -5
- package/dist/components/DropZone.js +134 -49
- package/dist/components/DropZone.js.map +1 -1
- package/dist/components/ProgressBar.d.ts +6 -4
- package/dist/components/ProgressBar.js +35 -15
- package/dist/components/ProgressBar.js.map +1 -1
- package/dist/index.d.ts +12 -12
- package/dist/index.js +1696 -10
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +12 -10
- package/dist/types.js +2 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/format.d.ts +4 -3
- package/dist/utils/format.js +19 -26
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/theme.d.ts +8 -5
- package/dist/utils/theme.js +34 -30
- package/dist/utils/theme.js.map +1 -1
- package/package.json +4 -3
- package/dist/FileUploader.d.ts.map +0 -1
- package/dist/ImageUploader.d.ts.map +0 -1
- package/dist/MediaUploader.d.ts.map +0 -1
- package/dist/VideoPlayer.d.ts.map +0 -1
- package/dist/VideoUploader.d.ts.map +0 -1
- package/dist/components/DropZone.d.ts.map +0 -1
- package/dist/components/ProgressBar.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/utils/format.d.ts.map +0 -1
- package/dist/utils/theme.d.ts.map +0 -1
package/dist/VideoPlayer.js
CHANGED
|
@@ -1,738 +1,1043 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import React, { useMemo, useRef, useState, useCallback, useEffect } from 'react';
|
|
2
|
+
import gsap from 'gsap';
|
|
3
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
var AUTO_QUALITY = {
|
|
6
|
+
id: "auto",
|
|
7
|
+
label: "Auto",
|
|
8
|
+
type: "auto"
|
|
9
9
|
};
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
function Sources(_props) {
|
|
11
|
+
return null;
|
|
12
12
|
}
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
function Source(_props) {
|
|
14
|
+
return null;
|
|
15
15
|
}
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
function Subtitles(_props) {
|
|
17
|
+
return null;
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
function Subtitle(_props) {
|
|
20
|
+
return null;
|
|
21
21
|
}
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
function Storyboard(_props) {
|
|
23
|
+
return null;
|
|
24
24
|
}
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
function StoryboardFrame(_props) {
|
|
26
|
+
return null;
|
|
27
27
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
28
|
+
function Video({
|
|
29
|
+
title,
|
|
30
|
+
description,
|
|
31
|
+
poster,
|
|
32
|
+
children,
|
|
33
|
+
className,
|
|
34
|
+
autoHideControls = true,
|
|
35
|
+
defaultVolume = 0.72
|
|
36
|
+
}) {
|
|
37
|
+
const parsed = useMemo(() => parseVideoChildren(children), [children]);
|
|
38
|
+
const initialSourceIndex = useMemo(() => {
|
|
39
|
+
const index = parsed.sources.findIndex((source) => source.default);
|
|
40
|
+
return index >= 0 ? index : 0;
|
|
41
|
+
}, [parsed.sources]);
|
|
42
|
+
const initialSubtitleMode = useMemo(() => {
|
|
43
|
+
const track = parsed.subtitles.find((subtitle) => subtitle.default);
|
|
44
|
+
return track?.srclang ?? "off";
|
|
45
|
+
}, [parsed.subtitles]);
|
|
46
|
+
const containerRef = useRef(null);
|
|
47
|
+
const chromeRef = useRef(null);
|
|
48
|
+
const playerRef = useRef(null);
|
|
49
|
+
const videoRef = useRef(null);
|
|
50
|
+
const progressRef = useRef(null);
|
|
51
|
+
const hlsRef = useRef(null);
|
|
52
|
+
const dashRef = useRef(null);
|
|
53
|
+
const hideTimerRef = useRef(null);
|
|
54
|
+
const [sourceIndex, setSourceIndex] = useState(initialSourceIndex);
|
|
55
|
+
const [qualities, setQualities] = useState([AUTO_QUALITY]);
|
|
56
|
+
const [selectedQuality, setSelectedQuality] = useState("auto");
|
|
57
|
+
const [audioTracks, setAudioTracks] = useState([]);
|
|
58
|
+
const [selectedAudio, setSelectedAudio] = useState(0);
|
|
59
|
+
const [openMenu, setOpenMenu] = useState(null);
|
|
60
|
+
const [subtitleMode, setSubtitleMode] = useState(initialSubtitleMode);
|
|
61
|
+
const [storyboardCues, setStoryboardCues] = useState(
|
|
62
|
+
[]
|
|
63
|
+
);
|
|
64
|
+
const [preview, setPreview] = useState(null);
|
|
65
|
+
const [duration, setDuration] = useState(0);
|
|
66
|
+
const [currentTime, setCurrentTime] = useState(0);
|
|
67
|
+
const [bufferedTime, setBufferedTime] = useState(0);
|
|
68
|
+
const [isPlaying, setIsPlaying] = useState(false);
|
|
69
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
70
|
+
const [controlsVisible, setControlsVisible] = useState(true);
|
|
71
|
+
const [volume, setVolume] = useState(defaultVolume);
|
|
72
|
+
const [isMuted, setIsMuted] = useState(false);
|
|
73
|
+
const [isFullscreen, setIsFullscreen] = useState(false);
|
|
74
|
+
const [error, setError] = useState(null);
|
|
75
|
+
const activeSource = parsed.sources[sourceIndex] ?? parsed.sources[0] ?? null;
|
|
76
|
+
const progressPercent = duration ? currentTime / duration * 100 : 0;
|
|
77
|
+
const bufferedPercent = duration ? bufferedTime / duration * 100 : 0;
|
|
78
|
+
const destroyMediaEngines = useCallback(() => {
|
|
79
|
+
if (hlsRef.current) {
|
|
80
|
+
hlsRef.current.destroy();
|
|
81
|
+
hlsRef.current = null;
|
|
82
|
+
}
|
|
83
|
+
if (dashRef.current) {
|
|
84
|
+
dashRef.current.reset();
|
|
85
|
+
dashRef.current = null;
|
|
86
|
+
}
|
|
87
|
+
}, []);
|
|
88
|
+
const applySubtitleMode = useCallback((mode) => {
|
|
89
|
+
const video = videoRef.current;
|
|
90
|
+
if (!video) return;
|
|
91
|
+
Array.from(video.textTracks).forEach((track) => {
|
|
92
|
+
track.mode = mode !== "off" && track.language === mode ? "showing" : "disabled";
|
|
93
|
+
});
|
|
94
|
+
}, []);
|
|
95
|
+
const showControlsTemporarily = useCallback(() => {
|
|
96
|
+
setControlsVisible(true);
|
|
97
|
+
if (hideTimerRef.current) {
|
|
98
|
+
window.clearTimeout(hideTimerRef.current);
|
|
99
|
+
}
|
|
100
|
+
const video = videoRef.current;
|
|
101
|
+
if (autoHideControls && video && !video.paused) {
|
|
102
|
+
hideTimerRef.current = window.setTimeout(() => {
|
|
103
|
+
setControlsVisible(false);
|
|
104
|
+
}, 2400);
|
|
105
|
+
}
|
|
106
|
+
}, [autoHideControls]);
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (!containerRef.current) return;
|
|
109
|
+
gsap.fromTo(
|
|
110
|
+
containerRef.current,
|
|
111
|
+
{
|
|
112
|
+
opacity: 0,
|
|
113
|
+
y: 36,
|
|
114
|
+
scale: 0.985,
|
|
115
|
+
filter: "blur(10px)"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
opacity: 1,
|
|
119
|
+
y: 0,
|
|
120
|
+
scale: 1,
|
|
121
|
+
filter: "blur(0px)",
|
|
122
|
+
duration: 0.85,
|
|
123
|
+
ease: "power3.out"
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
}, []);
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
if (!chromeRef.current) return;
|
|
129
|
+
gsap.to(chromeRef.current, {
|
|
130
|
+
opacity: controlsVisible ? 1 : 0,
|
|
131
|
+
y: controlsVisible ? 0 : 10,
|
|
132
|
+
duration: 0.22,
|
|
133
|
+
ease: "power2.out"
|
|
134
|
+
});
|
|
135
|
+
}, [controlsVisible]);
|
|
136
|
+
useEffect(() => {
|
|
137
|
+
if (sourceIndex >= parsed.sources.length) {
|
|
138
|
+
setSourceIndex(initialSourceIndex);
|
|
139
|
+
}
|
|
140
|
+
}, [sourceIndex, parsed.sources.length, initialSourceIndex]);
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
if (subtitleMode !== "off" && !parsed.subtitles.some((subtitle) => subtitle.srclang === subtitleMode)) {
|
|
143
|
+
setSubtitleMode(initialSubtitleMode);
|
|
144
|
+
}
|
|
145
|
+
}, [subtitleMode, parsed.subtitles, initialSubtitleMode]);
|
|
146
|
+
useEffect(() => {
|
|
147
|
+
const video = videoRef.current;
|
|
148
|
+
if (!video) return;
|
|
149
|
+
const syncTime = () => {
|
|
150
|
+
setCurrentTime(video.currentTime || 0);
|
|
151
|
+
if (video.buffered.length > 0) {
|
|
152
|
+
setBufferedTime(video.buffered.end(video.buffered.length - 1));
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
const syncDuration = () => {
|
|
156
|
+
setDuration(Number.isFinite(video.duration) ? video.duration : 0);
|
|
157
|
+
applySubtitleMode(subtitleMode);
|
|
158
|
+
};
|
|
159
|
+
const onPlay = () => {
|
|
160
|
+
setIsPlaying(true);
|
|
161
|
+
showControlsTemporarily();
|
|
162
|
+
};
|
|
163
|
+
const onPause = () => {
|
|
164
|
+
setIsPlaying(false);
|
|
165
|
+
setControlsVisible(true);
|
|
166
|
+
};
|
|
167
|
+
const onWaiting = () => setIsLoading(true);
|
|
168
|
+
const onCanPlay = () => setIsLoading(false);
|
|
169
|
+
const onEnded = () => setControlsVisible(true);
|
|
170
|
+
video.addEventListener("timeupdate", syncTime);
|
|
171
|
+
video.addEventListener("progress", syncTime);
|
|
172
|
+
video.addEventListener("loadedmetadata", syncDuration);
|
|
173
|
+
video.addEventListener("durationchange", syncDuration);
|
|
174
|
+
video.addEventListener("play", onPlay);
|
|
175
|
+
video.addEventListener("pause", onPause);
|
|
176
|
+
video.addEventListener("waiting", onWaiting);
|
|
177
|
+
video.addEventListener("canplay", onCanPlay);
|
|
178
|
+
video.addEventListener("ended", onEnded);
|
|
179
|
+
return () => {
|
|
180
|
+
video.removeEventListener("timeupdate", syncTime);
|
|
181
|
+
video.removeEventListener("progress", syncTime);
|
|
182
|
+
video.removeEventListener("loadedmetadata", syncDuration);
|
|
183
|
+
video.removeEventListener("durationchange", syncDuration);
|
|
184
|
+
video.removeEventListener("play", onPlay);
|
|
185
|
+
video.removeEventListener("pause", onPause);
|
|
186
|
+
video.removeEventListener("waiting", onWaiting);
|
|
187
|
+
video.removeEventListener("canplay", onCanPlay);
|
|
188
|
+
video.removeEventListener("ended", onEnded);
|
|
189
|
+
};
|
|
190
|
+
}, [applySubtitleMode, subtitleMode, showControlsTemporarily]);
|
|
191
|
+
useEffect(() => {
|
|
192
|
+
const video = videoRef.current;
|
|
193
|
+
if (!video) return;
|
|
194
|
+
video.volume = volume;
|
|
195
|
+
video.muted = isMuted || volume === 0;
|
|
196
|
+
}, [volume, isMuted]);
|
|
197
|
+
useEffect(() => {
|
|
198
|
+
applySubtitleMode(subtitleMode);
|
|
199
|
+
}, [subtitleMode, applySubtitleMode]);
|
|
200
|
+
useEffect(() => {
|
|
201
|
+
const onFullscreenChange = () => {
|
|
202
|
+
setIsFullscreen(Boolean(document.fullscreenElement));
|
|
203
|
+
};
|
|
204
|
+
document.addEventListener("fullscreenchange", onFullscreenChange);
|
|
205
|
+
return () => {
|
|
206
|
+
document.removeEventListener("fullscreenchange", onFullscreenChange);
|
|
207
|
+
};
|
|
208
|
+
}, []);
|
|
209
|
+
useEffect(() => {
|
|
210
|
+
let cancelled = false;
|
|
211
|
+
async function loadStoryboard() {
|
|
212
|
+
if (!parsed.storyboard) {
|
|
213
|
+
setStoryboardCues([]);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (parsed.storyboard.frames.length > 0) {
|
|
217
|
+
setStoryboardCues(parsed.storyboard.frames);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (!parsed.storyboard.src) {
|
|
221
|
+
setStoryboardCues([]);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
try {
|
|
225
|
+
const response = await fetch(parsed.storyboard.src);
|
|
226
|
+
if (!response.ok) {
|
|
227
|
+
throw new Error("Storyboard not found");
|
|
129
228
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
229
|
+
const text = await response.text();
|
|
230
|
+
const cues = parseStoryboardVtt(
|
|
231
|
+
text,
|
|
232
|
+
new URL(parsed.storyboard.src, window.location.href).href
|
|
233
|
+
);
|
|
234
|
+
if (!cancelled) {
|
|
235
|
+
setStoryboardCues(cues);
|
|
135
236
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
const syncDuration = () => {
|
|
148
|
-
setDuration(Number.isFinite(video.duration) ? video.duration : 0);
|
|
149
|
-
applySubtitleMode(subtitleMode);
|
|
150
|
-
};
|
|
151
|
-
const onPlay = () => {
|
|
152
|
-
setIsPlaying(true);
|
|
153
|
-
showControlsTemporarily();
|
|
154
|
-
};
|
|
155
|
-
const onPause = () => {
|
|
156
|
-
setIsPlaying(false);
|
|
157
|
-
setControlsVisible(true);
|
|
158
|
-
};
|
|
159
|
-
const onWaiting = () => setIsLoading(true);
|
|
160
|
-
const onCanPlay = () => setIsLoading(false);
|
|
161
|
-
const onEnded = () => setControlsVisible(true);
|
|
162
|
-
video.addEventListener("timeupdate", syncTime);
|
|
163
|
-
video.addEventListener("progress", syncTime);
|
|
164
|
-
video.addEventListener("loadedmetadata", syncDuration);
|
|
165
|
-
video.addEventListener("durationchange", syncDuration);
|
|
166
|
-
video.addEventListener("play", onPlay);
|
|
167
|
-
video.addEventListener("pause", onPause);
|
|
168
|
-
video.addEventListener("waiting", onWaiting);
|
|
169
|
-
video.addEventListener("canplay", onCanPlay);
|
|
170
|
-
video.addEventListener("ended", onEnded);
|
|
171
|
-
return () => {
|
|
172
|
-
video.removeEventListener("timeupdate", syncTime);
|
|
173
|
-
video.removeEventListener("progress", syncTime);
|
|
174
|
-
video.removeEventListener("loadedmetadata", syncDuration);
|
|
175
|
-
video.removeEventListener("durationchange", syncDuration);
|
|
176
|
-
video.removeEventListener("play", onPlay);
|
|
177
|
-
video.removeEventListener("pause", onPause);
|
|
178
|
-
video.removeEventListener("waiting", onWaiting);
|
|
179
|
-
video.removeEventListener("canplay", onCanPlay);
|
|
180
|
-
video.removeEventListener("ended", onEnded);
|
|
181
|
-
};
|
|
182
|
-
}, [applySubtitleMode, subtitleMode, showControlsTemporarily]);
|
|
183
|
-
useEffect(() => {
|
|
184
|
-
const video = videoRef.current;
|
|
185
|
-
if (!video)
|
|
186
|
-
return;
|
|
187
|
-
video.volume = volume;
|
|
188
|
-
video.muted = isMuted || volume === 0;
|
|
189
|
-
}, [volume, isMuted]);
|
|
190
|
-
useEffect(() => {
|
|
191
|
-
applySubtitleMode(subtitleMode);
|
|
192
|
-
}, [subtitleMode, applySubtitleMode]);
|
|
193
|
-
useEffect(() => {
|
|
194
|
-
const onFullscreenChange = () => {
|
|
195
|
-
setIsFullscreen(Boolean(document.fullscreenElement));
|
|
196
|
-
};
|
|
197
|
-
document.addEventListener("fullscreenchange", onFullscreenChange);
|
|
198
|
-
return () => {
|
|
199
|
-
document.removeEventListener("fullscreenchange", onFullscreenChange);
|
|
200
|
-
};
|
|
201
|
-
}, []);
|
|
202
|
-
useEffect(() => {
|
|
203
|
-
let cancelled = false;
|
|
204
|
-
async function loadStoryboard() {
|
|
205
|
-
if (!parsed.storyboard) {
|
|
206
|
-
setStoryboardCues([]);
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
if (parsed.storyboard.frames.length > 0) {
|
|
210
|
-
setStoryboardCues(parsed.storyboard.frames);
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
if (!parsed.storyboard.src) {
|
|
214
|
-
setStoryboardCues([]);
|
|
215
|
-
return;
|
|
237
|
+
} catch {
|
|
238
|
+
if (!cancelled && parsed.storyboard.fallbackImage) {
|
|
239
|
+
setStoryboardCues([
|
|
240
|
+
{
|
|
241
|
+
start: 0,
|
|
242
|
+
end: Number.MAX_SAFE_INTEGER,
|
|
243
|
+
image: parsed.storyboard.fallbackImage,
|
|
244
|
+
w: parsed.storyboard.width ?? 160,
|
|
245
|
+
h: parsed.storyboard.height ?? 90
|
|
216
246
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
247
|
+
]);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
loadStoryboard();
|
|
252
|
+
return () => {
|
|
253
|
+
cancelled = true;
|
|
254
|
+
};
|
|
255
|
+
}, [parsed.storyboard]);
|
|
256
|
+
useEffect(() => {
|
|
257
|
+
const video = videoRef.current;
|
|
258
|
+
let cancelled = false;
|
|
259
|
+
if (!video || !activeSource) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
destroyMediaEngines();
|
|
263
|
+
setError(null);
|
|
264
|
+
setIsLoading(true);
|
|
265
|
+
setCurrentTime(0);
|
|
266
|
+
setBufferedTime(0);
|
|
267
|
+
setDuration(0);
|
|
268
|
+
setSelectedQuality("auto");
|
|
269
|
+
setQualities([AUTO_QUALITY]);
|
|
270
|
+
setAudioTracks([]);
|
|
271
|
+
setSelectedAudio(0);
|
|
272
|
+
setOpenMenu(null);
|
|
273
|
+
video.pause();
|
|
274
|
+
video.removeAttribute("src");
|
|
275
|
+
video.load();
|
|
276
|
+
const sourceType = inferSourceType(activeSource);
|
|
277
|
+
if (sourceType === "dash") {
|
|
278
|
+
void (async () => {
|
|
279
|
+
try {
|
|
280
|
+
const dashModule = await import('dashjs');
|
|
281
|
+
if (cancelled) return;
|
|
282
|
+
const dashjs = dashModule;
|
|
283
|
+
const dash = dashjs.MediaPlayer().create();
|
|
284
|
+
dashRef.current = dash;
|
|
285
|
+
dash.updateSettings({
|
|
286
|
+
streaming: {
|
|
287
|
+
abr: {
|
|
288
|
+
autoSwitchBitrate: {
|
|
289
|
+
video: true
|
|
239
290
|
}
|
|
291
|
+
}
|
|
240
292
|
}
|
|
293
|
+
});
|
|
294
|
+
dash.on(dashjs.MediaPlayer.events.STREAM_INITIALIZED, () => {
|
|
295
|
+
const bitrates = dash.getBitrateInfoListFor("video") ?? [];
|
|
296
|
+
setQualities([
|
|
297
|
+
AUTO_QUALITY,
|
|
298
|
+
...bitrates.map((item, index) => ({
|
|
299
|
+
id: `dash-${index}`,
|
|
300
|
+
label: item.height ? `${item.height}p` : `${Math.round((item.bitrate ?? 0) / 1e3)} kbps`,
|
|
301
|
+
type: "dash",
|
|
302
|
+
index
|
|
303
|
+
}))
|
|
304
|
+
]);
|
|
305
|
+
setIsLoading(false);
|
|
306
|
+
});
|
|
307
|
+
dash.on(dashjs.MediaPlayer.events.ERROR, () => {
|
|
308
|
+
setError("N\xE3o foi poss\xEDvel reproduzir o stream MPEG-DASH.");
|
|
309
|
+
setIsLoading(false);
|
|
310
|
+
});
|
|
311
|
+
dash.initialize(video, activeSource.src, false);
|
|
312
|
+
} catch {
|
|
313
|
+
if (!cancelled) {
|
|
314
|
+
setError("N\xE3o foi poss\xEDvel carregar o player MPEG-DASH.");
|
|
315
|
+
setIsLoading(false);
|
|
316
|
+
}
|
|
241
317
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
};
|
|
246
|
-
}, [parsed.storyboard]);
|
|
247
|
-
useEffect(() => {
|
|
248
|
-
const video = videoRef.current;
|
|
249
|
-
let cancelled = false;
|
|
250
|
-
if (!video || !activeSource) {
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
318
|
+
})();
|
|
319
|
+
return () => {
|
|
320
|
+
cancelled = true;
|
|
253
321
|
destroyMediaEngines();
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
setDuration(0);
|
|
259
|
-
setSelectedQuality("auto");
|
|
260
|
-
setQualities([AUTO_QUALITY]);
|
|
261
|
-
setAudioTracks([]);
|
|
262
|
-
setSelectedAudio(0);
|
|
263
|
-
setOpenMenu(null);
|
|
264
|
-
video.pause();
|
|
265
|
-
video.removeAttribute("src");
|
|
266
|
-
video.load();
|
|
267
|
-
const sourceType = inferSourceType(activeSource);
|
|
268
|
-
if (sourceType === "dash") {
|
|
269
|
-
void (async () => {
|
|
270
|
-
try {
|
|
271
|
-
const dashModule = await import("dashjs");
|
|
272
|
-
if (cancelled)
|
|
273
|
-
return;
|
|
274
|
-
const dashjs = dashModule;
|
|
275
|
-
const dash = dashjs.MediaPlayer().create();
|
|
276
|
-
dashRef.current = dash;
|
|
277
|
-
dash.updateSettings({
|
|
278
|
-
streaming: {
|
|
279
|
-
abr: {
|
|
280
|
-
autoSwitchBitrate: {
|
|
281
|
-
video: true,
|
|
282
|
-
},
|
|
283
|
-
},
|
|
284
|
-
},
|
|
285
|
-
});
|
|
286
|
-
dash.on(dashjs.MediaPlayer.events.STREAM_INITIALIZED, () => {
|
|
287
|
-
const bitrates = dash.getBitrateInfoListFor("video") ?? [];
|
|
288
|
-
setQualities([
|
|
289
|
-
AUTO_QUALITY,
|
|
290
|
-
...bitrates.map((item, index) => ({
|
|
291
|
-
id: `dash-${index}`,
|
|
292
|
-
label: item.height
|
|
293
|
-
? `${item.height}p`
|
|
294
|
-
: `${Math.round((item.bitrate ?? 0) / 1000)} kbps`,
|
|
295
|
-
type: "dash",
|
|
296
|
-
index,
|
|
297
|
-
})),
|
|
298
|
-
]);
|
|
299
|
-
setIsLoading(false);
|
|
300
|
-
});
|
|
301
|
-
dash.on(dashjs.MediaPlayer.events.ERROR, () => {
|
|
302
|
-
setError("Não foi possível reproduzir o stream MPEG-DASH.");
|
|
303
|
-
setIsLoading(false);
|
|
304
|
-
});
|
|
305
|
-
dash.initialize(video, activeSource.src, false);
|
|
306
|
-
}
|
|
307
|
-
catch {
|
|
308
|
-
if (!cancelled) {
|
|
309
|
-
setError("Não foi possível carregar o player MPEG-DASH.");
|
|
310
|
-
setIsLoading(false);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
})();
|
|
314
|
-
return () => {
|
|
315
|
-
cancelled = true;
|
|
316
|
-
destroyMediaEngines();
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
if (sourceType === "hls") {
|
|
320
|
-
void (async () => {
|
|
321
|
-
try {
|
|
322
|
-
const HlsModule = await import("hls.js");
|
|
323
|
-
if (cancelled)
|
|
324
|
-
return;
|
|
325
|
-
const Hls = HlsModule.default;
|
|
326
|
-
if (Hls.isSupported()) {
|
|
327
|
-
const hls = new Hls({
|
|
328
|
-
enableWorker: true,
|
|
329
|
-
maxBufferLength: 30,
|
|
330
|
-
maxMaxBufferLength: 60,
|
|
331
|
-
maxBufferSize: 60 * 1000 * 1000,
|
|
332
|
-
});
|
|
333
|
-
hlsRef.current = hls;
|
|
334
|
-
hls.loadSource(activeSource.src);
|
|
335
|
-
hls.attachMedia(video);
|
|
336
|
-
hls.on(Hls.Events.MANIFEST_PARSED, (_, data) => {
|
|
337
|
-
const levels = data.levels ?? hls.levels ?? [];
|
|
338
|
-
setQualities([
|
|
339
|
-
AUTO_QUALITY,
|
|
340
|
-
...levels.map((level, index) => ({
|
|
341
|
-
id: `hls-${index}`,
|
|
342
|
-
label: level.height
|
|
343
|
-
? `${level.height}p`
|
|
344
|
-
: `${Math.round((level.bitrate ?? 0) / 1000)} kbps`,
|
|
345
|
-
type: "hls",
|
|
346
|
-
index,
|
|
347
|
-
})),
|
|
348
|
-
]);
|
|
349
|
-
const tracks = hls.audioTracks ?? [];
|
|
350
|
-
if (tracks.length > 1) {
|
|
351
|
-
setAudioTracks(tracks.map((t, i) => ({
|
|
352
|
-
id: i,
|
|
353
|
-
label: t.name ?? t.lang ?? `Track ${i + 1}`,
|
|
354
|
-
})));
|
|
355
|
-
setSelectedAudio(hls.audioTrack ?? 0);
|
|
356
|
-
}
|
|
357
|
-
setIsLoading(false);
|
|
358
|
-
});
|
|
359
|
-
hls.on(Hls.Events.AUDIO_TRACKS_UPDATED, (_, data) => {
|
|
360
|
-
const tracks = data.audioTracks ?? [];
|
|
361
|
-
if (tracks.length > 1) {
|
|
362
|
-
setAudioTracks(tracks.map((t, i) => ({
|
|
363
|
-
id: i,
|
|
364
|
-
label: t.name ?? t.lang ?? `Track ${i + 1}`,
|
|
365
|
-
})));
|
|
366
|
-
}
|
|
367
|
-
});
|
|
368
|
-
hls.on(Hls.Events.ERROR, (_, data) => {
|
|
369
|
-
if (!data.fatal)
|
|
370
|
-
return;
|
|
371
|
-
if (data.type === Hls.ErrorTypes.MEDIA_ERROR) {
|
|
372
|
-
hls.recoverMediaError();
|
|
373
|
-
return;
|
|
374
|
-
}
|
|
375
|
-
setError("Não foi possível reproduzir o stream HLS.");
|
|
376
|
-
setIsLoading(false);
|
|
377
|
-
});
|
|
378
|
-
return;
|
|
379
|
-
}
|
|
380
|
-
if (video.canPlayType("application/vnd.apple.mpegurl")) {
|
|
381
|
-
video.src = activeSource.src;
|
|
382
|
-
video.load();
|
|
383
|
-
setIsLoading(false);
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
setError("Este navegador não suporta HLS.");
|
|
387
|
-
setIsLoading(false);
|
|
388
|
-
}
|
|
389
|
-
catch {
|
|
390
|
-
if (!cancelled) {
|
|
391
|
-
setError("Não foi possível carregar o player HLS.");
|
|
392
|
-
setIsLoading(false);
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
})();
|
|
396
|
-
return () => {
|
|
397
|
-
cancelled = true;
|
|
398
|
-
destroyMediaEngines();
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
video.src = activeSource.src;
|
|
402
|
-
video.load();
|
|
403
|
-
setIsLoading(false);
|
|
404
|
-
return () => {
|
|
405
|
-
cancelled = true;
|
|
406
|
-
video.removeAttribute("src");
|
|
407
|
-
video.load();
|
|
408
|
-
};
|
|
409
|
-
}, [activeSource, destroyMediaEngines]);
|
|
410
|
-
const togglePlay = useCallback(async () => {
|
|
411
|
-
const video = videoRef.current;
|
|
412
|
-
if (!video)
|
|
413
|
-
return;
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
if (sourceType === "hls") {
|
|
325
|
+
void (async () => {
|
|
414
326
|
try {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
327
|
+
const HlsModule = await import('hls.js');
|
|
328
|
+
if (cancelled) return;
|
|
329
|
+
const Hls = HlsModule.default;
|
|
330
|
+
if (Hls.isSupported()) {
|
|
331
|
+
const hls = new Hls({
|
|
332
|
+
enableWorker: true,
|
|
333
|
+
maxBufferLength: 30,
|
|
334
|
+
maxMaxBufferLength: 60,
|
|
335
|
+
maxBufferSize: 60 * 1e3 * 1e3
|
|
336
|
+
});
|
|
337
|
+
hlsRef.current = hls;
|
|
338
|
+
hls.loadSource(activeSource.src);
|
|
339
|
+
hls.attachMedia(video);
|
|
340
|
+
hls.on(Hls.Events.MANIFEST_PARSED, (_, data) => {
|
|
341
|
+
const levels = data.levels ?? hls.levels ?? [];
|
|
342
|
+
setQualities([
|
|
343
|
+
AUTO_QUALITY,
|
|
344
|
+
...levels.map((level, index) => ({
|
|
345
|
+
id: `hls-${index}`,
|
|
346
|
+
label: level.height ? `${level.height}p` : `${Math.round((level.bitrate ?? 0) / 1e3)} kbps`,
|
|
347
|
+
type: "hls",
|
|
348
|
+
index
|
|
349
|
+
}))
|
|
350
|
+
]);
|
|
351
|
+
const tracks = hls.audioTracks ?? [];
|
|
352
|
+
if (tracks.length > 1) {
|
|
353
|
+
setAudioTracks(tracks.map((t, i) => ({
|
|
354
|
+
id: i,
|
|
355
|
+
label: t.name ?? t.lang ?? `Track ${i + 1}`
|
|
356
|
+
})));
|
|
357
|
+
setSelectedAudio(hls.audioTrack ?? 0);
|
|
358
|
+
}
|
|
359
|
+
setIsLoading(false);
|
|
360
|
+
});
|
|
361
|
+
hls.on(Hls.Events.AUDIO_TRACKS_UPDATED, (_, data) => {
|
|
362
|
+
const tracks = data.audioTracks ?? [];
|
|
363
|
+
if (tracks.length > 1) {
|
|
364
|
+
setAudioTracks(tracks.map((t, i) => ({
|
|
365
|
+
id: i,
|
|
366
|
+
label: t.name ?? t.lang ?? `Track ${i + 1}`
|
|
367
|
+
})));
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
hls.on(Hls.Events.ERROR, (_, data) => {
|
|
371
|
+
if (!data.fatal) return;
|
|
372
|
+
if (data.type === Hls.ErrorTypes.MEDIA_ERROR) {
|
|
373
|
+
hls.recoverMediaError();
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
setError("N\xE3o foi poss\xEDvel reproduzir o stream HLS.");
|
|
377
|
+
setIsLoading(false);
|
|
378
|
+
});
|
|
429
379
|
return;
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
380
|
+
}
|
|
381
|
+
if (video.canPlayType("application/vnd.apple.mpegurl")) {
|
|
382
|
+
video.src = activeSource.src;
|
|
383
|
+
video.load();
|
|
384
|
+
setIsLoading(false);
|
|
435
385
|
return;
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
catch {
|
|
445
|
-
setError("Não foi possível alterar o modo fullscreen.");
|
|
386
|
+
}
|
|
387
|
+
setError("Este navegador n\xE3o suporta HLS.");
|
|
388
|
+
setIsLoading(false);
|
|
389
|
+
} catch {
|
|
390
|
+
if (!cancelled) {
|
|
391
|
+
setError("N\xE3o foi poss\xEDvel carregar o player HLS.");
|
|
392
|
+
setIsLoading(false);
|
|
393
|
+
}
|
|
446
394
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
395
|
+
})();
|
|
396
|
+
return () => {
|
|
397
|
+
cancelled = true;
|
|
398
|
+
destroyMediaEngines();
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
video.src = activeSource.src;
|
|
402
|
+
video.load();
|
|
403
|
+
setIsLoading(false);
|
|
404
|
+
return () => {
|
|
405
|
+
cancelled = true;
|
|
406
|
+
video.removeAttribute("src");
|
|
407
|
+
video.load();
|
|
408
|
+
};
|
|
409
|
+
}, [activeSource, destroyMediaEngines]);
|
|
410
|
+
const togglePlay = useCallback(async () => {
|
|
411
|
+
const video = videoRef.current;
|
|
412
|
+
if (!video) return;
|
|
413
|
+
try {
|
|
414
|
+
if (video.paused) {
|
|
415
|
+
await video.play();
|
|
416
|
+
} else {
|
|
417
|
+
video.pause();
|
|
418
|
+
}
|
|
419
|
+
} catch {
|
|
420
|
+
setError("O navegador bloqueou a reprodu\xE7\xE3o autom\xE1tica.");
|
|
421
|
+
}
|
|
422
|
+
}, []);
|
|
423
|
+
const seekRelative = useCallback((seconds) => {
|
|
424
|
+
const video = videoRef.current;
|
|
425
|
+
if (!video) return;
|
|
426
|
+
video.currentTime = Math.max(
|
|
427
|
+
0,
|
|
428
|
+
Math.min(video.currentTime + seconds, video.duration || 0)
|
|
429
|
+
);
|
|
430
|
+
}, []);
|
|
431
|
+
const toggleFullscreen = useCallback(async () => {
|
|
432
|
+
const player = playerRef.current;
|
|
433
|
+
if (!player) return;
|
|
434
|
+
try {
|
|
435
|
+
if (!document.fullscreenElement) {
|
|
436
|
+
await player.requestFullscreen();
|
|
437
|
+
} else {
|
|
438
|
+
await document.exitFullscreen();
|
|
439
|
+
}
|
|
440
|
+
} catch {
|
|
441
|
+
setError("N\xE3o foi poss\xEDvel alterar o modo fullscreen.");
|
|
442
|
+
}
|
|
443
|
+
}, []);
|
|
444
|
+
const changeAudio = useCallback((trackId) => {
|
|
445
|
+
setSelectedAudio(trackId);
|
|
446
|
+
setOpenMenu(null);
|
|
447
|
+
if (hlsRef.current) {
|
|
448
|
+
hlsRef.current.audioTrack = trackId;
|
|
449
|
+
}
|
|
450
|
+
}, []);
|
|
451
|
+
const changeQuality = useCallback(
|
|
452
|
+
(qualityId) => {
|
|
453
|
+
const option = qualities.find((quality) => quality.id === qualityId);
|
|
454
|
+
if (!option) return;
|
|
455
|
+
setSelectedQuality(qualityId);
|
|
456
|
+
setOpenMenu(null);
|
|
457
|
+
if (option.type === "auto") {
|
|
451
458
|
if (hlsRef.current) {
|
|
452
|
-
|
|
459
|
+
hlsRef.current.currentLevel = -1;
|
|
453
460
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
if (hlsRef.current) {
|
|
463
|
-
hlsRef.current.currentLevel = -1;
|
|
464
|
-
}
|
|
465
|
-
if (dashRef.current) {
|
|
466
|
-
dashRef.current.updateSettings({
|
|
467
|
-
streaming: {
|
|
468
|
-
abr: {
|
|
469
|
-
autoSwitchBitrate: {
|
|
470
|
-
video: true,
|
|
471
|
-
},
|
|
472
|
-
},
|
|
473
|
-
},
|
|
474
|
-
});
|
|
461
|
+
if (dashRef.current) {
|
|
462
|
+
dashRef.current.updateSettings({
|
|
463
|
+
streaming: {
|
|
464
|
+
abr: {
|
|
465
|
+
autoSwitchBitrate: {
|
|
466
|
+
video: true
|
|
467
|
+
}
|
|
468
|
+
}
|
|
475
469
|
}
|
|
476
|
-
|
|
477
|
-
}
|
|
478
|
-
if (option.type === "hls" && hlsRef.current && option.index != null) {
|
|
479
|
-
hlsRef.current.currentLevel = option.index;
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
if (option.type === "dash" && dashRef.current && option.index != null) {
|
|
483
|
-
dashRef.current.updateSettings({
|
|
484
|
-
streaming: {
|
|
485
|
-
abr: {
|
|
486
|
-
autoSwitchBitrate: {
|
|
487
|
-
video: false,
|
|
488
|
-
},
|
|
489
|
-
},
|
|
490
|
-
},
|
|
491
|
-
});
|
|
492
|
-
dashRef.current.setQualityFor("video", option.index);
|
|
493
|
-
}
|
|
494
|
-
}, [qualities]);
|
|
495
|
-
const handleProgressPointerMove = useCallback((event) => {
|
|
496
|
-
const progress = progressRef.current;
|
|
497
|
-
if (!progress || !duration || storyboardCues.length === 0) {
|
|
498
|
-
setPreview(null);
|
|
499
|
-
return;
|
|
500
|
-
}
|
|
501
|
-
const rect = progress.getBoundingClientRect();
|
|
502
|
-
const x = Math.max(0, Math.min(event.clientX - rect.left, rect.width));
|
|
503
|
-
const time = (x / rect.width) * duration;
|
|
504
|
-
const cue = findStoryboardCue(storyboardCues, time);
|
|
505
|
-
if (!cue) {
|
|
506
|
-
setPreview(null);
|
|
507
|
-
return;
|
|
470
|
+
});
|
|
508
471
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
if (option.type === "hls" && hlsRef.current && option.index != null) {
|
|
475
|
+
hlsRef.current.currentLevel = option.index;
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
if (option.type === "dash" && dashRef.current && option.index != null) {
|
|
479
|
+
dashRef.current.updateSettings({
|
|
480
|
+
streaming: {
|
|
481
|
+
abr: {
|
|
482
|
+
autoSwitchBitrate: {
|
|
483
|
+
video: false
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
513
487
|
});
|
|
514
|
-
|
|
515
|
-
|
|
488
|
+
dashRef.current.setQualityFor("video", option.index);
|
|
489
|
+
}
|
|
490
|
+
},
|
|
491
|
+
[qualities]
|
|
492
|
+
);
|
|
493
|
+
const handleProgressPointerMove = useCallback(
|
|
494
|
+
(event) => {
|
|
495
|
+
const progress = progressRef.current;
|
|
496
|
+
if (!progress || !duration || storyboardCues.length === 0) {
|
|
516
497
|
setPreview(null);
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
const rect = progress.getBoundingClientRect();
|
|
501
|
+
const x = Math.max(0, Math.min(event.clientX - rect.left, rect.width));
|
|
502
|
+
const time = x / rect.width * duration;
|
|
503
|
+
const cue = findStoryboardCue(storyboardCues, time);
|
|
504
|
+
if (!cue) {
|
|
505
|
+
setPreview(null);
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
setPreview({
|
|
509
|
+
cue,
|
|
510
|
+
time,
|
|
511
|
+
left: Math.max(80, Math.min(x, rect.width - 80))
|
|
512
|
+
});
|
|
513
|
+
},
|
|
514
|
+
[duration, storyboardCues]
|
|
515
|
+
);
|
|
516
|
+
const handleProgressPointerLeave = useCallback(() => {
|
|
517
|
+
setPreview(null);
|
|
518
|
+
}, []);
|
|
519
|
+
const handleProgressPointerDown = useCallback(
|
|
520
|
+
(event) => {
|
|
521
|
+
const video = videoRef.current;
|
|
522
|
+
const progress = progressRef.current;
|
|
523
|
+
if (!video || !progress || !duration) return;
|
|
524
|
+
const rect = progress.getBoundingClientRect();
|
|
525
|
+
const x = Math.max(0, Math.min(event.clientX - rect.left, rect.width));
|
|
526
|
+
const nextTime = x / rect.width * duration;
|
|
527
|
+
video.currentTime = nextTime;
|
|
528
|
+
setCurrentTime(nextTime);
|
|
529
|
+
},
|
|
530
|
+
[duration]
|
|
531
|
+
);
|
|
532
|
+
const handleKeyDown = useCallback(
|
|
533
|
+
(event) => {
|
|
534
|
+
const target = event.target;
|
|
535
|
+
const tagName = target.tagName.toLowerCase();
|
|
536
|
+
if (["input", "select", "button"].includes(tagName)) {
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
if (event.key === " ") {
|
|
540
|
+
event.preventDefault();
|
|
541
|
+
togglePlay();
|
|
542
|
+
}
|
|
543
|
+
if (event.key === "ArrowRight") {
|
|
544
|
+
seekRelative(10);
|
|
545
|
+
}
|
|
546
|
+
if (event.key === "ArrowLeft") {
|
|
547
|
+
seekRelative(-10);
|
|
548
|
+
}
|
|
549
|
+
if (event.key.toLowerCase() === "f") {
|
|
550
|
+
toggleFullscreen();
|
|
551
|
+
}
|
|
552
|
+
if (event.key.toLowerCase() === "m") {
|
|
553
|
+
setIsMuted((value) => !value);
|
|
554
|
+
}
|
|
555
|
+
},
|
|
556
|
+
[seekRelative, toggleFullscreen, togglePlay]
|
|
557
|
+
);
|
|
558
|
+
if (!activeSource) {
|
|
559
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-red-200 bg-red-50 p-4 text-sm text-red-700", children: [
|
|
560
|
+
"O componente Video precisa de pelo menos um",
|
|
561
|
+
" ",
|
|
562
|
+
/* @__PURE__ */ jsx("code", { children: "<Source />" }),
|
|
563
|
+
"."
|
|
564
|
+
] });
|
|
565
|
+
}
|
|
566
|
+
return /* @__PURE__ */ jsx(
|
|
567
|
+
"div",
|
|
568
|
+
{
|
|
569
|
+
ref: containerRef,
|
|
570
|
+
className: `mx-auto w-full max-w-6xl ${className ?? ""}`,
|
|
571
|
+
children: /* @__PURE__ */ jsxs(
|
|
572
|
+
"div",
|
|
573
|
+
{
|
|
574
|
+
ref: playerRef,
|
|
575
|
+
tabIndex: 0,
|
|
576
|
+
onKeyDown: handleKeyDown,
|
|
577
|
+
onMouseMove: showControlsTemporarily,
|
|
578
|
+
onMouseLeave: () => {
|
|
579
|
+
if (isPlaying && autoHideControls) {
|
|
580
|
+
setControlsVisible(false);
|
|
581
|
+
}
|
|
582
|
+
},
|
|
583
|
+
className: "relative aspect-video w-full overflow-hidden rounded-[14px] bg-black shadow-[0_30px_90px_rgba(15,15,15,0.22)] outline-none ring-1 ring-black/5",
|
|
584
|
+
children: [
|
|
585
|
+
/* @__PURE__ */ jsx(
|
|
586
|
+
"video",
|
|
587
|
+
{
|
|
588
|
+
ref: videoRef,
|
|
589
|
+
className: "h-full w-full object-contain",
|
|
590
|
+
poster,
|
|
591
|
+
playsInline: true,
|
|
592
|
+
preload: "metadata",
|
|
593
|
+
crossOrigin: "anonymous",
|
|
594
|
+
children: parsed.subtitles.map((subtitle) => /* @__PURE__ */ jsx(
|
|
595
|
+
"track",
|
|
596
|
+
{
|
|
597
|
+
kind: "subtitles",
|
|
598
|
+
src: subtitle.src,
|
|
599
|
+
srcLang: subtitle.srclang,
|
|
600
|
+
label: subtitle.label,
|
|
601
|
+
default: subtitle.default
|
|
602
|
+
},
|
|
603
|
+
`${activeSource.src}-${subtitle.srclang}`
|
|
604
|
+
))
|
|
605
|
+
}
|
|
606
|
+
),
|
|
607
|
+
/* @__PURE__ */ jsx(
|
|
608
|
+
"div",
|
|
609
|
+
{
|
|
610
|
+
className: `pointer-events-none absolute inset-0 z-10 grid place-items-center transition ${isPlaying ? "opacity-0" : "opacity-100"}`,
|
|
611
|
+
children: /* @__PURE__ */ jsx("span", { className: "grid size-20 place-items-center rounded-full bg-white/15 text-white backdrop-blur-xl ring-1 ring-white/20", children: /* @__PURE__ */ jsx(PlayIcon, { className: "ml-1 size-9" }) })
|
|
612
|
+
}
|
|
613
|
+
),
|
|
614
|
+
isLoading && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0 z-20 grid place-items-center bg-black/10", children: /* @__PURE__ */ jsx("div", { className: "size-9 animate-spin rounded-full border-2 border-white/25 border-t-white" }) }),
|
|
615
|
+
error && /* @__PURE__ */ jsx("div", { className: "absolute inset-x-8 top-1/2 z-40 -translate-y-1/2 rounded-2xl border border-white/10 bg-black/75 p-5 text-center text-sm text-white shadow-2xl backdrop-blur-xl", children: error }),
|
|
616
|
+
/* @__PURE__ */ jsxs(
|
|
617
|
+
"div",
|
|
618
|
+
{
|
|
619
|
+
ref: chromeRef,
|
|
620
|
+
onClick: togglePlay,
|
|
621
|
+
className: `absolute inset-0 z-30 flex flex-col justify-between bg-gradient-to-b from-black/55 via-black/10 to-black/75 ${controlsVisible ? "" : "pointer-events-none"}`,
|
|
622
|
+
children: [
|
|
623
|
+
/* @__PURE__ */ jsxs("header", { onClick: (e) => e.stopPropagation(), className: "flex items-start justify-between gap-4 px-7 pt-7 text-white md:px-9 md:pt-8", children: [
|
|
624
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
625
|
+
title && /* @__PURE__ */ jsx("h1", { className: "text-lg font-bold tracking-wide md:text-xl", children: title }),
|
|
626
|
+
description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm font-medium text-white/85", children: description })
|
|
627
|
+
] }),
|
|
628
|
+
parsed.sources.length > 1 && /* @__PURE__ */ jsx(
|
|
629
|
+
"select",
|
|
630
|
+
{
|
|
631
|
+
value: String(sourceIndex),
|
|
632
|
+
onChange: (e) => setSourceIndex(Number(e.target.value)),
|
|
633
|
+
"aria-label": "Video source",
|
|
634
|
+
className: "h-8 rounded-full border border-white/15 bg-white/10 px-3 text-xs font-semibold text-white outline-none backdrop-blur-md transition hover:bg-white/15",
|
|
635
|
+
children: parsed.sources.map((source, index) => /* @__PURE__ */ jsx("option", { value: String(index), className: "text-black", children: source.label ?? source.type ?? `Source ${index + 1}` }, `${source.src}-${index}`))
|
|
636
|
+
}
|
|
637
|
+
)
|
|
638
|
+
] }),
|
|
639
|
+
/* @__PURE__ */ jsxs("footer", { onClick: (e) => e.stopPropagation(), className: "px-7 pb-7 text-white md:px-9 md:pb-8", children: [
|
|
640
|
+
/* @__PURE__ */ jsxs(
|
|
641
|
+
"div",
|
|
642
|
+
{
|
|
643
|
+
ref: progressRef,
|
|
644
|
+
onPointerMove: handleProgressPointerMove,
|
|
645
|
+
onPointerLeave: handleProgressPointerLeave,
|
|
646
|
+
onPointerDown: handleProgressPointerDown,
|
|
647
|
+
className: "relative mb-6 h-5 cursor-pointer",
|
|
648
|
+
children: [
|
|
649
|
+
preview && /* @__PURE__ */ jsxs(
|
|
650
|
+
"div",
|
|
651
|
+
{
|
|
652
|
+
className: "pointer-events-none absolute bottom-8 z-20 -translate-x-1/2 rounded-lg bg-black/80 p-1 shadow-2xl ring-1 ring-white/15 backdrop-blur",
|
|
653
|
+
style: { left: preview.left },
|
|
654
|
+
children: [
|
|
655
|
+
/* @__PURE__ */ jsx(
|
|
656
|
+
"div",
|
|
657
|
+
{
|
|
658
|
+
className: "overflow-hidden rounded-md bg-neutral-900",
|
|
659
|
+
style: {
|
|
660
|
+
width: preview.cue.w ?? 160,
|
|
661
|
+
height: preview.cue.h ?? 90,
|
|
662
|
+
backgroundImage: `url(${preview.cue.image})`,
|
|
663
|
+
backgroundPosition: preview.cue.x != null && preview.cue.y != null ? `-${preview.cue.x}px -${preview.cue.y}px` : "center",
|
|
664
|
+
backgroundSize: preview.cue.x != null ? "auto" : "cover",
|
|
665
|
+
backgroundRepeat: "no-repeat"
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
),
|
|
669
|
+
/* @__PURE__ */ jsx("div", { className: "pt-1 text-center text-[11px] font-semibold text-white/80", children: formatTime(preview.time) })
|
|
670
|
+
]
|
|
671
|
+
}
|
|
672
|
+
),
|
|
673
|
+
/* @__PURE__ */ jsxs("div", { className: "absolute left-0 right-0 top-1/2 h-[5px] -translate-y-1/2 overflow-hidden rounded-full bg-white/22", children: [
|
|
674
|
+
/* @__PURE__ */ jsx(
|
|
675
|
+
"div",
|
|
676
|
+
{
|
|
677
|
+
className: "absolute inset-y-0 left-0 bg-white/30",
|
|
678
|
+
style: { width: `${bufferedPercent}%` }
|
|
679
|
+
}
|
|
680
|
+
),
|
|
681
|
+
/* @__PURE__ */ jsx(
|
|
682
|
+
"div",
|
|
683
|
+
{
|
|
684
|
+
className: "absolute inset-y-0 left-0 bg-white/85",
|
|
685
|
+
style: { width: `${progressPercent}%` }
|
|
686
|
+
}
|
|
687
|
+
)
|
|
688
|
+
] })
|
|
689
|
+
]
|
|
690
|
+
}
|
|
691
|
+
),
|
|
692
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-5", children: [
|
|
693
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-5", children: [
|
|
694
|
+
/* @__PURE__ */ jsx(
|
|
695
|
+
"button",
|
|
696
|
+
{
|
|
697
|
+
type: "button",
|
|
698
|
+
onClick: togglePlay,
|
|
699
|
+
className: "grid size-8 place-items-center text-white transition hover:scale-105 hover:text-white/80",
|
|
700
|
+
"aria-label": isPlaying ? "Pause" : "Play",
|
|
701
|
+
children: isPlaying ? /* @__PURE__ */ jsx(PauseIcon, { className: "size-7" }) : /* @__PURE__ */ jsx(PlayIcon, { className: "size-7" })
|
|
702
|
+
}
|
|
703
|
+
),
|
|
704
|
+
/* @__PURE__ */ jsx(
|
|
705
|
+
"button",
|
|
706
|
+
{
|
|
707
|
+
type: "button",
|
|
708
|
+
onClick: () => seekRelative(10),
|
|
709
|
+
className: "hidden size-8 place-items-center text-white transition hover:scale-105 hover:text-white/80 sm:grid",
|
|
710
|
+
"aria-label": "Forward 10 seconds",
|
|
711
|
+
children: /* @__PURE__ */ jsx(ForwardIcon, { className: "size-7" })
|
|
712
|
+
}
|
|
713
|
+
),
|
|
714
|
+
/* @__PURE__ */ jsxs("div", { className: "hidden items-center gap-2 text-sm font-semibold text-white/75 sm:flex", children: [
|
|
715
|
+
/* @__PURE__ */ jsx("span", { children: formatTime(currentTime) }),
|
|
716
|
+
/* @__PURE__ */ jsx("span", { className: "text-white/35", children: "/" }),
|
|
717
|
+
/* @__PURE__ */ jsx("span", { children: formatTime(duration) })
|
|
718
|
+
] })
|
|
719
|
+
] }),
|
|
720
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
721
|
+
/* @__PURE__ */ jsx(
|
|
722
|
+
"button",
|
|
723
|
+
{
|
|
724
|
+
type: "button",
|
|
725
|
+
onClick: () => setIsMuted((value) => !value),
|
|
726
|
+
className: "grid size-8 place-items-center text-white transition hover:scale-105 hover:text-white/80",
|
|
727
|
+
"aria-label": isMuted ? "Unmute" : "Mute",
|
|
728
|
+
children: isMuted || volume === 0 ? /* @__PURE__ */ jsx(MutedIcon, { className: "size-6" }) : /* @__PURE__ */ jsx(VolumeIcon, { className: "size-6" })
|
|
729
|
+
}
|
|
730
|
+
),
|
|
731
|
+
/* @__PURE__ */ jsx(
|
|
732
|
+
"input",
|
|
733
|
+
{
|
|
734
|
+
type: "range",
|
|
735
|
+
min: "0",
|
|
736
|
+
max: "1",
|
|
737
|
+
step: "0.01",
|
|
738
|
+
value: isMuted ? 0 : volume,
|
|
739
|
+
onChange: (event) => {
|
|
740
|
+
const nextVolume = Number(event.target.value);
|
|
741
|
+
setVolume(nextVolume);
|
|
742
|
+
setIsMuted(nextVolume === 0);
|
|
743
|
+
},
|
|
744
|
+
className: "hidden h-1 w-20 accent-white md:block",
|
|
745
|
+
"aria-label": "Audio level"
|
|
746
|
+
}
|
|
747
|
+
),
|
|
748
|
+
/* @__PURE__ */ jsx("div", { className: "mx-1 hidden h-4 w-px bg-white/20 md:block" }),
|
|
749
|
+
audioTracks.length > 1 && /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
750
|
+
/* @__PURE__ */ jsx(
|
|
751
|
+
"button",
|
|
752
|
+
{
|
|
753
|
+
type: "button",
|
|
754
|
+
onClick: () => setOpenMenu(openMenu === "audio" ? null : "audio"),
|
|
755
|
+
className: `grid size-8 place-items-center rounded transition hover:text-white/80 ${openMenu === "audio" ? "text-white" : "text-white/60"}`,
|
|
756
|
+
"aria-label": "Audio track",
|
|
757
|
+
children: /* @__PURE__ */ jsx(AudioIcon, { className: "size-5" })
|
|
758
|
+
}
|
|
759
|
+
),
|
|
760
|
+
openMenu === "audio" && /* @__PURE__ */ jsx(PopoverMenu, { onClose: () => setOpenMenu(null), children: audioTracks.map((track) => /* @__PURE__ */ jsx(
|
|
761
|
+
PopoverItem,
|
|
762
|
+
{
|
|
763
|
+
active: selectedAudio === track.id,
|
|
764
|
+
onClick: () => changeAudio(track.id),
|
|
765
|
+
children: track.label
|
|
766
|
+
},
|
|
767
|
+
track.id
|
|
768
|
+
)) })
|
|
769
|
+
] }),
|
|
770
|
+
parsed.subtitles.length > 0 && /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
771
|
+
/* @__PURE__ */ jsx(
|
|
772
|
+
"button",
|
|
773
|
+
{
|
|
774
|
+
type: "button",
|
|
775
|
+
onClick: () => setOpenMenu(openMenu === "captions" ? null : "captions"),
|
|
776
|
+
className: `grid size-8 place-items-center rounded transition hover:text-white/80 ${subtitleMode !== "off" || openMenu === "captions" ? "text-white" : "text-white/60"}`,
|
|
777
|
+
"aria-label": "Captions",
|
|
778
|
+
children: /* @__PURE__ */ jsx(CaptionsIcon, { className: "size-5" })
|
|
779
|
+
}
|
|
780
|
+
),
|
|
781
|
+
openMenu === "captions" && /* @__PURE__ */ jsxs(PopoverMenu, { onClose: () => setOpenMenu(null), children: [
|
|
782
|
+
/* @__PURE__ */ jsx(
|
|
783
|
+
PopoverItem,
|
|
784
|
+
{
|
|
785
|
+
active: subtitleMode === "off",
|
|
786
|
+
onClick: () => {
|
|
787
|
+
setSubtitleMode("off");
|
|
788
|
+
setOpenMenu(null);
|
|
789
|
+
},
|
|
790
|
+
children: "Off"
|
|
791
|
+
}
|
|
792
|
+
),
|
|
793
|
+
parsed.subtitles.map((subtitle) => /* @__PURE__ */ jsx(
|
|
794
|
+
PopoverItem,
|
|
795
|
+
{
|
|
796
|
+
active: subtitleMode === subtitle.srclang,
|
|
797
|
+
onClick: () => {
|
|
798
|
+
setSubtitleMode(subtitle.srclang);
|
|
799
|
+
setOpenMenu(null);
|
|
800
|
+
},
|
|
801
|
+
children: subtitle.label
|
|
802
|
+
},
|
|
803
|
+
subtitle.srclang
|
|
804
|
+
))
|
|
805
|
+
] })
|
|
806
|
+
] }),
|
|
807
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
808
|
+
/* @__PURE__ */ jsxs(
|
|
809
|
+
"button",
|
|
810
|
+
{
|
|
811
|
+
type: "button",
|
|
812
|
+
onClick: () => setOpenMenu(openMenu === "quality" ? null : "quality"),
|
|
813
|
+
className: `flex h-8 items-center gap-1 rounded px-2 text-xs font-semibold transition hover:text-white/80 ${openMenu === "quality" ? "text-white" : "text-white/60"}`,
|
|
814
|
+
"aria-label": "Quality",
|
|
815
|
+
children: [
|
|
816
|
+
/* @__PURE__ */ jsx(QualityIcon, { className: "size-4" }),
|
|
817
|
+
/* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: qualities.find((q) => q.id === selectedQuality)?.label ?? "Auto" })
|
|
818
|
+
]
|
|
819
|
+
}
|
|
820
|
+
),
|
|
821
|
+
openMenu === "quality" && /* @__PURE__ */ jsx(PopoverMenu, { onClose: () => setOpenMenu(null), children: [...qualities].reverse().map((quality) => /* @__PURE__ */ jsxs(
|
|
822
|
+
PopoverItem,
|
|
823
|
+
{
|
|
824
|
+
active: selectedQuality === quality.id,
|
|
825
|
+
onClick: () => changeQuality(quality.id),
|
|
826
|
+
children: [
|
|
827
|
+
quality.label,
|
|
828
|
+
quality.id === "auto" && /* @__PURE__ */ jsx("span", { className: "ml-1 text-[10px] text-white/40", children: "ABR" })
|
|
829
|
+
]
|
|
830
|
+
},
|
|
831
|
+
quality.id
|
|
832
|
+
)) })
|
|
833
|
+
] }),
|
|
834
|
+
/* @__PURE__ */ jsx(
|
|
835
|
+
"button",
|
|
836
|
+
{
|
|
837
|
+
type: "button",
|
|
838
|
+
onClick: toggleFullscreen,
|
|
839
|
+
className: "grid size-8 place-items-center text-white/60 transition hover:scale-105 hover:text-white/80",
|
|
840
|
+
"aria-label": isFullscreen ? "Exit fullscreen" : "Fullscreen",
|
|
841
|
+
children: /* @__PURE__ */ jsx(FullscreenIcon, { className: "size-7" })
|
|
842
|
+
}
|
|
843
|
+
)
|
|
844
|
+
] })
|
|
845
|
+
] })
|
|
846
|
+
] })
|
|
847
|
+
]
|
|
848
|
+
}
|
|
849
|
+
)
|
|
850
|
+
]
|
|
550
851
|
}
|
|
551
|
-
|
|
552
|
-
if (!activeSource) {
|
|
553
|
-
return (_jsxs("div", { className: "rounded-xl border border-red-200 bg-red-50 p-4 text-sm text-red-700", children: ["O componente Video precisa de pelo menos um", " ", _jsx("code", { children: "<Source />" }), "."] }));
|
|
852
|
+
)
|
|
554
853
|
}
|
|
555
|
-
|
|
556
|
-
if (isPlaying && autoHideControls) {
|
|
557
|
-
setControlsVisible(false);
|
|
558
|
-
}
|
|
559
|
-
}, className: "relative aspect-video w-full overflow-hidden rounded-[14px] bg-black shadow-[0_30px_90px_rgba(15,15,15,0.22)] outline-none ring-1 ring-black/5", children: [_jsx("video", { ref: videoRef, className: "h-full w-full object-contain", poster: poster, playsInline: true, preload: "metadata", crossOrigin: "anonymous", children: parsed.subtitles.map((subtitle) => (_jsx("track", { kind: "subtitles", src: subtitle.src, srcLang: subtitle.srclang, label: subtitle.label, default: subtitle.default }, `${activeSource.src}-${subtitle.srclang}`))) }), _jsx("div", { className: `pointer-events-none absolute inset-0 z-10 grid place-items-center transition ${isPlaying ? "opacity-0" : "opacity-100"}`, children: _jsx("span", { className: "grid size-20 place-items-center rounded-full bg-white/15 text-white backdrop-blur-xl ring-1 ring-white/20", children: _jsx(PlayIcon, { className: "ml-1 size-9" }) }) }), isLoading && (_jsx("div", { className: "pointer-events-none absolute inset-0 z-20 grid place-items-center bg-black/10", children: _jsx("div", { className: "size-9 animate-spin rounded-full border-2 border-white/25 border-t-white" }) })), error && (_jsx("div", { className: "absolute inset-x-8 top-1/2 z-40 -translate-y-1/2 rounded-2xl border border-white/10 bg-black/75 p-5 text-center text-sm text-white shadow-2xl backdrop-blur-xl", children: error })), _jsxs("div", { ref: chromeRef, onClick: togglePlay, className: `absolute inset-0 z-30 flex flex-col justify-between bg-gradient-to-b from-black/55 via-black/10 to-black/75 ${controlsVisible ? "" : "pointer-events-none"}`, children: [_jsxs("header", { onClick: (e) => e.stopPropagation(), className: "flex items-start justify-between gap-4 px-7 pt-7 text-white md:px-9 md:pt-8", children: [_jsxs("div", { children: [title && (_jsx("h1", { className: "text-lg font-bold tracking-wide md:text-xl", children: title })), description && (_jsx("p", { className: "mt-1 text-sm font-medium text-white/85", children: description }))] }), parsed.sources.length > 1 && (_jsx("select", { value: String(sourceIndex), onChange: (e) => setSourceIndex(Number(e.target.value)), "aria-label": "Video source", className: "h-8 rounded-full border border-white/15 bg-white/10 px-3 text-xs font-semibold text-white outline-none backdrop-blur-md transition hover:bg-white/15", children: parsed.sources.map((source, index) => (_jsx("option", { value: String(index), className: "text-black", children: source.label ?? source.type ?? `Source ${index + 1}` }, `${source.src}-${index}`))) }))] }), _jsxs("footer", { onClick: (e) => e.stopPropagation(), className: "px-7 pb-7 text-white md:px-9 md:pb-8", children: [_jsxs("div", { ref: progressRef, onPointerMove: handleProgressPointerMove, onPointerLeave: handleProgressPointerLeave, onPointerDown: handleProgressPointerDown, className: "relative mb-6 h-5 cursor-pointer", children: [preview && (_jsxs("div", { className: "pointer-events-none absolute bottom-8 z-20 -translate-x-1/2 rounded-lg bg-black/80 p-1 shadow-2xl ring-1 ring-white/15 backdrop-blur", style: { left: preview.left }, children: [_jsx("div", { className: "overflow-hidden rounded-md bg-neutral-900", style: {
|
|
560
|
-
width: preview.cue.w ?? 160,
|
|
561
|
-
height: preview.cue.h ?? 90,
|
|
562
|
-
backgroundImage: `url(${preview.cue.image})`,
|
|
563
|
-
backgroundPosition: preview.cue.x != null && preview.cue.y != null
|
|
564
|
-
? `-${preview.cue.x}px -${preview.cue.y}px`
|
|
565
|
-
: "center",
|
|
566
|
-
backgroundSize: preview.cue.x != null ? "auto" : "cover",
|
|
567
|
-
backgroundRepeat: "no-repeat",
|
|
568
|
-
} }), _jsx("div", { className: "pt-1 text-center text-[11px] font-semibold text-white/80", children: formatTime(preview.time) })] })), _jsxs("div", { className: "absolute left-0 right-0 top-1/2 h-[5px] -translate-y-1/2 overflow-hidden rounded-full bg-white/22", children: [_jsx("div", { className: "absolute inset-y-0 left-0 bg-white/30", style: { width: `${bufferedPercent}%` } }), _jsx("div", { className: "absolute inset-y-0 left-0 bg-white/85", style: { width: `${progressPercent}%` } })] })] }), _jsxs("div", { className: "flex items-center justify-between gap-5", children: [_jsxs("div", { className: "flex items-center gap-5", children: [_jsx("button", { type: "button", onClick: togglePlay, className: "grid size-8 place-items-center text-white transition hover:scale-105 hover:text-white/80", "aria-label": isPlaying ? "Pause" : "Play", children: isPlaying ? (_jsx(PauseIcon, { className: "size-7" })) : (_jsx(PlayIcon, { className: "size-7" })) }), _jsx("button", { type: "button", onClick: () => seekRelative(10), className: "hidden size-8 place-items-center text-white transition hover:scale-105 hover:text-white/80 sm:grid", "aria-label": "Forward 10 seconds", children: _jsx(ForwardIcon, { className: "size-7" }) }), _jsxs("div", { className: "hidden items-center gap-2 text-sm font-semibold text-white/75 sm:flex", children: [_jsx("span", { children: formatTime(currentTime) }), _jsx("span", { className: "text-white/35", children: "/" }), _jsx("span", { children: formatTime(duration) })] })] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { type: "button", onClick: () => setIsMuted((value) => !value), className: "grid size-8 place-items-center text-white transition hover:scale-105 hover:text-white/80", "aria-label": isMuted ? "Unmute" : "Mute", children: isMuted || volume === 0 ? (_jsx(MutedIcon, { className: "size-6" })) : (_jsx(VolumeIcon, { className: "size-6" })) }), _jsx("input", { type: "range", min: "0", max: "1", step: "0.01", value: isMuted ? 0 : volume, onChange: (event) => {
|
|
569
|
-
const nextVolume = Number(event.target.value);
|
|
570
|
-
setVolume(nextVolume);
|
|
571
|
-
setIsMuted(nextVolume === 0);
|
|
572
|
-
}, className: "hidden h-1 w-20 accent-white md:block", "aria-label": "Audio level" }), _jsx("div", { className: "mx-1 hidden h-4 w-px bg-white/20 md:block" }), audioTracks.length > 1 && (_jsxs("div", { className: "relative", children: [_jsx("button", { type: "button", onClick: () => setOpenMenu(openMenu === "audio" ? null : "audio"), className: `grid size-8 place-items-center rounded transition hover:text-white/80 ${openMenu === "audio" ? "text-white" : "text-white/60"}`, "aria-label": "Audio track", children: _jsx(AudioIcon, { className: "size-5" }) }), openMenu === "audio" && (_jsx(PopoverMenu, { onClose: () => setOpenMenu(null), children: audioTracks.map((track) => (_jsx(PopoverItem, { active: selectedAudio === track.id, onClick: () => changeAudio(track.id), children: track.label }, track.id))) }))] })), parsed.subtitles.length > 0 && (_jsxs("div", { className: "relative", children: [_jsx("button", { type: "button", onClick: () => setOpenMenu(openMenu === "captions" ? null : "captions"), className: `grid size-8 place-items-center rounded transition hover:text-white/80 ${subtitleMode !== "off" || openMenu === "captions" ? "text-white" : "text-white/60"}`, "aria-label": "Captions", children: _jsx(CaptionsIcon, { className: "size-5" }) }), openMenu === "captions" && (_jsxs(PopoverMenu, { onClose: () => setOpenMenu(null), children: [_jsx(PopoverItem, { active: subtitleMode === "off", onClick: () => { setSubtitleMode("off"); setOpenMenu(null); }, children: "Off" }), parsed.subtitles.map((subtitle) => (_jsx(PopoverItem, { active: subtitleMode === subtitle.srclang, onClick: () => { setSubtitleMode(subtitle.srclang); setOpenMenu(null); }, children: subtitle.label }, subtitle.srclang)))] }))] })), _jsxs("div", { className: "relative", children: [_jsxs("button", { type: "button", onClick: () => setOpenMenu(openMenu === "quality" ? null : "quality"), className: `flex h-8 items-center gap-1 rounded px-2 text-xs font-semibold transition hover:text-white/80 ${openMenu === "quality" ? "text-white" : "text-white/60"}`, "aria-label": "Quality", children: [_jsx(QualityIcon, { className: "size-4" }), _jsx("span", { className: "hidden sm:inline", children: qualities.find((q) => q.id === selectedQuality)?.label ?? "Auto" })] }), openMenu === "quality" && (_jsx(PopoverMenu, { onClose: () => setOpenMenu(null), children: [...qualities].reverse().map((quality) => (_jsxs(PopoverItem, { active: selectedQuality === quality.id, onClick: () => changeQuality(quality.id), children: [quality.label, quality.id === "auto" && (_jsx("span", { className: "ml-1 text-[10px] text-white/40", children: "ABR" }))] }, quality.id))) }))] }), _jsx("button", { type: "button", onClick: toggleFullscreen, className: "grid size-8 place-items-center text-white/60 transition hover:scale-105 hover:text-white/80", "aria-label": isFullscreen ? "Exit fullscreen" : "Fullscreen", children: _jsx(FullscreenIcon, { className: "size-7" }) })] })] })] })] })] }) }));
|
|
854
|
+
);
|
|
573
855
|
}
|
|
574
|
-
|
|
856
|
+
var VideoPlayer = Video;
|
|
575
857
|
function PopoverMenu({ children, onClose }) {
|
|
576
|
-
|
|
858
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
859
|
+
/* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-40", onClick: onClose }),
|
|
860
|
+
/* @__PURE__ */ jsx("div", { className: "absolute bottom-full right-0 z-50 mb-2 min-w-[120px] overflow-hidden rounded-xl border border-white/10 bg-black/85 py-1 shadow-2xl backdrop-blur-xl", children })
|
|
861
|
+
] });
|
|
577
862
|
}
|
|
578
|
-
function PopoverItem({
|
|
579
|
-
|
|
863
|
+
function PopoverItem({
|
|
864
|
+
children,
|
|
865
|
+
active,
|
|
866
|
+
onClick
|
|
867
|
+
}) {
|
|
868
|
+
return /* @__PURE__ */ jsxs(
|
|
869
|
+
"button",
|
|
870
|
+
{
|
|
871
|
+
type: "button",
|
|
872
|
+
onClick,
|
|
873
|
+
className: `flex w-full items-center gap-2 px-4 py-2 text-left text-sm font-medium transition hover:bg-white/10 ${active ? "text-white" : "text-white/55"}`,
|
|
874
|
+
children: [
|
|
875
|
+
/* @__PURE__ */ jsx("span", { className: `size-1.5 rounded-full ${active ? "bg-white" : "bg-transparent"}` }),
|
|
876
|
+
children
|
|
877
|
+
]
|
|
878
|
+
}
|
|
879
|
+
);
|
|
580
880
|
}
|
|
581
881
|
function parseVideoChildren(children) {
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
if (
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
if (
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
...(element.props.width !== undefined && { width: element.props.width }),
|
|
626
|
-
...(element.props.height !== undefined && { height: element.props.height }),
|
|
627
|
-
frames,
|
|
628
|
-
};
|
|
629
|
-
}
|
|
630
|
-
});
|
|
631
|
-
return parsed;
|
|
882
|
+
const parsed = {
|
|
883
|
+
sources: [],
|
|
884
|
+
subtitles: []
|
|
885
|
+
};
|
|
886
|
+
React.Children.forEach(children, (child) => {
|
|
887
|
+
if (!React.isValidElement(child)) return;
|
|
888
|
+
if (child.type === Sources) {
|
|
889
|
+
const element = child;
|
|
890
|
+
React.Children.forEach(element.props.children, (sourceChild) => {
|
|
891
|
+
if (!React.isValidElement(sourceChild)) return;
|
|
892
|
+
if (sourceChild.type !== Source) return;
|
|
893
|
+
const sourceElement = sourceChild;
|
|
894
|
+
parsed.sources.push(sourceElement.props);
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
if (child.type === Subtitles) {
|
|
898
|
+
const element = child;
|
|
899
|
+
React.Children.forEach(element.props.children, (subtitleChild) => {
|
|
900
|
+
if (!React.isValidElement(subtitleChild)) return;
|
|
901
|
+
if (subtitleChild.type !== Subtitle) return;
|
|
902
|
+
const subtitleElement = subtitleChild;
|
|
903
|
+
parsed.subtitles.push(subtitleElement.props);
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
if (child.type === Storyboard) {
|
|
907
|
+
const element = child;
|
|
908
|
+
const frames = [];
|
|
909
|
+
React.Children.forEach(element.props.children, (frameChild) => {
|
|
910
|
+
if (!React.isValidElement(frameChild)) return;
|
|
911
|
+
if (frameChild.type !== StoryboardFrame) return;
|
|
912
|
+
const frameElement = frameChild;
|
|
913
|
+
frames.push(frameElement.props);
|
|
914
|
+
});
|
|
915
|
+
parsed.storyboard = {
|
|
916
|
+
...element.props.src !== void 0 && { src: element.props.src },
|
|
917
|
+
...element.props.fallbackImage !== void 0 && { fallbackImage: element.props.fallbackImage },
|
|
918
|
+
...element.props.width !== void 0 && { width: element.props.width },
|
|
919
|
+
...element.props.height !== void 0 && { height: element.props.height },
|
|
920
|
+
frames
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
});
|
|
924
|
+
return parsed;
|
|
632
925
|
}
|
|
633
926
|
function inferSourceType(source) {
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
if (src.includes(".m3u8") || src.includes("/hls"))
|
|
640
|
-
return "hls";
|
|
641
|
-
return "file";
|
|
927
|
+
if (source.type) return source.type;
|
|
928
|
+
const src = source.src.toLowerCase();
|
|
929
|
+
if (src.includes(".mpd") || src.includes("/dash")) return "dash";
|
|
930
|
+
if (src.includes(".m3u8") || src.includes("/hls")) return "hls";
|
|
931
|
+
return "file";
|
|
642
932
|
}
|
|
643
933
|
function findStoryboardCue(cues, time) {
|
|
644
|
-
|
|
934
|
+
return cues.find((cue) => time >= cue.start && time <= cue.end) ?? null;
|
|
645
935
|
}
|
|
646
936
|
function parseStoryboardVtt(text, baseUrl) {
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
end: parseVttTimestamp(endRaw),
|
|
666
|
-
...image,
|
|
667
|
-
});
|
|
668
|
-
}
|
|
669
|
-
return cues;
|
|
937
|
+
const lines = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
938
|
+
const cues = [];
|
|
939
|
+
for (let index = 0; index < lines.length; index++) {
|
|
940
|
+
const line = lines[index];
|
|
941
|
+
if (!line || !line.includes("-->")) continue;
|
|
942
|
+
const parts = line.split(/\s+-->\s+/);
|
|
943
|
+
const startRaw = parts[0];
|
|
944
|
+
const endRaw = parts[1];
|
|
945
|
+
const imageLine = lines[index + 1];
|
|
946
|
+
if (!startRaw || !endRaw || !imageLine) continue;
|
|
947
|
+
const image = parseStoryboardImageLine(imageLine, baseUrl);
|
|
948
|
+
cues.push({
|
|
949
|
+
start: parseVttTimestamp(startRaw),
|
|
950
|
+
end: parseVttTimestamp(endRaw),
|
|
951
|
+
...image
|
|
952
|
+
});
|
|
953
|
+
}
|
|
954
|
+
return cues;
|
|
670
955
|
}
|
|
671
956
|
function parseStoryboardImageLine(line, baseUrl) {
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
957
|
+
const splitIndex = line.indexOf("#xywh=");
|
|
958
|
+
const imageRaw = splitIndex >= 0 ? line.slice(0, splitIndex) : line;
|
|
959
|
+
const xywhRaw = splitIndex >= 0 ? line.slice(splitIndex + 6) : null;
|
|
960
|
+
const image = new URL(imageRaw, baseUrl).href;
|
|
961
|
+
if (!xywhRaw) {
|
|
962
|
+
return { image };
|
|
963
|
+
}
|
|
964
|
+
const [x, y, w, h] = xywhRaw.split(",").map(Number);
|
|
965
|
+
return {
|
|
966
|
+
image,
|
|
967
|
+
...x !== void 0 && !isNaN(x) && { x },
|
|
968
|
+
...y !== void 0 && !isNaN(y) && { y },
|
|
969
|
+
...w !== void 0 && !isNaN(w) && { w },
|
|
970
|
+
...h !== void 0 && !isNaN(h) && { h }
|
|
971
|
+
};
|
|
687
972
|
}
|
|
688
973
|
function parseVttTimestamp(value) {
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
974
|
+
const normalized = value.replace(",", ".");
|
|
975
|
+
const parts = normalized.split(":").map(Number);
|
|
976
|
+
if (parts.length === 3) {
|
|
977
|
+
return (parts[0] ?? 0) * 3600 + (parts[1] ?? 0) * 60 + (parts[2] ?? 0);
|
|
978
|
+
}
|
|
979
|
+
if (parts.length === 2) {
|
|
980
|
+
return (parts[0] ?? 0) * 60 + (parts[1] ?? 0);
|
|
981
|
+
}
|
|
982
|
+
return Number(normalized) || 0;
|
|
698
983
|
}
|
|
699
984
|
function formatTime(seconds) {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
985
|
+
if (!Number.isFinite(seconds)) return "00:00";
|
|
986
|
+
const safeSeconds = Math.max(0, Math.floor(seconds));
|
|
987
|
+
const hours = Math.floor(safeSeconds / 3600);
|
|
988
|
+
const minutes = Math.floor(safeSeconds % 3600 / 60);
|
|
989
|
+
const secs = safeSeconds % 60;
|
|
990
|
+
if (hours > 0) {
|
|
991
|
+
return `${hours}:${String(minutes).padStart(2, "0")}:${String(secs).padStart(
|
|
992
|
+
2,
|
|
993
|
+
"0"
|
|
994
|
+
)}`;
|
|
995
|
+
}
|
|
996
|
+
return `${String(minutes).padStart(2, "0")}:${String(secs).padStart(2, "0")}`;
|
|
710
997
|
}
|
|
711
998
|
function PlayIcon({ className }) {
|
|
712
|
-
|
|
999
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", className, children: /* @__PURE__ */ jsx("path", { d: "M8 5.14v13.72c0 .76.84 1.22 1.48.8l10.2-6.86a.96.96 0 0 0 0-1.6L9.48 4.34A.96.96 0 0 0 8 5.14Z" }) });
|
|
713
1000
|
}
|
|
714
1001
|
function PauseIcon({ className }) {
|
|
715
|
-
|
|
1002
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", className, children: /* @__PURE__ */ jsx("path", { d: "M7 5.5A1.5 1.5 0 0 1 8.5 4h1A1.5 1.5 0 0 1 11 5.5v13A1.5 1.5 0 0 1 9.5 20h-1A1.5 1.5 0 0 1 7 18.5v-13Zm6 0A1.5 1.5 0 0 1 14.5 4h1A1.5 1.5 0 0 1 17 5.5v13a1.5 1.5 0 0 1-1.5 1.5h-1a1.5 1.5 0 0 1-1.5-1.5v-13Z" }) });
|
|
716
1003
|
}
|
|
717
1004
|
function ForwardIcon({ className }) {
|
|
718
|
-
|
|
1005
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", className, children: /* @__PURE__ */ jsx("path", { d: "M5 5.14v13.72c0 .76.84 1.22 1.48.8l8.7-5.86a.96.96 0 0 0 0-1.6L6.48 4.34A.96.96 0 0 0 5 5.14Zm12.5-.64A1.5 1.5 0 0 0 16 6v12a1.5 1.5 0 0 0 3 0V6a1.5 1.5 0 0 0-1.5-1.5Z" }) });
|
|
719
1006
|
}
|
|
720
1007
|
function VolumeIcon({ className }) {
|
|
721
|
-
|
|
1008
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", className, children: /* @__PURE__ */ jsx("path", { d: "M4 9.5A2.5 2.5 0 0 1 6.5 7H9l5-4v18l-5-4H6.5A2.5 2.5 0 0 1 4 14.5v-5Zm12.5-2.15a1 1 0 0 1 1.4.2 7.5 7.5 0 0 1 0 8.9 1 1 0 1 1-1.6-1.2 5.5 5.5 0 0 0 0-6.5 1 1 0 0 1 .2-1.4Zm3-2.25a1 1 0 0 1 1.4.2 11.25 11.25 0 0 1 0 13.4 1 1 0 1 1-1.6-1.2 9.25 9.25 0 0 0 0-11.2 1 1 0 0 1 .2-1.2Z" }) });
|
|
722
1009
|
}
|
|
723
1010
|
function MutedIcon({ className }) {
|
|
724
|
-
|
|
1011
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", className, children: /* @__PURE__ */ jsx("path", { d: "M4 9.5A2.5 2.5 0 0 1 6.5 7H9l5-4v18l-5-4H6.5A2.5 2.5 0 0 1 4 14.5v-5Zm13.7.1a1 1 0 0 1 1.4 0l1.4 1.4 1.4-1.4a1 1 0 1 1 1.4 1.4L21.9 12l1.4 1.4a1 1 0 0 1-1.4 1.4l-1.4-1.4-1.4 1.4a1 1 0 1 1-1.4-1.4L19.1 12l-1.4-1.4a1 1 0 0 1 0-1Z" }) });
|
|
725
1012
|
}
|
|
726
1013
|
function FullscreenIcon({ className }) {
|
|
727
|
-
|
|
1014
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", className, children: /* @__PURE__ */ jsx(
|
|
1015
|
+
"path",
|
|
1016
|
+
{
|
|
1017
|
+
d: "M8.5 4H5.75A1.75 1.75 0 0 0 4 5.75V8.5M15.5 4h2.75A1.75 1.75 0 0 1 20 5.75V8.5M20 15.5v2.75A1.75 1.75 0 0 1 18.25 20H15.5M8.5 20H5.75A1.75 1.75 0 0 1 4 18.25V15.5",
|
|
1018
|
+
stroke: "currentColor",
|
|
1019
|
+
strokeWidth: "2",
|
|
1020
|
+
strokeLinecap: "round"
|
|
1021
|
+
}
|
|
1022
|
+
) });
|
|
728
1023
|
}
|
|
729
1024
|
function CaptionsIcon({ className }) {
|
|
730
|
-
|
|
1025
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", className, children: [
|
|
1026
|
+
/* @__PURE__ */ jsx("rect", { x: "2", y: "5", width: "20", height: "14", rx: "2", stroke: "currentColor", strokeWidth: "1.75" }),
|
|
1027
|
+
/* @__PURE__ */ jsx("path", { d: "M6 12h4M6 15h8M14 12h4", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round" })
|
|
1028
|
+
] });
|
|
731
1029
|
}
|
|
732
1030
|
function AudioIcon({ className }) {
|
|
733
|
-
|
|
1031
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", className, children: /* @__PURE__ */ jsx("path", { d: "M12 3a1 1 0 0 0-1.707-.707l-4 4H4a2 2 0 0 0-2 2v3.414a2 2 0 0 0 2 2h2.293l4 4A1 1 0 0 0 12 17V3ZM17.5 8.5a1 1 0 0 1 1.414 0 6 6 0 0 1 0 8.486 1 1 0 1 1-1.414-1.414 4 4 0 0 0 0-5.657 1 1 0 0 1 0-1.415ZM15.086 10.914a1 1 0 0 1 1.414 0 3 3 0 0 1 0 4.243 1 1 0 0 1-1.414-1.414 1 1 0 0 0 0-1.415 1 1 0 0 1 0-1.414Z" }) });
|
|
734
1032
|
}
|
|
735
1033
|
function QualityIcon({ className }) {
|
|
736
|
-
|
|
1034
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", className, children: [
|
|
1035
|
+
/* @__PURE__ */ jsx("rect", { x: "2", y: "7", width: "20", height: "13", rx: "2", stroke: "currentColor", strokeWidth: "1.75" }),
|
|
1036
|
+
/* @__PURE__ */ jsx("path", { d: "M8 4h8", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round" }),
|
|
1037
|
+
/* @__PURE__ */ jsx("path", { d: "M9 13.5v-3l1.5 1.5L12 10v3.5M14 10.5h2.5M14 12.5h2", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
1038
|
+
] });
|
|
737
1039
|
}
|
|
1040
|
+
|
|
1041
|
+
export { Source, Sources, Storyboard, StoryboardFrame, Subtitle, Subtitles, Video, VideoPlayer };
|
|
1042
|
+
//# sourceMappingURL=VideoPlayer.js.map
|
|
738
1043
|
//# sourceMappingURL=VideoPlayer.js.map
|