Ott 1701 part 5

keep-around/fdb88b04b32b9392e76795099e2ec47c9856b38b
Макситалиев Мирлан 4 years ago committed by Andrei Dekterev
parent 7e0f83ad2a
commit 86a9ed498d
  1. 3
      package.json
  2. 15
      src/features/MatchPage/components/LiveMatch/helpers.tsx
  3. 5
      src/features/MatchPage/store/hooks/useMatchPlaylists.tsx
  4. 6
      src/features/StreamPlayer/components/ProgressBar/helpers/calculateChapterStyles/index.tsx
  5. 4
      src/features/StreamPlayer/config.tsx
  6. 32
      src/features/StreamPlayer/helpers/index.tsx
  7. 6
      src/features/StreamPlayer/hooks/index.tsx
  8. 4
      src/features/StreamPlayer/hooks/useProgressChangeHandler.tsx
  9. 6
      src/features/StreamPlayer/hooks/useVideoQuality.tsx
  10. 3
      src/features/StreamPlayer/index.tsx
  11. 22
      src/features/StreamPlayer/types.tsx
  12. 10
      src/features/VideoPlayer/index.tsx

@ -24,7 +24,7 @@
"babel-polyfill": "^6.26.0",
"date-fns": "^2.14.0",
"history": "^4.10.1",
"hls.js": "^0.14.15",
"hls.js": "^1.1.1",
"lodash": "^4.17.15",
"m3u8-parser": "^4.7.0",
"oidc-client": "^1.11.5",
@ -56,7 +56,6 @@
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^7.1.2",
"@types/history": "^4.7.6",
"@types/hls.js": "^0.13.2",
"@types/jest": "^26.0.15",
"@types/lodash": "^4.14.154",
"@types/node": "^12.0.0",

