react-helios 2.0.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/LICENSE +21 -0
- package/README.md +261 -0
- package/dist/index.css +2 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.mts +244 -0
- package/dist/index.d.ts +244 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3 -0
- package/dist/index.mjs.map +1 -0
- package/dist/styles.css +501 -0
- package/package.json +68 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import react__default from 'react';
|
|
3
|
+
import { HlsConfig } from 'hls.js';
|
|
4
|
+
|
|
5
|
+
interface BufferedRange {
|
|
6
|
+
start: number;
|
|
7
|
+
end: number;
|
|
8
|
+
}
|
|
9
|
+
type VideoErrorCode = "MEDIA_ERR_ABORTED" | "MEDIA_ERR_NETWORK" | "MEDIA_ERR_DECODE" | "MEDIA_ERR_SRC_NOT_SUPPORTED" | "HLS_NETWORK_ERROR" | "HLS_FATAL_ERROR" | "UNKNOWN";
|
|
10
|
+
interface VideoError {
|
|
11
|
+
code: VideoErrorCode;
|
|
12
|
+
message: string;
|
|
13
|
+
}
|
|
14
|
+
interface HLSQualityLevel {
|
|
15
|
+
id: number;
|
|
16
|
+
height: number;
|
|
17
|
+
width: number;
|
|
18
|
+
bitrate: number;
|
|
19
|
+
/** Display name e.g. "1080p", "720p", "Auto" */
|
|
20
|
+
name: string;
|
|
21
|
+
}
|
|
22
|
+
interface SubtitleTrack {
|
|
23
|
+
id: string;
|
|
24
|
+
src: string;
|
|
25
|
+
label: string;
|
|
26
|
+
srclang: string;
|
|
27
|
+
default?: boolean;
|
|
28
|
+
}
|
|
29
|
+
interface PlayerState {
|
|
30
|
+
isPlaying: boolean;
|
|
31
|
+
currentTime: number;
|
|
32
|
+
duration: number;
|
|
33
|
+
volume: number;
|
|
34
|
+
isMuted: boolean;
|
|
35
|
+
playbackRate: number;
|
|
36
|
+
bufferedRanges: BufferedRange[];
|
|
37
|
+
isBuffering: boolean;
|
|
38
|
+
error: VideoError | null;
|
|
39
|
+
isFullscreen: boolean;
|
|
40
|
+
isPictureInPicture: boolean;
|
|
41
|
+
/** True when the stream is a live HLS stream (Infinity duration) */
|
|
42
|
+
isLive: boolean;
|
|
43
|
+
/** Available HLS quality levels; empty for non-HLS sources */
|
|
44
|
+
qualityLevels: HLSQualityLevel[];
|
|
45
|
+
/** Currently active quality level id; -1 = ABR auto */
|
|
46
|
+
currentQualityLevel: number;
|
|
47
|
+
}
|
|
48
|
+
type PlaybackRate = 0.25 | 0.5 | 0.75 | 1 | 1.25 | 1.5 | 1.75 | 2;
|
|
49
|
+
interface VideoPlayerRef {
|
|
50
|
+
play: () => Promise<void>;
|
|
51
|
+
pause: () => void;
|
|
52
|
+
seek: (time: number) => void;
|
|
53
|
+
setVolume: (volume: number) => void;
|
|
54
|
+
/** Toggle mute while remembering the pre-mute volume */
|
|
55
|
+
toggleMute: () => void;
|
|
56
|
+
setPlaybackRate: (rate: PlaybackRate) => void;
|
|
57
|
+
/** Set HLS quality level; pass -1 for automatic ABR */
|
|
58
|
+
setQualityLevel: (level: number) => void;
|
|
59
|
+
/** Jump to the live edge of an HLS live stream */
|
|
60
|
+
seekToLive: () => void;
|
|
61
|
+
toggleFullscreen: () => Promise<void>;
|
|
62
|
+
togglePictureInPicture: () => Promise<void>;
|
|
63
|
+
getState: () => PlayerState;
|
|
64
|
+
getVideoElement: () => HTMLVideoElement | null;
|
|
65
|
+
}
|
|
66
|
+
interface VideoPlayerProps {
|
|
67
|
+
src: string;
|
|
68
|
+
poster?: string;
|
|
69
|
+
autoplay?: boolean;
|
|
70
|
+
muted?: boolean;
|
|
71
|
+
loop?: boolean;
|
|
72
|
+
controls?: boolean;
|
|
73
|
+
preload?: "none" | "metadata" | "auto";
|
|
74
|
+
playbackRates?: PlaybackRate[];
|
|
75
|
+
className?: string;
|
|
76
|
+
enableHLS?: boolean;
|
|
77
|
+
enablePreview?: boolean;
|
|
78
|
+
/** Additional hls.js configuration options */
|
|
79
|
+
hlsConfig?: Partial<HlsConfig>;
|
|
80
|
+
/** Subtitle / caption tracks */
|
|
81
|
+
subtitles?: SubtitleTrack[];
|
|
82
|
+
/** crossorigin attribute for CORS-enabled preview thumbnails */
|
|
83
|
+
crossOrigin?: "anonymous" | "use-credentials";
|
|
84
|
+
onPlay?: () => void;
|
|
85
|
+
onPause?: () => void;
|
|
86
|
+
onEnded?: () => void;
|
|
87
|
+
onError?: (error: VideoError) => void;
|
|
88
|
+
onTimeUpdate?: (time: number) => void;
|
|
89
|
+
onDurationChange?: (duration: number) => void;
|
|
90
|
+
onBuffering?: (isBuffering: boolean) => void;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
declare const VideoPlayer: react__default.ForwardRefExoticComponent<VideoPlayerProps & react__default.RefAttributes<VideoPlayerRef>>;
|
|
94
|
+
|
|
95
|
+
interface ControlsProps {
|
|
96
|
+
playerRef: VideoPlayerRef;
|
|
97
|
+
/** Ref to the outer player container; used to scope keyboard shortcuts to the focused player */
|
|
98
|
+
playerContainerRef: react__default.RefObject<HTMLElement | null>;
|
|
99
|
+
playbackRates: PlaybackRate[];
|
|
100
|
+
enablePreview: boolean;
|
|
101
|
+
isPlaying: boolean;
|
|
102
|
+
currentTime: number;
|
|
103
|
+
duration: number;
|
|
104
|
+
volume: number;
|
|
105
|
+
isMuted: boolean;
|
|
106
|
+
playbackRate: number;
|
|
107
|
+
isFullscreen: boolean;
|
|
108
|
+
isPictureInPicture: boolean;
|
|
109
|
+
isLive: boolean;
|
|
110
|
+
qualityLevels: HLSQualityLevel[];
|
|
111
|
+
currentQualityLevel: number;
|
|
112
|
+
bufferedRanges: BufferedRange[];
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Controls is intentionally NOT wrapped in React.memo here – it receives
|
|
116
|
+
* currentTime which changes every tick, so memo wouldn't help at this level.
|
|
117
|
+
* Instead, all its CHILDREN are memoized so they skip renders when their
|
|
118
|
+
* specific props haven't changed.
|
|
119
|
+
*/
|
|
120
|
+
declare const Controls: react__default.FC<ControlsProps>;
|
|
121
|
+
|
|
122
|
+
interface TimeDisplayProps {
|
|
123
|
+
currentTime: number;
|
|
124
|
+
duration: number;
|
|
125
|
+
isLive?: boolean;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* TimeDisplay re-renders every timeupdate tick (currentTime changes).
|
|
129
|
+
* Wrapped in memo anyway so that if its specific props haven't changed
|
|
130
|
+
* (e.g. when only volume changes) it skips the render.
|
|
131
|
+
*/
|
|
132
|
+
declare const TimeDisplay: react.NamedExoticComponent<TimeDisplayProps>;
|
|
133
|
+
|
|
134
|
+
interface SettingsMenuProps {
|
|
135
|
+
currentRate: number;
|
|
136
|
+
playbackRates: PlaybackRate[];
|
|
137
|
+
onRateChange: (rate: PlaybackRate) => void;
|
|
138
|
+
qualityLevels?: HLSQualityLevel[];
|
|
139
|
+
currentQualityLevel?: number;
|
|
140
|
+
onQualityChange?: (level: number) => void;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* SettingsMenu wrapped in React.memo.
|
|
144
|
+
* playbackRate and qualityLevel rarely change → this component skips
|
|
145
|
+
* almost all re-renders during normal playback.
|
|
146
|
+
*
|
|
147
|
+
* sortedLevels is memoized so the .sort() only runs when qualityLevels
|
|
148
|
+
* actually changes (usually once after manifest is parsed).
|
|
149
|
+
*/
|
|
150
|
+
declare const SettingsMenu: react.NamedExoticComponent<SettingsMenuProps>;
|
|
151
|
+
|
|
152
|
+
interface ProgressBarProps {
|
|
153
|
+
playerRef: VideoPlayerRef;
|
|
154
|
+
currentTime: number;
|
|
155
|
+
duration: number;
|
|
156
|
+
bufferedRanges: BufferedRange[];
|
|
157
|
+
enablePreview?: boolean;
|
|
158
|
+
}
|
|
159
|
+
declare const ProgressBar: react__default.FC<ProgressBarProps>;
|
|
160
|
+
|
|
161
|
+
interface VolumeControlProps {
|
|
162
|
+
volume: number;
|
|
163
|
+
isMuted: boolean;
|
|
164
|
+
onVolumeChange: (volume: number) => void;
|
|
165
|
+
onToggleMute: () => void;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* VolumeControl wrapped in React.memo.
|
|
169
|
+
* volume and isMuted don't change during timeupdate → 0 re-renders during
|
|
170
|
+
* normal playback when the user isn't touching volume controls.
|
|
171
|
+
*/
|
|
172
|
+
declare const VolumeControl: react.NamedExoticComponent<VolumeControlProps>;
|
|
173
|
+
|
|
174
|
+
interface PlayButtonProps {
|
|
175
|
+
onClick: () => void;
|
|
176
|
+
}
|
|
177
|
+
interface PauseButtonProps {
|
|
178
|
+
onClick: () => void;
|
|
179
|
+
}
|
|
180
|
+
interface FullscreenButtonProps {
|
|
181
|
+
onClick: () => void;
|
|
182
|
+
isFullscreen?: boolean;
|
|
183
|
+
}
|
|
184
|
+
interface PiPButtonProps {
|
|
185
|
+
onClick: () => void;
|
|
186
|
+
isPiP?: boolean;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* All button components are wrapped in React.memo.
|
|
190
|
+
* They receive stable callback refs (useCallback in Controls), so they
|
|
191
|
+
* only re-render when their specific props (isFullscreen, isPiP, etc.)
|
|
192
|
+
* actually change – not on every timeupdate tick.
|
|
193
|
+
*/
|
|
194
|
+
declare const PlayButton: react.NamedExoticComponent<PlayButtonProps>;
|
|
195
|
+
declare const PauseButton: react.NamedExoticComponent<PauseButtonProps>;
|
|
196
|
+
declare const FullscreenButton: react.NamedExoticComponent<FullscreenButtonProps>;
|
|
197
|
+
declare const PiPButton: react.NamedExoticComponent<PiPButtonProps>;
|
|
198
|
+
|
|
199
|
+
declare const ControlElements: {
|
|
200
|
+
PlayButton: react.NamedExoticComponent<PlayButtonProps>;
|
|
201
|
+
PauseButton: react.NamedExoticComponent<PauseButtonProps>;
|
|
202
|
+
FullscreenButton: react.NamedExoticComponent<FullscreenButtonProps>;
|
|
203
|
+
PiPButton: react.NamedExoticComponent<PiPButtonProps>;
|
|
204
|
+
VolumeControl: react.NamedExoticComponent<VolumeControlProps>;
|
|
205
|
+
ProgressBar: react.FC<ProgressBarProps>;
|
|
206
|
+
SettingsMenu: react.NamedExoticComponent<SettingsMenuProps>;
|
|
207
|
+
TimeDisplay: react.NamedExoticComponent<TimeDisplayProps>;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
declare const index_ControlElements: typeof ControlElements;
|
|
211
|
+
declare const index_FullscreenButton: typeof FullscreenButton;
|
|
212
|
+
type index_FullscreenButtonProps = FullscreenButtonProps;
|
|
213
|
+
declare const index_PauseButton: typeof PauseButton;
|
|
214
|
+
type index_PauseButtonProps = PauseButtonProps;
|
|
215
|
+
declare const index_PiPButton: typeof PiPButton;
|
|
216
|
+
type index_PiPButtonProps = PiPButtonProps;
|
|
217
|
+
declare const index_PlayButton: typeof PlayButton;
|
|
218
|
+
type index_PlayButtonProps = PlayButtonProps;
|
|
219
|
+
declare const index_ProgressBar: typeof ProgressBar;
|
|
220
|
+
type index_ProgressBarProps = ProgressBarProps;
|
|
221
|
+
declare const index_SettingsMenu: typeof SettingsMenu;
|
|
222
|
+
type index_SettingsMenuProps = SettingsMenuProps;
|
|
223
|
+
declare const index_TimeDisplay: typeof TimeDisplay;
|
|
224
|
+
type index_TimeDisplayProps = TimeDisplayProps;
|
|
225
|
+
declare const index_VolumeControl: typeof VolumeControl;
|
|
226
|
+
type index_VolumeControlProps = VolumeControlProps;
|
|
227
|
+
declare namespace index {
|
|
228
|
+
export { index_ControlElements as ControlElements, index_FullscreenButton as FullscreenButton, type index_FullscreenButtonProps as FullscreenButtonProps, index_PauseButton as PauseButton, type index_PauseButtonProps as PauseButtonProps, index_PiPButton as PiPButton, type index_PiPButtonProps as PiPButtonProps, index_PlayButton as PlayButton, type index_PlayButtonProps as PlayButtonProps, index_ProgressBar as ProgressBar, type index_ProgressBarProps as ProgressBarProps, index_SettingsMenu as SettingsMenu, type index_SettingsMenuProps as SettingsMenuProps, index_TimeDisplay as TimeDisplay, type index_TimeDisplayProps as TimeDisplayProps, index_VolumeControl as VolumeControl, type index_VolumeControlProps as VolumeControlProps };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Format seconds → MM:SS or HH:MM:SS
|
|
233
|
+
*/
|
|
234
|
+
declare function formatTime(seconds: number): string;
|
|
235
|
+
/**
|
|
236
|
+
* Detect an HLS stream URL regardless of query-string parameters.
|
|
237
|
+
*/
|
|
238
|
+
declare function isHLSUrl(url: string): boolean;
|
|
239
|
+
/**
|
|
240
|
+
* Return the MIME type for a given video URL.
|
|
241
|
+
*/
|
|
242
|
+
declare function getMimeType(url: string): string;
|
|
243
|
+
|
|
244
|
+
export { type BufferedRange, index as ControlElements, Controls, type HLSQualityLevel, type PlaybackRate, type PlayerState, type SubtitleTrack, type VideoError, type VideoErrorCode, VideoPlayer, type VideoPlayerProps, type VideoPlayerRef, formatTime, getMimeType, isHLSUrl };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
'use strict';var it=require('react'),de=require('hls.js'),jsxRuntime=require('react/jsx-runtime');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var it__default=/*#__PURE__*/_interopDefault(it);var de__default=/*#__PURE__*/_interopDefault(de);var _e=Object.defineProperty;var $e=(e,a)=>{for(var l in a)_e(e,l,{get:a[l],enumerable:true});};function K(e){if(!Number.isFinite(e)||e<0)return "0:00";let a=Math.floor(e),l=Math.floor(a/3600),f=Math.floor(a%3600/60),d=a%60;return l>0?`${l}:${String(f).padStart(2,"0")}:${String(d).padStart(2,"0")}`:`${f}:${String(d).padStart(2,"0")}`}function ce(e){try{return new URL(e,"https://x").pathname.toLowerCase().endsWith(".m3u8")||/\/hls\//i.test(e)||/\/stream\.m3u8/i.test(e)}catch{return e.toLowerCase().includes(".m3u8")}}function Ue(e){if(ce(e))return "application/x-mpegURL";let a=e.toLowerCase().split("?")[0];return a.endsWith(".mp4")?"video/mp4":a.endsWith(".webm")?"video/webm":a.endsWith(".ogv")||a.endsWith(".ogg")?"video/ogg":a.endsWith(".mov")?"video/quicktime":"video/mp4"}function we(e){return e.map((a,l)=>({id:l,height:a.height??0,width:a.width??0,bitrate:a.bitrate??0,name:a.height?`${a.height}p`:`Level ${l+1}`}))}var Ke={isPlaying:false,currentTime:0,duration:0,volume:1,isMuted:false,playbackRate:1,isFullscreen:false,isPictureInPicture:false,isBuffering:false,bufferedRanges:[],error:null,isLive:false,qualityLevels:[],currentQualityLevel:-1};function Te(e,a,l={}){let f=it.useRef(null),d=it.useRef(null),h=it.useRef(1),v=it.useRef(0),u=it.useRef(l);u.current=l;let[g,s]=it.useState({...Ke,isMuted:l.muted??false,volume:l.muted?0:1}),B=it.useRef(g);B.current=g,it.useEffect(()=>{let t=e.current;if(!t||(f.current&&(f.current.destroy(),f.current=null),v.current=0,s(c=>({...c,currentTime:0,duration:0,error:null,isBuffering:false,isLive:false,qualityLevels:[],currentQualityLevel:-1})),!a))return;let n=u.current;if(n.enableHLS!==false&&ce(a)){if(t.canPlayType("application/vnd.apple.mpegurl"))t.src=a,t.load(),n.autoplay&&t.play().catch(()=>{});else if(de__default.default.isSupported()){let c=new de__default.default({autoStartLoad:true,startLevel:-1,capLevelToPlayerSize:true,capLevelOnFPSDrop:true,enableWorker:true,maxBufferLength:30,maxMaxBufferLength:600,maxBufferSize:6e7,liveBackBufferLength:30,liveSyncDurationCount:3,...n.hlsConfig});c.attachMedia(t),c.loadSource(a),c.on(de.Events.MANIFEST_PARSED,(z,E)=>{let i=we(E.levels);s(m=>({...m,qualityLevels:i,currentQualityLevel:-1})),u.current.autoplay&&t.play().catch(()=>{});}),c.on(de.Events.LEVEL_SWITCHED,(z,E)=>{s(i=>({...i,currentQualityLevel:E.level}));});let F=3;c.on(de.Events.ERROR,(z,E)=>{if(!E.fatal){console.warn("[hls] non-fatal:",E.details);return}switch(E.type){case de__default.default.ErrorTypes.NETWORK_ERROR:if(v.current<F){v.current+=1;let i=1e3*v.current;console.warn(`[hls] network error \u2013 retry ${v.current}/${F} in ${i}ms`),setTimeout(()=>{f.current===c&&c.startLoad();},i);}else {let i={code:"HLS_NETWORK_ERROR",message:"Failed to load stream after multiple retries."};s(m=>({...m,error:i})),u.current.onError?.(i);}break;case de__default.default.ErrorTypes.MEDIA_ERROR:console.warn("[hls] media error \u2013 recovering"),c.recoverMediaError();break;default:{c.destroy(),f.current=null;let i={code:"HLS_FATAL_ERROR",message:"An unrecoverable HLS error occurred."};s(m=>({...m,error:i})),u.current.onError?.(i);break}}}),f.current=c;}}else t.src=a,t.load(),n.autoplay&&t.play().catch(()=>{});return ()=>{f.current&&(f.current.destroy(),f.current=null);}},[a,e]),it.useEffect(()=>{let t=e.current;if(!t)return;u.current.muted&&(t.muted=true),u.current.loop&&(t.loop=true);let n=()=>{s(r=>({...r,isPlaying:true})),u.current.onPlay?.();},c=()=>{s(r=>({...r,isPlaying:false})),u.current.onPause?.();},F=()=>{s(r=>({...r,isPlaying:false})),u.current.onEnded?.();},z=()=>{s(r=>({...r,currentTime:t.currentTime})),u.current.onTimeUpdate?.(t.currentTime);},E=()=>{let r=t.duration,o=!Number.isFinite(r);s(b=>({...b,duration:o?0:r,isLive:o})),o||u.current.onDurationChange?.(r);},i=()=>{let r=t.volume;r>0&&!t.muted&&(h.current=r),s(o=>({...o,volume:r,isMuted:t.muted||r===0}));},m=()=>{s(r=>({...r,playbackRate:t.playbackRate}));},W=()=>{let r=t.error;if(!r)return;let b={code:{1:"MEDIA_ERR_ABORTED",2:"MEDIA_ERR_NETWORK",3:"MEDIA_ERR_DECODE",4:"MEDIA_ERR_SRC_NOT_SUPPORTED"}[r.code]??"UNKNOWN",message:r.message||"Unknown media error"};s(O=>({...O,error:b})),u.current.onError?.(b);},ee=()=>{s(r=>({...r,isBuffering:true})),u.current.onBuffering?.(true);},G=()=>{s(r=>({...r,isBuffering:false})),u.current.onBuffering?.(false);},Y=()=>s(r=>({...r,isBuffering:false})),j=()=>{let r=[];for(let o=0;o<t.buffered.length;o++)r.push({start:t.buffered.start(o),end:t.buffered.end(o)});s(o=>({...o,bufferedRanges:r}));},$=()=>{let r=!!(document.fullscreenElement||document.webkitFullscreenElement);s(o=>({...o,isFullscreen:r}));},Q=()=>{s(r=>({...r,isPictureInPicture:document.pictureInPictureElement===t}));};return t.addEventListener("play",n),t.addEventListener("pause",c),t.addEventListener("ended",F),t.addEventListener("timeupdate",z),t.addEventListener("durationchange",E),t.addEventListener("volumechange",i),t.addEventListener("ratechange",m),t.addEventListener("error",W),t.addEventListener("waiting",ee),t.addEventListener("canplay",G),t.addEventListener("playing",Y),t.addEventListener("progress",j),document.addEventListener("fullscreenchange",$),document.addEventListener("webkitfullscreenchange",$),t.addEventListener("enterpictureinpicture",Q),t.addEventListener("leavepictureinpicture",Q),()=>{t.removeEventListener("play",n),t.removeEventListener("pause",c),t.removeEventListener("ended",F),t.removeEventListener("timeupdate",z),t.removeEventListener("durationchange",E),t.removeEventListener("volumechange",i),t.removeEventListener("ratechange",m),t.removeEventListener("error",W),t.removeEventListener("waiting",ee),t.removeEventListener("canplay",G),t.removeEventListener("playing",Y),t.removeEventListener("progress",j),document.removeEventListener("fullscreenchange",$),document.removeEventListener("webkitfullscreenchange",$),t.removeEventListener("enterpictureinpicture",Q),t.removeEventListener("leavepictureinpicture",Q);}},[e]);let k=it.useCallback(async()=>{let t=e.current;if(t)try{await t.play();}catch(n){n instanceof Error&&n.name!=="AbortError"&&console.error("[player] play() failed:",n);}},[e]),x=it.useCallback(()=>{e.current?.pause();},[e]),H=it.useCallback(t=>{let n=e.current;n&&(n.currentTime=Math.max(0,Math.min(t,n.duration||t)));},[e]),p=it.useCallback(t=>{let n=e.current;if(!n)return;let c=Math.max(0,Math.min(t,1));c>0&&(h.current=c),n.volume=c,n.muted=c===0;},[e]),T=it.useCallback(()=>{let t=e.current;if(t)if(t.muted||t.volume===0){let n=h.current>0?h.current:1;t.volume=n,t.muted=false;}else h.current=t.volume,t.muted=true;},[e]),S=it.useCallback(t=>{let n=e.current;n&&(n.playbackRate=t);},[e]),y=it.useCallback(t=>{let n=f.current;n&&(n.currentLevel=t,s(c=>({...c,currentQualityLevel:t})));},[]),C=it.useCallback(()=>{let t=f.current,n=e.current;if(!t||!n)return;let c=t.liveSyncPosition;c!=null&&Number.isFinite(c)&&(n.currentTime=c);},[e]),N=it.useCallback(async()=>{let t=e.current;if(!t)return;let n=d.current??t.parentElement;if(n)try{!document.fullscreenElement&&!document.webkitFullscreenElement?n.requestFullscreen?await n.requestFullscreen():n.webkitRequestFullscreen?.():document.exitFullscreen?await document.exitFullscreen():document.webkitExitFullscreen?.();}catch(c){console.error("[player] fullscreen toggle failed:",c);}},[e]),P=it.useCallback(async()=>{let t=e.current;if(t)try{document.pictureInPictureElement?await document.exitPictureInPicture():await t.requestPictureInPicture();}catch(n){console.error("[player] PiP toggle failed:",n);}},[e]),_=it.useCallback(()=>({...B.current}),[]),L=it.useCallback(()=>e.current??null,[e]),M=it.useMemo(()=>({play:k,pause:x,seek:H,setVolume:p,toggleMute:T,setPlaybackRate:S,setQualityLevel:y,seekToLive:C,toggleFullscreen:N,togglePictureInPicture:P,getState:_,getVideoElement:L}),[k,x,H,p,T,S,y,C,N,P,_,L]);return {state:g,ref:M,hlsRef:f,fullscreenContainerRef:d}}var He={};$e(He,{ControlElements:()=>I,FullscreenButton:()=>ie,PauseButton:()=>ae,PiPButton:()=>se,PlayButton:()=>oe,ProgressBar:()=>fe,SettingsMenu:()=>ve,TimeDisplay:()=>ge,VolumeControl:()=>me});var oe=it.memo(({onClick:e})=>jsxRuntime.jsx("button",{onClick:e,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"})})}));oe.displayName="PlayButton";var ae=it.memo(({onClick:e})=>jsxRuntime.jsx("button",{onClick:e,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"})})}));ae.displayName="PauseButton";var ie=it.memo(({onClick:e,isFullscreen:a=false})=>jsxRuntime.jsx("button",{onClick:e,className:"controlButton","aria-label":a?"Exit Fullscreen":"Fullscreen",title:a?"Exit Fullscreen (F)":"Fullscreen (F)",children:jsxRuntime.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:a?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"})})}));ie.displayName="FullscreenButton";var se=it.memo(({onClick:e,isPiP:a=false})=>jsxRuntime.jsx("button",{onClick:e,className:"controlButton","aria-label":a?"Exit Picture-in-Picture":"Picture-in-Picture",title:a?"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"})})}));se.displayName="PiPButton";var Se=it.memo(({volume:e,isMuted:a,onVolumeChange:l,onToggleMute:f})=>{let[d,h]=it.useState(false),v=a?0:e,u=v*100,g=it.useMemo(()=>`linear-gradient(to right, #60a5fa 0%, #60a5fa ${u}%, rgba(255,255,255,0.3) ${u}%, rgba(255,255,255,0.3) 100%)`,[u]);return jsxRuntime.jsxs("div",{className:"volumeContainer",onMouseEnter:()=>h(true),onMouseLeave:()=>h(false),children:[jsxRuntime.jsx("button",{onClick:f,className:"controlButton","aria-label":a?"Unmute":"Mute",title:a?"Unmute (M)":"Mute (M)",children:jsxRuntime.jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:v===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"}):v<.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"})})}),d&&jsxRuntime.jsx("input",{type:"range",min:"0",max:"100",value:u,onChange:s=>l(Number(s.target.value)/100),className:"volumeSlider",style:{background:g},"aria-label":"Volume","aria-valuenow":Math.round(u)})]})});Se.displayName="VolumeControl";var me=Se;var Ce=it.memo(({playerRef:e,currentTime:a,duration:l,bufferedRanges:f,enablePreview:d=true})=>{let h=it.useRef(null),v=it.useRef(null),u=it.useRef(null),[g,s]=it.useState(false),[B,k]=it.useState(0),[x,H]=it.useState(0),[p,T]=it.useState(false),[S,y]=it.useState(false),C=it.useRef(null),N=it.useRef(0),P=it.useRef(null),_=it.useRef(false);_.current=g;let L=it.useRef(null);it.useEffect(()=>{let r=()=>{L.current=null;};return window.addEventListener("resize",r,{passive:true}),()=>window.removeEventListener("resize",r)},[]);let M=it.useCallback(()=>(!L.current&&h.current&&(L.current=h.current.getBoundingClientRect()),L.current),[]);it.useEffect(()=>{if(!d)return;let r=e.getVideoElement(),o=v.current;if(!r||!o)return;let b=r.currentSrc||r.src;if(!b)return;o.src=b,o.muted=true,o.preload="auto",o.crossOrigin=r.crossOrigin;let O=()=>y(true),te=()=>{console.warn("[preview] failed to load"),y(false);};return o.addEventListener("loadedmetadata",O),o.addEventListener("loadeddata",O),o.addEventListener("error",te),()=>{o.removeEventListener("loadedmetadata",O),o.removeEventListener("loadeddata",O),o.removeEventListener("error",te),o.removeAttribute("src"),o.load(),y(false);}},[e,d]),it.useEffect(()=>{let r=h.current;if(!r)return;let o=b=>{_.current&&b.preventDefault();};return r.addEventListener("touchmove",o,{passive:false}),()=>r.removeEventListener("touchmove",o)},[]);let t=it.useCallback(r=>{if(!d||!S)return;let o=v.current,b=u.current;if(!o||!b)return;let O=Date.now();if(O-N.current<100)return;N.current=O,C.current&&clearTimeout(C.current),P.current&&cancelAnimationFrame(P.current);let te=false,xe=()=>{te||o.readyState>=2&&(te=true,P.current&&cancelAnimationFrame(P.current),P.current=requestAnimationFrame(()=>{let Le=b.getContext("2d",{alpha:false,willReadFrequently:false});Le&&(b.width=160,b.height=90,Le.drawImage(o,0,0,160,90));}));};o.currentTime=r,o.addEventListener("seeked",xe,{once:true}),C.current=setTimeout(()=>{te||xe();},200);},[d,S]),n=it.useCallback(r=>{let o=M();return !o||o.width===0?0:Math.max(0,Math.min(r-o.left,o.width))/o.width*l},[M,l]),c=it.useCallback(r=>{let o=M();return o?Math.max(0,Math.min(r-o.left,o.width)):0},[M]),F=it.useCallback(r=>{switch(r.key){case "ArrowLeft":case "ArrowRight":{r.preventDefault(),r.nativeEvent.stopImmediatePropagation();let o=r.shiftKey?10:5,b=r.key==="ArrowLeft"?Math.max(0,a-o):Math.min(l||0,a+o);e.seek(b);break}case "Home":r.preventDefault(),r.nativeEvent.stopImmediatePropagation(),e.seek(0);break;case "End":l>0&&(r.preventDefault(),r.nativeEvent.stopImmediatePropagation(),e.seek(l));break}},[a,l,e]),z=it.useCallback(r=>{let o=n(r.clientX),b=c(r.clientX);k(o),H(b),g?e.seek(o):d&&S&&t(o);},[g,d,S,e,t,n,c]),E=it.useCallback(()=>{L.current=null,T(true);},[]),i=it.useCallback(()=>{T(false),s(false);},[]),m=it.useCallback(r=>{r.preventDefault(),s(true),e.seek(n(r.clientX));},[n,e]),W=it.useCallback(()=>s(false),[]),ee=it.useCallback(r=>{g||e.seek(n(r.clientX));},[g,n,e]),G=it.useCallback(r=>{L.current=null,s(true),e.seek(n(r.touches[0].clientX));},[n,e]),Y=it.useCallback(r=>{g&&e.seek(n(r.touches[0].clientX));},[g,n,e]),j=it.useCallback(()=>s(false),[]);it.useEffect(()=>{let r=()=>s(false);return window.addEventListener("mouseup",r),()=>window.removeEventListener("mouseup",r)},[]),it.useEffect(()=>()=>{C.current&&clearTimeout(C.current),P.current&&cancelAnimationFrame(P.current);},[]);let $=l>0?a/l*100:0,Q=it.useMemo(()=>l<=0?null:f.map((r,o)=>{let b=r.start/l*100,O=(r.end-r.start)/l*100;return jsxRuntime.jsx("div",{className:"bufferedSegment",style:{left:`${b}%`,width:`${O}%`}},o)}),[f,l]);return jsxRuntime.jsxs("div",{ref:h,className:"progressContainer",onMouseMove:z,onMouseEnter:E,onMouseLeave:i,onMouseDown:m,onMouseUp:W,onClick:ee,onTouchStart:G,onTouchMove:Y,onTouchEnd:j,onKeyDown:F,role:"slider","aria-label":"Video progress","aria-valuemin":0,"aria-valuemax":Math.round(l),"aria-valuenow":Math.round(a),"aria-valuetext":K(a),tabIndex:0,children:[d&&jsxRuntime.jsx("video",{ref:v,className:"previewVideo",playsInline:true,muted:true,preload:"auto","aria-hidden":"true"}),d&&p&&S&&jsxRuntime.jsxs("div",{className:"previewTooltip",style:{left:x},"aria-hidden":"true",children:[jsxRuntime.jsx("canvas",{ref:u,className:"previewCanvas"}),jsxRuntime.jsx("div",{className:"previewTime",children:K(B)})]}),jsxRuntime.jsxs("div",{className:"progressBackground",children:[Q,jsxRuntime.jsx("div",{className:"progressFilled",style:{width:`${$}%`}}),p&&jsxRuntime.jsx("div",{className:"hoverIndicator",style:{left:x},"aria-hidden":"true"})]}),jsxRuntime.jsx("div",{className:`scrubHandle${g?" dragging":""}`,style:{left:`${$}%`},"aria-hidden":"true"})]})});Ce.displayName="ProgressBar";var fe=Ce;var Be=it.memo(({currentRate:e,playbackRates:a,onRateChange:l,qualityLevels:f=[],currentQualityLevel:d=-1,onQualityChange:h})=>{let[v,u]=it.useState(false),[g,s]=it.useState("speed"),B=it.useRef(null),k=f.length>0&&!!h;it.useEffect(()=>{let p=T=>{B.current&&!B.current.contains(T.target)&&u(false);};return v&&document.addEventListener("mousedown",p),()=>document.removeEventListener("mousedown",p)},[v]);let x=it.useMemo(()=>[...f].sort((p,T)=>T.bitrate-p.bitrate),[f]),H=it.useMemo(()=>d===-1?"Auto":f.find(p=>p.id===d)?.name??"Auto",[f,d]);return jsxRuntime.jsxs("div",{ref:B,className:"settingsContainer",children:[jsxRuntime.jsx("button",{onClick:()=>u(p=>!p),className:"controlButton","aria-label":"Settings",title:"Settings","aria-expanded":v,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"})})}),v&&jsxRuntime.jsxs("div",{className:"settingsDropdown",role:"menu",children:[k&&jsxRuntime.jsxs("div",{className:"settingsTabs",children:[jsxRuntime.jsx("button",{className:`settingsTab${g==="speed"?" active":""}`,onClick:()=>s("speed"),children:"Speed"}),jsxRuntime.jsx("button",{className:`settingsTab${g==="quality"?" active":""}`,onClick:()=>s("quality"),children:"Quality"})]}),(!k||g==="speed")&&jsxRuntime.jsxs("div",{children:[!k&&jsxRuntime.jsx("div",{className:"settingsPanelLabel",children:"Playback Speed"}),a.map(p=>jsxRuntime.jsx("button",{onClick:()=>{l(p),u(false);},className:`settingsOption${e===p?" active":""}`,role:"menuitemradio","aria-checked":e===p,children:p===1?"Normal":`${p}\xD7`},p))]}),k&&g==="quality"&&jsxRuntime.jsxs("div",{children:[jsxRuntime.jsxs("button",{onClick:()=>{h(-1),u(false);},className:`settingsOption${d===-1?" active":""}`,role:"menuitemradio","aria-checked":d===-1,children:["Auto ",d===-1&&H!=="Auto"?`(${H})`:""]}),x.map(p=>jsxRuntime.jsxs("button",{onClick:()=>{h(p.id),u(false);},className:`settingsOption${d===p.id?" active":""}`,role:"menuitemradio","aria-checked":d===p.id,children:[p.name,p.bitrate>0&&jsxRuntime.jsxs("span",{className:"settingsOptionBadge",children:[Math.round(p.bitrate/1e3)," kbps"]})]},p.id))]})]})]})});Be.displayName="SettingsMenu";var ve=Be;var Ve=it.memo(({currentTime:e,duration:a,isLive:l=false})=>l?jsxRuntime.jsx("span",{className:"timeDisplay",style:{opacity:.7},children:K(e)}):jsxRuntime.jsxs("span",{className:"timeDisplay",children:[K(e),jsxRuntime.jsxs("span",{style:{opacity:.6},children:[" / ",K(a)]})]}));Ve.displayName="TimeDisplay";var ge=Ve;var I={PlayButton:oe,PauseButton:ae,FullscreenButton:ie,PiPButton:se,VolumeControl:me,ProgressBar:fe,SettingsMenu:ve,TimeDisplay:ge};var Ee=({playerRef:e,playerContainerRef:a,playbackRates:l,enablePreview:f,isPlaying:d,currentTime:h,duration:v,volume:u,isMuted:g,playbackRate:s,isFullscreen:B,isPictureInPicture:k,isLive:x,qualityLevels:H,currentQualityLevel:p,bufferedRanges:T})=>{let S=it.useRef(null),y=it.useRef(null),[C,N]=it.useState(true),P=it.useRef({isPlaying:d,currentTime:h,duration:v,volume:u,isMuted:g,isLive:x});P.current={isPlaying:d,currentTime:h,duration:v,volume:u,isMuted:g,isLive:x},it.useEffect(()=>{if(!d){N(true),y.current&&clearTimeout(y.current);return}let i=()=>{N(true),y.current&&clearTimeout(y.current),y.current=setTimeout(()=>N(false),3e3);},m=S.current;return m&&(m.addEventListener("mousemove",i),m.addEventListener("mouseenter",i),m.addEventListener("touchstart",i,{passive:true})),i(),()=>{m&&(m.removeEventListener("mousemove",i),m.removeEventListener("mouseenter",i),m.removeEventListener("touchstart",i)),y.current&&clearTimeout(y.current);}},[d]),it.useEffect(()=>{let i=m=>{if(!a.current?.contains(document.activeElement))return;let W=m.target;if(W.tagName==="INPUT"||W.tagName==="TEXTAREA"||W.isContentEditable)return;let{isPlaying:ee,currentTime:G,duration:Y,volume:j,isLive:$}=P.current;switch(m.code){case "Space":case "KeyK":m.preventDefault(),ee?e.pause():e.play();break;case "ArrowLeft":m.preventDefault(),e.seek(Math.max(0,G-5));break;case "ArrowRight":m.preventDefault(),e.seek(Math.min(Y||1/0,G+5));break;case "ArrowUp":m.preventDefault(),e.setVolume(Math.min(1,j+.1));break;case "ArrowDown":m.preventDefault(),e.setVolume(Math.max(0,j-.1));break;case "KeyM":m.preventDefault(),e.toggleMute();break;case "KeyF":m.preventDefault(),e.toggleFullscreen();break;case "KeyP":m.preventDefault(),e.togglePictureInPicture();break;case "KeyL":m.preventDefault(),$&&e.seekToLive();break;case "Digit0":case "Digit1":case "Digit2":case "Digit3":case "Digit4":case "Digit5":case "Digit6":case "Digit7":case "Digit8":case "Digit9":{m.preventDefault();let Q=Number(m.code.replace("Digit",""))*10;e.seek(Y/100*Q);break}}};return window.addEventListener("keydown",i),()=>window.removeEventListener("keydown",i)},[e,a]);let _=it.useCallback(()=>e.play(),[e]),L=it.useCallback(()=>e.pause(),[e]),M=it.useCallback(i=>e.setVolume(i),[e]),t=it.useCallback(()=>e.toggleMute(),[e]),n=it.useCallback(i=>e.setPlaybackRate(i),[e]),c=it.useCallback(i=>e.setQualityLevel(i),[e]),F=it.useCallback(()=>e.togglePictureInPicture(),[e]),z=it.useCallback(()=>e.toggleFullscreen(),[e]),E=it.useCallback(()=>e.seekToLive(),[e]);return jsxRuntime.jsx("div",{ref:S,style:{position:"absolute",inset:0,display:"flex",flexDirection:"column",justifyContent:"flex-end",opacity:C?1:0,transition:"opacity 0.3s",pointerEvents:C?"auto":"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"},role:"region","aria-label":"Video player controls",children:[jsxRuntime.jsx(I.ProgressBar,{playerRef:e,currentTime:h,duration:v,bufferedRanges:T,enablePreview:f}),jsxRuntime.jsxs("div",{style:{display:"flex",alignItems:"center",gap:4,marginTop:4},children:[d?jsxRuntime.jsx(I.PauseButton,{onClick:L}):jsxRuntime.jsx(I.PlayButton,{onClick:_}),jsxRuntime.jsx(I.VolumeControl,{volume:u,isMuted:g,onVolumeChange:M,onToggleMute:t}),jsxRuntime.jsx(I.TimeDisplay,{currentTime:h,duration:v,isLive:x}),jsxRuntime.jsx("div",{style:{flex:1}}),x&&jsxRuntime.jsx(ze,{onClick:E}),jsxRuntime.jsx(I.SettingsMenu,{currentRate:s,playbackRates:l,onRateChange:n,qualityLevels:H,currentQualityLevel:p,onQualityChange:c}),jsxRuntime.jsx(I.PiPButton,{onClick:F,isPiP:k}),jsxRuntime.jsx(I.FullscreenButton,{onClick:z,isFullscreen:B})]})]})})},ze=it.memo(({onClick:e})=>jsxRuntime.jsx("button",{onClick:e,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"}));ze.displayName="GoLiveButton";var Ae=it.forwardRef(({src:e,poster:a,autoplay:l=false,muted:f=false,loop:d=false,controls:h=true,preload:v="metadata",playbackRates:u=[.25,.5,.75,1,1.25,1.5,1.75,2],className:g,enableHLS:s=true,enablePreview:B=true,hlsConfig:k,subtitles:x,crossOrigin:H,onPlay:p,onPause:T,onEnded:S,onError:y,onTimeUpdate:C,onDurationChange:N,onBuffering:P},_)=>{let L=it.useRef(null),M=it.useRef(null),{state:t,ref:n,fullscreenContainerRef:c}=Te(L,e,{autoplay:l,muted:f,loop:d,playbackRates:u,enableHLS:s,hlsConfig:k,onPlay:p,onPause:T,onEnded:S,onError:y,onTimeUpdate:C,onDurationChange:N,onBuffering:P});it.useEffect(()=>{c.current=M.current;},[c]),it__default.default.useImperativeHandle(_,()=>n,[n]);let F=it.useCallback(()=>{M.current?.focus(),t.isPlaying?n.pause():n.play();},[t.isPlaying,n]),z=it.useCallback(()=>{n.toggleFullscreen();},[n]),E=it.useMemo(()=>s&&!!e&&e.toLowerCase().includes(".m3u8"),[s,e]);return jsxRuntime.jsxs("div",{ref:M,tabIndex:0,style:{position:"relative",width:"100%",backgroundColor:"#000",aspectRatio:"16 / 9",userSelect:"none",outline:"none"},className:g,"data-test":"video-player-container",children:[jsxRuntime.jsx("video",{ref:L,poster:a,preload:v,crossOrigin:H,onClick:F,onDoubleClick:z,playsInline:true,style:{width:"100%",height:"100%",display:"block",cursor:"pointer"},"data-test":"video-element",children:x?.map(i=>jsxRuntime.jsx("track",{kind:"subtitles",src:i.src,label:i.label,srcLang:i.srclang,default:i.default},i.id))}),h&&jsxRuntime.jsx(Ee,{playerRef:n,playerContainerRef:M,playbackRates:u,enablePreview:B&&!E,isPlaying:t.isPlaying,currentTime:t.currentTime,duration:t.duration,volume:t.volume,isMuted:t.isMuted,playbackRate:t.playbackRate,isFullscreen:t.isFullscreen,isPictureInPicture:t.isPictureInPicture,isLive:t.isLive,qualityLevels:t.qualityLevels,currentQualityLevel:t.currentQualityLevel,bufferedRanges:t.bufferedRanges}),t.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"}),t.isBuffering&&!t.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); } }"})]}),t.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:t.error.code==="MEDIA_ERR_SRC_NOT_SUPPORTED"?"Unsupported Format":t.error.code.startsWith("HLS")?"Stream Error":"Playback Error"}),jsxRuntime.jsx("p",{style:{margin:0,fontSize:13,opacity:.75},children:t.error.message})]})})]})});Ae.displayName="VideoPlayer";var ct=Ae;
|
|
2
|
+
exports.ControlElements=He;exports.Controls=Ee;exports.VideoPlayer=ct;exports.formatTime=K;exports.getMimeType=Ue;exports.isHLSUrl=ce;//# sourceMappingURL=index.js.map
|
|
3
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/VideoPlayer.tsx","../src/lib/format.ts","../src/lib/hls.ts","../src/hooks/useVideoPlayer.ts","../src/components/control-elements/index.ts","../src/components/control-elements/control-buttons.tsx","../src/components/control-elements/volume-control.tsx","../src/components/control-elements/progress-bar.tsx","../src/components/control-elements/settings-menu.tsx","../src/components/control-elements/time-display.tsx","../src/components/Controls.tsx"],"names":["formatTime","seconds","total","h","m","s","isHLSUrl","url","getMimeType","lower","buildQualityLevels","levels","l","i","DEFAULT_STATE","useVideoPlayer","videoRef","src","options","hlsRef","useRef","fullscreenContainerRef","lastVolumeRef","networkRetriesRef","optionsRef","state","setState","useState","stateRef","useEffect","video","prev","opts","HLS","hls","Events","_","data","MAX_RETRIES","delay","err","fatalErr","handlePlay","handlePause","handleEnded","handleTimeUpdate","handleDurationChange","dur","live","handleVolumeChange","vol","handleRateChange","handleError","e","handleWaiting","handleCanPlay","handlePlaying","handleProgress","ranges","handleFullscreenChange","fs","handlePiPChange","play","useCallback","pause","seek","time","setVolume","volume","v","toggleMute","restore","setPlaybackRate","rate","setQualityLevel","level","seekToLive","livePos","toggleFullscreen","container","togglePictureInPicture","getState","getVideoElement","ref","useMemo","control_elements_exports","__export","ControlElements","FullscreenButton","PauseButton","PiPButton","PlayButton","progress_bar_default","settings_menu_default","time_display_default","volume_control_default","memo","onClick","jsx","isFullscreen","isPiP","VolumeControl","isMuted","onVolumeChange","onToggleMute","showSlider","setShowSlider","displayVolume","percentage","sliderBackground","jsxs","ProgressBar","playerRef","currentTime","duration","bufferedRanges","enablePreview","containerRef","previewVideoRef","canvasRef","isDragging","setIsDragging","hoverTime","setHoverTime","hoverPos","setHoverPos","showPreview","setShowPreview","previewLoaded","setPreviewLoaded","updateTimeoutRef","lastSeekTimeRef","rafRef","isDraggingRef","rectCacheRef","invalidate","getRect","mainVideo","previewVideo","videoSrc","onReady","onErr","onTouchMove","updatePreview","canvas","now","drawn","drawFrame","ctx","getTimeFromClientX","clientX","rect","getPxFromClientX","handleKeyDown","step","newTime","handleMouseMove","px","handleMouseEnter","handleMouseLeave","handleMouseDown","handleMouseUp","handleClick","handleTouchStart","handleTouchMove","handleTouchEnd","up","progress","bufferedSegments","range","start","width","SettingsMenu","currentRate","playbackRates","onRateChange","qualityLevels","currentQualityLevel","onQualityChange","open","setOpen","tab","setTab","hasQuality","handler","sortedLevels","a","b","currentQualityName","o","TimeDisplay","isLive","Controls","playerContainerRef","isPlaying","playbackRate","isPictureInPicture","hideTimeoutRef","showControls","setShowControls","liveRef","reset","el","target","playing","ct","pct","handleToggleMute","r","handleQualityChange","handlePiP","handleFullscreen","handleSeekToLive","GoLiveButton","VideoPlayer","forwardRef","poster","autoplay","muted","loop","controls","preload","className","enableHLS","hlsConfig","subtitles","crossOrigin","onPlay","onPause","onEnded","onError","onTimeUpdate","onDurationChange","onBuffering","forwardedRef","React","handleVideoClick","handleDoubleClick","isHLSSrc","track","VideoPlayer_default"],"mappings":"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","file":"index.js","sourcesContent":["\"use client\";\n\nimport React, { forwardRef, useEffect, useRef, useCallback, useMemo } from \"react\";\nimport type { VideoPlayerProps, VideoPlayerRef } from \"../lib/types\";\nimport { useVideoPlayer } from \"../hooks/useVideoPlayer\";\nimport { Controls } from \"./Controls\";\n\nconst VideoPlayer = forwardRef<VideoPlayerRef, VideoPlayerProps>(\n (\n {\n src,\n poster,\n autoplay = false,\n muted = false,\n loop = false,\n controls = true,\n preload = \"metadata\",\n playbackRates = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2],\n className,\n enableHLS = true,\n enablePreview = true,\n hlsConfig,\n subtitles,\n crossOrigin,\n onPlay,\n onPause,\n onEnded,\n onError,\n onTimeUpdate,\n onDurationChange,\n onBuffering,\n },\n forwardedRef,\n ) => {\n const videoRef = useRef<HTMLVideoElement | null>(null);\n const containerRef = useRef<HTMLDivElement | null>(null);\n\n const { state, ref: playerRef, fullscreenContainerRef } = useVideoPlayer(\n videoRef,\n src,\n {\n autoplay,\n muted,\n loop,\n playbackRates,\n enableHLS,\n hlsConfig,\n onPlay,\n onPause,\n onEnded,\n onError,\n onTimeUpdate,\n onDurationChange,\n onBuffering,\n },\n );\n\n useEffect(() => {\n fullscreenContainerRef.current = containerRef.current;\n }, [fullscreenContainerRef]);\n\n /**\n * useImperativeHandle only fires once after mount – not 60× per second.\n */\n React.useImperativeHandle(forwardedRef, () => playerRef, [playerRef]);\n\n const handleVideoClick = useCallback(() => {\n // Focus the container so keyboard shortcuts activate for this player\n containerRef.current?.focus();\n if (state.isPlaying) playerRef.pause();\n else playerRef.play();\n }, [state.isPlaying, playerRef]);\n\n const handleDoubleClick = useCallback(() => {\n playerRef.toggleFullscreen();\n }, [playerRef]);\n\n /** Precompute once per src change, not on every render */\n const isHLSSrc = useMemo(\n () => enableHLS && !!src && src.toLowerCase().includes(\".m3u8\"),\n [enableHLS, src],\n );\n\n /**\n * Pass FLAT state props to Controls instead of the whole state object.\n * With React.memo on each control sub-component this ensures that\n * buttons / volume / settings only re-render when THEIR props change –\n * not on every timeupdate tick.\n */\n return (\n <div\n ref={containerRef}\n tabIndex={0}\n style={{\n position: \"relative\",\n width: \"100%\",\n backgroundColor: \"#000\",\n aspectRatio: \"16 / 9\",\n userSelect: \"none\",\n outline: \"none\",\n }}\n className={className}\n data-test=\"video-player-container\"\n >\n <video\n ref={videoRef}\n poster={poster}\n preload={preload}\n crossOrigin={crossOrigin}\n onClick={handleVideoClick}\n onDoubleClick={handleDoubleClick}\n playsInline\n style={{ width: \"100%\", height: \"100%\", display: \"block\", cursor: \"pointer\" }}\n data-test=\"video-element\"\n >\n {subtitles?.map((track) => (\n <track\n key={track.id}\n kind=\"subtitles\"\n src={track.src}\n label={track.label}\n srcLang={track.srclang}\n default={track.default}\n />\n ))}\n </video>\n\n {controls && (\n <Controls\n playerRef={playerRef}\n playerContainerRef={containerRef}\n playbackRates={playbackRates}\n enablePreview={enablePreview && !isHLSSrc}\n isPlaying={state.isPlaying}\n currentTime={state.currentTime}\n duration={state.duration}\n volume={state.volume}\n isMuted={state.isMuted}\n playbackRate={state.playbackRate}\n isFullscreen={state.isFullscreen}\n isPictureInPicture={state.isPictureInPicture}\n isLive={state.isLive}\n qualityLevels={state.qualityLevels}\n currentQualityLevel={state.currentQualityLevel}\n bufferedRanges={state.bufferedRanges}\n />\n )}\n\n {/* LIVE badge */}\n {state.isLive && (\n <div\n style={{\n position: \"absolute\",\n top: 12,\n left: 12,\n backgroundColor: \"#e53935\",\n color: \"#fff\",\n fontSize: 11,\n fontWeight: 700,\n letterSpacing: \"0.08em\",\n padding: \"2px 8px\",\n borderRadius: 3,\n pointerEvents: \"none\",\n }}\n >\n LIVE\n </div>\n )}\n\n {/* Buffering spinner */}\n {state.isBuffering && !state.error && (\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: 12,\n color: \"#fff\",\n pointerEvents: \"none\",\n }}\n data-test=\"buffering-indicator\"\n >\n <div\n style={{\n width: 48,\n height: 48,\n border: \"4px solid rgba(255,255,255,0.25)\",\n borderTop: \"4px solid #fff\",\n borderRadius: \"50%\",\n animation: \"rvp-spin 0.8s linear infinite\",\n }}\n />\n <style>{`@keyframes rvp-spin { to { transform: rotate(360deg); } }`}</style>\n </div>\n )}\n\n {/* Error overlay */}\n {state.error && (\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n backgroundColor: \"rgba(0,0,0,0.85)\",\n color: \"#fff\",\n padding: 24,\n }}\n data-test=\"error-overlay\"\n >\n <div style={{ textAlign: \"center\", maxWidth: 400 }}>\n <div style={{ fontSize: 36, marginBottom: 12 }}>⚠</div>\n <h3 style={{ margin: \"0 0 8px\", fontSize: 18 }}>\n {state.error.code === \"MEDIA_ERR_SRC_NOT_SUPPORTED\"\n ? \"Unsupported Format\"\n : state.error.code.startsWith(\"HLS\")\n ? \"Stream Error\"\n : \"Playback Error\"}\n </h3>\n <p style={{ margin: 0, fontSize: 13, opacity: 0.75 }}>\n {state.error.message}\n </p>\n </div>\n </div>\n )}\n </div>\n );\n },\n);\n\nVideoPlayer.displayName = \"VideoPlayer\";\n\nexport default VideoPlayer;\n","/**\n * Format seconds → MM:SS or HH:MM:SS\n */\nexport function formatTime(seconds: number): string {\n if (!Number.isFinite(seconds) || seconds < 0) return \"0:00\";\n\n const total = Math.floor(seconds);\n const h = Math.floor(total / 3600);\n const m = Math.floor((total % 3600) / 60);\n const s = total % 60;\n\n if (h > 0) {\n return `${h}:${String(m).padStart(2, \"0\")}:${String(s).padStart(2, \"0\")}`;\n }\n return `${m}:${String(s).padStart(2, \"0\")}`;\n}\n\n/**\n * Detect an HLS stream URL regardless of query-string parameters.\n */\nexport function isHLSUrl(url: string): boolean {\n try {\n const pathname = new URL(url, \"https://x\").pathname.toLowerCase();\n return (\n pathname.endsWith(\".m3u8\") ||\n /\\/hls\\//i.test(url) ||\n /\\/stream\\.m3u8/i.test(url)\n );\n } catch {\n return url.toLowerCase().includes(\".m3u8\");\n }\n}\n\n/**\n * Return the MIME type for a given video URL.\n */\nexport function getMimeType(url: string): string {\n if (isHLSUrl(url)) return \"application/x-mpegURL\";\n\n const lower = url.toLowerCase().split(\"?\")[0];\n if (lower.endsWith(\".mp4\")) return \"video/mp4\";\n if (lower.endsWith(\".webm\")) return \"video/webm\";\n if (lower.endsWith(\".ogv\") || lower.endsWith(\".ogg\")) return \"video/ogg\";\n if (lower.endsWith(\".mov\")) return \"video/quicktime\";\n\n return \"video/mp4\";\n}\n","import HLS, { type HlsConfig, Events } from \"hls.js\";\nimport type { HLSQualityLevel, PlayerError } from \"./types\";\n\nconst MAX_NETWORK_RETRIES = 3;\n\n/**\n * Initialize an HLS.js instance with sensible production defaults.\n * Returns null when HLS.js is not supported (caller should fall back to native).\n */\nexport function initializeHLS(\n video: HTMLVideoElement,\n hlsUrl: string,\n config?: Partial<HlsConfig>,\n): HLS | null {\n if (!HLS.isSupported()) {\n return null;\n }\n\n const hls = new HLS({\n autoStartLoad: true,\n startLevel: -1, // start with auto ABR\n capLevelOnFPSDrop: true,\n capLevelToPlayerSize: true,\n enableWorker: true,\n\n // ABR tuning\n abrEwmaFastLive: 3,\n abrEwmaSlowLive: 9,\n abrEwmaFastVoD: 3,\n abrEwmaSlowVoD: 9,\n abrBandWidthFactor: 0.95,\n\n // Buffer tuning\n maxBufferLength: 30,\n maxMaxBufferLength: 600,\n maxBufferSize: 60 * 1000 * 1000, // 60 MB\n\n // Low-latency live\n liveBackBufferLength: 30,\n liveSyncDurationCount: 3,\n\n ...config,\n });\n\n hls.attachMedia(video);\n hls.loadSource(hlsUrl);\n\n return hls;\n}\n\n/**\n * Map HLS.js level objects to our own quality-level shape.\n */\nexport function buildQualityLevels(levels: HLS[\"levels\"]): HLSQualityLevel[] {\n return levels.map((l, i) => ({\n id: i,\n height: l.height ?? 0,\n width: l.width ?? 0,\n bitrate: l.bitrate ?? 0,\n name: l.height ? `${l.height}p` : `Level ${i + 1}`,\n }));\n}\n\n/**\n * Attach a robust error-recovery handler to an HLS instance.\n * Returns the retry-count object so callers can inspect / reset it.\n */\nexport function attachHLSErrorHandler(\n hls: HLS,\n onFatalError: (err: PlayerError) => void,\n): { retries: number } {\n const state = { retries: 0 };\n\n hls.on(Events.ERROR, (_, data) => {\n if (!data.fatal) {\n // Non-fatal: log and let HLS.js auto-recover\n console.warn(\"[hls] non-fatal error:\", data.type, data.details);\n return;\n }\n\n switch (data.type) {\n case HLS.ErrorTypes.NETWORK_ERROR:\n if (state.retries < MAX_NETWORK_RETRIES) {\n state.retries += 1;\n console.warn(\n `[hls] network error – retry ${state.retries}/${MAX_NETWORK_RETRIES}`,\n );\n // Exponential back-off before retrying\n setTimeout(() => hls.startLoad(), 1000 * state.retries);\n } else {\n console.error(\"[hls] fatal network error after retries:\", data);\n onFatalError({\n code: \"HLS_NETWORK_ERROR\",\n message: \"Failed to load video stream after multiple retries.\",\n details: data.details,\n });\n }\n break;\n\n case HLS.ErrorTypes.MEDIA_ERROR:\n console.warn(\"[hls] media error – attempting recovery\");\n hls.recoverMediaError();\n break;\n\n default:\n console.error(\"[hls] unrecoverable fatal error:\", data);\n hls.destroy();\n onFatalError({\n code: \"HLS_FATAL_ERROR\",\n message: \"An unrecoverable HLS error occurred.\",\n details: data.details,\n });\n }\n });\n\n return state;\n}\n\n/** Safely destroy an HLS instance */\nexport function destroyHLS(hls: HLS | null): void {\n hls?.destroy();\n}\n","\"use client\";\n\nimport { useState, useEffect, useRef, useCallback, useMemo } from \"react\";\nimport HLS, { Events } from \"hls.js\";\nimport type {\n PlayerState,\n VideoPlayerRef,\n PlaybackRate,\n HLSQualityLevel,\n VideoError,\n VideoErrorCode,\n} from \"../lib/types\";\nimport type { HlsConfig } from \"hls.js\";\nimport { isHLSUrl } from \"../lib/format\";\nimport { buildQualityLevels } from \"../lib/hls\";\n\ninterface UseVideoPlayerOptions {\n autoplay?: boolean;\n muted?: boolean;\n loop?: boolean;\n playbackRates?: PlaybackRate[];\n enableHLS?: boolean;\n hlsConfig?: Partial<HlsConfig>;\n onPlay?: () => void;\n onPause?: () => void;\n onEnded?: () => void;\n onError?: (error: VideoError) => void;\n onTimeUpdate?: (currentTime: number) => void;\n onDurationChange?: (duration: number) => void;\n onBuffering?: (isBuffering: boolean) => void;\n}\n\nconst DEFAULT_STATE: PlayerState = {\n isPlaying: false,\n currentTime: 0,\n duration: 0,\n volume: 1,\n isMuted: false,\n playbackRate: 1,\n isFullscreen: false,\n isPictureInPicture: false,\n isBuffering: false,\n bufferedRanges: [],\n error: null,\n isLive: false,\n qualityLevels: [],\n currentQualityLevel: -1,\n};\n\nexport function useVideoPlayer(\n videoRef: React.RefObject<HTMLVideoElement | null>,\n src: string,\n options: UseVideoPlayerOptions = {},\n) {\n const hlsRef = useRef<HLS | null>(null);\n const fullscreenContainerRef = useRef<HTMLElement | null>(null);\n const lastVolumeRef = useRef<number>(1);\n const networkRetriesRef = useRef<number>(0);\n\n // ── Stable refs so effects never need options/state in their dep arrays ──────\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const [state, setState] = useState<PlayerState>({\n ...DEFAULT_STATE,\n isMuted: options.muted ?? false,\n volume: options.muted ? 0 : 1,\n });\n\n const stateRef = useRef(state);\n stateRef.current = state;\n\n // ─── Source / HLS initialisation ────────────────────────────────────────────\n useEffect(() => {\n const video = videoRef.current;\n if (!video) return;\n\n if (hlsRef.current) {\n hlsRef.current.destroy();\n hlsRef.current = null;\n }\n networkRetriesRef.current = 0;\n\n setState((prev) => ({\n ...prev,\n currentTime: 0,\n duration: 0,\n error: null,\n isBuffering: false,\n isLive: false,\n qualityLevels: [],\n currentQualityLevel: -1,\n }));\n\n if (!src) return;\n\n const opts = optionsRef.current;\n\n if (opts.enableHLS !== false && isHLSUrl(src)) {\n if (video.canPlayType(\"application/vnd.apple.mpegurl\")) {\n // Native HLS (Safari) – no HLS.js instance needed\n video.src = src;\n video.load();\n if (opts.autoplay) video.play().catch(() => {});\n } else if (HLS.isSupported()) {\n const hls = new HLS({\n autoStartLoad: true,\n startLevel: -1,\n capLevelToPlayerSize: true,\n capLevelOnFPSDrop: true,\n enableWorker: true,\n maxBufferLength: 30,\n maxMaxBufferLength: 600,\n maxBufferSize: 60 * 1000 * 1000,\n liveBackBufferLength: 30,\n liveSyncDurationCount: 3,\n ...opts.hlsConfig,\n });\n\n hls.attachMedia(video);\n hls.loadSource(src);\n\n hls.on(Events.MANIFEST_PARSED, (_, data) => {\n const levels: HLSQualityLevel[] = buildQualityLevels(data.levels);\n setState((prev) => ({\n ...prev,\n qualityLevels: levels,\n currentQualityLevel: -1,\n }));\n if (optionsRef.current.autoplay) video.play().catch(() => {});\n });\n\n hls.on(Events.LEVEL_SWITCHED, (_, data) => {\n setState((prev) => ({ ...prev, currentQualityLevel: data.level }));\n });\n\n const MAX_RETRIES = 3;\n hls.on(Events.ERROR, (_, data) => {\n if (!data.fatal) {\n console.warn(\"[hls] non-fatal:\", data.details);\n return;\n }\n switch (data.type) {\n case HLS.ErrorTypes.NETWORK_ERROR:\n if (networkRetriesRef.current < MAX_RETRIES) {\n networkRetriesRef.current += 1;\n const delay = 1000 * networkRetriesRef.current;\n console.warn(\n `[hls] network error – retry ${networkRetriesRef.current}/${MAX_RETRIES} in ${delay}ms`,\n );\n // Guard against retry firing after this HLS instance was replaced/destroyed\n setTimeout(() => {\n if (hlsRef.current === hls) hls.startLoad();\n }, delay);\n } else {\n const err: VideoError = {\n code: \"HLS_NETWORK_ERROR\",\n message: \"Failed to load stream after multiple retries.\",\n };\n setState((prev) => ({ ...prev, error: err }));\n optionsRef.current.onError?.(err);\n }\n break;\n case HLS.ErrorTypes.MEDIA_ERROR:\n console.warn(\"[hls] media error – recovering\");\n hls.recoverMediaError();\n break;\n default: {\n hls.destroy();\n hlsRef.current = null;\n const fatalErr: VideoError = {\n code: \"HLS_FATAL_ERROR\",\n message: \"An unrecoverable HLS error occurred.\",\n };\n setState((prev) => ({ ...prev, error: fatalErr }));\n optionsRef.current.onError?.(fatalErr);\n break;\n }\n }\n });\n\n hlsRef.current = hls;\n }\n } else {\n // Regular video (mp4, webm, etc.)\n video.src = src;\n video.load();\n if (opts.autoplay) video.play().catch(() => {});\n }\n\n return () => {\n if (hlsRef.current) {\n hlsRef.current.destroy();\n hlsRef.current = null;\n }\n };\n }, [src, videoRef]);\n\n // ─── Video element event listeners ──────────────────────────────────────────\n useEffect(() => {\n const video = videoRef.current;\n if (!video) return;\n\n if (optionsRef.current.muted) video.muted = true;\n if (optionsRef.current.loop) video.loop = true;\n\n const handlePlay = () => {\n setState((prev) => ({ ...prev, isPlaying: true }));\n optionsRef.current.onPlay?.();\n };\n const handlePause = () => {\n setState((prev) => ({ ...prev, isPlaying: false }));\n optionsRef.current.onPause?.();\n };\n const handleEnded = () => {\n setState((prev) => ({ ...prev, isPlaying: false }));\n optionsRef.current.onEnded?.();\n };\n const handleTimeUpdate = () => {\n // Only notify external callback – internal state is updated but\n // onTimeUpdate is called outside setState to avoid extra renders.\n setState((prev) => ({ ...prev, currentTime: video.currentTime }));\n optionsRef.current.onTimeUpdate?.(video.currentTime);\n };\n const handleDurationChange = () => {\n const dur = video.duration;\n const live = !Number.isFinite(dur);\n setState((prev) => ({ ...prev, duration: live ? 0 : dur, isLive: live }));\n if (!live) optionsRef.current.onDurationChange?.(dur);\n };\n const handleVolumeChange = () => {\n const vol = video.volume;\n if (vol > 0 && !video.muted) lastVolumeRef.current = vol;\n setState((prev) => ({\n ...prev,\n volume: vol,\n isMuted: video.muted || vol === 0,\n }));\n };\n const handleRateChange = () => {\n setState((prev) => ({ ...prev, playbackRate: video.playbackRate }));\n };\n const handleError = () => {\n const e = video.error;\n if (!e) return;\n const codeMap: Partial<Record<number, VideoErrorCode>> = {\n 1: \"MEDIA_ERR_ABORTED\",\n 2: \"MEDIA_ERR_NETWORK\",\n 3: \"MEDIA_ERR_DECODE\",\n 4: \"MEDIA_ERR_SRC_NOT_SUPPORTED\",\n };\n const err: VideoError = {\n code: codeMap[e.code] ?? \"UNKNOWN\",\n message: e.message || \"Unknown media error\",\n };\n setState((prev) => ({ ...prev, error: err }));\n optionsRef.current.onError?.(err);\n };\n const handleWaiting = () => {\n setState((prev) => ({ ...prev, isBuffering: true }));\n optionsRef.current.onBuffering?.(true);\n };\n const handleCanPlay = () => {\n setState((prev) => ({ ...prev, isBuffering: false }));\n optionsRef.current.onBuffering?.(false);\n };\n const handlePlaying = () =>\n setState((prev) => ({ ...prev, isBuffering: false }));\n const handleProgress = () => {\n const ranges: Array<{ start: number; end: number }> = [];\n for (let i = 0; i < video.buffered.length; i++) {\n ranges.push({\n start: video.buffered.start(i),\n end: video.buffered.end(i),\n });\n }\n setState((prev) => ({ ...prev, bufferedRanges: ranges }));\n };\n const handleFullscreenChange = () => {\n const fs = !!(\n document.fullscreenElement || (document as any).webkitFullscreenElement\n );\n setState((prev) => ({ ...prev, isFullscreen: fs }));\n };\n const handlePiPChange = () => {\n setState((prev) => ({\n ...prev,\n isPictureInPicture: document.pictureInPictureElement === video,\n }));\n };\n\n video.addEventListener(\"play\", handlePlay);\n video.addEventListener(\"pause\", handlePause);\n video.addEventListener(\"ended\", handleEnded);\n video.addEventListener(\"timeupdate\", handleTimeUpdate);\n video.addEventListener(\"durationchange\", handleDurationChange);\n video.addEventListener(\"volumechange\", handleVolumeChange);\n video.addEventListener(\"ratechange\", handleRateChange);\n video.addEventListener(\"error\", handleError);\n video.addEventListener(\"waiting\", handleWaiting);\n video.addEventListener(\"canplay\", handleCanPlay);\n video.addEventListener(\"playing\", handlePlaying);\n video.addEventListener(\"progress\", handleProgress);\n document.addEventListener(\"fullscreenchange\", handleFullscreenChange);\n document.addEventListener(\"webkitfullscreenchange\", handleFullscreenChange);\n video.addEventListener(\"enterpictureinpicture\", handlePiPChange);\n video.addEventListener(\"leavepictureinpicture\", handlePiPChange);\n\n return () => {\n video.removeEventListener(\"play\", handlePlay);\n video.removeEventListener(\"pause\", handlePause);\n video.removeEventListener(\"ended\", handleEnded);\n video.removeEventListener(\"timeupdate\", handleTimeUpdate);\n video.removeEventListener(\"durationchange\", handleDurationChange);\n video.removeEventListener(\"volumechange\", handleVolumeChange);\n video.removeEventListener(\"ratechange\", handleRateChange);\n video.removeEventListener(\"error\", handleError);\n video.removeEventListener(\"waiting\", handleWaiting);\n video.removeEventListener(\"canplay\", handleCanPlay);\n video.removeEventListener(\"playing\", handlePlaying);\n video.removeEventListener(\"progress\", handleProgress);\n document.removeEventListener(\"fullscreenchange\", handleFullscreenChange);\n document.removeEventListener(\n \"webkitfullscreenchange\",\n handleFullscreenChange,\n );\n video.removeEventListener(\"enterpictureinpicture\", handlePiPChange);\n video.removeEventListener(\"leavepictureinpicture\", handlePiPChange);\n };\n }, [videoRef]); // stable – options accessed via optionsRef\n\n // ─── Control methods (all stable via useCallback with empty or minimal deps) ─\n const play = useCallback(async () => {\n const video = videoRef.current;\n if (!video) return;\n try {\n await video.play();\n } catch (err: unknown) {\n if (err instanceof Error && err.name !== \"AbortError\")\n console.error(\"[player] play() failed:\", err);\n }\n }, [videoRef]);\n\n const pause = useCallback(() => {\n videoRef.current?.pause();\n }, [videoRef]);\n\n const seek = useCallback(\n (time: number) => {\n const video = videoRef.current;\n if (!video) return;\n video.currentTime = Math.max(0, Math.min(time, video.duration || time));\n },\n [videoRef],\n );\n\n const setVolume = useCallback(\n (volume: number) => {\n const video = videoRef.current;\n if (!video) return;\n const v = Math.max(0, Math.min(volume, 1));\n if (v > 0) lastVolumeRef.current = v;\n video.volume = v;\n video.muted = v === 0;\n },\n [videoRef],\n );\n\n const toggleMute = useCallback(() => {\n const video = videoRef.current;\n if (!video) return;\n if (video.muted || video.volume === 0) {\n const restore = lastVolumeRef.current > 0 ? lastVolumeRef.current : 1;\n video.volume = restore;\n video.muted = false;\n } else {\n lastVolumeRef.current = video.volume;\n video.muted = true;\n }\n }, [videoRef]);\n\n const setPlaybackRate = useCallback(\n (rate: PlaybackRate) => {\n const video = videoRef.current;\n if (video) video.playbackRate = rate;\n },\n [videoRef],\n );\n\n const setQualityLevel = useCallback((level: number) => {\n const hls = hlsRef.current;\n if (!hls) return;\n hls.currentLevel = level;\n setState((prev) => ({ ...prev, currentQualityLevel: level }));\n }, []);\n\n const seekToLive = useCallback(() => {\n const hls = hlsRef.current;\n const video = videoRef.current;\n if (!hls || !video) return;\n const livePos = hls.liveSyncPosition;\n if (livePos != null && Number.isFinite(livePos))\n video.currentTime = livePos;\n }, [videoRef]);\n\n const toggleFullscreen = useCallback(async () => {\n const video = videoRef.current;\n if (!video) return;\n const container = fullscreenContainerRef.current ?? video.parentElement;\n if (!container) return;\n try {\n if (\n !document.fullscreenElement &&\n !(document as any).webkitFullscreenElement\n ) {\n if (container.requestFullscreen) await container.requestFullscreen();\n else (container as any).webkitRequestFullscreen?.();\n } else {\n if (document.exitFullscreen) await document.exitFullscreen();\n else (document as any).webkitExitFullscreen?.();\n }\n } catch (err) {\n console.error(\"[player] fullscreen toggle failed:\", err);\n }\n }, [videoRef]);\n\n const togglePictureInPicture = useCallback(async () => {\n const video = videoRef.current;\n if (!video) return;\n try {\n if (document.pictureInPictureElement)\n await document.exitPictureInPicture();\n else await video.requestPictureInPicture();\n } catch (err) {\n console.error(\"[player] PiP toggle failed:\", err);\n }\n }, [videoRef]);\n\n /**\n * getState reads from stateRef so it doesn't need `state` in its deps.\n * This keeps it stable (same function reference) across all renders,\n * which in turn keeps the VideoPlayerRef object stable (via useMemo).\n */\n const getState = useCallback(\n (): PlayerState => ({ ...stateRef.current }),\n [],\n );\n\n const getVideoElement = useCallback(\n (): HTMLVideoElement | null => videoRef.current ?? null,\n [videoRef],\n );\n\n const ref = useMemo<VideoPlayerRef>(\n () => ({\n play,\n pause,\n seek,\n setVolume,\n toggleMute,\n setPlaybackRate,\n setQualityLevel,\n seekToLive,\n toggleFullscreen,\n togglePictureInPicture,\n getState,\n getVideoElement,\n }),\n [\n play,\n pause,\n seek,\n setVolume,\n toggleMute,\n setPlaybackRate,\n setQualityLevel,\n seekToLive,\n toggleFullscreen,\n togglePictureInPicture,\n getState,\n getVideoElement,\n ],\n );\n\n return { state, ref, hlsRef, fullscreenContainerRef };\n}\n","import {\n PlayButton,\n PauseButton,\n FullscreenButton,\n PiPButton,\n} from \"./control-buttons\";\nimport VolumeControl from \"./volume-control\";\nimport ProgressBar from \"./progress-bar\";\nimport SettingsMenu from \"./settings-menu\";\nimport TimeDisplay from \"./time-display\";\n\nexport type {\n PlayButtonProps,\n PauseButtonProps,\n FullscreenButtonProps,\n PiPButtonProps,\n} from \"./control-buttons\";\n\nexport type { VolumeControlProps } from \"./volume-control\";\nexport type { ProgressBarProps } from \"./progress-bar\";\nexport type { SettingsMenuProps } from \"./settings-menu\";\nexport type { TimeDisplayProps } from \"./time-display\";\n\nexport {\n PlayButton,\n PauseButton,\n FullscreenButton,\n PiPButton,\n} from \"./control-buttons\";\n\nexport { default as VolumeControl } from \"./volume-control\";\nexport { default as ProgressBar } from \"./progress-bar\";\nexport { default as SettingsMenu } from \"./settings-menu\";\nexport { default as TimeDisplay } from \"./time-display\";\n\nexport const ControlElements = {\n PlayButton,\n PauseButton,\n FullscreenButton,\n PiPButton,\n VolumeControl,\n ProgressBar,\n SettingsMenu,\n TimeDisplay,\n};\n","\"use client\";\n\nimport { memo } from \"react\";\nimport \"../../styles/ControlElements.css\";\n\nexport interface PlayButtonProps { onClick: () => void; }\nexport interface PauseButtonProps { onClick: () => void; }\nexport interface FullscreenButtonProps { onClick: () => void; isFullscreen?: boolean; }\nexport interface PiPButtonProps { onClick: () => void; isPiP?: boolean; }\n\n/**\n * All button components are wrapped in React.memo.\n * They receive stable callback refs (useCallback in Controls), so they\n * only re-render when their specific props (isFullscreen, isPiP, etc.)\n * actually change – not on every timeupdate tick.\n */\nexport const PlayButton = memo<PlayButtonProps>(({ onClick }) => (\n <button onClick={onClick} className=\"controlButton\" aria-label=\"Play\" title=\"Play (Space)\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M8 5v14l11-7z\" />\n </svg>\n </button>\n));\nPlayButton.displayName = \"PlayButton\";\n\nexport const PauseButton = memo<PauseButtonProps>(({ onClick }) => (\n <button onClick={onClick} className=\"controlButton\" aria-label=\"Pause\" title=\"Pause (Space)\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M6 4h4v16H6V4zm8 0h4v16h-4V4z\" />\n </svg>\n </button>\n));\nPauseButton.displayName = \"PauseButton\";\n\nexport const FullscreenButton = memo<FullscreenButtonProps>(({ onClick, isFullscreen = false }) => (\n <button\n onClick={onClick}\n className=\"controlButton\"\n aria-label={isFullscreen ? \"Exit Fullscreen\" : \"Fullscreen\"}\n title={isFullscreen ? \"Exit Fullscreen (F)\" : \"Fullscreen (F)\"}\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n {isFullscreen ? (\n <path d=\"M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z\" />\n ) : (\n <path d=\"M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z\" />\n )}\n </svg>\n </button>\n));\nFullscreenButton.displayName = \"FullscreenButton\";\n\nexport const PiPButton = memo<PiPButtonProps>(({ onClick, isPiP = false }) => (\n <button\n onClick={onClick}\n className=\"controlButton\"\n aria-label={isPiP ? \"Exit Picture-in-Picture\" : \"Picture-in-Picture\"}\n title={isPiP ? \"Exit Picture-in-Picture (P)\" : \"Picture-in-Picture (P)\"}\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <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\" />\n </svg>\n </button>\n));\nPiPButton.displayName = \"PiPButton\";\n\nexport default { PlayButton, PauseButton, FullscreenButton, PiPButton };\n","\"use client\";\n\nimport { memo, useState, useMemo } from \"react\";\nimport \"../../styles/ControlElements.css\";\n\nexport interface VolumeControlProps {\n volume: number;\n isMuted: boolean;\n onVolumeChange: (volume: number) => void;\n onToggleMute: () => void;\n}\n\n/**\n * VolumeControl wrapped in React.memo.\n * volume and isMuted don't change during timeupdate → 0 re-renders during\n * normal playback when the user isn't touching volume controls.\n */\nconst VolumeControl = memo<VolumeControlProps>(({\n volume,\n isMuted,\n onVolumeChange,\n onToggleMute,\n}) => {\n const [showSlider, setShowSlider] = useState(false);\n const displayVolume = isMuted ? 0 : volume;\n const percentage = displayVolume * 100;\n\n /**\n * Memoize the gradient background string so it's not recreated on every\n * render of the parent that happens to pass the same volume value.\n * This is a micro-optimisation but relevant because the gradient string\n * is computed on every slider render.\n */\n const sliderBackground = useMemo(\n () =>\n `linear-gradient(to right, #60a5fa 0%, #60a5fa ${percentage}%, rgba(255,255,255,0.3) ${percentage}%, rgba(255,255,255,0.3) 100%)`,\n [percentage],\n );\n\n return (\n <div\n className=\"volumeContainer\"\n onMouseEnter={() => setShowSlider(true)}\n onMouseLeave={() => setShowSlider(false)}\n >\n <button\n onClick={onToggleMute}\n className=\"controlButton\"\n aria-label={isMuted ? \"Unmute\" : \"Mute\"}\n title={isMuted ? \"Unmute (M)\" : \"Mute (M)\"}\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n {displayVolume === 0 ? (\n <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\" />\n ) : displayVolume < 0.5 ? (\n <path d=\"M7 9v6h4l5 5V4l-5 5H7z\" />\n ) : (\n <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\" />\n )}\n </svg>\n </button>\n\n {showSlider && (\n <input\n type=\"range\"\n min=\"0\"\n max=\"100\"\n value={percentage}\n onChange={(e) => onVolumeChange(Number(e.target.value) / 100)}\n className=\"volumeSlider\"\n style={{ background: sliderBackground }}\n aria-label=\"Volume\"\n aria-valuenow={Math.round(percentage)}\n />\n )}\n </div>\n );\n});\n\nVolumeControl.displayName = \"VolumeControl\";\nexport default VolumeControl;\n","\"use client\";\n\nimport React, { memo, useRef, useState, useEffect, useCallback, useMemo } from \"react\";\nimport type { VideoPlayerRef, BufferedRange } from \"../../lib/types\";\nimport { formatTime } from \"../../lib/format\";\nimport \"../../styles/ProgressBar.css\";\n\nexport interface ProgressBarProps {\n playerRef: VideoPlayerRef;\n currentTime: number;\n duration: number;\n bufferedRanges: BufferedRange[];\n enablePreview?: boolean;\n}\n\nconst ProgressBar: React.FC<ProgressBarProps> = memo(({\n playerRef,\n currentTime,\n duration,\n bufferedRanges,\n enablePreview = true,\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const previewVideoRef = useRef<HTMLVideoElement>(null);\n const canvasRef = useRef<HTMLCanvasElement>(null);\n\n const [isDragging, setIsDragging] = useState(false);\n const [hoverTime, setHoverTime] = useState(0);\n const [hoverPos, setHoverPos] = useState(0);\n const [showPreview, setShowPreview] = useState(false);\n const [previewLoaded, setPreviewLoaded] = useState(false);\n\n const updateTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const lastSeekTimeRef = useRef<number>(0);\n const rafRef = useRef<number | null>(null);\n const isDraggingRef = useRef(false);\n isDraggingRef.current = isDragging;\n\n /**\n * Cache the bounding rect so mouse-move doesn't trigger layout reflow\n * on every pixel. Invalidated whenever the window is resized.\n */\n const rectCacheRef = useRef<DOMRect | null>(null);\n\n useEffect(() => {\n const invalidate = () => { rectCacheRef.current = null; };\n window.addEventListener(\"resize\", invalidate, { passive: true });\n return () => window.removeEventListener(\"resize\", invalidate);\n }, []);\n\n const getRect = useCallback((): DOMRect | null => {\n if (!rectCacheRef.current && containerRef.current) {\n rectCacheRef.current = containerRef.current.getBoundingClientRect();\n }\n return rectCacheRef.current;\n }, []);\n\n // ─── Preview video setup ────────────────────────────────────────────────\n useEffect(() => {\n if (!enablePreview) return;\n\n const mainVideo = playerRef.getVideoElement();\n const previewVideo = previewVideoRef.current;\n if (!mainVideo || !previewVideo) return;\n\n const videoSrc = mainVideo.currentSrc || mainVideo.src;\n if (!videoSrc) return;\n\n previewVideo.src = videoSrc;\n previewVideo.muted = true;\n previewVideo.preload = \"auto\";\n previewVideo.crossOrigin = mainVideo.crossOrigin;\n\n const onReady = () => setPreviewLoaded(true);\n const onErr = () => { console.warn(\"[preview] failed to load\"); setPreviewLoaded(false); };\n\n previewVideo.addEventListener(\"loadedmetadata\", onReady);\n previewVideo.addEventListener(\"loadeddata\", onReady);\n previewVideo.addEventListener(\"error\", onErr);\n\n return () => {\n previewVideo.removeEventListener(\"loadedmetadata\", onReady);\n previewVideo.removeEventListener(\"loadeddata\", onReady);\n previewVideo.removeEventListener(\"error\", onErr);\n previewVideo.removeAttribute(\"src\");\n previewVideo.load();\n setPreviewLoaded(false);\n };\n }, [playerRef, enablePreview]);\n\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n const onTouchMove = (e: TouchEvent) => {\n if (isDraggingRef.current) e.preventDefault();\n };\n container.addEventListener(\"touchmove\", onTouchMove, { passive: false });\n return () => container.removeEventListener(\"touchmove\", onTouchMove);\n }, []);\n\n // ─── Draw preview frame ─────────────────────────────────────────────────\n const updatePreview = useCallback((time: number) => {\n if (!enablePreview || !previewLoaded) return;\n\n const previewVideo = previewVideoRef.current;\n const canvas = canvasRef.current;\n if (!previewVideo || !canvas) return;\n\n // Throttle to ~10 seeks/s\n const now = Date.now();\n if (now - lastSeekTimeRef.current < 100) return;\n lastSeekTimeRef.current = now;\n\n if (updateTimeoutRef.current) clearTimeout(updateTimeoutRef.current);\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n\n let drawn = false;\n\n const drawFrame = () => {\n if (drawn) return;\n if (previewVideo.readyState >= 2) {\n drawn = true;\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n rafRef.current = requestAnimationFrame(() => {\n const ctx = canvas.getContext(\"2d\", { alpha: false, willReadFrequently: false });\n if (!ctx) return;\n canvas.width = 160;\n canvas.height = 90;\n ctx.drawImage(previewVideo, 0, 0, 160, 90);\n });\n }\n };\n\n previewVideo.currentTime = time;\n previewVideo.addEventListener(\"seeked\", drawFrame, { once: true });\n\n updateTimeoutRef.current = setTimeout(() => {\n if (!drawn) drawFrame();\n }, 200);\n }, [enablePreview, previewLoaded]);\n\n // ─── Geometry helpers (no layout thrash) ───────────────────────────────\n const getTimeFromClientX = useCallback((clientX: number): number => {\n const rect = getRect();\n if (!rect || rect.width === 0) return 0;\n const pos = Math.max(0, Math.min(clientX - rect.left, rect.width));\n return (pos / rect.width) * duration;\n }, [getRect, duration]);\n\n const getPxFromClientX = useCallback((clientX: number): number => {\n const rect = getRect();\n if (!rect) return 0;\n return Math.max(0, Math.min(clientX - rect.left, rect.width));\n }, [getRect]);\n\n\n const handleKeyDown = useCallback((e: React.KeyboardEvent<HTMLDivElement>) => {\n switch (e.key) {\n case \"ArrowLeft\":\n case \"ArrowRight\": {\n e.preventDefault();\n e.nativeEvent.stopImmediatePropagation();\n const step = e.shiftKey ? 10 : 5;\n const newTime = e.key === \"ArrowLeft\"\n ? Math.max(0, currentTime - step)\n : Math.min(duration || 0, currentTime + step);\n playerRef.seek(newTime);\n break;\n }\n case \"Home\":\n e.preventDefault();\n e.nativeEvent.stopImmediatePropagation();\n playerRef.seek(0);\n break;\n case \"End\":\n if (duration > 0) {\n e.preventDefault();\n e.nativeEvent.stopImmediatePropagation();\n playerRef.seek(duration);\n }\n break;\n }\n }, [currentTime, duration, playerRef]);\n\n // ─── Mouse / touch handlers ─────────────────────────────────────────────\n const handleMouseMove = useCallback((e: React.MouseEvent<HTMLDivElement>) => {\n const time = getTimeFromClientX(e.clientX);\n const px = getPxFromClientX(e.clientX);\n setHoverTime(time);\n setHoverPos(px);\n\n if (isDragging) {\n playerRef.seek(time);\n } else if (enablePreview && previewLoaded) {\n updatePreview(time);\n }\n }, [isDragging, enablePreview, previewLoaded, playerRef, updatePreview, getTimeFromClientX, getPxFromClientX]);\n\n const handleMouseEnter = useCallback(() => {\n rectCacheRef.current = null;\n setShowPreview(true);\n }, []);\n\n const handleMouseLeave = useCallback(() => {\n setShowPreview(false);\n setIsDragging(false);\n }, []);\n\n const handleMouseDown = useCallback((e: React.MouseEvent<HTMLDivElement>) => {\n e.preventDefault();\n setIsDragging(true);\n playerRef.seek(getTimeFromClientX(e.clientX));\n }, [getTimeFromClientX, playerRef]);\n\n const handleMouseUp = useCallback(() => setIsDragging(false), []);\n\n const handleClick = useCallback((e: React.MouseEvent<HTMLDivElement>) => {\n if (!isDragging) playerRef.seek(getTimeFromClientX(e.clientX));\n }, [isDragging, getTimeFromClientX, playerRef]);\n\n\n const handleTouchStart = useCallback((e: React.TouchEvent<HTMLDivElement>) => {\n rectCacheRef.current = null;\n setIsDragging(true);\n playerRef.seek(getTimeFromClientX(e.touches[0].clientX));\n }, [getTimeFromClientX, playerRef]);\n\n const handleTouchMove = useCallback((e: React.TouchEvent<HTMLDivElement>) => {\n if (!isDragging) return;\n playerRef.seek(getTimeFromClientX(e.touches[0].clientX));\n }, [isDragging, getTimeFromClientX, playerRef]);\n\n const handleTouchEnd = useCallback(() => setIsDragging(false), []);\n\n useEffect(() => {\n const up = () => setIsDragging(false);\n window.addEventListener(\"mouseup\", up);\n return () => window.removeEventListener(\"mouseup\", up);\n }, []);\n\n // Cleanup\n useEffect(() => {\n return () => {\n if (updateTimeoutRef.current) clearTimeout(updateTimeoutRef.current);\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n };\n }, []);\n\n const progress = duration > 0 ? (currentTime / duration) * 100 : 0;\n\n const bufferedSegments = useMemo(() => {\n if (duration <= 0) return null;\n return bufferedRanges.map((range, i) => {\n const start = (range.start / duration) * 100;\n const width = ((range.end - range.start) / duration) * 100;\n return (\n <div\n key={i}\n className=\"bufferedSegment\"\n style={{ left: `${start}%`, width: `${width}%` }}\n />\n );\n });\n }, [bufferedRanges, duration]);\n\n return (\n <div\n ref={containerRef}\n className=\"progressContainer\"\n onMouseMove={handleMouseMove}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n onMouseDown={handleMouseDown}\n onMouseUp={handleMouseUp}\n onClick={handleClick}\n onTouchStart={handleTouchStart}\n onTouchMove={handleTouchMove}\n onTouchEnd={handleTouchEnd}\n onKeyDown={handleKeyDown}\n role=\"slider\"\n aria-label=\"Video progress\"\n aria-valuemin={0}\n aria-valuemax={Math.round(duration)}\n aria-valuenow={Math.round(currentTime)}\n aria-valuetext={formatTime(currentTime)}\n tabIndex={0}\n >\n {/* Hidden preview video */}\n {enablePreview && (\n <video ref={previewVideoRef} className=\"previewVideo\" playsInline muted preload=\"auto\" aria-hidden=\"true\" />\n )}\n\n {/* Preview thumbnail tooltip */}\n {enablePreview && showPreview && previewLoaded && (\n <div className=\"previewTooltip\" style={{ left: hoverPos }} aria-hidden=\"true\">\n <canvas ref={canvasRef} className=\"previewCanvas\" />\n <div className=\"previewTime\">{formatTime(hoverTime)}</div>\n </div>\n )}\n\n {/* Track background (overflow:hidden – keeps buffered/filled bars clipped) */}\n <div className=\"progressBackground\">\n {bufferedSegments}\n <div className=\"progressFilled\" style={{ width: `${progress}%` }} />\n {showPreview && (\n <div className=\"hoverIndicator\" style={{ left: hoverPos }} aria-hidden=\"true\" />\n )}\n </div>\n\n\n <div\n className={`scrubHandle${isDragging ? \" dragging\" : \"\"}`}\n style={{ left: `${progress}%` }}\n aria-hidden=\"true\"\n />\n </div>\n );\n});\n\nProgressBar.displayName = \"ProgressBar\";\n\nexport default ProgressBar;\n","\"use client\";\n\nimport { memo, useState, useRef, useEffect, useMemo } from \"react\";\nimport type { PlaybackRate, HLSQualityLevel } from \"../../lib/types\";\nimport \"../../styles/ControlElements.css\";\n\nexport interface SettingsMenuProps {\n currentRate: number;\n playbackRates: PlaybackRate[];\n onRateChange: (rate: PlaybackRate) => void;\n qualityLevels?: HLSQualityLevel[];\n currentQualityLevel?: number;\n onQualityChange?: (level: number) => void;\n}\n\ntype Tab = \"speed\" | \"quality\";\n\n/**\n * SettingsMenu wrapped in React.memo.\n * playbackRate and qualityLevel rarely change → this component skips\n * almost all re-renders during normal playback.\n *\n * sortedLevels is memoized so the .sort() only runs when qualityLevels\n * actually changes (usually once after manifest is parsed).\n */\nconst SettingsMenu = memo<SettingsMenuProps>(({\n currentRate,\n playbackRates,\n onRateChange,\n qualityLevels = [],\n currentQualityLevel = -1,\n onQualityChange,\n}) => {\n const [open, setOpen] = useState(false);\n const [tab, setTab] = useState<Tab>(\"speed\");\n const containerRef = useRef<HTMLDivElement>(null);\n\n const hasQuality = qualityLevels.length > 0 && !!onQualityChange;\n\n // Close when clicking outside\n useEffect(() => {\n const handler = (e: MouseEvent) => {\n if (containerRef.current && !containerRef.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n if (open) document.addEventListener(\"mousedown\", handler);\n return () => document.removeEventListener(\"mousedown\", handler);\n }, [open]);\n\n /**\n * Sort once when qualityLevels changes – not on every render.\n */\n const sortedLevels = useMemo(\n () => [...qualityLevels].sort((a, b) => b.bitrate - a.bitrate),\n [qualityLevels],\n );\n\n const currentQualityName = useMemo(() => {\n if (currentQualityLevel === -1) return \"Auto\";\n return qualityLevels.find((l) => l.id === currentQualityLevel)?.name ?? \"Auto\";\n }, [qualityLevels, currentQualityLevel]);\n\n return (\n <div ref={containerRef} className=\"settingsContainer\">\n <button\n onClick={() => setOpen((o) => !o)}\n className=\"controlButton\"\n aria-label=\"Settings\"\n title=\"Settings\"\n aria-expanded={open}\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <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\" />\n </svg>\n </button>\n\n {open && (\n <div className=\"settingsDropdown\" role=\"menu\">\n {hasQuality && (\n <div className=\"settingsTabs\">\n <button\n className={`settingsTab${tab === \"speed\" ? \" active\" : \"\"}`}\n onClick={() => setTab(\"speed\")}\n >\n Speed\n </button>\n <button\n className={`settingsTab${tab === \"quality\" ? \" active\" : \"\"}`}\n onClick={() => setTab(\"quality\")}\n >\n Quality\n </button>\n </div>\n )}\n\n {(!hasQuality || tab === \"speed\") && (\n <div>\n {!hasQuality && <div className=\"settingsPanelLabel\">Playback Speed</div>}\n {playbackRates.map((rate) => (\n <button\n key={rate}\n onClick={() => { onRateChange(rate); setOpen(false); }}\n className={`settingsOption${currentRate === rate ? \" active\" : \"\"}`}\n role=\"menuitemradio\"\n aria-checked={currentRate === rate}\n >\n {rate === 1 ? \"Normal\" : `${rate}×`}\n </button>\n ))}\n </div>\n )}\n\n {hasQuality && tab === \"quality\" && (\n <div>\n <button\n onClick={() => { onQualityChange!(-1); setOpen(false); }}\n className={`settingsOption${currentQualityLevel === -1 ? \" active\" : \"\"}`}\n role=\"menuitemradio\"\n aria-checked={currentQualityLevel === -1}\n >\n Auto {currentQualityLevel === -1 && currentQualityName !== \"Auto\"\n ? `(${currentQualityName})`\n : \"\"}\n </button>\n {sortedLevels.map((level) => (\n <button\n key={level.id}\n onClick={() => { onQualityChange!(level.id); setOpen(false); }}\n className={`settingsOption${currentQualityLevel === level.id ? \" active\" : \"\"}`}\n role=\"menuitemradio\"\n aria-checked={currentQualityLevel === level.id}\n >\n {level.name}\n {level.bitrate > 0 && (\n <span className=\"settingsOptionBadge\">\n {Math.round(level.bitrate / 1000)} kbps\n </span>\n )}\n </button>\n ))}\n </div>\n )}\n </div>\n )}\n </div>\n );\n});\n\nSettingsMenu.displayName = \"SettingsMenu\";\nexport default SettingsMenu;\n","\"use client\";\n\nimport { memo } from \"react\";\nimport { formatTime } from \"../../lib/format\";\nimport \"../../styles/ControlElements.css\";\n\nexport interface TimeDisplayProps {\n currentTime: number;\n duration: number;\n isLive?: boolean;\n}\n\n/**\n * TimeDisplay re-renders every timeupdate tick (currentTime changes).\n * Wrapped in memo anyway so that if its specific props haven't changed\n * (e.g. when only volume changes) it skips the render.\n */\nconst TimeDisplay = memo<TimeDisplayProps>(({ currentTime, duration, isLive = false }) => {\n if (isLive) {\n return (\n <span className=\"timeDisplay\" style={{ opacity: 0.7 }}>\n {formatTime(currentTime)}\n </span>\n );\n }\n return (\n <span className=\"timeDisplay\">\n {formatTime(currentTime)}\n <span style={{ opacity: 0.6 }}> / {formatTime(duration)}</span>\n </span>\n );\n});\n\nTimeDisplay.displayName = \"TimeDisplay\";\nexport default TimeDisplay;\n","\"use client\";\n\nimport React, { memo, useEffect, useRef, useState, useCallback } from \"react\";\nimport type {\n PlaybackRate,\n VideoPlayerRef,\n HLSQualityLevel,\n BufferedRange,\n} from \"../lib/types\";\nimport { ControlElements } from \"./control-elements\";\n\ninterface ControlsProps {\n playerRef: VideoPlayerRef;\n /** Ref to the outer player container; used to scope keyboard shortcuts to the focused player */\n playerContainerRef: React.RefObject<HTMLElement | null>;\n playbackRates: PlaybackRate[];\n enablePreview: boolean;\n isPlaying: boolean;\n currentTime: number;\n duration: number;\n volume: number;\n isMuted: boolean;\n playbackRate: number;\n isFullscreen: boolean;\n isPictureInPicture: boolean;\n isLive: boolean;\n qualityLevels: HLSQualityLevel[];\n currentQualityLevel: number;\n bufferedRanges: BufferedRange[];\n}\n\n/**\n * Controls is intentionally NOT wrapped in React.memo here – it receives\n * currentTime which changes every tick, so memo wouldn't help at this level.\n * Instead, all its CHILDREN are memoized so they skip renders when their\n * specific props haven't changed.\n */\nexport const Controls: React.FC<ControlsProps> = ({\n playerRef,\n playerContainerRef,\n playbackRates,\n enablePreview,\n isPlaying,\n currentTime,\n duration,\n volume,\n isMuted,\n playbackRate,\n isFullscreen,\n isPictureInPicture,\n isLive,\n qualityLevels,\n currentQualityLevel,\n bufferedRanges,\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const hideTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const [showControls, setShowControls] = useState(true);\n\n /**\n * A ref that always holds the latest state values.\n * The keyboard handler reads from this ref so the effect only needs\n * playerRef as a dependency – it NEVER re-registers on every timeupdate.\n *\n */\n const liveRef = useRef({\n isPlaying, currentTime, duration, volume, isMuted, isLive,\n });\n liveRef.current = { isPlaying, currentTime, duration, volume, isMuted, isLive };\n\n // ─── Auto-hide controls ──────────────────────────────────────────────────\n useEffect(() => {\n if (!isPlaying) {\n setShowControls(true);\n if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);\n return;\n }\n\n const reset = () => {\n setShowControls(true);\n if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);\n hideTimeoutRef.current = setTimeout(() => setShowControls(false), 3000);\n };\n\n const el = containerRef.current;\n if (el) {\n el.addEventListener(\"mousemove\", reset);\n el.addEventListener(\"mouseenter\", reset);\n el.addEventListener(\"touchstart\", reset, { passive: true });\n }\n reset();\n\n return () => {\n if (el) {\n el.removeEventListener(\"mousemove\", reset);\n el.removeEventListener(\"mouseenter\", reset);\n el.removeEventListener(\"touchstart\", reset);\n }\n if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);\n };\n }, [isPlaying]);\n\n // ─── Keyboard shortcuts ─────────────────────────────────────────────────\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n // Only handle keyboard events when this player container has focus,\n // preventing shortcuts from firing on all players simultaneously.\n if (!playerContainerRef.current?.contains(document.activeElement)) return;\n\n const target = e.target as HTMLElement;\n if (target.tagName === \"INPUT\" || target.tagName === \"TEXTAREA\" || target.isContentEditable) return;\n\n // Read latest values from the ref – no state in closure\n const { isPlaying: playing, currentTime: ct, duration: dur, volume: vol, isLive: live } = liveRef.current;\n\n switch (e.code) {\n case \"Space\": case \"KeyK\":\n e.preventDefault();\n playing ? playerRef.pause() : playerRef.play();\n break;\n case \"ArrowLeft\":\n e.preventDefault();\n playerRef.seek(Math.max(0, ct - 5));\n break;\n case \"ArrowRight\":\n e.preventDefault();\n playerRef.seek(Math.min(dur || Infinity, ct + 5));\n break;\n case \"ArrowUp\":\n e.preventDefault();\n playerRef.setVolume(Math.min(1, vol + 0.1));\n break;\n case \"ArrowDown\":\n e.preventDefault();\n playerRef.setVolume(Math.max(0, vol - 0.1));\n break;\n case \"KeyM\":\n e.preventDefault();\n playerRef.toggleMute();\n break;\n case \"KeyF\":\n e.preventDefault();\n playerRef.toggleFullscreen();\n break;\n case \"KeyP\":\n e.preventDefault();\n playerRef.togglePictureInPicture();\n break;\n case \"KeyL\":\n e.preventDefault();\n if (live) playerRef.seekToLive();\n break;\n case \"Digit0\": case \"Digit1\": case \"Digit2\": case \"Digit3\": case \"Digit4\":\n case \"Digit5\": case \"Digit6\": case \"Digit7\": case \"Digit8\": case \"Digit9\": {\n e.preventDefault();\n const pct = Number(e.code.replace(\"Digit\", \"\")) * 10;\n playerRef.seek((dur / 100) * pct);\n break;\n }\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, [playerRef, playerContainerRef]); // ← state read via liveRef; container ref for focus check\n\n // ─── Stable callbacks for child components ───────────────────────────────\n // These are memoized so child React.memo components don't re-render due\n // to new function references on every render.\n const handlePlay = useCallback(() => playerRef.play(), [playerRef]);\n const handlePause = useCallback(() => playerRef.pause(), [playerRef]);\n const handleVolumeChange = useCallback((v: number) => playerRef.setVolume(v), [playerRef]);\n const handleToggleMute = useCallback(() => playerRef.toggleMute(), [playerRef]);\n const handleRateChange = useCallback((r: PlaybackRate) => playerRef.setPlaybackRate(r), [playerRef]);\n const handleQualityChange = useCallback((l: number) => playerRef.setQualityLevel(l), [playerRef]);\n const handlePiP = useCallback(() => playerRef.togglePictureInPicture(), [playerRef]);\n const handleFullscreen = useCallback(() => playerRef.toggleFullscreen(), [playerRef]);\n const handleSeekToLive = useCallback(() => playerRef.seekToLive(), [playerRef]);\n\n return (\n <div\n ref={containerRef}\n style={{\n position: \"absolute\",\n inset: 0,\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"flex-end\",\n opacity: showControls ? 1 : 0,\n transition: \"opacity 0.3s\",\n pointerEvents: showControls ? \"auto\" : \"none\",\n }}\n >\n <div\n style={{\n background: \"linear-gradient(to top, rgba(0,0,0,0.75) 0%, rgba(0,0,0,0.2) 60%, transparent 100%)\",\n padding: \"48px 12px 12px\",\n }}\n role=\"region\"\n aria-label=\"Video player controls\"\n >\n {/* Progress bar – re-renders on every tick, intentionally */}\n <ControlElements.ProgressBar\n playerRef={playerRef}\n currentTime={currentTime}\n duration={duration}\n bufferedRanges={bufferedRanges}\n enablePreview={enablePreview}\n />\n\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 4, marginTop: 4 }}>\n {/* Play/Pause – memoized; only re-renders when isPlaying changes */}\n {isPlaying ? (\n <ControlElements.PauseButton onClick={handlePause} />\n ) : (\n <ControlElements.PlayButton onClick={handlePlay} />\n )}\n\n {/* Volume – memoized; skips timeupdate renders */}\n <ControlElements.VolumeControl\n volume={volume}\n isMuted={isMuted}\n onVolumeChange={handleVolumeChange}\n onToggleMute={handleToggleMute}\n />\n\n {/* Time – re-renders on every tick (needs currentTime) */}\n <ControlElements.TimeDisplay\n currentTime={currentTime}\n duration={duration}\n isLive={isLive}\n />\n\n <div style={{ flex: 1 }} />\n\n {/* GO LIVE – memoized; only shown for live streams */}\n {isLive && (\n <GoLiveButton onClick={handleSeekToLive} />\n )}\n\n {/* Settings – memoized; skips timeupdate renders */}\n <ControlElements.SettingsMenu\n currentRate={playbackRate}\n playbackRates={playbackRates}\n onRateChange={handleRateChange}\n qualityLevels={qualityLevels}\n currentQualityLevel={currentQualityLevel}\n onQualityChange={handleQualityChange}\n />\n\n {/* PiP */}\n <ControlElements.PiPButton onClick={handlePiP} isPiP={isPictureInPicture} />\n\n {/* Fullscreen */}\n <ControlElements.FullscreenButton onClick={handleFullscreen} isFullscreen={isFullscreen} />\n </div>\n </div>\n </div>\n );\n};\n\n/**\n * GO LIVE button – only rendered for live streams.\n * Stable onClick prop (useCallback in parent) prevents unnecessary re-renders.\n */\nconst GoLiveButton = memo(({ onClick }: { onClick: () => void }) => (\n <button\n onClick={onClick}\n style={{\n background: \"none\",\n border: \"1px solid rgba(255,255,255,0.6)\",\n color: \"#fff\",\n borderRadius: 3,\n padding: \"2px 8px\",\n fontSize: 11,\n fontWeight: 700,\n cursor: \"pointer\",\n letterSpacing: \"0.06em\",\n }}\n title=\"Go to live (L)\"\n >\n GO LIVE\n </button>\n));\nGoLiveButton.displayName = \"GoLiveButton\";\n"]}
|