fix(#2979): audio tracks selection

keep-around/9d11a525a1ee745f785976389c40d7a0601133e1
Rakov Roman 3 years ago
parent 8b484e1fd2
commit 41d7131610
  1. 4
      src/features/AirPlay/styled.tsx
  2. 98
      src/features/AudioTracks/index.tsx
  3. 97
      src/features/AudioTracks/styled.tsx
  4. 8
      src/features/ChromeCast/index.tsx
  5. 6
      src/features/ChromeCast/styled.tsx
  6. 4
      src/features/MultiSourcePlayer/components/Settings/styled.tsx
  7. 18
      src/features/StreamPlayer/components/Controls/Components/ControlsMobile/index.tsx
  8. 1
      src/features/StreamPlayer/components/Controls/Components/ControlsMobile/styled.tsx
  9. 9
      src/features/StreamPlayer/components/Controls/Components/ControlsWeb/index.tsx
  10. 5
      src/features/StreamPlayer/components/Controls/index.tsx
  11. 2
      src/features/StreamPlayer/hooks/index.tsx
  12. 43
      src/features/StreamPlayer/hooks/useAudioTrack.tsx
  13. 6
      src/features/StreamPlayer/index.tsx
  14. 5
      src/features/StreamPlayer/styled.tsx

@ -9,13 +9,13 @@ export const AirplayButton = styled(ButtonBase)`
height: 24px;
padding: 0;
display: none;
margin-left: 25px;
margin-right: 25px;
${isMobileDevice
? css`
width: 22px;
height: 22px;
margin-left: 15px;
margin-right: 15px;
`
: ''};
`

@ -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;
`

@ -19,6 +19,8 @@ type Props = {
src?: string,
}
const NO_DEVICES_AVAILABLE = 'NO_DEVICES_AVAILABLE'
export const ChromeCast = memo(({ src } : Props) => {
const [isCastAvailable, setIsCastAvailable] = useState(false)
@ -56,7 +58,11 @@ export const ChromeCast = memo(({ src } : Props) => {
(window as any).__onGCastApiAvailable = (isAvailable: boolean) => {
if (isAvailable) {
castPlayer.initializeCastPlayer()
setIsCastAvailable(true)
if ((window as any).cast.framework.CastContext.getInstance().getCastState()
!== NO_DEVICES_AVAILABLE) {
setIsCastAvailable(true)
}
}
}

@ -5,15 +5,15 @@ import { isMobileDevice } from 'config/userAgent'
export const Container = styled.div`
display: flex;
align-items: center;
margin-left: 25px;
margin-right: 25px;
height: 24px;
width: 24px;
& #castbutton:hover {
& #castbutton {
--disconnected-color: #FFFFFF;
}
${isMobileDevice ? css`
margin-left: 15px;
margin-right: 15px;
` : ''}
`

@ -5,14 +5,14 @@ import { ButtonBase } from 'features/StreamPlayer/styled'
export const SettingsButton = styled(ButtonBase)`
width: 22px;
height: 20px;
margin-left: 25px;
margin-right: 25px;
background-image: url(/images/settings.svg);
${isMobileDevice
? css`
width: 20px;
height: 18px;
margin-left: 15px;
margin-right: 15px;
cursor: pointer;
`
: ''};

