@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.
Files changed (45) hide show
  1. package/dist/FileUploader.d.ts +7 -3
  2. package/dist/FileUploader.js +279 -38
  3. package/dist/FileUploader.js.map +1 -1
  4. package/dist/ImageUploader.d.ts +7 -3
  5. package/dist/ImageUploader.js +329 -66
  6. package/dist/ImageUploader.js.map +1 -1
  7. package/dist/MediaUploader.d.ts +7 -3
  8. package/dist/MediaUploader.js +654 -43
  9. package/dist/MediaUploader.js.map +1 -1
  10. package/dist/VideoPlayer.d.ts +20 -18
  11. package/dist/VideoPlayer.js +980 -675
  12. package/dist/VideoPlayer.js.map +1 -1
  13. package/dist/VideoUploader.d.ts +7 -3
  14. package/dist/VideoUploader.js +295 -43
  15. package/dist/VideoUploader.js.map +1 -1
  16. package/dist/components/DropZone.d.ts +8 -5
  17. package/dist/components/DropZone.js +134 -49
  18. package/dist/components/DropZone.js.map +1 -1
  19. package/dist/components/ProgressBar.d.ts +6 -4
  20. package/dist/components/ProgressBar.js +35 -15
  21. package/dist/components/ProgressBar.js.map +1 -1
  22. package/dist/index.d.ts +12 -12
  23. package/dist/index.js +1696 -10
  24. package/dist/index.js.map +1 -1
  25. package/dist/types.d.ts +12 -10
  26. package/dist/types.js +2 -1
  27. package/dist/types.js.map +1 -1
  28. package/dist/utils/format.d.ts +4 -3
  29. package/dist/utils/format.js +19 -26
  30. package/dist/utils/format.js.map +1 -1
  31. package/dist/utils/theme.d.ts +8 -5
  32. package/dist/utils/theme.js +34 -30
  33. package/dist/utils/theme.js.map +1 -1
  34. package/package.json +4 -3
  35. package/dist/FileUploader.d.ts.map +0 -1
  36. package/dist/ImageUploader.d.ts.map +0 -1
  37. package/dist/MediaUploader.d.ts.map +0 -1
  38. package/dist/VideoPlayer.d.ts.map +0 -1
  39. package/dist/VideoUploader.d.ts.map +0 -1
  40. package/dist/components/DropZone.d.ts.map +0 -1
  41. package/dist/components/ProgressBar.d.ts.map +0 -1
  42. package/dist/index.d.ts.map +0 -1
  43. package/dist/types.d.ts.map +0 -1
  44. package/dist/utils/format.d.ts.map +0 -1
  45. package/dist/utils/theme.d.ts.map +0 -1
