@@ -25,6 +25,7 @@ const RouteVideoPlayer: VoidComponent<RouteVideoPlayerProps> = (props) => {
2525 let controls ! : HTMLDivElement
2626
2727 const [ isPlaying , setIsPlaying ] = createSignal ( true )
28+ const [ isMuted , setIsMuted ] = createSignal ( true )
2829 const [ currentTime , setCurrentTime ] = createSignal ( 0 )
2930 const [ duration , setDuration ] = createSignal ( 0 )
3031 const [ videoLoading , setVideoLoading ] = createSignal ( true )
@@ -47,14 +48,28 @@ const RouteVideoPlayer: VoidComponent<RouteVideoPlayerProps> = (props) => {
4748 const startProgressTracking = ( ) => {
4849 requestAnimationFrame ( updateProgressContinuously )
4950 }
51+ const requestPlay = ( ) => {
52+ const playResult = video . play ( )
53+ if ( playResult ) {
54+ void playResult . catch ( ( error : unknown ) => {
55+ if ( error instanceof DOMException && error . name === 'AbortError' ) return
56+ console . debug ( '[RouteVideoPlayer] play interrupted' , error )
57+ } )
58+ }
59+ }
5060
5161 const togglePlayback = ( ) => {
5262 if ( video . paused ) {
53- void video . play ( )
63+ requestPlay ( )
5464 } else {
5565 video . pause ( )
5666 }
5767 }
68+ const toggleMuted = ( e : Event ) => {
69+ e . preventDefault ( )
70+ e . stopPropagation ( )
71+ setIsMuted ( ( muted ) => ! muted )
72+ }
5873 const onClick = ( e : Event ) => {
5974 e . preventDefault ( )
6075 togglePlayback ( )
@@ -83,14 +98,16 @@ const RouteVideoPlayer: VoidComponent<RouteVideoPlayerProps> = (props) => {
8398 const onEnded = ( ) => setIsPlaying ( false )
8499 const onStalled = ( ) => {
85100 if ( ! isPlaying ( ) ) return
86- void video . play ( )
101+ requestPlay ( )
87102 }
88103
89104 onMount ( ( ) => {
90105 if ( props . selection . startTime > 0 ) {
91106 video . currentTime = props . selection . startTime
92107 }
93108
109+ video . defaultMuted = true
110+ video . muted = true
94111 props . ref ?.( video )
95112
96113 controls . addEventListener ( 'click' , onClick )
@@ -143,9 +160,17 @@ const RouteVideoPlayer: VoidComponent<RouteVideoPlayerProps> = (props) => {
143160 on ( routeName , ( ) => {
144161 setVideoLoading ( true )
145162 setErrorMessage ( '' )
163+ setIsMuted ( true )
146164 } ) ,
147165 )
148166
167+ createEffect ( ( ) => {
168+ if ( ! video ) return
169+ const muted = isMuted ( )
170+ video . defaultMuted = muted
171+ video . muted = muted
172+ } )
173+
149174 createEffect ( ( ) => {
150175 const url = streamUrl ( )
151176 const player = hls ( )
@@ -172,7 +197,7 @@ const RouteVideoPlayer: VoidComponent<RouteVideoPlayerProps> = (props) => {
172197 class = "size-full object-cover"
173198 data-testid = "route-video"
174199 autoplay
175- muted
200+ muted = { isMuted ( ) }
176201 controls = { false }
177202 playsinline
178203 loop
@@ -205,6 +230,9 @@ const RouteVideoPlayer: VoidComponent<RouteVideoPlayerProps> = (props) => {
205230 < div class = "font-mono text-sm text-on-surface" >
206231 { formatVideoTime ( currentTime ( ) ) } / { formatVideoTime ( duration ( ) ) }
207232 </ div >
233+
234+ < div class = "grow" />
235+ < IconButton name = { isMuted ( ) ? 'volume_off' : 'volume_up' } aria-label = { isMuted ( ) ? 'Unmute' : 'Mute' } onClick = { toggleMuted } />
208236 </ div >
209237 </ div >
210238 </ div >
0 commit comments