@ -2,6 +2,7 @@ import { T9n } from 'features/T9n'
import { Settings } from 'features/MultiSourcePlayer/components/Settings'
import { AirPlay } from 'features/AirPlay'
import { ChromeCast } from 'features/ChromeCast'
import { AudioTracks } from 'features/AudioTracks'
import { ControlsPropsExtended } from '../..'
import { LiveBtn } from '../../../../styled'
@ -17,7 +18,9 @@ export const ControlsMobile = (controlsProps: {props: ControlsPropsExtended}) =>
const { props } = controlsProps
const {
audioTracks,
backToLive,
changeAudioTrack,
controlsVisible,
isFullscreen,
isLive,
@ -25,6 +28,7 @@ export const ControlsMobile = (controlsProps: {props: ControlsPropsExtended}) =>
onQualitySelect,
playBackTime,
progressBarElement,
selectedAudioTrack,
selectedQuality,
src,
videoQualities,
@ -34,13 +38,17 @@ export const ControlsMobile = (controlsProps: {props: ControlsPropsExtended}) =>
return (
<Controls isFullscreen={isFullscreen}>
<ControlsRow visible={controlsVisible}>
<ControlsGroup>
<PlaybackTime>
{playBackTime}
</PlaybackTime>
</ControlsGroup>
<PlaybackTime>
{playBackTime}
</PlaybackTime>
<ControlsGroup>
<AudioTracks
audioTracks={audioTracks!}
changeAudioTrack={changeAudioTrack!}
isFullscreen={isFullscreen}
selectedAudioTrack={selectedAudioTrack!}
/>
{isLive && (
<LiveBtn onClick={backToLive}>
<T9n t='live' />

@ -56,7 +56,6 @@ export const ControlsGroup = styled.div`
export const Fullscreen = styled(ButtonBase)<FullscreenProps>`
width: 20px;
height: 18px;
margin-left: 15px;
background-image: ${({ isFullscreen }) => (
isFullscreen
? 'url(/images/player-fullscreen-off.svg)'

@ -9,6 +9,7 @@ import {
import { Settings } from 'features/MultiSourcePlayer/components/Settings'
import { T9n } from 'features/T9n'
import { ChromeCast } from 'features/ChromeCast'
import { AudioTracks } from 'features/AudioTracks'
import { ControlsPropsExtended } from '../..'
import { VolumeBar } from '../../../VolumeBar'
@ -28,7 +29,9 @@ export const ControlsWeb = (controlsProps: { props: ControlsPropsExtended }) =>
const { props } = controlsProps
const {
activeChapterIndex = 0,
audioTracks,
backToLive,
changeAudioTrack,
controlsVisible,
isFirstChapterPlaying,
isFullscreen,
@ -48,6 +51,7 @@ export const ControlsWeb = (controlsProps: { props: ControlsPropsExtended }) =>
progressBarElement,
rewindBackward,
rewindForward,
selectedAudioTrack,
selectedQuality,
src,
togglePlaying,
@ -95,6 +99,11 @@ export const ControlsWeb = (controlsProps: { props: ControlsPropsExtended }) =>
</ControlsGroup>
<ControlsGroup>
<AudioTracks
audioTracks={audioTracks!}
changeAudioTrack={changeAudioTrack!}
selectedAudioTrack={selectedAudioTrack!}
/>
{isLive && (
<LiveBtn onClick={backToLive}>
<T9n t='live' />

@ -1,5 +1,7 @@
import { Fragment, useMemo } from 'react'
import type { MediaPlaylist } from 'hls.js'
import { DebouncedFunc } from 'lodash'
import { isMobileDevice } from 'config/userAgent'
@ -17,7 +19,9 @@ import { ProgressBar } from '../ProgressBar'
export type ControlsProps = {
activeChapterIndex: number,
allPlayedProgress: number,
audioTracks?: Array<MediaPlaylist>,
backToLive?: () => void,
changeAudioTrack?: (trackId: number) => void,
controlsVisible: boolean,
duration: number,
isFirstChapterPlaying?: boolean,
@ -43,6 +47,7 @@ export type ControlsProps = {
playing: boolean,
rewindBackward: () => void,
rewindForward: () => void,
selectedAudioTrack?: MediaPlaylist,
selectedQuality: string,
src?: string,
togglePlaying: () => void,

@ -29,6 +29,7 @@ import { useControlsVisibility } from './useControlsVisibility'
import { useProgressChangeHandler } from './useProgressChangeHandler'
import { usePlayingHandlers } from './usePlayingHandlers'
import { useDuration } from './useDuration'
import { useAudioTrack } from './useAudioTrack'
export type PlayerState = typeof initialState
@ -458,5 +459,6 @@ export const useVideoPlayer = ({
...useControlsVisibility(isFullscreen, playing),
...useVolume(),
...useVideoQuality(hls),
...useAudioTrack(hls),
}
}

@ -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,
}
}

@ -36,9 +36,11 @@ export const StreamPlayer = (props: Props) => {
const {
activeChapterIndex,
allPlayedProgress,
audioTracks,
backToLive,
buffering,
centerControlsVisible,
changeAudioTrack,
chapters,
duration,
hideCenterControls,
@ -72,6 +74,7 @@ export const StreamPlayer = (props: Props) => {
rewindBackward,
rewindForward,
seek,
selectedAudioTrack,
selectedQuality,
showCenterControls,
togglePlaying,
@ -155,7 +158,9 @@ export const StreamPlayer = (props: Props) => {
<Controls
allPlayedProgress={allPlayedProgress}
audioTracks={audioTracks}
backToLive={backToLive}
changeAudioTrack={changeAudioTrack}
controlsVisible={mainControlsVisible}
duration={duration}
isFullscreen={isFullscreen}
@ -182,6 +187,7 @@ export const StreamPlayer = (props: Props) => {
volumeInPercent={volumeInPercent}
activeChapterIndex={activeChapterIndex}
liveChapters={chapters}
selectedAudioTrack={selectedAudioTrack}
/>
<ControlsGradient isVisible={mainControlsVisible} />
</PlayerWrapper>

@ -195,7 +195,6 @@ type FullscreenProps = {
export const Fullscreen = styled(ButtonBase)<FullscreenProps>`
width: 22px;
height: 20px;
margin-left: 26px;
background-image: ${({ isFullscreen }) => (
isFullscreen
? 'url(/images/player-fullscreen-off.svg)'
@ -205,7 +204,6 @@ export const Fullscreen = styled(ButtonBase)<FullscreenProps>`
? css`
width: 20px;
height: 18px;
margin-left: 15px;
`
: ''};
`
@ -313,8 +311,11 @@ export const LiveBtn = styled(ButtonBase)`
padding: 4.5px 8px;
background-color: #CC0000;
border-radius: 1.3px;
margin-right: 25px;
${isMobileDevice
? css`
margin-right: 15px;
`
: ''};
`

Loading…
Cancel
Save