fix(#2979): audio tracks selection
parent
8b484e1fd2
commit
41d7131610
@ -0,0 +1,98 @@ |
||||
import { Fragment, useMemo } from 'react' |
||||
|
||||
import type { MediaPlaylist } from 'hls.js' |
||||
|
||||
import map from 'lodash/map' |
||||
|
||||
import { OutsideClick } from 'features/OutsideClick' |
||||
|
||||
import { useToggle } from 'hooks' |
||||
|
||||
import { isMobileDevice } from 'config/userAgent' |
||||
|
||||
import { |
||||
AudioTrackItem, |
||||
AudioTrackItemText, |
||||
AudioTracksWrapper, |
||||
ScHeader, |
||||
ScModal, |
||||
SelectedAudioTrack, |
||||
} from './styled' |
||||
|
||||
export type AudioTracksProps = { |
||||
audioTracks: Array<MediaPlaylist>, |
||||
changeAudioTrack: (trackId: number) => void, |
||||
isFullscreen?: boolean, |
||||
selectedAudioTrack: MediaPlaylist, |
||||
} |
||||
|
||||
export const AudioTracks = ({ |
||||
audioTracks, |
||||
changeAudioTrack, |
||||
isFullscreen, |
||||
selectedAudioTrack, |
||||
}: AudioTracksProps) => { |
||||
const { |
||||
close, |
||||
isOpen, |
||||
open, |
||||
} = useToggle() |
||||
|
||||
const AudioTracksList = useMemo(() => ( |
||||
map(audioTracks, (track) => ( |
||||
<AudioTrackItem |
||||
key={`${track.id}_${track.name}`} |
||||
isFullScreen={Boolean(isFullscreen)} |
||||
selected={track.id === selectedAudioTrack?.id} |
||||
onClick={() => { |
||||
changeAudioTrack(track.id) |
||||
close() |
||||
}} |
||||
> |
||||
<AudioTrackItemText> |
||||
{track.name} |
||||
</AudioTrackItemText> |
||||
</AudioTrackItem> |
||||
)) |
||||
), [ |
||||
audioTracks, |
||||
changeAudioTrack, |
||||
close, |
||||
isFullscreen, |
||||
selectedAudioTrack?.id, |
||||
]) |
||||
|
||||
const content = isMobileDevice && !isFullscreen |
||||
? ( |
||||
<ScModal |
||||
closeSize={12} |
||||
close={close} |
||||
isOpen={isOpen} |
||||
> |
||||
<ScHeader>Audio</ScHeader> |
||||
{AudioTracksList} |
||||
</ScModal> |
||||
) |
||||
: ( |
||||
<Fragment> |
||||
{isOpen && ( |
||||
<AudioTracksWrapper> |
||||
<OutsideClick onClick={close}> |
||||
{AudioTracksList} |
||||
</OutsideClick> |
||||
</AudioTracksWrapper> |
||||
)} |
||||
</Fragment> |
||||
) |
||||
|
||||
return ( |
||||
<Fragment> |
||||
{audioTracks?.length > 1 && ( |
||||
<SelectedAudioTrack onClick={open}> |
||||
{selectedAudioTrack?.name} |
||||
</SelectedAudioTrack> |
||||
)} |
||||
{content} |
||||
</Fragment> |
||||
) |
||||
} |
||||
@ -0,0 +1,97 @@ |
||||
import styled, { css } from 'styled-components/macro' |
||||
|
||||
import { ButtonBase } from 'features/StreamPlayer/styled' |
||||
import { Modal } from 'features/Modal' |
||||
import { ModalCloseButton, ModalWindow } from 'features/Modal/styled' |
||||
|
||||
import { isMobileDevice } from 'config/userAgent' |
||||
|
||||
export const SelectedAudioTrack = styled(ButtonBase)` |
||||
width: 200px; |
||||
height: 100%; |
||||
font-size: 16px; |
||||
color: rgba(255, 255, 255, 0.7); |
||||
font-weight: 600; |
||||
|
||||
${isMobileDevice ? css` |
||||
width: 80px; |
||||
font-size: 12px; |
||||
` : ''}
|
||||
` |
||||
|
||||
export const AudioTracksWrapper = styled.div` |
||||
position: absolute; |
||||
bottom: 30px; |
||||
z-index: 5; |
||||
background: #333333; |
||||
filter: drop-shadow(0px 2px 40px rgba(0, 0, 0, 0.6)); |
||||
border-radius: 2px; |
||||
width: 200px; |
||||
|
||||
${isMobileDevice ? css` |
||||
bottom: 45px; |
||||
width: 80px; |
||||
` : ''}
|
||||
` |
||||
|
||||
type AudioTrackItemProps = { |
||||
isFullScreen: boolean, |
||||
selected: boolean, |
||||
} |
||||
|
||||
export const AudioTrackItem = styled.div<AudioTrackItemProps>` |
||||
color: rgba(255, 255, 255, 0.7); |
||||
font-size: 12px; |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
padding: 10px; |
||||
cursor: pointer; |
||||
font-weight: normal; |
||||
|
||||
:hover, :focus { |
||||
background-color: rgba(255, 255, 255, 0.1); |
||||
} |
||||
|
||||
${({ selected }) => (selected ? css` |
||||
font-weight: 600; |
||||
color: #FFFFFF; |
||||
` : '')}
|
||||
|
||||
${({ isFullScreen }) => (isMobileDevice ? css` |
||||
justify-content: ${isFullScreen ? 'center' : 'flex-start'}; |
||||
padding: 10px 15px; |
||||
` : '')}
|
||||
` |
||||
|
||||
export const AudioTrackItemText = styled.span`
|
||||
text-overflow: ellipsis; |
||||
overflow: hidden; |
||||
white-space: nowrap; |
||||
` |
||||
|
||||
export const ScModal = styled(Modal)` |
||||
${ModalWindow} { |
||||
width: 280px; |
||||
position: fixed; |
||||
z-index: 5; |
||||
left: 0; |
||||
right: 0; |
||||
top: 3%; |
||||
padding: 0 0 5px; |
||||
border-radius: 3px; |
||||
} |
||||
|
||||
${ModalCloseButton} { |
||||
background: transparent; |
||||
padding: 0; |
||||
} |
||||
` |
||||
|
||||
export const ScHeader = styled.div` |
||||
border-bottom: 1px solid #505050; |
||||
padding: 13px 15px; |
||||
font-size: 10px; |
||||
font-weight: 700; |
||||
text-transform: uppercase; |
||||
` |
||||
@ -0,0 +1,43 @@ |
||||
import { useEffect, useState } from 'react' |
||||
|
||||
import find from 'lodash/find' |
||||
|
||||
import Hls, { MediaPlaylist } from 'hls.js' |
||||
|
||||
export const useAudioTrack = (hls: Hls | null) => { |
||||
const [audioTracks, setAudioTracks] = useState<Array<MediaPlaylist>>() |
||||
const [selectedAudioTrack, setSelectedAudioTrack] = useState<MediaPlaylist>() |
||||
|
||||
const changeAudioTrack = (trackId: number) => { |
||||
if (!hls) return |
||||
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
hls.audioTrack = trackId |
||||
const track = find(audioTracks, { id: trackId }) |
||||
|
||||
setSelectedAudioTrack(track) |
||||
} |
||||
|
||||
useEffect(() => { |
||||
if (!hls) return undefined |
||||
|
||||
const listener = () => { |
||||
const defaultTrack = find(hls.audioTracks, { id: selectedAudioTrack?.id }) |
||||
?? find(hls.audioTracks, { default: true }) |
||||
|
||||
setAudioTracks(hls.audioTracks) |
||||
setSelectedAudioTrack(defaultTrack) |
||||
} |
||||
hls.on(Hls.Events.AUDIO_TRACK_LOADED, listener) |
||||
|
||||
return () => { |
||||
hls.off(Hls.Events.AUDIO_TRACK_LOADED, listener) |
||||
} |
||||
}, [hls, selectedAudioTrack]) |
||||
|
||||
return { |
||||
audioTracks, |
||||
changeAudioTrack, |
||||
selectedAudioTrack, |
||||
} |
||||
} |
||||
Loading…
Reference in new issue