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