react-helios 2.3.2 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -216,6 +216,36 @@ export default function App() {
216
216
  | `getState` | `() => PlayerState` | Snapshot of current player state |
217
217
  | `getVideoElement` | `() => HTMLVideoElement \| null` | Access the underlying `<video>` element |
218
218
 
219
+ ## Theater Mode
220
+
221
+ The player fires `onTheaterModeChange` when theater mode is toggled (via the `T` key, the control bar button, or `playerRef.current?.toggleTheaterMode()`). Wire it to your layout state to widen your container:
222
+
223
+ ```tsx
224
+ "use client";
225
+
226
+ import { useState } from "react";
227
+ import { VideoPlayer } from "react-helios";
228
+
229
+ export default function Page() {
230
+ const [isTheater, setIsTheater] = useState(false);
231
+
232
+ return (
233
+ <main
234
+ style={{ maxWidth: isTheater ? "1600px" : "1200px" }}
235
+ className="mx-auto px-6 transition-[max-width] duration-300"
236
+ >
237
+ <VideoPlayer
238
+ src="https://example.com/stream.m3u8"
239
+ controls
240
+ onTheaterModeChange={(t) => setIsTheater(t)}
241
+ />
242
+ </main>
243
+ );
244
+ }
245
+ ```
246
+
247
+ The player itself does not manage your page layout — it only notifies you so you can adapt your design.
248
+
219
249
  ## Subtitles
220
250
 
221
251
  ```tsx
@@ -228,6 +258,8 @@ export default function App() {
228
258
  />
229
259
  ```
230
260
 
261
+ Subtitle files must be served with `Access-Control-Allow-Origin` if hosted on a different origin than the page.
262
+
231
263
  ## Keyboard Shortcuts
232
264
 
233
265
  Shortcuts activate when the player has focus (click the player or tab to it).
@@ -352,6 +384,38 @@ interface ContextMenuItem {
352
384
  }
353
385
  ```
354
386
 
387
+ ## Utility Functions
388
+
389
+ The package exports a few helper utilities used internally, exposed for custom integrations:
390
+
391
+ ```ts
392
+ import { formatTime, isHLSUrl, getMimeType } from "react-helios";
393
+
394
+ formatTime(90); // "1:30"
395
+ formatTime(3661); // "1:01:01"
396
+
397
+ isHLSUrl("stream.m3u8"); // true
398
+ isHLSUrl("video.mp4"); // false
399
+
400
+ getMimeType("video.mp4"); // "video/mp4"
401
+ getMimeType("video.webm"); // "video/webm"
402
+ ```
403
+
404
+ For VTT parsing in custom UIs or server-side pre-processing:
405
+
406
+ ```ts
407
+ import { parseThumbnailVtt, findThumbnailCue } from "react-helios";
408
+ import type { ThumbnailCue } from "react-helios";
409
+
410
+ const cues: ThumbnailCue[] = parseThumbnailVtt(vttText, baseUrl);
411
+
412
+ // Binary search — O(log n)
413
+ const cue = findThumbnailCue(cues, currentTime);
414
+ if (cue) {
415
+ // cue.url, cue.x, cue.y, cue.w, cue.h
416
+ }
417
+ ```
418
+
355
419
  ## Performance
356
420
 
357
421
  The player is architected to produce **zero React re-renders during playback**:
package/dist/index.css ADDED
@@ -0,0 +1,2 @@
1
+ .rvp-audio-overlay{position:absolute;inset:0;background-color:#0f0f0f;display:flex;align-items:center;justify-content:center}.rvp-audio-content{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:24px;width:100%;height:100%;padding:0 24px 56px;box-sizing:border-box}.rvp-audio-artwork-wrapper{display:flex;align-items:center;justify-content:center;flex:1;min-height:0}.rvp-audio-artwork{height:100%;max-height:240px;aspect-ratio:1 / 1;object-fit:cover;border-radius:8px;box-shadow:0 8px 32px #000000b3,0 2px 8px #0006}.rvp-audio-logo{max-width:120px;max-height:64px;object-fit:contain;filter:brightness(0) invert(1);opacity:.8}.rvp-audio-logo-node{display:flex;align-items:center;justify-content:center;color:#fffc;max-width:120px;max-height:64px}.rvp-audio-badge{display:flex;align-items:center;gap:6px;font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:#ffffff73;user-select:none}.rvp-audio-equalizer{display:flex;align-items:flex-end;gap:3px;height:40px;flex-shrink:0}.rvp-audio-bar{width:4px;border-radius:2px 2px 0 0;background:linear-gradient(to top,#ffffffe6,#ffffff80);height:5px;transform-origin:bottom;will-change:height;transition:height .15s ease}.rvp-audio-equalizer--playing .rvp-audio-bar{animation:rvp-eq-pulse var(--rvp-eq-dur, .9s) ease-in-out calc(var(--bar-index) * 45ms) infinite alternate}.rvp-audio-equalizer--playing .rvp-audio-bar:nth-child(3n+1){--rvp-eq-dur: .75s}.rvp-audio-equalizer--playing .rvp-audio-bar:nth-child(3n+2){--rvp-eq-dur: 1.05s}.rvp-audio-equalizer--playing .rvp-audio-bar:nth-child(3n){--rvp-eq-dur: .6s}.rvp-audio-equalizer--playing .rvp-audio-bar:nth-child(5n){--rvp-eq-dur: 1.2s}.rvp-audio-equalizer--playing .rvp-audio-bar:nth-child(7n){--rvp-eq-dur: .85s}@keyframes rvp-eq-pulse{0%{height:5px}20%{height:28px}45%{height:12px}70%{height:38px}85%{height:18px}to{height:32px}}@media(max-width:480px){.rvp-audio-artwork{max-height:130px}.rvp-audio-equalizer{height:28px}.rvp-audio-content{gap:16px;padding-bottom:52px}}
2
+ /*# sourceMappingURL=index.css.map */
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/styles/AudioMode.css"],"sourcesContent":["/* ─── Audio Mode Overlay ───────────────────────────────────────────────────── */\n\n.rvp-audio-overlay {\n position: absolute;\n inset: 0;\n background-color: #0f0f0f;\n display: flex;\n align-items: center;\n justify-content: center;\n /* Sits above the video but Controls comes later in DOM so it paints on top */\n}\n\n.rvp-audio-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 24px;\n width: 100%;\n height: 100%;\n padding: 0 24px 56px; /* bottom padding reserves space for the control bar */\n box-sizing: border-box;\n}\n\n/* ─── Artwork ─────────────────────────────────────────────────────────────── */\n\n.rvp-audio-artwork-wrapper {\n display: flex;\n align-items: center;\n justify-content: center;\n flex: 1;\n min-height: 0;\n}\n\n.rvp-audio-artwork {\n height: 100%;\n max-height: 240px;\n aspect-ratio: 1 / 1;\n object-fit: cover;\n border-radius: 8px;\n box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.7),\n 0 2px 8px rgba(0, 0, 0, 0.4);\n}\n\n/* Logo: smaller, shown white on the dark background */\n.rvp-audio-logo {\n max-width: 120px;\n max-height: 64px;\n object-fit: contain;\n filter: brightness(0) invert(1);\n opacity: 0.8;\n}\n\n.rvp-audio-logo-node {\n display: flex;\n align-items: center;\n justify-content: center;\n color: rgba(255, 255, 255, 0.8);\n max-width: 120px;\n max-height: 64px;\n}\n\n/* ─── AUDIO ONLY badge ────────────────────────────────────────────────────── */\n\n.rvp-audio-badge {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: rgba(255, 255, 255, 0.45);\n user-select: none;\n}\n\n/* ─── Equalizer waveform ──────────────────────────────────────────────────── */\n\n.rvp-audio-equalizer {\n display: flex;\n align-items: flex-end;\n gap: 3px;\n height: 40px;\n flex-shrink: 0;\n}\n\n.rvp-audio-bar {\n width: 4px;\n border-radius: 2px 2px 0 0;\n background: linear-gradient(to top, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.5));\n height: 5px; /* resting height when paused */\n transform-origin: bottom;\n will-change: height;\n transition: height 0.15s ease;\n}\n\n/* ── Animate only while playing ── */\n.rvp-audio-equalizer--playing .rvp-audio-bar {\n animation: rvp-eq-pulse var(--rvp-eq-dur, 0.9s) ease-in-out\n calc(var(--bar-index) * 0.045s) infinite alternate;\n}\n\n/* Varied durations create a natural, non-repeating wave */\n.rvp-audio-equalizer--playing .rvp-audio-bar:nth-child(3n+1) { --rvp-eq-dur: 0.75s; }\n.rvp-audio-equalizer--playing .rvp-audio-bar:nth-child(3n+2) { --rvp-eq-dur: 1.05s; }\n.rvp-audio-equalizer--playing .rvp-audio-bar:nth-child(3n) { --rvp-eq-dur: 0.60s; }\n.rvp-audio-equalizer--playing .rvp-audio-bar:nth-child(5n) { --rvp-eq-dur: 1.20s; }\n.rvp-audio-equalizer--playing .rvp-audio-bar:nth-child(7n) { --rvp-eq-dur: 0.85s; }\n\n@keyframes rvp-eq-pulse {\n 0% { height: 5px; }\n 20% { height: 28px; }\n 45% { height: 12px; }\n 70% { height: 38px; }\n 85% { height: 18px; }\n 100% { height: 32px; }\n}\n\n/* ─── Responsive ──────────────────────────────────────────────────────────── */\n\n@media (max-width: 480px) {\n .rvp-audio-artwork { max-height: 130px; }\n .rvp-audio-equalizer { height: 28px; }\n .rvp-audio-content { gap: 16px; padding-bottom: 52px; }\n}\n"],"mappings":"AAEA,CAAC,kBACC,SAAU,SAHZ,MAIS,EACP,iBAAkB,QAClB,QAAS,KACT,YAAa,OACb,gBAAiB,MAEnB,CAEA,CAAC,kBACC,QAAS,KACT,eAAgB,OAChB,YAAa,OACb,gBAAiB,OACjB,IAAK,KACL,MAAO,KACP,OAAQ,KAnBV,QAoBW,EAAE,KAAK,KAChB,WAAY,UACd,CAIA,CAAC,0BACC,QAAS,KACT,YAAa,OACb,gBAAiB,OACjB,KAAM,EACN,WAAY,CACd,CAEA,CAAC,kBACC,OAAQ,KACR,WAAY,MACZ,aAAc,EAAE,EAAE,EAClB,WAAY,MAtCd,cAuCiB,IACf,WACE,EAAE,IAAI,KAAK,SAAkB,CAC7B,EAAE,IAAI,IAAK,KACf,CAGA,CAAC,eACC,UAAW,MACX,WAAY,KACZ,WAAY,QACZ,OAAQ,WAAW,GAAG,OAAO,GAC7B,QAAS,EACX,CAEA,CAAC,oBACC,QAAS,KACT,YAAa,OACb,gBAAiB,OACjB,MAAO,MACP,UAAW,MACX,WAAY,IACd,CAIA,CAAC,gBACC,QAAS,KACT,YAAa,OACb,IAAK,IACL,UAAW,KACX,YAAa,IACb,eAAgB,KAChB,eAAgB,UAChB,MAAO,UACP,YAAa,IACf,CAIA,CAAC,oBACC,QAAS,KACT,YAAa,SACb,IAAK,IACL,OAAQ,KACR,YAAa,CACf,CAEA,CAAC,cACC,MAAO,IAxFT,cAyFiB,IAAI,IAAI,EAAE,EACzB,WAAY,gBAAgB,GAAG,GAAnB,CAAwB,SAAxB,CAAkD,WAC9D,OAAQ,IACR,iBAAkB,OAClB,YAAa,OACb,WAAY,OAAO,KAAM,IAC3B,CAGA,CAAC,6BAA6B,CAX7B,cAYC,UAAW,aAAa,IAAI,YAAY,EAAE,KAAM,YACrC,KAAK,IAAI,aAAa,EAAE,MAAQ,SAAS,SACtD,CAGA,CANC,6BAM6B,CAjB7B,aAiB2C,iBAAmB,cAAc,IAAO,CACpF,CAPC,6BAO6B,CAlB7B,aAkB2C,iBAAmB,cAAc,KAAO,CACpF,CARC,6BAQ6B,CAnB7B,aAmB2C,eAAmB,cAAc,GAAO,CACpF,CATC,6BAS6B,CApB7B,aAoB2C,eAAmB,cAAc,IAAO,CACpF,CAVC,6BAU6B,CArB7B,aAqB2C,eAAmB,cAAc,IAAO,CAEpF,WAXa,aAYX,GAAO,OAAQ,GAAM,CACrB,IAAO,OAAQ,IAAM,CACrB,IAAO,OAAQ,IAAM,CACrB,IAAO,OAAQ,IAAM,CACrB,IAAO,OAAQ,IAAM,CACrB,GAAO,OAAQ,IAAM,CACvB,CAIA,OAAO,UAAY,OACjB,CAxFD,kBAwFsB,WAAY,KAAO,CACxC,CA5CD,oBA4CwB,OAAQ,IAAM,CACrC,CAhHD,kBAgHsB,IAAK,KAAM,eAAgB,IAAM,CACxD","names":[]}
package/dist/index.d.mts CHANGED
@@ -2,6 +2,33 @@ import * as react from 'react';
2
2
  import react__default, { ReactNode } from 'react';
3
3
  import { HlsConfig } from 'hls.js';
4
4
 
5
+ /**
6
+ * Preset bandwidth thresholds (Kbps) for automatic audio mode switching.
7
+ *
8
+ * | Preset | Kbps | Typical connection |
9
+ * |-----------|------|-------------------------|
10
+ * | EXTREME | 100 | 2G / Edge |
11
+ * | POOR | 300 | Slow 3G ← **default** |
12
+ * | FAIR | 700 | 3G |
13
+ * | GOOD | 1500 | 4G / Wi-Fi |
14
+ *
15
+ * Pass any of these (or a custom number) as `audioBandwidthThreshold`.
16
+ * Set to `0` to disable automatic switching entirely.
17
+ *
18
+ * @example
19
+ * import { AUDIO_BANDWIDTH_THRESHOLDS } from "react-helios";
20
+ * <VideoPlayer audioBandwidthThreshold={AUDIO_BANDWIDTH_THRESHOLDS.FAIR} ... />
21
+ */
22
+ declare const AUDIO_BANDWIDTH_THRESHOLDS: {
23
+ /** < 100 Kbps — very poor, 2G / Edge */
24
+ readonly EXTREME: 100;
25
+ /** < 300 Kbps — poor, slow 3G (default) */
26
+ readonly POOR: 300;
27
+ /** < 700 Kbps — fair, 3G */
28
+ readonly FAIR: 700;
29
+ /** < 1500 Kbps — decent, 4G / Wi-Fi */
30
+ readonly GOOD: 1500;
31
+ };
5
32
  interface BufferedRange {
6
33
  start: number;
7
34
  end: number;
@@ -39,6 +66,8 @@ interface PlayerState {
39
66
  isFullscreen: boolean;
40
67
  isPictureInPicture: boolean;
41
68
  isTheaterMode: boolean;
69
+ /** True when the player is in audio-only mode (video hidden, waveform shown). */
70
+ isAudioMode: boolean;
42
71
  isLive: boolean;
43
72
  qualityLevels: HLSQualityLevel[];
44
73
  currentQualityLevel: number;
@@ -56,6 +85,8 @@ interface VideoPlayerRef {
56
85
  toggleFullscreen: () => Promise<void>;
57
86
  togglePictureInPicture: () => Promise<void>;
58
87
  toggleTheaterMode: () => void;
88
+ /** Toggle audio-only mode. Can also be triggered programmatically from outside the player. */
89
+ toggleAudioMode: () => void;
59
90
  getState: () => PlayerState;
60
91
  getVideoElement: () => HTMLVideoElement | null;
61
92
  }
@@ -111,6 +142,34 @@ interface VideoPlayerProps {
111
142
  onDurationChange?: (duration: number) => void;
112
143
  onBuffering?: (isBuffering: boolean) => void;
113
144
  onTheaterModeChange?: (isTheater: boolean) => void;
145
+ /**
146
+ * Image URL or ReactNode shown as artwork in audio mode.
147
+ * Priority: `poster` prop → `logo` string/ReactNode → waveform-only.
148
+ * If a string URL is provided the image is rendered white-normalised (filter invert)
149
+ * so it stands out on the dark background.
150
+ */
151
+ logo?: string | ReactNode;
152
+ /**
153
+ * Show the headphones / audio-mode toggle button in the control bar.
154
+ * @default true
155
+ */
156
+ showAudioButton?: boolean;
157
+ /**
158
+ * Start the player in audio-only mode on mount.
159
+ * @default false
160
+ */
161
+ defaultAudioMode?: boolean;
162
+ /**
163
+ * Bandwidth threshold in **Kbps**. When the measured download speed falls below
164
+ * this value the player automatically switches to audio mode.
165
+ * Use the exported `AUDIO_BANDWIDTH_THRESHOLDS` presets for convenience.
166
+ * Set to `0` to disable automatic switching.
167
+ * Only applies to HLS streams (where hls.js measures real segment bandwidth).
168
+ * @default 300 (AUDIO_BANDWIDTH_THRESHOLDS.POOR)
169
+ */
170
+ audioBandwidthThreshold?: number;
171
+ /** Fired whenever audio mode is toggled — either automatically or by the user. */
172
+ onAudioModeChange?: (isAudio: boolean) => void;
114
173
  contextMenuItems?: ContextMenuItem[];
115
174
  controlBarItems?: ControlBarItem[];
116
175
  }
@@ -131,6 +190,8 @@ interface ControlsProps {
131
190
  isFullscreen: boolean;
132
191
  isPictureInPicture: boolean;
133
192
  isTheaterMode: boolean;
193
+ isAudioMode: boolean;
194
+ showAudioButton: boolean;
134
195
  isLive: boolean;
135
196
  qualityLevels: HLSQualityLevel[];
136
197
  currentQualityLevel: number;
@@ -278,4 +339,4 @@ declare function parseThumbnailVtt(text: string, baseUrl?: string): ThumbnailCue
278
339
  */
279
340
  declare function findThumbnailCue(cues: ThumbnailCue[], time: number): ThumbnailCue | null;
280
341
 
281
- export { type BufferedRange, type ContextMenuItem, type ControlBarItem, index as ControlElements, Controls, type HLSQualityLevel, type PlaybackRate, type PlayerState, type SubtitleTrack, type ThumbnailCue, type VideoError, type VideoErrorCode, VideoPlayer, type VideoPlayerProps, type VideoPlayerRef, findThumbnailCue, formatTime, getMimeType, isHLSUrl, parseThumbnailVtt };
342
+ export { AUDIO_BANDWIDTH_THRESHOLDS, type BufferedRange, type ContextMenuItem, type ControlBarItem, index as ControlElements, Controls, type HLSQualityLevel, type PlaybackRate, type PlayerState, type SubtitleTrack, type ThumbnailCue, type VideoError, type VideoErrorCode, VideoPlayer, type VideoPlayerProps, type VideoPlayerRef, findThumbnailCue, formatTime, getMimeType, isHLSUrl, parseThumbnailVtt };
package/dist/index.d.ts CHANGED
@@ -2,6 +2,33 @@ import * as react from 'react';
2
2
  import react__default, { ReactNode } from 'react';
3
3
  import { HlsConfig } from 'hls.js';
4
4
 
5
+ /**
6
+ * Preset bandwidth thresholds (Kbps) for automatic audio mode switching.
7
+ *
8
+ * | Preset | Kbps | Typical connection |
9
+ * |-----------|------|-------------------------|
10
+ * | EXTREME | 100 | 2G / Edge |
11
+ * | POOR | 300 | Slow 3G ← **default** |
12
+ * | FAIR | 700 | 3G |
13
+ * | GOOD | 1500 | 4G / Wi-Fi |
14
+ *
15
+ * Pass any of these (or a custom number) as `audioBandwidthThreshold`.
16
+ * Set to `0` to disable automatic switching entirely.
17
+ *
18
+ * @example
19
+ * import { AUDIO_BANDWIDTH_THRESHOLDS } from "react-helios";
20
+ * <VideoPlayer audioBandwidthThreshold={AUDIO_BANDWIDTH_THRESHOLDS.FAIR} ... />
21
+ */
22
+ declare const AUDIO_BANDWIDTH_THRESHOLDS: {
23
+ /** < 100 Kbps — very poor, 2G / Edge */
24
+ readonly EXTREME: 100;
25
+ /** < 300 Kbps — poor, slow 3G (default) */
26
+ readonly POOR: 300;
27
+ /** < 700 Kbps — fair, 3G */
28
+ readonly FAIR: 700;
29
+ /** < 1500 Kbps — decent, 4G / Wi-Fi */
30
+ readonly GOOD: 1500;
31
+ };
5
32
  interface BufferedRange {
6
33
  start: number;
7
34
  end: number;
@@ -39,6 +66,8 @@ interface PlayerState {
39
66
  isFullscreen: boolean;
40
67
  isPictureInPicture: boolean;
41
68
  isTheaterMode: boolean;
69
+ /** True when the player is in audio-only mode (video hidden, waveform shown). */
70
+ isAudioMode: boolean;
42
71
  isLive: boolean;
43
72
  qualityLevels: HLSQualityLevel[];
44
73
  currentQualityLevel: number;
@@ -56,6 +85,8 @@ interface VideoPlayerRef {
56
85
  toggleFullscreen: () => Promise<void>;
57
86
  togglePictureInPicture: () => Promise<void>;
58
87
  toggleTheaterMode: () => void;
88
+ /** Toggle audio-only mode. Can also be triggered programmatically from outside the player. */
89
+ toggleAudioMode: () => void;
59
90
  getState: () => PlayerState;
60
91
  getVideoElement: () => HTMLVideoElement | null;
61
92
  }
@@ -111,6 +142,34 @@ interface VideoPlayerProps {
111
142
  onDurationChange?: (duration: number) => void;
112
143
  onBuffering?: (isBuffering: boolean) => void;
113
144
  onTheaterModeChange?: (isTheater: boolean) => void;
145
+ /**
146
+ * Image URL or ReactNode shown as artwork in audio mode.
147
+ * Priority: `poster` prop → `logo` string/ReactNode → waveform-only.
148
+ * If a string URL is provided the image is rendered white-normalised (filter invert)
149
+ * so it stands out on the dark background.
150
+ */
151
+ logo?: string | ReactNode;
152
+ /**
153
+ * Show the headphones / audio-mode toggle button in the control bar.
154
+ * @default true
155
+ */
156
+ showAudioButton?: boolean;
157
+ /**
158
+ * Start the player in audio-only mode on mount.
159
+ * @default false
160
+ */
161
+ defaultAudioMode?: boolean;
162
+ /**
163
+ * Bandwidth threshold in **Kbps**. When the measured download speed falls below
164
+ * this value the player automatically switches to audio mode.
165
+ * Use the exported `AUDIO_BANDWIDTH_THRESHOLDS` presets for convenience.
166
+ * Set to `0` to disable automatic switching.
167
+ * Only applies to HLS streams (where hls.js measures real segment bandwidth).
168
+ * @default 300 (AUDIO_BANDWIDTH_THRESHOLDS.POOR)
169
+ */
170
+ audioBandwidthThreshold?: number;
171
+ /** Fired whenever audio mode is toggled — either automatically or by the user. */
172
+ onAudioModeChange?: (isAudio: boolean) => void;
114
173
  contextMenuItems?: ContextMenuItem[];
115
174
  controlBarItems?: ControlBarItem[];
116
175
  }
@@ -131,6 +190,8 @@ interface ControlsProps {
131
190
  isFullscreen: boolean;
132
191
  isPictureInPicture: boolean;
133
192
  isTheaterMode: boolean;
193
+ isAudioMode: boolean;
194
+ showAudioButton: boolean;
134
195
  isLive: boolean;
135
196
  qualityLevels: HLSQualityLevel[];
136
197
  currentQualityLevel: number;
@@ -278,4 +339,4 @@ declare function parseThumbnailVtt(text: string, baseUrl?: string): ThumbnailCue
278
339
  */
279
340
  declare function findThumbnailCue(cues: ThumbnailCue[], time: number): ThumbnailCue | null;
280
341
 
281
- export { type BufferedRange, type ContextMenuItem, type ControlBarItem, index as ControlElements, Controls, type HLSQualityLevel, type PlaybackRate, type PlayerState, type SubtitleTrack, type ThumbnailCue, type VideoError, type VideoErrorCode, VideoPlayer, type VideoPlayerProps, type VideoPlayerRef, findThumbnailCue, formatTime, getMimeType, isHLSUrl, parseThumbnailVtt };
342
+ export { AUDIO_BANDWIDTH_THRESHOLDS, type BufferedRange, type ContextMenuItem, type ControlBarItem, index as ControlElements, Controls, type HLSQualityLevel, type PlaybackRate, type PlayerState, type SubtitleTrack, type ThumbnailCue, type VideoError, type VideoErrorCode, VideoPlayer, type VideoPlayerProps, type VideoPlayerRef, findThumbnailCue, formatTime, getMimeType, isHLSUrl, parseThumbnailVtt };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- 'use strict';var Lt=require('react'),fe=require('hls.js'),jsxRuntime=require('react/jsx-runtime');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var Lt__default=/*#__PURE__*/_interopDefault(Lt);var fe__default=/*#__PURE__*/_interopDefault(fe);var Xe=Object.defineProperty;var je=(n,e)=>{for(var d in e)Xe(n,d,{get:e[d],enumerable:true});};function Z(n){if(!Number.isFinite(n)||n<0)return "0:00";let e=Math.floor(n),d=Math.floor(e/3600),s=Math.floor(e%3600/60),o=e%60;return d>0?`${d}:${String(s).padStart(2,"0")}:${String(o).padStart(2,"0")}`:`${s}:${String(o).padStart(2,"0")}`}function pe(n){try{return new URL(n,"https://x").pathname.toLowerCase().endsWith(".m3u8")||/\/hls\//i.test(n)||/\/stream\.m3u8/i.test(n)}catch{return n.toLowerCase().includes(".m3u8")}}function Ge(n){if(pe(n))return "application/x-mpegURL";let e=n.toLowerCase().split("?")[0];return e.endsWith(".mp4")?"video/mp4":e.endsWith(".webm")?"video/webm":e.endsWith(".ogv")||e.endsWith(".ogg")?"video/ogg":e.endsWith(".mov")?"video/quicktime":"video/mp4"}function Se(n){return n.map((e,d)=>({id:d,height:e.height??0,width:e.width??0,bitrate:e.bitrate??0,name:e.height?`${e.height}p`:`Level ${d+1}`}))}var Ze={isPlaying:false,currentTime:0,duration:0,volume:1,isMuted:false,playbackRate:1,isFullscreen:false,isPictureInPicture:false,isTheaterMode:false,isBuffering:false,bufferedRanges:[],error:null,isLive:false,qualityLevels:[],currentQualityLevel:-1};function Be(n,e,d={}){let s=Lt.useRef(null),o=Lt.useRef(null),h=Lt.useRef(1),c=Lt.useRef(0),l=Lt.useRef(d);l.current=d;let[y,m]=Lt.useState({...Ze,isMuted:d.muted??false,volume:d.muted?0:1}),x=Lt.useRef(y);x.current=y,Lt.useEffect(()=>{let t=n.current;if(!t||(s.current&&(s.current.destroy(),s.current=null),c.current=0,m(f=>({...f,currentTime:0,duration:0,error:null,isBuffering:false,isLive:false,qualityLevels:[],currentQualityLevel:-1})),!e))return;let u=l.current;if(u.enableHLS!==false&&pe(e)){if(t.canPlayType("application/vnd.apple.mpegurl"))t.src=e,t.load(),u.autoplay&&t.play().catch(()=>{});else if(fe__default.default.isSupported()){let f=new fe__default.default({autoStartLoad:true,startLevel:-1,capLevelToPlayerSize:true,capLevelOnFPSDrop:true,enableWorker:true,maxBufferLength:30,maxMaxBufferLength:600,maxBufferSize:6e7,liveBackBufferLength:30,liveSyncDurationCount:3,...u.hlsConfig});f.attachMedia(t),f.loadSource(e),f.on(fe.Events.MANIFEST_PARSED,(w,B)=>{let p=Se(B.levels);m(v=>({...v,qualityLevels:p,currentQualityLevel:-1})),l.current.autoplay&&t.play().catch(()=>{});}),f.on(fe.Events.LEVEL_SWITCHED,(w,B)=>{m(p=>({...p,currentQualityLevel:B.level}));});let b=3;f.on(fe.Events.ERROR,(w,B)=>{if(!B.fatal){console.warn("[hls] non-fatal:",B.details);return}switch(B.type){case fe__default.default.ErrorTypes.NETWORK_ERROR:if(c.current<b){c.current+=1;let p=1e3*c.current;console.warn(`[hls] network error \u2013 retry ${c.current}/${b} in ${p}ms`),setTimeout(()=>{s.current===f&&f.startLoad();},p);}else {let p={code:"HLS_NETWORK_ERROR",message:"Failed to load stream after multiple retries."};m(v=>({...v,error:p})),l.current.onError?.(p);}break;case fe__default.default.ErrorTypes.MEDIA_ERROR:console.warn("[hls] media error \u2013 recovering"),f.recoverMediaError();break;default:{f.destroy(),s.current=null;let p={code:"HLS_FATAL_ERROR",message:"An unrecoverable HLS error occurred."};m(v=>({...v,error:p})),l.current.onError?.(p);break}}}),s.current=f;}}else t.src=e,t.load(),u.autoplay&&t.play().catch(()=>{});return ()=>{s.current&&(s.current.destroy(),s.current=null);}},[e,n]),Lt.useEffect(()=>{let t=n.current;if(!t)return;l.current.muted&&(t.muted=true),l.current.loop&&(t.loop=true);let u=()=>{m(a=>({...a,isPlaying:true})),l.current.onPlay?.();},f=()=>{m(a=>({...a,isPlaying:false})),l.current.onPause?.();},b=()=>{m(a=>({...a,isPlaying:false})),l.current.onEnded?.();},w=()=>{l.current.onTimeUpdate?.(t.currentTime);},B=()=>{let a=t.duration,E=!Number.isFinite(a);m(I=>({...I,duration:E?0:a,isLive:E})),E||l.current.onDurationChange?.(a);},p=()=>{let a=t.volume;a>0&&!t.muted&&(h.current=a),m(E=>({...E,volume:a,isMuted:t.muted||a===0}));},v=()=>{m(a=>({...a,playbackRate:t.playbackRate}));},U=()=>{let a=t.error;if(!a)return;let I={code:{1:"MEDIA_ERR_ABORTED",2:"MEDIA_ERR_NETWORK",3:"MEDIA_ERR_DECODE",4:"MEDIA_ERR_SRC_NOT_SUPPORTED"}[a.code]??"UNKNOWN",message:a.message||"Unknown media error"};m(te=>({...te,error:I})),l.current.onError?.(I);},G=()=>{m(a=>({...a,isBuffering:true})),l.current.onBuffering?.(true);},X=()=>{m(a=>({...a,isBuffering:false})),l.current.onBuffering?.(false);},V=()=>m(a=>({...a,isBuffering:false})),J=()=>{},r=()=>{let a=!!(document.fullscreenElement||document.webkitFullscreenElement);m(E=>({...E,isFullscreen:a}));},i=()=>{m(a=>({...a,isPictureInPicture:document.pictureInPictureElement===t}));};return t.addEventListener("play",u),t.addEventListener("pause",f),t.addEventListener("ended",b),t.addEventListener("timeupdate",w),t.addEventListener("durationchange",B),t.addEventListener("volumechange",p),t.addEventListener("ratechange",v),t.addEventListener("error",U),t.addEventListener("waiting",G),t.addEventListener("canplay",X),t.addEventListener("playing",V),t.addEventListener("progress",J),document.addEventListener("fullscreenchange",r),document.addEventListener("webkitfullscreenchange",r),t.addEventListener("enterpictureinpicture",i),t.addEventListener("leavepictureinpicture",i),()=>{t.removeEventListener("play",u),t.removeEventListener("pause",f),t.removeEventListener("ended",b),t.removeEventListener("timeupdate",w),t.removeEventListener("durationchange",B),t.removeEventListener("volumechange",p),t.removeEventListener("ratechange",v),t.removeEventListener("error",U),t.removeEventListener("waiting",G),t.removeEventListener("canplay",X),t.removeEventListener("playing",V),t.removeEventListener("progress",J),document.removeEventListener("fullscreenchange",r),document.removeEventListener("webkitfullscreenchange",r),t.removeEventListener("enterpictureinpicture",i),t.removeEventListener("leavepictureinpicture",i);}},[n]);let k=Lt.useCallback(async()=>{let t=n.current;if(t)try{await t.play();}catch(u){u instanceof Error&&u.name!=="AbortError"&&console.error("[player] play() failed:",u);}},[n]),H=Lt.useCallback(()=>{n.current?.pause();},[n]),P=Lt.useCallback(t=>{let u=n.current;u&&(u.currentTime=Math.max(0,Math.min(t,u.duration||t)));},[n]),g=Lt.useCallback(t=>{let u=n.current;if(!u)return;let f=Math.max(0,Math.min(t,1));f>0&&(h.current=f),u.volume=f,u.muted=f===0;},[n]),R=Lt.useCallback(()=>{let t=n.current;if(t)if(t.muted||t.volume===0){let u=h.current>0?h.current:1;t.volume=u,t.muted=false;}else h.current=t.volume,t.muted=true;},[n]),N=Lt.useCallback(t=>{let u=n.current;u&&(u.playbackRate=t);},[n]),M=Lt.useCallback(t=>{let u=s.current;u&&(u.currentLevel=t,m(f=>({...f,currentQualityLevel:t})));},[]),L=Lt.useCallback(()=>{let t=s.current,u=n.current;if(!t||!u)return;let f=t.liveSyncPosition;f!=null&&Number.isFinite(f)&&(u.currentTime=f);},[n]),T=Lt.useCallback(async()=>{let t=n.current;if(!t)return;let u=o.current??t.parentElement;if(u)try{!document.fullscreenElement&&!document.webkitFullscreenElement?u.requestFullscreen?await u.requestFullscreen():u.webkitRequestFullscreen?.():document.exitFullscreen?await document.exitFullscreen():document.webkitExitFullscreen?.();}catch(f){console.error("[player] fullscreen toggle failed:",f);}},[n]),F=Lt.useCallback(async()=>{let t=n.current;if(t)try{document.pictureInPictureElement?await document.exitPictureInPicture():await t.requestPictureInPicture();}catch(u){console.error("[player] PiP toggle failed:",u);}},[n]),C=Lt.useCallback(()=>{m(t=>{let u=!t.isTheaterMode;return l.current.onTheaterModeChange?.(u),{...t,isTheaterMode:u}});},[]),Q=Lt.useCallback(()=>{let t=n.current,u=t?.currentTime??0,f=[];if(t)for(let b=0;b<t.buffered.length;b++)f.push({start:t.buffered.start(b),end:t.buffered.end(b)});return {...x.current,currentTime:u,bufferedRanges:f}},[n]),q=Lt.useCallback(()=>n.current??null,[n]),j=Lt.useMemo(()=>({play:k,pause:H,seek:P,setVolume:g,toggleMute:R,setPlaybackRate:N,setQualityLevel:M,seekToLive:L,toggleFullscreen:T,togglePictureInPicture:F,toggleTheaterMode:C,getState:Q,getVideoElement:q}),[k,H,P,g,R,N,M,L,T,F,C,Q,q]);return {state:y,ref:j,hlsRef:s,fullscreenContainerRef:o}}var Ae={};je(Ae,{ControlElements:()=>_,FullscreenButton:()=>ue,PauseButton:()=>le,PiPButton:()=>ce,PlayButton:()=>se,ProgressBar:()=>he,SettingsMenu:()=>ge,TheaterButton:()=>de,TimeDisplay:()=>ye,VolumeControl:()=>ve});var se=Lt.memo(({onClick:n})=>jsxRuntime.jsx("button",{onClick:n,className:"controlButton","aria-label":"Play",title:"Play (Space)",children:jsxRuntime.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:jsxRuntime.jsx("path",{d:"M8 5v14l11-7z"})})}));se.displayName="PlayButton";var le=Lt.memo(({onClick:n})=>jsxRuntime.jsx("button",{onClick:n,className:"controlButton","aria-label":"Pause",title:"Pause (Space)",children:jsxRuntime.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:jsxRuntime.jsx("path",{d:"M6 4h4v16H6V4zm8 0h4v16h-4V4z"})})}));le.displayName="PauseButton";var ue=Lt.memo(({onClick:n,isFullscreen:e=false})=>jsxRuntime.jsx("button",{onClick:n,className:"controlButton","aria-label":e?"Exit Fullscreen":"Fullscreen",title:e?"Exit Fullscreen (F)":"Fullscreen (F)",children:jsxRuntime.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:e?jsxRuntime.jsx("path",{d:"M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"}):jsxRuntime.jsx("path",{d:"M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"})})}));ue.displayName="FullscreenButton";var ce=Lt.memo(({onClick:n,isPiP:e=false})=>jsxRuntime.jsx("button",{onClick:n,className:"controlButton","aria-label":e?"Exit Picture-in-Picture":"Picture-in-Picture",title:e?"Exit Picture-in-Picture (P)":"Picture-in-Picture (P)",children:jsxRuntime.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:jsxRuntime.jsx("path",{d:"M19 11h-8v6h8v-6zm4 8V4.98C23 3.88 22.1 3 21 3H3c-1.1 0-2 .88-2 1.98V19c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2zm-2 .02H3V5h18v14.02z"})})}));ce.displayName="PiPButton";var de=Lt.memo(({onClick:n,isTheater:e=false})=>jsxRuntime.jsx("button",{onClick:n,className:"controlButton","aria-label":e?"Exit Theater Mode":"Theater Mode",title:e?"Exit Theater Mode (T)":"Theater Mode (T)",children:jsxRuntime.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:e?jsxRuntime.jsx("path",{d:"M19 7H5c-1.1 0-2 .9-2 2v6c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2zm0 8H5V9h14v6z"}):jsxRuntime.jsx("path",{d:"M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14z"})})}));de.displayName="TheaterButton";var De=Lt.memo(({volume:n,isMuted:e,onVolumeChange:d,onToggleMute:s})=>{let[o,h]=Lt.useState(false),c=e?0:n,l=c*100,y=Lt.useMemo(()=>`linear-gradient(to right, #60a5fa 0%, #60a5fa ${l}%, rgba(255,255,255,0.3) ${l}%, rgba(255,255,255,0.3) 100%)`,[l]);return jsxRuntime.jsxs("div",{className:"volumeContainer",onMouseEnter:()=>h(true),onMouseLeave:()=>h(false),children:[jsxRuntime.jsx("button",{onClick:s,className:"controlButton","aria-label":e?"Unmute":"Mute",title:e?"Unmute (M)":"Mute (M)",children:jsxRuntime.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:c===0?jsxRuntime.jsx("path",{d:"M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C23.16 14.42 24 13.3 24 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"}):c<.5?jsxRuntime.jsx("path",{d:"M7 9v6h4l5 5V4l-5 5H7z"}):jsxRuntime.jsx("path",{d:"M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"})})}),o&&jsxRuntime.jsx("input",{type:"range",min:"0",max:"100",value:l,onChange:m=>d(Number(m.target.value)/100),className:"volumeSlider",style:{background:y},"aria-label":"Volume","aria-valuenow":Math.round(l)})]})});De.displayName="VolumeControl";var ve=De;function Ve(n){let e=n.trim().split(":");return e.length===3?+e[0]*3600+ +e[1]*60+parseFloat(e[2]):+e[0]*60+parseFloat(e[1])}function ot(n,e){if(/^https?:\/\//i.test(e))return e;try{return new URL(e,n).href}catch{return e}}function Le(n,e=""){let d=[],s=n.replace(/\r\n/g,`
1
+ 'use strict';var St=require('react'),ye=require('hls.js'),jsxRuntime=require('react/jsx-runtime');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var St__default=/*#__PURE__*/_interopDefault(St);var ye__default=/*#__PURE__*/_interopDefault(ye);var nt=Object.defineProperty;var ot=(t,e)=>{for(var c in e)nt(t,c,{get:e[c],enumerable:true});};function oe(t){if(!Number.isFinite(t)||t<0)return "0:00";let e=Math.floor(t),c=Math.floor(e/3600),s=Math.floor(e%3600/60),o=e%60;return c>0?`${c}:${String(s).padStart(2,"0")}:${String(o).padStart(2,"0")}`:`${s}:${String(o).padStart(2,"0")}`}function be(t){try{return new URL(t,"https://x").pathname.toLowerCase().endsWith(".m3u8")||/\/hls\//i.test(t)||/\/stream\.m3u8/i.test(t)}catch{return t.toLowerCase().includes(".m3u8")}}function it(t){if(be(t))return "application/x-mpegURL";let e=t.toLowerCase().split("?")[0];return e.endsWith(".mp4")?"video/mp4":e.endsWith(".webm")?"video/webm":e.endsWith(".ogv")||e.endsWith(".ogg")?"video/ogg":e.endsWith(".mov")?"video/quicktime":"video/mp4"}function Ae(t){return t.map((e,c)=>({id:c,height:e.height??0,width:e.width??0,bitrate:e.bitrate??0,name:e.height?`${e.height}p`:`Level ${c+1}`}))}var lt={isPlaying:false,currentTime:0,duration:0,volume:1,isMuted:false,playbackRate:1,isFullscreen:false,isPictureInPicture:false,isTheaterMode:false,isAudioMode:false,isBuffering:false,bufferedRanges:[],error:null,isLive:false,qualityLevels:[],currentQualityLevel:-1};function Ie(t,e,c={}){let s=St.useRef(null),o=St.useRef(null),g=St.useRef(1),u=St.useRef(0),a=St.useRef(c);a.current=c;let[x,d]=St.useState({...lt,isMuted:c.muted??false,volume:c.muted?0:1,isAudioMode:c.defaultAudioMode??false}),k=St.useRef(x);k.current=x;let w=St.useRef([]),B=St.useRef(false),T=St.useRef(false),p=St.useRef(null);St.useEffect(()=>{let r=t.current;if(!r||(s.current&&(s.current.destroy(),s.current=null),u.current=0,w.current=[],B.current=false,T.current=false,p.current&&(clearTimeout(p.current),p.current=null),d(f=>({...f,currentTime:0,duration:0,error:null,isBuffering:false,isLive:false,qualityLevels:[],currentQualityLevel:-1,isAudioMode:a.current.defaultAudioMode??false})),!e))return;let l=a.current;if(l.enableHLS!==false&&be(e)){if(r.canPlayType("application/vnd.apple.mpegurl"))r.src=e,r.load(),l.autoplay&&r.play().catch(()=>{});else if(ye__default.default.isSupported()){let f=new ye__default.default({autoStartLoad:true,startLevel:-1,capLevelToPlayerSize:true,capLevelOnFPSDrop:true,enableWorker:true,maxBufferLength:30,maxMaxBufferLength:600,maxBufferSize:6e7,liveBackBufferLength:30,liveSyncDurationCount:3,...l.hlsConfig});f.attachMedia(r),f.loadSource(e),f.on(ye.Events.MANIFEST_PARSED,(m,v)=>{let E=Ae(v.levels);d(D=>({...D,qualityLevels:E,currentQualityLevel:-1})),a.current.autoplay&&r.play().catch(()=>{});}),f.on(ye.Events.LEVEL_SWITCHED,(m,v)=>{d(E=>({...E,currentQualityLevel:v.level}));}),f.on(ye.Events.FRAG_LOADED,()=>{let m=a.current.audioBandwidthThreshold??300;if(!m||T.current)return;let v=f.bandwidthEstimate/1e3;if(v<=0)return;let E=w.current;if(E.push(v),E.length>5&&E.shift(),E.length<3)return;let D=E.reduce((n,i)=>n+i,0)/E.length;d(n=>!n.isAudioMode&&D<m?(B.current=true,a.current.onAudioModeChange?.(true),{...n,isAudioMode:true}):n.isAudioMode&&B.current&&D>m*1.5?(B.current=false,a.current.onAudioModeChange?.(false),{...n,isAudioMode:false}):n);});let h=3;f.on(ye.Events.ERROR,(m,v)=>{if(!v.fatal){console.warn("[hls] non-fatal:",v.details);return}switch(v.type){case ye__default.default.ErrorTypes.NETWORK_ERROR:if(u.current<h){u.current+=1;let E=1e3*u.current;console.warn(`[hls] network error \u2013 retry ${u.current}/${h} in ${E}ms`),setTimeout(()=>{s.current===f&&f.startLoad();},E);}else {let E={code:"HLS_NETWORK_ERROR",message:"Failed to load stream after multiple retries."};d(D=>({...D,error:E})),a.current.onError?.(E);}break;case ye__default.default.ErrorTypes.MEDIA_ERROR:console.warn("[hls] media error \u2013 recovering"),f.recoverMediaError();break;default:{f.destroy(),s.current=null;let E={code:"HLS_FATAL_ERROR",message:"An unrecoverable HLS error occurred."};d(D=>({...D,error:E})),a.current.onError?.(E);break}}}),s.current=f;}}else r.src=e,r.load(),l.autoplay&&r.play().catch(()=>{});return ()=>{s.current&&(s.current.destroy(),s.current=null),p.current&&(clearTimeout(p.current),p.current=null);}},[e,t]),St.useEffect(()=>{let r=t.current;if(!r)return;a.current.muted&&(r.muted=true),a.current.loop&&(r.loop=true);let l=()=>{d(P=>({...P,isPlaying:true})),a.current.onPlay?.();},f=()=>{d(P=>({...P,isPlaying:false})),a.current.onPause?.();},h=()=>{d(P=>({...P,isPlaying:false})),a.current.onEnded?.();},m=()=>{a.current.onTimeUpdate?.(r.currentTime);},v=()=>{let P=r.duration,te=!Number.isFinite(P);d(ge=>({...ge,duration:te?0:P,isLive:te})),te||a.current.onDurationChange?.(P);},E=()=>{let P=r.volume;P>0&&!r.muted&&(g.current=P),d(te=>({...te,volume:P,isMuted:r.muted||P===0}));},D=()=>{d(P=>({...P,playbackRate:r.playbackRate}));},n=()=>{let P=r.error;if(!P)return;let ge={code:{1:"MEDIA_ERR_ABORTED",2:"MEDIA_ERR_NETWORK",3:"MEDIA_ERR_DECODE",4:"MEDIA_ERR_SRC_NOT_SUPPORTED"}[P.code]??"UNKNOWN",message:P.message||"Unknown media error"};d(rt=>({...rt,error:ge})),a.current.onError?.(ge);},i=()=>{d(P=>({...P,isBuffering:true})),a.current.onBuffering?.(true);},b=()=>{d(P=>({...P,isBuffering:false})),a.current.onBuffering?.(false);},y=()=>d(P=>({...P,isBuffering:false})),F=()=>{},Y=()=>{let P=!!(document.fullscreenElement||document.webkitFullscreenElement);d(te=>({...te,isFullscreen:P}));},ae=()=>{d(P=>({...P,isPictureInPicture:document.pictureInPictureElement===r}));};return r.addEventListener("play",l),r.addEventListener("pause",f),r.addEventListener("ended",h),r.addEventListener("timeupdate",m),r.addEventListener("durationchange",v),r.addEventListener("volumechange",E),r.addEventListener("ratechange",D),r.addEventListener("error",n),r.addEventListener("waiting",i),r.addEventListener("canplay",b),r.addEventListener("playing",y),r.addEventListener("progress",F),document.addEventListener("fullscreenchange",Y),document.addEventListener("webkitfullscreenchange",Y),r.addEventListener("enterpictureinpicture",ae),r.addEventListener("leavepictureinpicture",ae),()=>{r.removeEventListener("play",l),r.removeEventListener("pause",f),r.removeEventListener("ended",h),r.removeEventListener("timeupdate",m),r.removeEventListener("durationchange",v),r.removeEventListener("volumechange",E),r.removeEventListener("ratechange",D),r.removeEventListener("error",n),r.removeEventListener("waiting",i),r.removeEventListener("canplay",b),r.removeEventListener("playing",y),r.removeEventListener("progress",F),document.removeEventListener("fullscreenchange",Y),document.removeEventListener("webkitfullscreenchange",Y),r.removeEventListener("enterpictureinpicture",ae),r.removeEventListener("leavepictureinpicture",ae);}},[t]);let R=St.useCallback(async()=>{let r=t.current;if(r)try{await r.play();}catch(l){l instanceof Error&&l.name!=="AbortError"&&console.error("[player] play() failed:",l);}},[t]),V=St.useCallback(()=>{t.current?.pause();},[t]),O=St.useCallback(r=>{let l=t.current;l&&(l.currentTime=Math.max(0,Math.min(r,l.duration||r)));},[t]),M=St.useCallback(r=>{let l=t.current;if(!l)return;let f=Math.max(0,Math.min(r,1));f>0&&(g.current=f),l.volume=f,l.muted=f===0;},[t]),L=St.useCallback(()=>{let r=t.current;if(r)if(r.muted||r.volume===0){let l=g.current>0?g.current:1;r.volume=l,r.muted=false;}else g.current=r.volume,r.muted=true;},[t]),I=St.useCallback(r=>{let l=t.current;l&&(l.playbackRate=r);},[t]),C=St.useCallback(r=>{let l=s.current;l&&(l.currentLevel=r,d(f=>({...f,currentQualityLevel:r})));},[]),W=St.useCallback(()=>{let r=s.current,l=t.current;if(!r||!l)return;let f=r.liveSyncPosition;f!=null&&Number.isFinite(f)&&(l.currentTime=f);},[t]),X=St.useCallback(async()=>{let r=t.current;if(!r)return;let l=o.current??r.parentElement;if(l)try{!document.fullscreenElement&&!document.webkitFullscreenElement?l.requestFullscreen?await l.requestFullscreen():l.webkitRequestFullscreen?.():document.exitFullscreen?await document.exitFullscreen():document.webkitExitFullscreen?.();}catch(f){console.error("[player] fullscreen toggle failed:",f);}},[t]),j=St.useCallback(async()=>{let r=t.current;if(r)try{document.pictureInPictureElement?await document.exitPictureInPicture():await r.requestPictureInPicture();}catch(l){console.error("[player] PiP toggle failed:",l);}},[t]),H=St.useCallback(()=>{let r=!k.current.isTheaterMode;d(l=>({...l,isTheaterMode:r})),a.current.onTheaterModeChange?.(r);},[]),G=St.useCallback(()=>{p.current&&clearTimeout(p.current),B.current=false,T.current=true,p.current=setTimeout(()=>{T.current=false,w.current=[];},6e4);let r=!k.current.isAudioMode;d(l=>({...l,isAudioMode:r})),a.current.onAudioModeChange?.(r);},[]),Z=St.useCallback(()=>{let r=t.current,l=r?.currentTime??0,f=[];if(r)for(let h=0;h<r.buffered.length;h++)f.push({start:r.buffered.start(h),end:r.buffered.end(h)});return {...k.current,currentTime:l,bufferedRanges:f}},[t]),ee=St.useCallback(()=>t.current??null,[t]),ne=St.useMemo(()=>({play:R,pause:V,seek:O,setVolume:M,toggleMute:L,setPlaybackRate:I,setQualityLevel:C,seekToLive:W,toggleFullscreen:X,togglePictureInPicture:j,toggleTheaterMode:H,toggleAudioMode:G,getState:Z,getVideoElement:ee}),[R,V,O,M,L,I,C,W,X,j,H,G,Z,ee]);return {state:x,ref:ne,hlsRef:s,fullscreenContainerRef:o}}var Ke={};ot(Ke,{ControlElements:()=>q,FullscreenButton:()=>pe,PauseButton:()=>me,PiPButton:()=>ve,PlayButton:()=>de,ProgressBar:()=>Le,SettingsMenu:()=>xe,TheaterButton:()=>fe,TimeDisplay:()=>Te,VolumeControl:()=>Pe});var de=St.memo(({onClick:t})=>jsxRuntime.jsx("button",{onClick:t,className:"controlButton","aria-label":"Play",title:"Play (Space)",children:jsxRuntime.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:jsxRuntime.jsx("path",{d:"M8 5v14l11-7z"})})}));de.displayName="PlayButton";var me=St.memo(({onClick:t})=>jsxRuntime.jsx("button",{onClick:t,className:"controlButton","aria-label":"Pause",title:"Pause (Space)",children:jsxRuntime.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:jsxRuntime.jsx("path",{d:"M6 4h4v16H6V4zm8 0h4v16h-4V4z"})})}));me.displayName="PauseButton";var pe=St.memo(({onClick:t,isFullscreen:e=false})=>jsxRuntime.jsx("button",{onClick:t,className:"controlButton","aria-label":e?"Exit Fullscreen":"Fullscreen",title:e?"Exit Fullscreen (F)":"Fullscreen (F)",children:jsxRuntime.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:e?jsxRuntime.jsx("path",{d:"M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"}):jsxRuntime.jsx("path",{d:"M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"})})}));pe.displayName="FullscreenButton";var ve=St.memo(({onClick:t,isPiP:e=false})=>jsxRuntime.jsx("button",{onClick:t,className:"controlButton","aria-label":e?"Exit Picture-in-Picture":"Picture-in-Picture",title:e?"Exit Picture-in-Picture (P)":"Picture-in-Picture (P)",children:jsxRuntime.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:jsxRuntime.jsx("path",{d:"M19 11h-8v6h8v-6zm4 8V4.98C23 3.88 22.1 3 21 3H3c-1.1 0-2 .88-2 1.98V19c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2zm-2 .02H3V5h18v14.02z"})})}));ve.displayName="PiPButton";var fe=St.memo(({onClick:t,isTheater:e=false})=>jsxRuntime.jsx("button",{onClick:t,className:"controlButton","aria-label":e?"Exit Theater Mode":"Theater Mode",title:e?"Exit Theater Mode (T)":"Theater Mode (T)",children:jsxRuntime.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:e?jsxRuntime.jsx("path",{d:"M19 7H5c-1.1 0-2 .9-2 2v6c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2zm0 8H5V9h14v6z"}):jsxRuntime.jsx("path",{d:"M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14z"})})}));fe.displayName="TheaterButton";var Oe=St.memo(({volume:t,isMuted:e,onVolumeChange:c,onToggleMute:s})=>{let[o,g]=St.useState(false),u=e?0:t,a=u*100,x=St.useMemo(()=>`linear-gradient(to right, #60a5fa 0%, #60a5fa ${a}%, rgba(255,255,255,0.3) ${a}%, rgba(255,255,255,0.3) 100%)`,[a]);return jsxRuntime.jsxs("div",{className:"volumeContainer",onMouseEnter:()=>g(true),onMouseLeave:()=>g(false),children:[jsxRuntime.jsx("button",{onClick:s,className:"controlButton","aria-label":e?"Unmute":"Mute",title:e?"Unmute (M)":"Mute (M)",children:jsxRuntime.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:u===0?jsxRuntime.jsx("path",{d:"M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C23.16 14.42 24 13.3 24 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"}):u<.5?jsxRuntime.jsx("path",{d:"M7 9v6h4l5 5V4l-5 5H7z"}):jsxRuntime.jsx("path",{d:"M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"})})}),o&&jsxRuntime.jsx("input",{type:"range",min:"0",max:"100",value:a,onChange:d=>c(Number(d.target.value)/100),className:"volumeSlider",style:{background:x},"aria-label":"Volume","aria-valuenow":Math.round(a)})]})});Oe.displayName="VolumeControl";var Pe=Oe;function Fe(t){let e=t.trim().split(":");return e.length===3?+e[0]*3600+ +e[1]*60+parseFloat(e[2]):+e[0]*60+parseFloat(e[1])}function pt(t,e){if(/^https?:\/\//i.test(e))return e;try{return new URL(e,t).href}catch{return e}}function ke(t,e=""){let c=[],s=t.replace(/\r\n/g,`
2
2
  `).split(`
3
- `),o=0;for(;o<s.length;){let h=s[o].trim();if(h.includes("-->")){let c=h.indexOf("-->"),l=Ve(h.slice(0,c)),y=Ve(h.slice(c+3));for(o++;o<s.length&&!s[o].trim();)o++;if(o<s.length){let m=s[o].trim(),x=m.lastIndexOf("#xywh="),k=m,H=0,P=0,g=160,R=90;if(x!==-1){k=m.slice(0,x);let N=m.slice(x+6).split(",").map(Number);H=N[0]??0,P=N[1]??0,g=N[2]??160,R=N[3]??90;}d.push({start:l,end:y,url:ot(e,k),x:H,y:P,w:g,h:R});}}o++;}return d}function Pe(n,e){if(!n.length)return null;let d=0,s=n.length-1;for(;d<=s;){let o=d+s>>1;if(n[o].end<=e)d=o+1;else if(n[o].start>e)s=o-1;else return n[o]}return null}var He=Lt.memo(({videoRef:n,playerRef:e,enablePreview:d=true,thumbnailVtt:s})=>{let o=Lt.useRef(null),h=Lt.useRef(null),c=Lt.useRef(null),l=Lt.useRef(null),y=Lt.useRef(null),m=Lt.useRef(null),x=Lt.useRef(null),[k,H]=Lt.useState([]),P=Lt.useRef(false),g=Lt.useRef(0),R=Lt.useRef(0),N=Lt.useRef(null),M=Lt.useRef([]),L=Lt.useRef(null);Lt.useEffect(()=>{let r=()=>{L.current=null;};return window.addEventListener("resize",r,{passive:true}),()=>window.removeEventListener("resize",r)},[]);let T=Lt.useCallback(()=>(!L.current&&o.current&&(L.current=o.current.getBoundingClientRect()),L.current),[]);Lt.useEffect(()=>{if(!s){M.current=[];return}let r=false;return fetch(s).then(i=>i.text()).then(i=>{r||(M.current=Le(i,s));}).catch(()=>{r||(M.current=[]);}),()=>{r=true;}},[s]),Lt.useEffect(()=>{let r=n.current;if(!r)return;let i=()=>{let a=isFinite(r.duration)?r.duration:0,E=r.currentTime,I=a>0?E/a*100:0;h.current&&(h.current.style.width=`${I}%`),c.current&&(c.current.style.left=`${I}%`),o.current&&(o.current.setAttribute("aria-valuenow",String(Math.round(E))),o.current.setAttribute("aria-valuemax",String(Math.round(a))),o.current.setAttribute("aria-valuetext",Z(E)));};return r.addEventListener("timeupdate",i),r.addEventListener("durationchange",i),r.addEventListener("seeked",i),i(),()=>{r.removeEventListener("timeupdate",i),r.removeEventListener("durationchange",i),r.removeEventListener("seeked",i);}},[n]),Lt.useEffect(()=>{let r=n.current;if(!r)return;let i=()=>{let a=[];for(let E=0;E<r.buffered.length;E++)a.push({start:r.buffered.start(E),end:r.buffered.end(E)});H(a);};return r.addEventListener("progress",i),()=>r.removeEventListener("progress",i)},[n]);let F=Lt.useCallback(()=>{P.current=true,c.current?.classList.add("dragging");},[]),C=Lt.useCallback(()=>{P.current=false,c.current?.classList.remove("dragging");},[]),Q=Lt.useCallback(()=>{d&&(L.current=null,l.current&&(l.current.style.display="block"),m.current&&(m.current.style.display="block"));},[d]),q=Lt.useCallback(()=>{l.current&&(l.current.style.display="none"),m.current&&(m.current.style.display="none");},[]),j=Lt.useCallback(r=>{if(!x.current||!M.current.length)return;let i=Pe(M.current,r);if(N.current=i,!i)return;let a=x.current;a.style.backgroundImage=`url(${i.url})`,a.style.backgroundPosition=`-${i.x}px -${i.y}px`,a.style.width=`${i.w}px`,a.style.height=`${i.h}px`;},[]),t=Lt.useCallback(r=>{let i=T(),a=n.current?.duration;return !i||i.width===0||!a||!isFinite(a)?0:Math.max(0,Math.min(r-i.left,i.width))/i.width*a},[T,n]),u=Lt.useCallback(r=>{let i=T();return i?Math.max(0,Math.min(r-i.left,i.width)):0},[T]),f=Lt.useCallback(r=>{let i=n.current;if(!i)return;let a=i.currentTime,E=isFinite(i.duration)?i.duration:0;switch(r.key){case "ArrowLeft":case "ArrowRight":{r.preventDefault(),r.nativeEvent.stopImmediatePropagation();let I=r.shiftKey?10:5;e.seek(r.key==="ArrowLeft"?Math.max(0,a-I):Math.min(E,a+I));break}case "Home":r.preventDefault(),r.nativeEvent.stopImmediatePropagation(),e.seek(0);break;case "End":E>0&&(r.preventDefault(),r.nativeEvent.stopImmediatePropagation(),e.seek(E));break}},[n,e]),b=Lt.useCallback(r=>{let i=t(r.clientX),a=u(r.clientX);if(g.current=a,R.current=i,m.current&&(m.current.style.left=`${a}px`),y.current&&(y.current.textContent=Z(i)),j(i),l.current){let E=l.current.offsetWidth,I=T()?.width??0,te=E/2,qe=Math.max(te,Math.min(a,I-te));l.current.style.left=`${qe}px`;}P.current&&e.seek(i);},[e,j,t,u,T]),w=Lt.useCallback(()=>{Q();},[Q]),B=Lt.useCallback(()=>{q(),C();},[q,C]),p=Lt.useCallback(r=>{r.preventDefault(),F(),e.seek(t(r.clientX));},[F,t,e]),v=Lt.useCallback(()=>C(),[C]),U=Lt.useCallback(r=>{P.current||e.seek(t(r.clientX));},[t,e]);Lt.useEffect(()=>{let r=o.current;if(!r)return;let i=a=>{P.current&&a.preventDefault();};return r.addEventListener("touchmove",i,{passive:false}),()=>r.removeEventListener("touchmove",i)},[]);let G=Lt.useCallback(r=>{L.current=null,F(),e.seek(t(r.touches[0].clientX));},[F,t,e]),X=Lt.useCallback(r=>{P.current&&e.seek(t(r.touches[0].clientX));},[t,e]),V=Lt.useCallback(()=>C(),[C]);Lt.useEffect(()=>{let r=()=>C();return window.addEventListener("mouseup",r),()=>window.removeEventListener("mouseup",r)},[C]);let J=Lt.useMemo(()=>{let r=n.current,i=r&&isFinite(r.duration)?r.duration:0;return i<=0||!k.length?null:k.map((a,E)=>{let I=a.start/i*100,te=(a.end-a.start)/i*100;return jsxRuntime.jsx("div",{className:"bufferedSegment",style:{left:`${I}%`,width:`${te}%`}},E)})},[k,n]);return jsxRuntime.jsxs("div",{ref:o,className:"progressContainer",onMouseMove:b,onMouseEnter:w,onMouseLeave:B,onMouseDown:p,onMouseUp:v,onClick:U,onTouchStart:G,onTouchMove:X,onTouchEnd:V,onKeyDown:f,role:"slider","aria-label":"Video progress","aria-valuemin":0,"aria-valuemax":0,"aria-valuenow":0,"aria-valuetext":"0:00",tabIndex:0,children:[d&&jsxRuntime.jsxs("div",{ref:l,className:"previewTooltip",style:{left:0,display:"none"},"aria-hidden":"true",children:[s&&jsxRuntime.jsx("div",{ref:x,className:"previewThumbnail"}),jsxRuntime.jsx("div",{ref:y,className:"previewTime"})]}),jsxRuntime.jsxs("div",{className:"progressBackground",children:[J,jsxRuntime.jsx("div",{ref:h,className:"progressFilled",style:{width:"0%"}}),d&&jsxRuntime.jsx("div",{ref:m,className:"hoverIndicator",style:{left:0,display:"none"},"aria-hidden":"true"})]}),jsxRuntime.jsx("div",{ref:c,className:"scrubHandle",style:{left:"0%"},"aria-hidden":"true"})]})});He.displayName="ProgressBar";var he=He;var Ie=Lt.memo(({currentRate:n,playbackRates:e,onRateChange:d,qualityLevels:s=[],currentQualityLevel:o=-1,onQualityChange:h})=>{let[c,l]=Lt.useState(false),[y,m]=Lt.useState("speed"),x=Lt.useRef(null),k=s.length>0&&!!h;Lt.useEffect(()=>{let g=R=>{x.current&&!x.current.contains(R.target)&&l(false);};return c&&document.addEventListener("mousedown",g),()=>document.removeEventListener("mousedown",g)},[c]);let H=Lt.useMemo(()=>[...s].sort((g,R)=>R.bitrate-g.bitrate),[s]),P=Lt.useMemo(()=>o===-1?"Auto":s.find(g=>g.id===o)?.name??"Auto",[s,o]);return jsxRuntime.jsxs("div",{ref:x,className:"settingsContainer",children:[jsxRuntime.jsx("button",{onClick:()=>l(g=>!g),className:"controlButton","aria-label":"Settings",title:"Settings","aria-expanded":c,children:jsxRuntime.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:jsxRuntime.jsx("path",{d:"M19.14 12.94c.04-.3.06-.61.06-.94s-.02-.64-.07-.94l2.03-1.58a.49.49 0 0 0 .12-.61l-1.92-3.32a.49.49 0 0 0-.59-.22l-2.39.96a7.02 7.02 0 0 0-1.62-.94l-.36-2.54a.484.484 0 0 0-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54a6.88 6.88 0 0 0-1.61.94l-2.39-.96a.488.488 0 0 0-.59.22L2.74 8.87a.48.48 0 0 0 .12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58a.49.49 0 0 0-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54a6.88 6.88 0 0 0 1.61-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32a.47.47 0 0 0-.12-.61l-2.01-1.58zM12 15.6A3.6 3.6 0 0 1 8.4 12 3.6 3.6 0 0 1 12 8.4a3.6 3.6 0 0 1 3.6 3.6 3.6 3.6 0 0 1-3.6 3.6z"})})}),c&&jsxRuntime.jsxs("div",{className:"settingsDropdown",role:"menu",children:[k&&jsxRuntime.jsxs("div",{className:"settingsTabs",children:[jsxRuntime.jsx("button",{className:`settingsTab${y==="speed"?" active":""}`,onClick:()=>m("speed"),children:"Speed"}),jsxRuntime.jsx("button",{className:`settingsTab${y==="quality"?" active":""}`,onClick:()=>m("quality"),children:"Quality"})]}),(!k||y==="speed")&&jsxRuntime.jsxs("div",{children:[!k&&jsxRuntime.jsx("div",{className:"settingsPanelLabel",children:"Playback Speed"}),e.map(g=>jsxRuntime.jsx("button",{onClick:()=>{d(g),l(false);},className:`settingsOption${n===g?" active":""}`,role:"menuitemradio","aria-checked":n===g,children:g===1?"Normal":`${g}\xD7`},g))]}),k&&y==="quality"&&jsxRuntime.jsxs("div",{children:[jsxRuntime.jsxs("button",{onClick:()=>{h(-1),l(false);},className:`settingsOption${o===-1?" active":""}`,role:"menuitemradio","aria-checked":o===-1,children:["Auto ",o===-1&&P!=="Auto"?`(${P})`:""]}),H.map(g=>jsxRuntime.jsxs("button",{onClick:()=>{h(g.id),l(false);},className:`settingsOption${o===g.id?" active":""}`,role:"menuitemradio","aria-checked":o===g.id,children:[g.name,g.bitrate>0&&jsxRuntime.jsxs("span",{className:"settingsOptionBadge",children:[Math.round(g.bitrate/1e3)," kbps"]})]},g.id))]})]})]})});Ie.displayName="SettingsMenu";var ge=Ie;var ze=Lt.memo(({videoRef:n,isLive:e=false})=>{let d=Lt.useRef(null),s=Lt.useRef(null);return Lt.useEffect(()=>{let o=n.current;if(!o)return;let h=()=>{d.current&&(d.current.textContent=Z(o.currentTime));},c=()=>{if(s.current){let l=isFinite(o.duration)?o.duration:0;s.current.textContent=` / ${Z(l)}`;}};return o.addEventListener("timeupdate",h),o.addEventListener("durationchange",c),o.addEventListener("seeked",h),h(),c(),()=>{o.removeEventListener("timeupdate",h),o.removeEventListener("durationchange",c),o.removeEventListener("seeked",h);}},[n,e]),e?jsxRuntime.jsx("span",{className:"timeDisplay",style:{opacity:.7},children:jsxRuntime.jsx("span",{ref:d,children:"0:00"})}):jsxRuntime.jsxs("span",{className:"timeDisplay",children:[jsxRuntime.jsx("span",{ref:d,children:"0:00"}),jsxRuntime.jsx("span",{ref:s,style:{opacity:.6},children:" / 0:00"})]})});ze.displayName="TimeDisplay";var ye=ze;var _={PlayButton:se,PauseButton:le,FullscreenButton:ue,PiPButton:ce,TheaterButton:de,VolumeControl:ve,ProgressBar:he,SettingsMenu:ge,TimeDisplay:ye};var Me=({videoRef:n,playerRef:e,playerContainerRef:d,playbackRates:s,enablePreview:o,thumbnailVtt:h,isPlaying:c,volume:l,isMuted:y,playbackRate:m,isFullscreen:x,isPictureInPicture:k,isTheaterMode:H,isLive:P,qualityLevels:g,currentQualityLevel:R,controlBarItems:N})=>{let M=Lt.useRef(null),[L,T]=Lt.useState(true),F=Lt.useRef({isPlaying:c,volume:l,isMuted:y,isLive:P});F.current={isPlaying:c,volume:l,isMuted:y,isLive:P},Lt.useEffect(()=>{if(!c){T(true),M.current&&clearTimeout(M.current);return}let p=()=>{T(true),M.current&&clearTimeout(M.current),M.current=setTimeout(()=>T(false),3e3);},v=d.current;return v&&(v.addEventListener("mousemove",p),v.addEventListener("mouseenter",p),v.addEventListener("mouseleave",()=>{M.current&&clearTimeout(M.current);}),v.addEventListener("touchstart",p,{passive:true})),p(),()=>{v&&(v.removeEventListener("mousemove",p),v.removeEventListener("mouseenter",p),v.removeEventListener("mouseleave",()=>{}),v.removeEventListener("touchstart",p)),M.current&&clearTimeout(M.current);}},[c,d]),Lt.useEffect(()=>{let p=v=>{if(!d.current?.contains(document.activeElement))return;let U=v.target;if(U.tagName==="INPUT"||U.tagName==="TEXTAREA"||U.isContentEditable)return;let{isPlaying:G,volume:X,isLive:V}=F.current,J=n.current?.currentTime??0,r=n.current?.duration??0;switch(v.code){case "Space":case "KeyK":v.preventDefault(),G?e.pause():e.play();break;case "ArrowLeft":v.preventDefault(),e.seek(Math.max(0,J-5));break;case "ArrowRight":v.preventDefault(),e.seek(Math.min(r||1/0,J+5));break;case "ArrowUp":v.preventDefault(),e.setVolume(Math.min(1,X+.1));break;case "ArrowDown":v.preventDefault(),e.setVolume(Math.max(0,X-.1));break;case "KeyM":v.preventDefault(),e.toggleMute();break;case "KeyF":v.preventDefault(),e.toggleFullscreen();break;case "KeyP":v.preventDefault(),e.togglePictureInPicture();break;case "KeyT":v.preventDefault(),e.toggleTheaterMode();break;case "KeyL":v.preventDefault(),V&&e.seekToLive();break;case "Digit0":case "Digit1":case "Digit2":case "Digit3":case "Digit4":case "Digit5":case "Digit6":case "Digit7":case "Digit8":case "Digit9":{v.preventDefault();let i=Number(v.code.replace("Digit",""))*10;e.seek(r/100*i);break}}};return window.addEventListener("keydown",p),()=>window.removeEventListener("keydown",p)},[e,d,n]);let C=Lt.useCallback(()=>e.play(),[e]),Q=Lt.useCallback(()=>e.pause(),[e]),q=Lt.useCallback(p=>e.setVolume(p),[e]),j=Lt.useCallback(()=>e.toggleMute(),[e]),t=Lt.useCallback(p=>e.setPlaybackRate(p),[e]),u=Lt.useCallback(p=>e.setQualityLevel(p),[e]),f=Lt.useCallback(()=>e.togglePictureInPicture(),[e]),b=Lt.useCallback(()=>e.toggleTheaterMode(),[e]),w=Lt.useCallback(()=>e.toggleFullscreen(),[e]),B=Lt.useCallback(()=>e.seekToLive(),[e]);return jsxRuntime.jsx("div",{style:{position:"absolute",inset:0,display:"flex",flexDirection:"column",justifyContent:"flex-end",opacity:L?1:0,transition:"opacity 0.3s",pointerEvents:"none"},children:jsxRuntime.jsxs("div",{style:{background:"linear-gradient(to top, rgba(0,0,0,0.75) 0%, rgba(0,0,0,0.2) 60%, transparent 100%)",padding:"48px 12px 12px",pointerEvents:L?"auto":"none"},role:"region","aria-label":"Video player controls",children:[jsxRuntime.jsx(_.ProgressBar,{videoRef:n,playerRef:e,enablePreview:o,thumbnailVtt:h}),jsxRuntime.jsxs("div",{style:{display:"flex",alignItems:"center",gap:4,marginTop:4},children:[c?jsxRuntime.jsx(_.PauseButton,{onClick:Q}):jsxRuntime.jsx(_.PlayButton,{onClick:C}),jsxRuntime.jsx(_.VolumeControl,{volume:l,isMuted:y,onVolumeChange:q,onToggleMute:j}),jsxRuntime.jsx(_.TimeDisplay,{videoRef:n,isLive:P}),jsxRuntime.jsx("div",{style:{flex:1}}),P&&jsxRuntime.jsx(We,{onClick:B}),jsxRuntime.jsx(_.SettingsMenu,{currentRate:m,playbackRates:s,onRateChange:t,qualityLevels:g,currentQualityLevel:R,onQualityChange:u}),N?.map(p=>jsxRuntime.jsx("button",{className:"controlButton","aria-label":p.label,title:p.title??p.label,onClick:p.onClick,children:p.icon},p.key)),jsxRuntime.jsx(_.PiPButton,{onClick:f,isPiP:k}),jsxRuntime.jsx(_.TheaterButton,{onClick:b,isTheater:H}),jsxRuntime.jsx(_.FullscreenButton,{onClick:w,isFullscreen:x})]})]})})},We=Lt.memo(({onClick:n})=>jsxRuntime.jsx("button",{onClick:n,style:{background:"none",border:"1px solid rgba(255,255,255,0.6)",color:"#fff",borderRadius:3,padding:"2px 8px",fontSize:11,fontWeight:700,cursor:"pointer",letterSpacing:"0.06em"},title:"Go to live (L)",children:"GO LIVE"}));We.displayName="GoLiveButton";var ke=Lt.memo(({x:n,y:e,isPlaying:d,src:s,videoRef:o,playerRef:h,onClose:c,contextMenuItems:l})=>{let y=Lt.useRef(null),[m,x]=Lt.useState(()=>o.current?.loop??false),k=Math.min(n,window.innerWidth-220),H=Math.min(e,window.innerHeight-290);Lt.useEffect(()=>{let L=C=>{y.current&&!y.current.contains(C.target)&&c();},T=C=>{C.key==="Escape"&&c();},F=()=>c();return document.addEventListener("mousedown",L),document.addEventListener("keydown",T),window.addEventListener("scroll",F,true),()=>{document.removeEventListener("mousedown",L),document.removeEventListener("keydown",T),window.removeEventListener("scroll",F,true);}},[c]);let P=Lt.useCallback(()=>{d?h.pause():h.play(),c();},[d,h,c]),g=Lt.useCallback(()=>{let L=o.current;if(!L)return;let T=!m;L.loop=T,x(T);},[o,m]),R=Lt.useCallback(async()=>{try{await navigator.clipboard.writeText(s);}catch{}c();},[s,c]),N=Lt.useCallback(async()=>{let L=Math.floor(o.current?.currentTime??0);try{await navigator.clipboard.writeText(`${s}?t=${L}`);}catch{}c();},[s,o,c]),M=Lt.useCallback(()=>{h.togglePictureInPicture(),c();},[h,c]);return jsxRuntime.jsxs("div",{ref:y,className:"contextMenu",style:{left:k,top:H},children:[jsxRuntime.jsx("button",{className:"contextMenuItem",onClick:P,children:d?"Pause":"Play"}),jsxRuntime.jsxs("button",{className:"contextMenuItem",onClick:g,children:[jsxRuntime.jsx("span",{children:"Loop"}),m&&jsxRuntime.jsx("span",{className:"contextMenuCheck",children:"\u2713"})]}),jsxRuntime.jsx("div",{className:"contextMenuDivider"}),jsxRuntime.jsx("button",{className:"contextMenuItem",onClick:R,children:"Copy video URL"}),jsxRuntime.jsx("button",{className:"contextMenuItem",onClick:N,children:"Copy video URL at current time"}),jsxRuntime.jsx("div",{className:"contextMenuDivider"}),jsxRuntime.jsx("button",{className:"contextMenuItem",onClick:M,children:"Picture-in-Picture"}),l&&l.length>0&&jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx("div",{className:"contextMenuDivider"}),l.map((L,T)=>jsxRuntime.jsx("button",{className:"contextMenuItem",onClick:()=>{L.onClick(),c();},children:L.label},T))]})]})});ke.displayName="ContextMenu";var Qe=Lt.forwardRef(({src:n,poster:e,autoplay:d=false,muted:s=false,loop:o=false,controls:h=true,preload:c="metadata",playbackRates:l=[.25,.5,.75,1,1.25,1.5,1.75,2],className:y,enableHLS:m=true,enablePreview:x=true,thumbnailVtt:k,hlsConfig:H,subtitles:P,crossOrigin:g,onPlay:R,onPause:N,onEnded:M,onError:L,onTimeUpdate:T,onDurationChange:F,onBuffering:C,onTheaterModeChange:Q,contextMenuItems:q,controlBarItems:j},t)=>{let u=Lt.useRef(null),f=Lt.useRef(null),{state:b,ref:w,fullscreenContainerRef:B}=Be(u,n,{autoplay:d,muted:s,loop:o,playbackRates:l,enableHLS:m,hlsConfig:H,onPlay:R,onPause:N,onEnded:M,onError:L,onTimeUpdate:T,onDurationChange:F,onBuffering:C,onTheaterModeChange:Q}),[p,v]=Lt.useState(null);Lt.useEffect(()=>{B.current=f.current;},[B]),Lt__default.default.useImperativeHandle(t,()=>w,[w]);let U=Lt.useCallback(()=>{f.current?.focus(),b.isPlaying?w.pause():w.play();},[b.isPlaying,w]),G=Lt.useCallback(()=>{w.toggleFullscreen();},[w]),X=Lt.useCallback(V=>{V.preventDefault(),v({x:V.clientX,y:V.clientY});},[]);return jsxRuntime.jsxs("div",{ref:f,tabIndex:0,style:{position:"relative",width:"100%",backgroundColor:"#000",aspectRatio:"16 / 9",userSelect:"none",outline:"none"},className:y,"data-test":"video-player-container","data-theater":b.isTheaterMode?"true":void 0,onContextMenu:X,children:[jsxRuntime.jsx("video",{ref:u,poster:e,preload:c,crossOrigin:g,onClick:U,onDoubleClick:G,playsInline:true,style:{width:"100%",height:"100%",display:"block",cursor:"pointer"},"data-test":"video-element",children:P?.map(V=>jsxRuntime.jsx("track",{kind:"subtitles",src:V.src,label:V.label,srcLang:V.srclang,default:V.default},V.id))}),h&&jsxRuntime.jsx(Me,{videoRef:u,playerRef:w,playerContainerRef:f,playbackRates:l,enablePreview:x,thumbnailVtt:k,isPlaying:b.isPlaying,volume:b.volume,isMuted:b.isMuted,playbackRate:b.playbackRate,isFullscreen:b.isFullscreen,isPictureInPicture:b.isPictureInPicture,isTheaterMode:b.isTheaterMode,isLive:b.isLive,qualityLevels:b.qualityLevels,currentQualityLevel:b.currentQualityLevel,controlBarItems:j}),p&&jsxRuntime.jsx(ke,{x:p.x,y:p.y,isPlaying:b.isPlaying,src:n,videoRef:u,playerRef:w,onClose:()=>v(null),contextMenuItems:q}),b.isLive&&jsxRuntime.jsx("div",{style:{position:"absolute",top:12,left:12,backgroundColor:"#e53935",color:"#fff",fontSize:11,fontWeight:700,letterSpacing:"0.08em",padding:"2px 8px",borderRadius:3,pointerEvents:"none"},children:"LIVE"}),b.isBuffering&&!b.error&&jsxRuntime.jsxs("div",{style:{position:"absolute",inset:0,display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",gap:12,color:"#fff",pointerEvents:"none"},"data-test":"buffering-indicator",children:[jsxRuntime.jsx("div",{style:{width:48,height:48,border:"4px solid rgba(255,255,255,0.25)",borderTop:"4px solid #fff",borderRadius:"50%",animation:"rvp-spin 0.8s linear infinite"}}),jsxRuntime.jsx("style",{children:"@keyframes rvp-spin { to { transform: rotate(360deg); } }"})]}),b.error&&jsxRuntime.jsx("div",{style:{position:"absolute",inset:0,display:"flex",alignItems:"center",justifyContent:"center",backgroundColor:"rgba(0,0,0,0.85)",color:"#fff",padding:24},"data-test":"error-overlay",children:jsxRuntime.jsxs("div",{style:{textAlign:"center",maxWidth:400},children:[jsxRuntime.jsx("div",{style:{fontSize:36,marginBottom:12},children:"\u26A0"}),jsxRuntime.jsx("h3",{style:{margin:"0 0 8px",fontSize:18},children:b.error.code==="MEDIA_ERR_SRC_NOT_SUPPORTED"?"Unsupported Format":b.error.code.startsWith("HLS")?"Stream Error":"Playback Error"}),jsxRuntime.jsx("p",{style:{margin:0,fontSize:13,opacity:.75},children:b.error.message})]})})]})});Qe.displayName="VideoPlayer";var xt=Qe;exports.ControlElements=Ae;exports.Controls=Me;exports.VideoPlayer=xt;exports.findThumbnailCue=Pe;exports.formatTime=Z;exports.getMimeType=Ge;exports.isHLSUrl=pe;exports.parseThumbnailVtt=Le;//# sourceMappingURL=index.js.map
3
+ `),o=0;for(;o<s.length;){let g=s[o].trim();if(g.includes("-->")){let u=g.indexOf("-->"),a=Fe(g.slice(0,u)),x=Fe(g.slice(u+3));for(o++;o<s.length&&!s[o].trim();)o++;if(o<s.length){let d=s[o].trim(),k=d.lastIndexOf("#xywh="),w=d,B=0,T=0,p=160,R=90;if(k!==-1){w=d.slice(0,k);let V=d.slice(k+6).split(",").map(Number);B=V[0]??0,T=V[1]??0,p=V[2]??160,R=V[3]??90;}c.push({start:a,end:x,url:pt(e,w),x:B,y:T,w:p,h:R});}}o++;}return c}function we(t,e){if(!t.length)return null;let c=0,s=t.length-1;for(;c<=s;){let o=c+s>>1;if(t[o].end<=e)c=o+1;else if(t[o].start>e)s=o-1;else return t[o]}return null}var _e=St.memo(({videoRef:t,playerRef:e,enablePreview:c=true,thumbnailVtt:s})=>{let o=St.useRef(null),g=St.useRef(null),u=St.useRef(null),a=St.useRef(null),x=St.useRef(null),d=St.useRef(null),k=St.useRef(null),[w,B]=St.useState([]),T=St.useRef(false),p=St.useRef(0),R=St.useRef(0),V=St.useRef(null),O=St.useRef([]),M=St.useRef(null);St.useEffect(()=>{let n=()=>{M.current=null;};return window.addEventListener("resize",n,{passive:true}),()=>window.removeEventListener("resize",n)},[]);let L=St.useCallback(()=>(!M.current&&o.current&&(M.current=o.current.getBoundingClientRect()),M.current),[]);St.useEffect(()=>{if(!s){O.current=[];return}let n=false;return fetch(s).then(i=>i.text()).then(i=>{n||(O.current=ke(i,s));}).catch(()=>{n||(O.current=[]);}),()=>{n=true;}},[s]),St.useEffect(()=>{let n=t.current;if(!n)return;let i=()=>{let b=isFinite(n.duration)?n.duration:0,y=n.currentTime,F=b>0?y/b*100:0;g.current&&(g.current.style.width=`${F}%`),u.current&&(u.current.style.left=`${F}%`),o.current&&(o.current.setAttribute("aria-valuenow",String(Math.round(y))),o.current.setAttribute("aria-valuemax",String(Math.round(b))),o.current.setAttribute("aria-valuetext",oe(y)));};return n.addEventListener("timeupdate",i),n.addEventListener("durationchange",i),n.addEventListener("seeked",i),i(),()=>{n.removeEventListener("timeupdate",i),n.removeEventListener("durationchange",i),n.removeEventListener("seeked",i);}},[t]),St.useEffect(()=>{let n=t.current;if(!n)return;let i=()=>{let b=[];for(let y=0;y<n.buffered.length;y++)b.push({start:n.buffered.start(y),end:n.buffered.end(y)});B(b);};return n.addEventListener("progress",i),()=>n.removeEventListener("progress",i)},[t]);let I=St.useCallback(()=>{T.current=true,u.current?.classList.add("dragging");},[]),C=St.useCallback(()=>{T.current=false,u.current?.classList.remove("dragging");},[]),W=St.useCallback(()=>{c&&(M.current=null,a.current&&(a.current.style.display="block"),d.current&&(d.current.style.display="block"));},[c]),X=St.useCallback(()=>{a.current&&(a.current.style.display="none"),d.current&&(d.current.style.display="none");},[]),j=St.useCallback(n=>{if(!k.current||!O.current.length)return;let i=we(O.current,n);if(V.current=i,!i)return;let b=k.current;b.style.backgroundImage=`url(${i.url})`,b.style.backgroundPosition=`-${i.x}px -${i.y}px`,b.style.width=`${i.w}px`,b.style.height=`${i.h}px`;},[]),H=St.useCallback(n=>{let i=L(),b=t.current?.duration;return !i||i.width===0||!b||!isFinite(b)?0:Math.max(0,Math.min(n-i.left,i.width))/i.width*b},[L,t]),G=St.useCallback(n=>{let i=L();return i?Math.max(0,Math.min(n-i.left,i.width)):0},[L]),Z=St.useCallback(n=>{let i=t.current;if(!i)return;let b=i.currentTime,y=isFinite(i.duration)?i.duration:0;switch(n.key){case "ArrowLeft":case "ArrowRight":{n.preventDefault(),n.nativeEvent.stopImmediatePropagation();let F=n.shiftKey?10:5;e.seek(n.key==="ArrowLeft"?Math.max(0,b-F):Math.min(y,b+F));break}case "Home":n.preventDefault(),n.nativeEvent.stopImmediatePropagation(),e.seek(0);break;case "End":y>0&&(n.preventDefault(),n.nativeEvent.stopImmediatePropagation(),e.seek(y));break}},[t,e]),ee=St.useCallback(n=>{let i=H(n.clientX),b=G(n.clientX);if(p.current=b,R.current=i,d.current&&(d.current.style.left=`${b}px`),x.current&&(x.current.textContent=oe(i)),j(i),a.current){let y=a.current.offsetWidth,F=L()?.width??0,Y=y/2,ae=Math.max(Y,Math.min(b,F-Y));a.current.style.left=`${ae}px`;}T.current&&e.seek(i);},[e,j,H,G,L]),ne=St.useCallback(()=>{W();},[W]),r=St.useCallback(()=>{X(),C();},[X,C]),l=St.useCallback(n=>{n.preventDefault(),I(),e.seek(H(n.clientX));},[I,H,e]),f=St.useCallback(()=>C(),[C]),h=St.useCallback(n=>{T.current||e.seek(H(n.clientX));},[H,e]);St.useEffect(()=>{let n=o.current;if(!n)return;let i=b=>{T.current&&b.preventDefault();};return n.addEventListener("touchmove",i,{passive:false}),()=>n.removeEventListener("touchmove",i)},[]);let m=St.useCallback(n=>{M.current=null,I(),e.seek(H(n.touches[0].clientX));},[I,H,e]),v=St.useCallback(n=>{T.current&&e.seek(H(n.touches[0].clientX));},[H,e]),E=St.useCallback(()=>C(),[C]);St.useEffect(()=>{let n=()=>C();return window.addEventListener("mouseup",n),()=>window.removeEventListener("mouseup",n)},[C]);let D=St.useMemo(()=>{let n=t.current,i=n&&isFinite(n.duration)?n.duration:0;return i<=0||!w.length?null:w.map((b,y)=>{let F=b.start/i*100,Y=(b.end-b.start)/i*100;return jsxRuntime.jsx("div",{className:"bufferedSegment",style:{left:`${F}%`,width:`${Y}%`}},y)})},[w,t]);return jsxRuntime.jsxs("div",{ref:o,className:"progressContainer",onMouseMove:ee,onMouseEnter:ne,onMouseLeave:r,onMouseDown:l,onMouseUp:f,onClick:h,onTouchStart:m,onTouchMove:v,onTouchEnd:E,onKeyDown:Z,role:"slider","aria-label":"Video progress","aria-valuemin":0,"aria-valuemax":0,"aria-valuenow":0,"aria-valuetext":"0:00",tabIndex:0,children:[c&&jsxRuntime.jsxs("div",{ref:a,className:"previewTooltip",style:{left:0,display:"none"},"aria-hidden":"true",children:[s&&jsxRuntime.jsx("div",{ref:k,className:"previewThumbnail"}),jsxRuntime.jsx("div",{ref:x,className:"previewTime"})]}),jsxRuntime.jsxs("div",{className:"progressBackground",children:[D,jsxRuntime.jsx("div",{ref:g,className:"progressFilled",style:{width:"0%"}}),c&&jsxRuntime.jsx("div",{ref:d,className:"hoverIndicator",style:{left:0,display:"none"},"aria-hidden":"true"})]}),jsxRuntime.jsx("div",{ref:u,className:"scrubHandle",style:{left:"0%"},"aria-hidden":"true"})]})});_e.displayName="ProgressBar";var Le=_e;var qe=St.memo(({currentRate:t,playbackRates:e,onRateChange:c,qualityLevels:s=[],currentQualityLevel:o=-1,onQualityChange:g})=>{let[u,a]=St.useState(false),[x,d]=St.useState("speed"),k=St.useRef(null),w=s.length>0&&!!g;St.useEffect(()=>{let p=R=>{k.current&&!k.current.contains(R.target)&&a(false);};return u&&document.addEventListener("mousedown",p),()=>document.removeEventListener("mousedown",p)},[u]);let B=St.useMemo(()=>[...s].sort((p,R)=>R.bitrate-p.bitrate),[s]),T=St.useMemo(()=>o===-1?"Auto":s.find(p=>p.id===o)?.name??"Auto",[s,o]);return jsxRuntime.jsxs("div",{ref:k,className:"settingsContainer",children:[jsxRuntime.jsx("button",{onClick:()=>a(p=>!p),className:"controlButton","aria-label":"Settings",title:"Settings","aria-expanded":u,children:jsxRuntime.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:jsxRuntime.jsx("path",{d:"M19.14 12.94c.04-.3.06-.61.06-.94s-.02-.64-.07-.94l2.03-1.58a.49.49 0 0 0 .12-.61l-1.92-3.32a.49.49 0 0 0-.59-.22l-2.39.96a7.02 7.02 0 0 0-1.62-.94l-.36-2.54a.484.484 0 0 0-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54a6.88 6.88 0 0 0-1.61.94l-2.39-.96a.488.488 0 0 0-.59.22L2.74 8.87a.48.48 0 0 0 .12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58a.49.49 0 0 0-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54a6.88 6.88 0 0 0 1.61-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32a.47.47 0 0 0-.12-.61l-2.01-1.58zM12 15.6A3.6 3.6 0 0 1 8.4 12 3.6 3.6 0 0 1 12 8.4a3.6 3.6 0 0 1 3.6 3.6 3.6 3.6 0 0 1-3.6 3.6z"})})}),u&&jsxRuntime.jsxs("div",{className:"settingsDropdown",role:"menu",children:[w&&jsxRuntime.jsxs("div",{className:"settingsTabs",children:[jsxRuntime.jsx("button",{className:`settingsTab${x==="speed"?" active":""}`,onClick:()=>d("speed"),children:"Speed"}),jsxRuntime.jsx("button",{className:`settingsTab${x==="quality"?" active":""}`,onClick:()=>d("quality"),children:"Quality"})]}),(!w||x==="speed")&&jsxRuntime.jsxs("div",{children:[!w&&jsxRuntime.jsx("div",{className:"settingsPanelLabel",children:"Playback Speed"}),e.map(p=>jsxRuntime.jsx("button",{onClick:()=>{c(p),a(false);},className:`settingsOption${t===p?" active":""}`,role:"menuitemradio","aria-checked":t===p,children:p===1?"Normal":`${p}\xD7`},p))]}),w&&x==="quality"&&jsxRuntime.jsxs("div",{children:[jsxRuntime.jsxs("button",{onClick:()=>{g(-1),a(false);},className:`settingsOption${o===-1?" active":""}`,role:"menuitemradio","aria-checked":o===-1,children:["Auto ",o===-1&&T!=="Auto"?`(${T})`:""]}),B.map(p=>jsxRuntime.jsxs("button",{onClick:()=>{g(p.id),a(false);},className:`settingsOption${o===p.id?" active":""}`,role:"menuitemradio","aria-checked":o===p.id,children:[p.name,p.bitrate>0&&jsxRuntime.jsxs("span",{className:"settingsOptionBadge",children:[Math.round(p.bitrate/1e3)," kbps"]})]},p.id))]})]})]})});qe.displayName="SettingsMenu";var xe=qe;var We=St.memo(({videoRef:t,isLive:e=false})=>{let c=St.useRef(null),s=St.useRef(null);return St.useEffect(()=>{let o=t.current;if(!o)return;let g=()=>{c.current&&(c.current.textContent=oe(o.currentTime));},u=()=>{if(s.current){let a=isFinite(o.duration)?o.duration:0;s.current.textContent=` / ${oe(a)}`;}};return o.addEventListener("timeupdate",g),o.addEventListener("durationchange",u),o.addEventListener("seeked",g),g(),u(),()=>{o.removeEventListener("timeupdate",g),o.removeEventListener("durationchange",u),o.removeEventListener("seeked",g);}},[t,e]),e?jsxRuntime.jsx("span",{className:"timeDisplay",style:{opacity:.7},children:jsxRuntime.jsx("span",{ref:c,children:"0:00"})}):jsxRuntime.jsxs("span",{className:"timeDisplay",children:[jsxRuntime.jsx("span",{ref:c,children:"0:00"}),jsxRuntime.jsx("span",{ref:s,style:{opacity:.6},children:" / 0:00"})]})});We.displayName="TimeDisplay";var Te=We;var q={PlayButton:de,PauseButton:me,FullscreenButton:pe,PiPButton:ve,TheaterButton:fe,VolumeControl:Pe,ProgressBar:Le,SettingsMenu:xe,TimeDisplay:Te};var Re=({videoRef:t,playerRef:e,playerContainerRef:c,playbackRates:s,enablePreview:o,thumbnailVtt:g,isPlaying:u,volume:a,isMuted:x,playbackRate:d,isFullscreen:k,isPictureInPicture:w,isTheaterMode:B,isAudioMode:T,showAudioButton:p,isLive:R,qualityLevels:V,currentQualityLevel:O,controlBarItems:M})=>{let L=St.useRef(null),[I,C]=St.useState(true),W=St.useRef({isPlaying:u,volume:a,isMuted:x,isLive:R});W.current={isPlaying:u,volume:a,isMuted:x,isLive:R},St.useEffect(()=>{if(!u){C(true),L.current&&clearTimeout(L.current);return}let m=()=>{C(true),L.current&&clearTimeout(L.current),L.current=setTimeout(()=>C(false),3e3);},v=c.current;return v&&(v.addEventListener("mousemove",m),v.addEventListener("mouseenter",m),v.addEventListener("mouseleave",()=>{L.current&&clearTimeout(L.current);}),v.addEventListener("touchstart",m,{passive:true})),m(),()=>{v&&(v.removeEventListener("mousemove",m),v.removeEventListener("mouseenter",m),v.removeEventListener("mouseleave",()=>{}),v.removeEventListener("touchstart",m)),L.current&&clearTimeout(L.current);}},[u,c]),St.useEffect(()=>{let m=v=>{if(!c.current?.contains(document.activeElement))return;let E=v.target;if(E.tagName==="INPUT"||E.tagName==="TEXTAREA"||E.isContentEditable)return;let{isPlaying:D,volume:n,isLive:i}=W.current,b=t.current?.currentTime??0,y=t.current?.duration??0;switch(v.code){case "Space":case "KeyK":v.preventDefault(),D?e.pause():e.play();break;case "ArrowLeft":v.preventDefault(),e.seek(Math.max(0,b-5));break;case "ArrowRight":v.preventDefault(),e.seek(Math.min(y||1/0,b+5));break;case "ArrowUp":v.preventDefault(),e.setVolume(Math.min(1,n+.1));break;case "ArrowDown":v.preventDefault(),e.setVolume(Math.max(0,n-.1));break;case "KeyM":v.preventDefault(),e.toggleMute();break;case "KeyF":v.preventDefault(),e.toggleFullscreen();break;case "KeyP":v.preventDefault(),e.togglePictureInPicture();break;case "KeyT":v.preventDefault(),e.toggleTheaterMode();break;case "KeyL":v.preventDefault(),i&&e.seekToLive();break;case "Digit0":case "Digit1":case "Digit2":case "Digit3":case "Digit4":case "Digit5":case "Digit6":case "Digit7":case "Digit8":case "Digit9":{v.preventDefault();let F=Number(v.code.replace("Digit",""))*10;e.seek(y/100*F);break}}};return window.addEventListener("keydown",m),()=>window.removeEventListener("keydown",m)},[e,c,t]);let X=St.useCallback(()=>e.play(),[e]),j=St.useCallback(()=>e.pause(),[e]),H=St.useCallback(m=>e.setVolume(m),[e]),G=St.useCallback(()=>e.toggleMute(),[e]),Z=St.useCallback(m=>e.setPlaybackRate(m),[e]),ee=St.useCallback(m=>e.setQualityLevel(m),[e]),ne=St.useCallback(()=>e.togglePictureInPicture(),[e]),r=St.useCallback(()=>e.toggleTheaterMode(),[e]),l=St.useCallback(()=>e.toggleAudioMode(),[e]),f=St.useCallback(()=>e.toggleFullscreen(),[e]),h=St.useCallback(()=>e.seekToLive(),[e]);return jsxRuntime.jsx("div",{style:{position:"absolute",inset:0,display:"flex",flexDirection:"column",justifyContent:"flex-end",opacity:I?1:0,transition:"opacity 0.3s",pointerEvents:"none"},children:jsxRuntime.jsxs("div",{style:{background:"linear-gradient(to top, rgba(0,0,0,0.75) 0%, rgba(0,0,0,0.2) 60%, transparent 100%)",padding:"48px 12px 12px",pointerEvents:I?"auto":"none"},role:"region","aria-label":"Video player controls",children:[jsxRuntime.jsx(q.ProgressBar,{videoRef:t,playerRef:e,enablePreview:o,thumbnailVtt:g}),jsxRuntime.jsxs("div",{style:{display:"flex",alignItems:"center",gap:4,marginTop:4},children:[u?jsxRuntime.jsx(q.PauseButton,{onClick:j}):jsxRuntime.jsx(q.PlayButton,{onClick:X}),jsxRuntime.jsx(q.VolumeControl,{volume:a,isMuted:x,onVolumeChange:H,onToggleMute:G}),jsxRuntime.jsx(q.TimeDisplay,{videoRef:t,isLive:R}),jsxRuntime.jsx("div",{style:{flex:1}}),R&&jsxRuntime.jsx(Je,{onClick:h}),jsxRuntime.jsx(q.SettingsMenu,{currentRate:d,playbackRates:s,onRateChange:Z,qualityLevels:V,currentQualityLevel:O,onQualityChange:ee}),M?.map(m=>jsxRuntime.jsx("button",{className:"controlButton","aria-label":m.label,title:m.title??m.label,onClick:m.onClick,children:m.icon},m.key)),p&&jsxRuntime.jsx(Ye,{onClick:l,isAudioMode:T}),jsxRuntime.jsx(q.PiPButton,{onClick:ne,isPiP:w}),jsxRuntime.jsx(q.TheaterButton,{onClick:r,isTheater:B}),jsxRuntime.jsx(q.FullscreenButton,{onClick:f,isFullscreen:k})]})]})})},Ye=St.memo(({onClick:t,isAudioMode:e})=>jsxRuntime.jsx("button",{onClick:t,className:"controlButton","aria-label":e?"Exit audio mode":"Audio only mode",title:e?"Exit audio mode":"Audio only mode","aria-pressed":e,children:e?jsxRuntime.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:jsxRuntime.jsx("path",{d:"M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"})}):jsxRuntime.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:jsxRuntime.jsx("path",{d:"M12 3a9 9 0 0 0-9 9v7c0 1.1.9 2 2 2h1a1 1 0 0 0 1-1v-5a1 1 0 0 0-1-1H4v-1a8 8 0 0 1 16 0v1h-2a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h1a3 3 0 0 0 3-3v-4c0-4.97-4.03-9-9-9z"})})}));Ye.displayName="AudioModeButton";var Je=St.memo(({onClick:t})=>jsxRuntime.jsx("button",{onClick:t,style:{background:"none",border:"1px solid rgba(255,255,255,0.6)",color:"#fff",borderRadius:3,padding:"2px 8px",fontSize:11,fontWeight:700,cursor:"pointer",letterSpacing:"0.06em"},title:"Go to live (L)",children:"GO LIVE"}));Je.displayName="GoLiveButton";var Be=St.memo(({x:t,y:e,isPlaying:c,src:s,videoRef:o,playerRef:g,onClose:u,contextMenuItems:a})=>{let x=St.useRef(null),[d,k]=St.useState(()=>o.current?.loop??false),w=Math.min(t,window.innerWidth-220),B=Math.min(e,window.innerHeight-290);St.useEffect(()=>{let M=C=>{x.current&&!x.current.contains(C.target)&&u();},L=C=>{C.key==="Escape"&&u();},I=()=>u();return document.addEventListener("mousedown",M),document.addEventListener("keydown",L),window.addEventListener("scroll",I,true),()=>{document.removeEventListener("mousedown",M),document.removeEventListener("keydown",L),window.removeEventListener("scroll",I,true);}},[u]);let T=St.useCallback(()=>{c?g.pause():g.play(),u();},[c,g,u]),p=St.useCallback(()=>{let M=o.current;if(!M)return;let L=!d;M.loop=L,k(L);},[o,d]),R=St.useCallback(async()=>{try{await navigator.clipboard.writeText(s);}catch{}u();},[s,u]),V=St.useCallback(async()=>{let M=Math.floor(o.current?.currentTime??0);try{await navigator.clipboard.writeText(`${s}?t=${M}`);}catch{}u();},[s,o,u]),O=St.useCallback(()=>{g.togglePictureInPicture(),u();},[g,u]);return jsxRuntime.jsxs("div",{ref:x,className:"contextMenu",style:{left:w,top:B},children:[jsxRuntime.jsx("button",{className:"contextMenuItem",onClick:T,children:c?"Pause":"Play"}),jsxRuntime.jsxs("button",{className:"contextMenuItem",onClick:p,children:[jsxRuntime.jsx("span",{children:"Loop"}),d&&jsxRuntime.jsx("span",{className:"contextMenuCheck",children:"\u2713"})]}),jsxRuntime.jsx("div",{className:"contextMenuDivider"}),jsxRuntime.jsx("button",{className:"contextMenuItem",onClick:R,children:"Copy video URL"}),jsxRuntime.jsx("button",{className:"contextMenuItem",onClick:V,children:"Copy video URL at current time"}),jsxRuntime.jsx("div",{className:"contextMenuDivider"}),jsxRuntime.jsx("button",{className:"contextMenuItem",onClick:O,children:"Picture-in-Picture"}),a&&a.length>0&&jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx("div",{className:"contextMenuDivider"}),a.map((M,L)=>jsxRuntime.jsx("button",{className:"contextMenuItem",onClick:()=>{M.onClick(),u();},children:M.label},L))]})]})});Be.displayName="ContextMenu";var Rt=Array.from({length:20},(t,e)=>e),De=({poster:t,logo:e,isPlaying:c})=>{let s=t?jsxRuntime.jsx("img",{src:t,alt:"Video artwork",className:"rvp-audio-artwork",draggable:false}):e?typeof e=="string"?jsxRuntime.jsx("img",{src:e,alt:"Logo",className:"rvp-audio-logo",draggable:false}):jsxRuntime.jsx("div",{className:"rvp-audio-logo-node",children:e}):null;return jsxRuntime.jsx("div",{className:"rvp-audio-overlay","data-test":"audio-mode-overlay",children:jsxRuntime.jsxs("div",{className:"rvp-audio-content",children:[s&&jsxRuntime.jsx("div",{className:"rvp-audio-artwork-wrapper",children:s}),jsxRuntime.jsxs("div",{className:"rvp-audio-badge","aria-label":"Audio only mode",children:[jsxRuntime.jsx("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"currentColor","aria-hidden":"true",children:jsxRuntime.jsx("path",{d:"M12 3a9 9 0 0 0-9 9v7c0 1.1.9 2 2 2h1a1 1 0 0 0 1-1v-5a1 1 0 0 0-1-1H4v-1a8 8 0 0 1 16 0v1h-2a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h1a3 3 0 0 0 3-3v-4c0-4.97-4.03-9-9-9z"})}),"Audio Only"]}),jsxRuntime.jsx("div",{className:`rvp-audio-equalizer${c?" rvp-audio-equalizer--playing":""}`,"aria-hidden":"true",children:Rt.map(o=>jsxRuntime.jsx("div",{className:"rvp-audio-bar",style:{"--bar-index":o}},o))})]})})};De.displayName="AudioModeOverlay";var tt=St.forwardRef(({src:t,poster:e,autoplay:c=false,muted:s=false,loop:o=false,controls:g=true,preload:u="metadata",playbackRates:a=[.25,.5,.75,1,1.25,1.5,1.75,2],className:x,enableHLS:d=true,enablePreview:k=true,thumbnailVtt:w,hlsConfig:B,subtitles:T,crossOrigin:p,logo:R,showAudioButton:V=true,defaultAudioMode:O,audioBandwidthThreshold:M,onPlay:L,onPause:I,onEnded:C,onError:W,onTimeUpdate:X,onDurationChange:j,onBuffering:H,onTheaterModeChange:G,onAudioModeChange:Z,contextMenuItems:ee,controlBarItems:ne},r)=>{let l=St.useRef(null),f=St.useRef(null),{state:h,ref:m,fullscreenContainerRef:v}=Ie(l,t,{autoplay:c,muted:s,loop:o,playbackRates:a,enableHLS:d,hlsConfig:B,defaultAudioMode:O,audioBandwidthThreshold:M,onPlay:L,onPause:I,onEnded:C,onError:W,onTimeUpdate:X,onDurationChange:j,onBuffering:H,onTheaterModeChange:G,onAudioModeChange:Z}),[E,D]=St.useState(null);St.useEffect(()=>{v.current=f.current;},[v]),St__default.default.useImperativeHandle(r,()=>m,[m]);let n=St.useCallback(()=>{f.current?.focus(),h.isPlaying?m.pause():m.play();},[h.isPlaying,m]),i=St.useCallback(()=>{m.toggleFullscreen();},[m]),b=St.useCallback(y=>{y.preventDefault(),D({x:y.clientX,y:y.clientY});},[]);return jsxRuntime.jsxs("div",{ref:f,tabIndex:0,style:{position:"relative",width:"100%",backgroundColor:"#000",aspectRatio:"16 / 9",userSelect:"none",outline:"none"},className:x,"data-test":"video-player-container","data-theater":h.isTheaterMode?"true":void 0,onContextMenu:b,children:[jsxRuntime.jsx("video",{ref:l,poster:e,preload:u,crossOrigin:p,onClick:n,onDoubleClick:i,playsInline:true,style:{width:"100%",height:"100%",display:"block",cursor:"pointer",visibility:h.isAudioMode?"hidden":"visible"},"data-test":"video-element",children:T?.map(y=>jsxRuntime.jsx("track",{kind:"subtitles",src:y.src,label:y.label,srcLang:y.srclang,default:y.default},y.id))}),h.isAudioMode&&jsxRuntime.jsx(De,{poster:e,logo:R,isPlaying:h.isPlaying}),g&&jsxRuntime.jsx(Re,{videoRef:l,playerRef:m,playerContainerRef:f,playbackRates:a,enablePreview:k,thumbnailVtt:w,isPlaying:h.isPlaying,volume:h.volume,isMuted:h.isMuted,playbackRate:h.playbackRate,isFullscreen:h.isFullscreen,isPictureInPicture:h.isPictureInPicture,isTheaterMode:h.isTheaterMode,isAudioMode:h.isAudioMode,showAudioButton:V,isLive:h.isLive,qualityLevels:h.qualityLevels,currentQualityLevel:h.currentQualityLevel,controlBarItems:ne}),E&&jsxRuntime.jsx(Be,{x:E.x,y:E.y,isPlaying:h.isPlaying,src:t,videoRef:l,playerRef:m,onClose:()=>D(null),contextMenuItems:ee}),h.isLive&&jsxRuntime.jsx("div",{style:{position:"absolute",top:12,left:12,backgroundColor:"#e53935",color:"#fff",fontSize:11,fontWeight:700,letterSpacing:"0.08em",padding:"2px 8px",borderRadius:3,pointerEvents:"none"},children:"LIVE"}),h.isBuffering&&!h.error&&jsxRuntime.jsxs("div",{style:{position:"absolute",inset:0,display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",gap:12,color:"#fff",pointerEvents:"none"},"data-test":"buffering-indicator",children:[jsxRuntime.jsx("div",{style:{width:48,height:48,border:"4px solid rgba(255,255,255,0.25)",borderTop:"4px solid #fff",borderRadius:"50%",animation:"rvp-spin 0.8s linear infinite"}}),jsxRuntime.jsx("style",{children:"@keyframes rvp-spin { to { transform: rotate(360deg); } }"})]}),h.error&&jsxRuntime.jsx("div",{style:{position:"absolute",inset:0,display:"flex",alignItems:"center",justifyContent:"center",backgroundColor:"rgba(0,0,0,0.85)",color:"#fff",padding:24},"data-test":"error-overlay",children:jsxRuntime.jsxs("div",{style:{textAlign:"center",maxWidth:400},children:[jsxRuntime.jsx("div",{style:{fontSize:36,marginBottom:12},children:"\u26A0"}),jsxRuntime.jsx("h3",{style:{margin:"0 0 8px",fontSize:18},children:h.error.code==="MEDIA_ERR_SRC_NOT_SUPPORTED"?"Unsupported Format":h.error.code.startsWith("HLS")?"Stream Error":"Playback Error"}),jsxRuntime.jsx("p",{style:{margin:0,fontSize:13,opacity:.75},children:h.error.message})]})})]})});tt.displayName="VideoPlayer";var Ht=tt;var At={EXTREME:100,POOR:300,FAIR:700,GOOD:1500};exports.AUDIO_BANDWIDTH_THRESHOLDS=At;exports.ControlElements=Ke;exports.Controls=Re;exports.VideoPlayer=Ht;exports.findThumbnailCue=we;exports.formatTime=oe;exports.getMimeType=it;exports.isHLSUrl=be;exports.parseThumbnailVtt=ke;//# sourceMappingURL=index.js.map
4
4
  //# sourceMappingURL=index.js.map