@@ -1,738 +1,1043 @@
1
- "use client";
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import React, { useCallback, useEffect, useMemo, useRef, useState, } from "react";
4
- import gsap from "gsap";
5
- const AUTO_QUALITY = {
6
- id: "auto",
7
- label: "Auto",
8
- type: "auto",
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
- export function Sources(_props) {
11
- return null;
10
+ function Sources(_props) {
11
+ return null;
12
12
  }
13
- export function Source(_props) {
14
- return null;
13
+ function Source(_props) {
14
+ return null;
15
15
  }
16
- export function Subtitles(_props) {
17
- return null;
16
+ function Subtitles(_props) {
17
+ return null;
18
18
  }
19
- export function Subtitle(_props) {
20
- return null;
19
+ function Subtitle(_props) {
20
+ return null;
21
21
  }
22
- export function Storyboard(_props) {
23
- return null;
22
+ function Storyboard(_props) {
23
+ return null;
24
24
  }
25
- export function StoryboardFrame(_props) {
26
- return null;
25
+ function StoryboardFrame(_props) {
26
+ return null;
27
27
  }
28
- export function Video({ title, description, poster, children, className, autoHideControls = true, defaultVolume = 0.72, }) {
29
- const parsed = useMemo(() => parseVideoChildren(children), [children]);
30
- const initialSourceIndex = useMemo(() => {
31
- const index = parsed.sources.findIndex((source) => source.default);
32
- return index >= 0 ? index : 0;
33
- }, [parsed.sources]);
34
- const initialSubtitleMode = useMemo(() => {
35
- const track = parsed.subtitles.find((subtitle) => subtitle.default);
36
- return track?.srclang ?? "off";
37
- }, [parsed.subtitles]);
38
- const containerRef = useRef(null);
39
- const chromeRef = useRef(null);
40
- const playerRef = useRef(null);
41
- const videoRef = useRef(null);
42
- const progressRef = useRef(null);
43
- const hlsRef = useRef(null);
44
- const dashRef = useRef(null);
45
- const hideTimerRef = useRef(null);
46
- const [sourceIndex, setSourceIndex] = useState(initialSourceIndex);
47
- const [qualities, setQualities] = useState([AUTO_QUALITY]);
48
- const [selectedQuality, setSelectedQuality] = useState("auto");
49
- const [audioTracks, setAudioTracks] = useState([]);
50
- const [selectedAudio, setSelectedAudio] = useState(0);
51
- const [openMenu, setOpenMenu] = useState(null);
52
- const [subtitleMode, setSubtitleMode] = useState(initialSubtitleMode);
53
- const [storyboardCues, setStoryboardCues] = useState([]);
54
- const [preview, setPreview] = useState(null);
55
- const [duration, setDuration] = useState(0);
56
- const [currentTime, setCurrentTime] = useState(0);
57
- const [bufferedTime, setBufferedTime] = useState(0);
58
- const [isPlaying, setIsPlaying] = useState(false);
59
- const [isLoading, setIsLoading] = useState(true);
60
- const [controlsVisible, setControlsVisible] = useState(true);
61
- const [volume, setVolume] = useState(defaultVolume);
62
- const [isMuted, setIsMuted] = useState(false);
63
- const [isFullscreen, setIsFullscreen] = useState(false);
64
- const [error, setError] = useState(null);
65
- const activeSource = parsed.sources[sourceIndex] ?? parsed.sources[0] ?? null;
66
- const progressPercent = duration ? (currentTime / duration) * 100 : 0;
67
- const bufferedPercent = duration ? (bufferedTime / duration) * 100 : 0;
68
- const destroyMediaEngines = useCallback(() => {
69
- if (hlsRef.current) {
70
- hlsRef.current.destroy();
71
- hlsRef.current = null;
72
- }
73
- if (dashRef.current) {
74
- dashRef.current.reset();
75
- dashRef.current = null;
76
- }
77
- }, []);
78
- const applySubtitleMode = useCallback((mode) => {
79
- const video = videoRef.current;
80
- if (!video)
81
- return;
82
- Array.from(video.textTracks).forEach((track) => {
83
- track.mode =
84
- mode !== "off" && track.language === mode ? "showing" : "disabled";
85
- });
86
- }, []);
87
- const showControlsTemporarily = useCallback(() => {
88
- setControlsVisible(true);
89
- if (hideTimerRef.current) {
90
- window.clearTimeout(hideTimerRef.current);
91
- }
92
- const video = videoRef.current;
93
- if (autoHideControls && video && !video.paused) {
94
- hideTimerRef.current = window.setTimeout(() => {
95
- setControlsVisible(false);
96
- }, 2400);
97
- }
98
- }, [autoHideControls]);
99
- useEffect(() => {
100
- if (!containerRef.current)
101
- return;
102
- gsap.fromTo(containerRef.current, {
103
- opacity: 0,
104
- y: 36,
105
- scale: 0.985,
106
- filter: "blur(10px)",
107
- }, {
108
- opacity: 1,
109
- y: 0,
110
- scale: 1,
111
- filter: "blur(0px)",
112
- duration: 0.85,
113
- ease: "power3.out",
114
- });
115
- }, []);
116
- useEffect(() => {
117
- if (!chromeRef.current)
118
- return;
119
- gsap.to(chromeRef.current, {
120
- opacity: controlsVisible ? 1 : 0,
121
- y: controlsVisible ? 0 : 10,
122
- duration: 0.22,
123
- ease: "power2.out",
124
- });
125
- }, [controlsVisible]);
126
- useEffect(() => {
127
- if (sourceIndex >= parsed.sources.length) {
128
- setSourceIndex(initialSourceIndex);
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
- }, [sourceIndex, parsed.sources.length, initialSourceIndex]);
131
- useEffect(() => {
132
- if (subtitleMode !== "off" &&
133
- !parsed.subtitles.some((subtitle) => subtitle.srclang === subtitleMode)) {
134
- setSubtitleMode(initialSubtitleMode);
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
- }, [subtitleMode, parsed.subtitles, initialSubtitleMode]);
137
- useEffect(() => {
138
- const video = videoRef.current;
139
- if (!video)
140
- return;
141
- const syncTime = () => {
142
- setCurrentTime(video.currentTime || 0);
143
- if (video.buffered.length > 0) {
144
- setBufferedTime(video.buffered.end(video.buffered.length - 1));
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
- try {
218
- const response = await fetch(parsed.storyboard.src);
219
- if (!response.ok) {
220
- throw new Error("Storyboard not found");
221
- }
222
- const text = await response.text();
223
- const cues = parseStoryboardVtt(text, new URL(parsed.storyboard.src, window.location.href).href);
224
- if (!cancelled) {
225
- setStoryboardCues(cues);
226
- }
227
- }
228
- catch {
229
- if (!cancelled && parsed.storyboard.fallbackImage) {
230
- setStoryboardCues([
231
- {
232
- start: 0,
233
- end: Number.MAX_SAFE_INTEGER,
234
- image: parsed.storyboard.fallbackImage,
235
- w: parsed.storyboard.width ?? 160,
236
- h: parsed.storyboard.height ?? 90,
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
- loadStoryboard();
243
- return () => {
244
- cancelled = true;
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
- setError(null);
255
- setIsLoading(true);
256
- setCurrentTime(0);
257
- setBufferedTime(0);
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
- if (video.paused) {
416
- await video.play();
417
- }
418
- else {
419
- video.pause();
420
- }
421
- }
422
- catch {
423
- setError("O navegador bloqueou a reprodução automática.");
424
- }
425
- }, []);
426
- const seekRelative = useCallback((seconds) => {
427
- const video = videoRef.current;
428
- if (!video)
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
- video.currentTime = Math.max(0, Math.min(video.currentTime + seconds, video.duration || 0));
431
- }, []);
432
- const toggleFullscreen = useCallback(async () => {
433
- const player = playerRef.current;
434
- if (!player)
380
+ }
381
+ if (video.canPlayType("application/vnd.apple.mpegurl")) {
382
+ video.src = activeSource.src;
383
+ video.load();
384
+ setIsLoading(false);
435
385
  return;
436
- try {
437
- if (!document.fullscreenElement) {
438
- await player.requestFullscreen();
439
- }
440
- else {
441
- await document.exitFullscreen();
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
- const changeAudio = useCallback((trackId) => {
449
- setSelectedAudio(trackId);
450
- setOpenMenu(null);
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
- hlsRef.current.audioTrack = trackId;
459
+ hlsRef.current.currentLevel = -1;
453
460
  }
454
- }, []);
455
- const changeQuality = useCallback((qualityId) => {
456
- const option = qualities.find((quality) => quality.id === qualityId);
457
- if (!option)
458
- return;
459
- setSelectedQuality(qualityId);
460
- setOpenMenu(null);
461
- if (option.type === "auto") {
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
- return;
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
- setPreview({
510
- cue,
511
- time,
512
- left: Math.max(80, Math.min(x, rect.width - 80)),
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
- }, [duration, storyboardCues]);
515
- const handleProgressPointerLeave = useCallback(() => {
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
- const handleProgressPointerDown = useCallback((event) => {
519
- const video = videoRef.current;
520
- const progress = progressRef.current;
521
- if (!video || !progress || !duration)
522
- return;
523
- const rect = progress.getBoundingClientRect();
524
- const x = Math.max(0, Math.min(event.clientX - rect.left, rect.width));
525
- const nextTime = (x / rect.width) * duration;
526
- video.currentTime = nextTime;
527
- setCurrentTime(nextTime);
528
- }, [duration]);
529
- const handleKeyDown = useCallback((event) => {
530
- const target = event.target;
531
- const tagName = target.tagName.toLowerCase();
532
- if (["input", "select", "button"].includes(tagName)) {
533
- return;
534
- }
535
- if (event.key === " ") {
536
- event.preventDefault();
537
- togglePlay();
538
- }
539
- if (event.key === "ArrowRight") {
540
- seekRelative(10);
541
- }
542
- if (event.key === "ArrowLeft") {
543
- seekRelative(-10);
544
- }
545
- if (event.key.toLowerCase() === "f") {
546
- toggleFullscreen();
547
- }
548
- if (event.key.toLowerCase() === "m") {
549
- setIsMuted((value) => !value);
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
- }, [seekRelative, toggleFullscreen, togglePlay]);
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
- return (_jsx("div", { ref: containerRef, className: `mx-auto w-full max-w-6xl ${className ?? ""}`, children: _jsxs("div", { ref: playerRef, tabIndex: 0, onKeyDown: handleKeyDown, onMouseMove: showControlsTemporarily, onMouseLeave: () => {
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
- export const VideoPlayer = Video;
856
+ var VideoPlayer = Video;
575
857
  function PopoverMenu({ children, onClose }) {
576
- return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40", onClick: onClose }), _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: children })] }));
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({ children, active, onClick, }) {
579
- return (_jsxs("button", { type: "button", onClick: onClick, 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"}`, children: [_jsx("span", { className: `size-1.5 rounded-full ${active ? "bg-white" : "bg-transparent"}` }), children] }));
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
- const parsed = {
583
- sources: [],
584
- subtitles: [],
585
- };
586
- React.Children.forEach(children, (child) => {
587
- if (!React.isValidElement(child))
588
- return;
589
- if (child.type === Sources) {
590
- const element = child;
591
- React.Children.forEach(element.props.children, (sourceChild) => {
592
- if (!React.isValidElement(sourceChild))
593
- return;
594
- if (sourceChild.type !== Source)
595
- return;
596
- const sourceElement = sourceChild;
597
- parsed.sources.push(sourceElement.props);
598
- });
599
- }
600
- if (child.type === Subtitles) {
601
- const element = child;
602
- React.Children.forEach(element.props.children, (subtitleChild) => {
603
- if (!React.isValidElement(subtitleChild))
604
- return;
605
- if (subtitleChild.type !== Subtitle)
606
- return;
607
- const subtitleElement = subtitleChild;
608
- parsed.subtitles.push(subtitleElement.props);
609
- });
610
- }
611
- if (child.type === Storyboard) {
612
- const element = child;
613
- const frames = [];
614
- React.Children.forEach(element.props.children, (frameChild) => {
615
- if (!React.isValidElement(frameChild))
616
- return;
617
- if (frameChild.type !== StoryboardFrame)
618
- return;
619
- const frameElement = frameChild;
620
- frames.push(frameElement.props);
621
- });
622
- parsed.storyboard = {
623
- ...(element.props.src !== undefined && { src: element.props.src }),
624
- ...(element.props.fallbackImage !== undefined && { fallbackImage: element.props.fallbackImage }),
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
- if (source.type)
635
- return source.type;
636
- const src = source.src.toLowerCase();
637
- if (src.includes(".mpd") || src.includes("/dash"))
638
- return "dash";
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
- return cues.find((cue) => time >= cue.start && time <= cue.end) ?? null;
934
+ return cues.find((cue) => time >= cue.start && time <= cue.end) ?? null;
645
935
  }
646
936
  function parseStoryboardVtt(text, baseUrl) {
647
- const lines = text
648
- .split(/\r?\n/)
649
- .map((line) => line.trim())
650
- .filter(Boolean);
651
- const cues = [];
652
- for (let index = 0; index < lines.length; index++) {
653
- const line = lines[index];
654
- if (!line || !line.includes("-->"))
655
- continue;
656
- const parts = line.split(/\s+-->\s+/);
657
- const startRaw = parts[0];
658
- const endRaw = parts[1];
659
- const imageLine = lines[index + 1];
660
- if (!startRaw || !endRaw || !imageLine)
661
- continue;
662
- const image = parseStoryboardImageLine(imageLine, baseUrl);
663
- cues.push({
664
- start: parseVttTimestamp(startRaw),
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
- const splitIndex = line.indexOf("#xywh=");
673
- const imageRaw = splitIndex >= 0 ? line.slice(0, splitIndex) : line;
674
- const xywhRaw = splitIndex >= 0 ? line.slice(splitIndex + 6) : null;
675
- const image = new URL(imageRaw, baseUrl).href;
676
- if (!xywhRaw) {
677
- return { image };
678
- }
679
- const [x, y, w, h] = xywhRaw.split(",").map(Number);
680
- return {
681
- image,
682
- ...(x !== undefined && !isNaN(x) && { x }),
683
- ...(y !== undefined && !isNaN(y) && { y }),
684
- ...(w !== undefined && !isNaN(w) && { w }),
685
- ...(h !== undefined && !isNaN(h) && { h }),
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
- const normalized = value.replace(",", ".");
690
- const parts = normalized.split(":").map(Number);
691
- if (parts.length === 3) {
692
- return (parts[0] ?? 0) * 3600 + (parts[1] ?? 0) * 60 + (parts[2] ?? 0);
693
- }
694
- if (parts.length === 2) {
695
- return (parts[0] ?? 0) * 60 + (parts[1] ?? 0);
696
- }
697
- return Number(normalized) || 0;
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
- if (!Number.isFinite(seconds))
701
- return "00:00";
702
- const safeSeconds = Math.max(0, Math.floor(seconds));
703
- const hours = Math.floor(safeSeconds / 3600);
704
- const minutes = Math.floor((safeSeconds % 3600) / 60);
705
- const secs = safeSeconds % 60;
706
- if (hours > 0) {
707
- return `${hours}:${String(minutes).padStart(2, "0")}:${String(secs).padStart(2, "0")}`;
708
- }
709
- return `${String(minutes).padStart(2, "0")}:${String(secs).padStart(2, "0")}`;
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
- return (_jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", className: className, children: _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" }) }));
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
- return (_jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", className: className, children: _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" }) }));
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
- return (_jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", className: className, children: _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" }) }));
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
- return (_jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", className: className, children: _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" }) }));
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
- return (_jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", className: className, children: _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" }) }));
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
- return (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", className: className, children: _jsx("path", { 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", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }));
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
- return (_jsxs("svg", { viewBox: "0 0 24 24", fill: "none", className: className, children: [_jsx("rect", { x: "2", y: "5", width: "20", height: "14", rx: "2", stroke: "currentColor", strokeWidth: "1.75" }), _jsx("path", { d: "M6 12h4M6 15h8M14 12h4", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round" })] }));
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
- return (_jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", className: className, children: _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" }) }));
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
- return (_jsxs("svg", { viewBox: "0 0 24 24", fill: "none", className: className, children: [_jsx("rect", { x: "2", y: "7", width: "20", height: "13", rx: "2", stroke: "currentColor", strokeWidth: "1.75" }), _jsx("path", { d: "M8 4h8", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round" }), _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" })] }));
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