@ -4,11 +4,15 @@ import concat from 'lodash/concat'
import type { Episodes } from 'requests/getMatchPlaylists'
import type { Chapters } from 'features/StreamPlayer/types'
import type { Chapters, Chapter } from 'features/StreamPlayer/types'
import type { MatchPlaylistOption, PlaylistOption } from '../../types'
import { FULL_GAME_KEY } from '../../helpers/buildPlaylists'
/**
* Формирует эпизоды плейлиста Полный матч
* API не выдает полный матч как плейлист, формируем на фронте
* */
const getFullMatchChapters = (url: string, playlist: MatchPlaylistOption) => {
const duration = (playlist.duration ?? 0) * 1000
return [
@ -17,6 +21,7 @@ const getFullMatchChapters = (url: string, playlist: MatchPlaylistOption) => {
endMs: duration,
endOffsetMs: duration,
index: 0,
isFullMatchChapter: true,
startMs: 0,
startOffsetMs: 0,
url,
@ -24,6 +29,9 @@ const getFullMatchChapters = (url: string, playlist: MatchPlaylistOption) => {
]
}
/**
* Формирует эпизоды плейлистов матча и игроков
* */
const getPlaylistChapters = (url: string, episodes: Episodes) => reduce(
episodes,
(
@ -35,7 +43,7 @@ const getPlaylistChapters = (url: string, episodes: Episodes) => reduce(
const episodeDuration = (episode.e - episode.s) * 1000
const prevVideoEndMs = last(acc)?.endMs || 0
const nextChapter = {
const nextChapter: Chapter = {
duration: episodeDuration,
endMs: prevVideoEndMs + episodeDuration,
endOffsetMs: episode.e * 1000,
@ -54,6 +62,9 @@ type Args = {
url: string,
}
/**
* Формирует список эпизодов из выбранного плейлиста для плеера
*/
export const buildChapters = ({
selectedPlaylist,
url,

@ -56,6 +56,11 @@ export const useMatchPlaylists = () => {
.then(setMatchPlaylists)
}, [fetchLexics, setInitialSeletedPlaylist])
/**
* API не выдает длительность Полного матча
* Здесь получаем его из самого видео
* и обновляем длительность плейлиста Полный матч
*/
const setFullMatchPlaylistDuration = (duration: number) => {
const playlists = [...matchPlaylists.match]
if (!playlists[0]) return

@ -49,9 +49,9 @@ export const calculateChapterStyles = ({
...chapter,
loaded: calculateChapterProgress(loadedProgress, chapter),
played: calculateChapterProgress(playedProgress, chapter),
width: chapter.duration
? chapter.duration * 100 / videoDuration
: 100,
width: chapter.isFullMatchChapter
? 100
: chapter.duration * 100 / videoDuration,
}
return [
...playedChapters,

@ -1,8 +1,8 @@
import Hls from 'hls.js'
import type { HlsConfig } from 'hls.js'
import { readToken } from 'helpers/token'
export const streamConfig: Partial<Hls.Config> = {
export const streamConfig: Partial<HlsConfig> = {
liveSyncDuration: 30,
maxBufferLength: 30,
xhrSetup: (xhr, urlString) => {

@ -1,33 +1,13 @@
import { RefObject } from 'react'
import findIndex from 'lodash/findIndex'
import size from 'lodash/size'
import type { Chapters } from '../types'
type Args = {
from?: number,
url: string,
videoRef: RefObject<HTMLVideoElement>,
}
export const preparePlayer = ({
from = 0,
url,
videoRef,
}: Args) => {
const video = videoRef?.current
if (!video) return
// eslint-disable-next-line no-param-reassign
video.src = url
if (from) {
video.currentTime = from
}
video.load()
}
export const findChapterByProgress = (chapters: Chapters, progressMs: number) => (
export const findChapterByProgress = (chapters: Chapters, progressMs: number) => {
if (size(chapters) === 1 && chapters[0].isFullMatchChapter) return 0
return (
findIndex(chapters, ({ endMs, startMs }) => (
startMs <= progressMs && progressMs <= endMs
))
)
)
}

@ -75,11 +75,7 @@ export const useVideoPlayer = ({
ready,
seek,
seeking,
}, setPlayerState] = useObjectState({
...initialState,
playedProgress: toMilliSeconds(resumeFrom || 0),
seek: resumeFrom || 0,
})
}, setPlayerState] = useObjectState(initialState)
const chaptersDuration = useDuration(chapters)

@ -31,8 +31,8 @@ export const useProgressChangeHandler = ({
? chapterIndex
: state.activeChapterIndex
// отнимаем начало главы на котором остановились от общего прогресса
// чтобы получить прогресс текущей главы
// отнимаем начало эпизода на котором остановились от общего прогресса
// чтобы получить прогресс текущего эпизода
const chapterProgressMs = (progressMs - chapter.startMs)
const seekMs = chapterProgressMs + chapter.startOffsetMs
return {

@ -4,7 +4,7 @@ import {
useCallback,
} from 'react'
import Hls from 'hls.js'
import Hls, { Level } from 'hls.js'
import map from 'lodash/map'
import find from 'lodash/find'
@ -28,9 +28,9 @@ const autoQuality = {
* непонятное качество без свойств height, width и тд для определения
* какое это качество
*/
const filterOutUnknownQualities = filter(({ height }: Hls.Level) => Boolean(height))
const filterOutUnknownQualities = filter(({ height }: Level) => Boolean(height))
const getVideoQualities = (levels: Array<Hls.Level>) => {
const getVideoQualities = (levels: Array<Level>) => {
if (isEmpty(levels)) return []
const filteredQualities = filterOutUnknownQualities(levels)

@ -32,6 +32,9 @@ import {
import type { Props } from './hooks'
import { useVideoPlayer } from './hooks'
/**
* HLS плеер, применяется на лайв и завершенных матчах
*/
export const StreamPlayer = (props: Props) => {
const { chapters, isLive } = props

@ -1,9 +1,31 @@
/**
* для примера матч с двумя эпизодами в плейлисте Голы, время в мс:
* [{start: 0, end: 20000}, {start: 60000, end: 80000}]
*/
export type Chapter = {
duration: number,
/**
* конец эпизода в плейлисте
* в первом эпизоде - 20000, во втором - 40000
*/
endMs: number,
/** конец эпизода как отмечено в матче */
endOffsetMs: number,
/** индекс эпизода для дебага */
index?: number,
isFullMatchChapter?: boolean,
/**
* начало эпизода в плейлисте
* в первом эпизоде - 0, во втором - 20000
*/
startMs: number,
/** начало эпизода как отмечено в матче */
startOffsetMs: number,
url: string,
}

@ -4,6 +4,16 @@ import type { Props } from './hooks'
import { useVideoPlayer } from './hooks'
import { Video } from './styled'
/**
* Низкоуровневый компонент для декларативной работы с HTMLVideoElement
* ```ts
* например старт и пауза плеера вместо
* video.play() | video.pause()
*
* контролируем через пропс playing
* <VideoPlayer playing={true | false} />
* ```
*/
export const VideoPlayer = forwardRef<HTMLVideoElement, Props>((props: Props, ref) => {
const {
className,

Loading…
Cancel
Save