@geekapps/silo-elements-nextjs 0.0.2 → 0.0.4

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