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.mjs
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import it,{memo,useState,useMemo,useRef,useEffect,useCallback,forwardRef}from'react';import de,{Events}from'hls.js';import {jsx,jsxs}from'react/jsx-runtime';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=useRef(null),d=useRef(null),h=useRef(1),v=useRef(0),u=useRef(l);u.current=l;let[g,s]=useState({...Ke,isMuted:l.muted??false,volume:l.muted?0:1}),B=useRef(g);B.current=g,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.isSupported()){let c=new de({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(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(Events.LEVEL_SWITCHED,(z,E)=>{s(i=>({...i,currentQualityLevel:E.level}));});let F=3;c.on(Events.ERROR,(z,E)=>{if(!E.fatal){console.warn("[hls] non-fatal:",E.details);return}switch(E.type){case de.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.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]),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=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=useCallback(()=>{e.current?.pause();},[e]),H=useCallback(t=>{let n=e.current;n&&(n.currentTime=Math.max(0,Math.min(t,n.duration||t)));},[e]),p=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=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=useCallback(t=>{let n=e.current;n&&(n.playbackRate=t);},[e]),y=useCallback(t=>{let n=f.current;n&&(n.currentLevel=t,s(c=>({...c,currentQualityLevel:t})));},[]),C=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=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=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]),_=useCallback(()=>({...B.current}),[]),L=useCallback(()=>e.current??null,[e]),M=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=memo(({onClick:e})=>jsx("button",{onClick:e,className:"controlButton","aria-label":"Play",title:"Play (Space)",children:jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:jsx("path",{d:"M8 5v14l11-7z"})})}));oe.displayName="PlayButton";var ae=memo(({onClick:e})=>jsx("button",{onClick:e,className:"controlButton","aria-label":"Pause",title:"Pause (Space)",children:jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:jsx("path",{d:"M6 4h4v16H6V4zm8 0h4v16h-4V4z"})})}));ae.displayName="PauseButton";var ie=memo(({onClick:e,isFullscreen:a=false})=>jsx("button",{onClick:e,className:"controlButton","aria-label":a?"Exit Fullscreen":"Fullscreen",title:a?"Exit Fullscreen (F)":"Fullscreen (F)",children:jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:a?jsx("path",{d:"M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"}):jsx("path",{d:"M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"})})}));ie.displayName="FullscreenButton";var se=memo(({onClick:e,isPiP:a=false})=>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:jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children: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=memo(({volume:e,isMuted:a,onVolumeChange:l,onToggleMute:f})=>{let[d,h]=useState(false),v=a?0:e,u=v*100,g=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 jsxs("div",{className:"volumeContainer",onMouseEnter:()=>h(true),onMouseLeave:()=>h(false),children:[jsx("button",{onClick:f,className:"controlButton","aria-label":a?"Unmute":"Mute",title:a?"Unmute (M)":"Mute (M)",children:jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children:v===0?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?jsx("path",{d:"M7 9v6h4l5 5V4l-5 5H7z"}):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&&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=memo(({playerRef:e,currentTime:a,duration:l,bufferedRanges:f,enablePreview:d=true})=>{let h=useRef(null),v=useRef(null),u=useRef(null),[g,s]=useState(false),[B,k]=useState(0),[x,H]=useState(0),[p,T]=useState(false),[S,y]=useState(false),C=useRef(null),N=useRef(0),P=useRef(null),_=useRef(false);_.current=g;let L=useRef(null);useEffect(()=>{let r=()=>{L.current=null;};return window.addEventListener("resize",r,{passive:true}),()=>window.removeEventListener("resize",r)},[]);let M=useCallback(()=>(!L.current&&h.current&&(L.current=h.current.getBoundingClientRect()),L.current),[]);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]),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=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=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=useCallback(r=>{let o=M();return o?Math.max(0,Math.min(r-o.left,o.width)):0},[M]),F=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=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=useCallback(()=>{L.current=null,T(true);},[]),i=useCallback(()=>{T(false),s(false);},[]),m=useCallback(r=>{r.preventDefault(),s(true),e.seek(n(r.clientX));},[n,e]),W=useCallback(()=>s(false),[]),ee=useCallback(r=>{g||e.seek(n(r.clientX));},[g,n,e]),G=useCallback(r=>{L.current=null,s(true),e.seek(n(r.touches[0].clientX));},[n,e]),Y=useCallback(r=>{g&&e.seek(n(r.touches[0].clientX));},[g,n,e]),j=useCallback(()=>s(false),[]);useEffect(()=>{let r=()=>s(false);return window.addEventListener("mouseup",r),()=>window.removeEventListener("mouseup",r)},[]),useEffect(()=>()=>{C.current&&clearTimeout(C.current),P.current&&cancelAnimationFrame(P.current);},[]);let $=l>0?a/l*100:0,Q=useMemo(()=>l<=0?null:f.map((r,o)=>{let b=r.start/l*100,O=(r.end-r.start)/l*100;return jsx("div",{className:"bufferedSegment",style:{left:`${b}%`,width:`${O}%`}},o)}),[f,l]);return 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&&jsx("video",{ref:v,className:"previewVideo",playsInline:true,muted:true,preload:"auto","aria-hidden":"true"}),d&&p&&S&&jsxs("div",{className:"previewTooltip",style:{left:x},"aria-hidden":"true",children:[jsx("canvas",{ref:u,className:"previewCanvas"}),jsx("div",{className:"previewTime",children:K(B)})]}),jsxs("div",{className:"progressBackground",children:[Q,jsx("div",{className:"progressFilled",style:{width:`${$}%`}}),p&&jsx("div",{className:"hoverIndicator",style:{left:x},"aria-hidden":"true"})]}),jsx("div",{className:`scrubHandle${g?" dragging":""}`,style:{left:`${$}%`},"aria-hidden":"true"})]})});Ce.displayName="ProgressBar";var fe=Ce;var Be=memo(({currentRate:e,playbackRates:a,onRateChange:l,qualityLevels:f=[],currentQualityLevel:d=-1,onQualityChange:h})=>{let[v,u]=useState(false),[g,s]=useState("speed"),B=useRef(null),k=f.length>0&&!!h;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=useMemo(()=>[...f].sort((p,T)=>T.bitrate-p.bitrate),[f]),H=useMemo(()=>d===-1?"Auto":f.find(p=>p.id===d)?.name??"Auto",[f,d]);return jsxs("div",{ref:B,className:"settingsContainer",children:[jsx("button",{onClick:()=>u(p=>!p),className:"controlButton","aria-label":"Settings",title:"Settings","aria-expanded":v,children:jsx("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor",children: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&&jsxs("div",{className:"settingsDropdown",role:"menu",children:[k&&jsxs("div",{className:"settingsTabs",children:[jsx("button",{className:`settingsTab${g==="speed"?" active":""}`,onClick:()=>s("speed"),children:"Speed"}),jsx("button",{className:`settingsTab${g==="quality"?" active":""}`,onClick:()=>s("quality"),children:"Quality"})]}),(!k||g==="speed")&&jsxs("div",{children:[!k&&jsx("div",{className:"settingsPanelLabel",children:"Playback Speed"}),a.map(p=>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"&&jsxs("div",{children:[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=>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&&jsxs("span",{className:"settingsOptionBadge",children:[Math.round(p.bitrate/1e3)," kbps"]})]},p.id))]})]})]})});Be.displayName="SettingsMenu";var ve=Be;var Ve=memo(({currentTime:e,duration:a,isLive:l=false})=>l?jsx("span",{className:"timeDisplay",style:{opacity:.7},children:K(e)}):jsxs("span",{className:"timeDisplay",children:[K(e),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=useRef(null),y=useRef(null),[C,N]=useState(true),P=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},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]),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 _=useCallback(()=>e.play(),[e]),L=useCallback(()=>e.pause(),[e]),M=useCallback(i=>e.setVolume(i),[e]),t=useCallback(()=>e.toggleMute(),[e]),n=useCallback(i=>e.setPlaybackRate(i),[e]),c=useCallback(i=>e.setQualityLevel(i),[e]),F=useCallback(()=>e.togglePictureInPicture(),[e]),z=useCallback(()=>e.toggleFullscreen(),[e]),E=useCallback(()=>e.seekToLive(),[e]);return 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: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:[jsx(I.ProgressBar,{playerRef:e,currentTime:h,duration:v,bufferedRanges:T,enablePreview:f}),jsxs("div",{style:{display:"flex",alignItems:"center",gap:4,marginTop:4},children:[d?jsx(I.PauseButton,{onClick:L}):jsx(I.PlayButton,{onClick:_}),jsx(I.VolumeControl,{volume:u,isMuted:g,onVolumeChange:M,onToggleMute:t}),jsx(I.TimeDisplay,{currentTime:h,duration:v,isLive:x}),jsx("div",{style:{flex:1}}),x&&jsx(ze,{onClick:E}),jsx(I.SettingsMenu,{currentRate:s,playbackRates:l,onRateChange:n,qualityLevels:H,currentQualityLevel:p,onQualityChange:c}),jsx(I.PiPButton,{onClick:F,isPiP:k}),jsx(I.FullscreenButton,{onClick:z,isFullscreen:B})]})]})})},ze=memo(({onClick:e})=>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=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=useRef(null),M=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});useEffect(()=>{c.current=M.current;},[c]),it.useImperativeHandle(_,()=>n,[n]);let F=useCallback(()=>{M.current?.focus(),t.isPlaying?n.pause():n.play();},[t.isPlaying,n]),z=useCallback(()=>{n.toggleFullscreen();},[n]),E=useMemo(()=>s&&!!e&&e.toLowerCase().includes(".m3u8"),[s,e]);return 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:[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=>jsx("track",{kind:"subtitles",src:i.src,label:i.label,srcLang:i.srclang,default:i.default},i.id))}),h&&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&&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&&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:[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"}}),jsx("style",{children:"@keyframes rvp-spin { to { transform: rotate(360deg); } }"})]}),t.error&&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:jsxs("div",{style:{textAlign:"center",maxWidth:400},children:[jsx("div",{style:{fontSize:36,marginBottom:12},children:"\u26A0"}),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"}),jsx("p",{style:{margin:0,fontSize:13,opacity:.75},children:t.error.message})]})})]})});Ae.displayName="VideoPlayer";var ct=Ae;
|
|
2
|
+
export{He as ControlElements,Ee as Controls,ct as VideoPlayer,K as formatTime,Ue as getMimeType,ce as isHLSUrl};//# sourceMappingURL=index.mjs.map
|
|
3
|
+
//# sourceMappingURL=index.mjs.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.mjs","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"]}
|