Ott 1701 part 2 (#553)

* refactor(1725): video width 100%

* refactor(1701): building playlists and chapters
keep-around/fdb88b04b32b9392e76795099e2ec47c9856b38b
Mirlan 4 years ago committed by Andrei Dekterev
parent 95d0194c5b
commit 950a02c7ae
  1. 66
      src/features/MatchPage/components/LiveMatch/helpers.tsx
  2. 65
      src/features/MatchPage/components/LiveMatch/hooks/index.tsx
  3. 28
      src/features/MatchPage/components/LiveMatch/hooks/useChapters.tsx
  4. 2
      src/features/MatchPage/components/LiveMatch/hooks/usePlaylistLogger.tsx
  5. 23
      src/features/MatchPage/components/LiveMatch/hooks/useResumeUrlParam.tsx
  6. 6
      src/features/MatchPage/components/LiveMatch/index.tsx
  7. 2
      src/features/MatchPage/helpers/buildPlaylists.tsx
  8. 5
      src/features/MatchPage/store/hooks/usePlaylistLexics.tsx
  9. 2
      src/requests/getMatchEvents.tsx
  10. 32
      src/requests/getMatchPlaylists.tsx

@ -0,0 +1,66 @@
import last from 'lodash/last'
import reduce from 'lodash/reduce'
import concat from 'lodash/concat'
import type { Episodes } from 'requests/getMatchPlaylists'
import type { Chapters } from 'features/StreamPlayer/types'
import type { MatchPlaylistOption, PlaylistOption } from '../../types'
import { FULL_GAME_KEY } from '../../helpers/buildPlaylists'
const getFullMatchChapters = (url: string, playlist: MatchPlaylistOption) => {
const duration = (playlist.duration ?? 0) * 1000
return [
{
duration,
endMs: duration,
endOffsetMs: duration,
index: 0,
startMs: 0,
startOffsetMs: 0,
url,
},
]
}
const getPlaylistChapters = (url: string, episodes: Episodes) => reduce(
episodes,
(
acc: Chapters,
episode,
index,
) => {
if (episode.s >= episode.e) return acc
const episodeDuration = (episode.e - episode.s) * 1000
const prevVideoEndMs = last(acc)?.endMs || 0
const nextChapter = {
duration: episodeDuration,
endMs: prevVideoEndMs + episodeDuration,
endOffsetMs: episode.e * 1000,
index,
startMs: prevVideoEndMs,
startOffsetMs: episode.s * 1000,
url,
}
return concat(acc, nextChapter)
},
[],
)
type Args = {
selectedPlaylist?: PlaylistOption,
url: string,
}
export const buildChapters = ({
selectedPlaylist,
url,
}: Args): Chapters => {
if (!selectedPlaylist) return []
if (selectedPlaylist.id === FULL_GAME_KEY) {
return getFullMatchChapters(url, selectedPlaylist)
}
return getPlaylistChapters(url, selectedPlaylist.episodes)
}

@ -2,25 +2,24 @@ import { useMemo } from 'react'
import { API_ROOT } from 'config' import { API_ROOT } from 'config'
import type { MatchInfo } from 'requests/getMatchInfo'
import { usePageParams } from 'hooks/usePageParams' import { usePageParams } from 'hooks/usePageParams'
import { useMatchPopupStore } from 'features/MatchPopup' import { useMatchPageStore } from 'features/MatchPage/store'
import { usePlayerProgressReporter } from './usePlayerProgressReporter' import { usePlayerProgressReporter } from './usePlayerProgressReporter'
import { useLastPlayPosition } from './useLastPlayPosition' import { useResumeUrlParam } from './useResumeUrlParam'
import { useUrlParam } from './useUrlParam' import { useChapters } from './useChapters'
import { usePlaylistLogger } from './usePlaylistLogger'
export const useLiveMatch = (profile: MatchInfo) => { export const useLiveMatch = () => {
const { const {
handlePlaylistClick, handlePlaylistClick,
matchPlaylists, profile,
selectedPlaylist, selectedPlaylist,
} = useMatchPopupStore() setFullMatchPlaylistDuration,
} = useMatchPageStore()
const { profileId: matchId, sportType } = usePageParams() const { profileId: matchId, sportType } = usePageParams()
const resume = useUrlParam() const resume = useResumeUrlParam()
const fromStartIfStreamPaused = useMemo( const fromStartIfStreamPaused = useMemo(
() => (profile && !profile.live ? 0 : undefined), () => (profile && !profile.live ? 0 : undefined),
@ -30,13 +29,49 @@ export const useLiveMatch = (profile: MatchInfo) => {
[], [],
) )
const { chapters } = useChapters({
selectedPlaylist,
url: `${API_ROOT}/video/stream/${sportType}/${matchId}.m3u8`,
})
const {
logPlaylistChange,
onPlayingChange: notifyPlaylistLogger,
} = usePlaylistLogger()
const {
onPlayerProgressChange,
onPlayingChange: notifyProgressLogger,
} = usePlayerProgressReporter()
const onDurationChange = (duration: number) => {
if (profile?.live) return
setFullMatchPlaylistDuration(duration)
}
const onPlayingChange = (playing: boolean) => {
notifyPlaylistLogger(playing)
notifyProgressLogger(playing)
}
const onPlaylistSelect: typeof handlePlaylistClick = (playlist, e) => {
if (selectedPlaylist) {
logPlaylistChange(selectedPlaylist)
}
handlePlaylistClick(playlist, e)
}
return { return {
matchPlaylists, chapters,
onPlaylistSelect: handlePlaylistClick, onDurationChange,
onPlayerProgressChange,
onPlayingChange,
onPlaylistSelect,
resume: resume ?? fromStartIfStreamPaused, resume: resume ?? fromStartIfStreamPaused,
selectedPlaylist, selectedPlaylist,
streamUrl: `${API_ROOT}/video/stream/${sportType}/${matchId}.m3u8`, streamUrl: (
...usePlayerProgressReporter(), profile?.playbackUrl
...useLastPlayPosition(), || `${API_ROOT}/video/stream/${sportType}/${matchId}.m3u8`
),
} }
} }

@ -0,0 +1,28 @@
import { useMemo } from 'react'
import type { PlaylistOption } from 'features/MatchPage/types'
import { buildChapters } from '../helpers'
type Args = {
selectedPlaylist?: PlaylistOption,
url: string,
}
export const useChapters = ({
selectedPlaylist,
url,
}: Args) => {
const chapters = useMemo(
() => buildChapters({
selectedPlaylist,
url,
}),
[
selectedPlaylist,
url,
],
)
return { chapters }
}

@ -21,7 +21,7 @@ const playlistTypeConfig = {
const getInitialData = () => ({ dateVisit: new Date().toISOString(), seconds: 0 }) const getInitialData = () => ({ dateVisit: new Date().toISOString(), seconds: 0 })
export const usePlayerLogger = () => { export const usePlaylistLogger = () => {
const location = useLocation() const location = useLocation()
const { profileId, sportType } = usePageParams() const { profileId, sportType } = usePageParams()
const data = useRef(getInitialData()) const data = useRef(getInitialData())

@ -0,0 +1,23 @@
import { useMemo } from 'react'
import { useLocation } from 'react-router'
import isNumber from 'lodash/isNumber'
export const RESUME_KEY = 'resume'
const readResumeParam = (search: string) => {
const params = new URLSearchParams(search)
const rawValue = params.get(RESUME_KEY)
if (!rawValue) return undefined
const value = JSON.parse(rawValue)
return isNumber(value) ? value : 0
}
export const useResumeUrlParam = () => {
const { search } = useLocation()
const resume = useMemo(() => readResumeParam(search), [search])
return resume
}

@ -38,15 +38,13 @@ export const LiveMatch = ({
onPlaylistSelect, onPlaylistSelect,
resume, resume,
streamUrl, streamUrl,
} = useLiveMatch(profile) } = useLiveMatch()
const Player = profile?.youtube_link ? YoutubePlayer : StreamPlayer
return ( return (
<Fragment> <Fragment>
<Container> <Container>
{profile?.youtube_link ? ( {profile?.youtube_link ? (
<Player <YoutubePlayer
onPlayingChange={onPlayingChange} onPlayingChange={onPlayingChange}
onProgressChange={onPlayerProgressChange} onProgressChange={onPlayerProgressChange}
profile={profile} profile={profile}

@ -25,7 +25,7 @@ const getMatchPlaylists = (matchPlaylists: MatchPlaylists | null): MatchPlaylist
return map(MATCH_KEYS, (key) => { return map(MATCH_KEYS, (key) => {
const playlist = matchPlaylists[key] const playlist = matchPlaylists[key]
const lexic = matchPlaylists.lexics[key] const lexic = matchPlaylists.lexics[key] ?? ''
return { return {
duration: playlist?.dur, duration: playlist?.dur,
episodes: sortBy(playlist?.data, ['h', 's']), episodes: sortBy(playlist?.data, ['h', 's']),

@ -1,6 +1,7 @@
import { useCallback } from 'react' import { useCallback } from 'react'
import isEmpty from 'lodash/isEmpty' import isEmpty from 'lodash/isEmpty'
import compact from 'lodash/compact'
import values from 'lodash/values' import values from 'lodash/values'
import type { MatchPlaylists } from 'requests' import type { MatchPlaylists } from 'requests'
@ -9,8 +10,8 @@ import { useLexicsStore } from 'features/LexicsStore'
export const usePlaylistLexics = () => { export const usePlaylistLexics = () => {
const { addLexicsConfig } = useLexicsStore() const { addLexicsConfig } = useLexicsStore()
const fetchLexics = useCallback((playlist: MatchPlaylists | null) => { const fetchLexics = useCallback((playlist: MatchPlaylists) => {
const lexics = values(playlist?.lexics) const lexics = compact(values(playlist.lexics))
if (!isEmpty(lexics)) { if (!isEmpty(lexics)) {
addLexicsConfig(lexics) addLexicsConfig(lexics)
} }

@ -76,5 +76,7 @@ export const getMatchEvents = async ({
url: `${DATA_URL}/${getSportLexic(sportType)}`, url: `${DATA_URL}/${getSportLexic(sportType)}`,
}) })
if (!response?.data) return Promise.reject(response)
return response?.data || [] return response?.data || []
} }

@ -49,12 +49,12 @@ type Player = {
export type Players = Array<Player> export type Players = Array<Player>
export type Lexics = { export type Lexics = {
ball_in_play: number, ball_in_play?: number,
full_game: number, full_game?: number,
goals: number, goals?: number,
highlights: number, highlights?: number,
interview: number, interview?: number,
players: number, players?: number,
} }
export type MatchPlaylists = { export type MatchPlaylists = {
@ -79,7 +79,7 @@ export const getMatchPlaylists = async ({
selectedActions, selectedActions,
sportType, sportType,
withFullMatchDuration, withFullMatchDuration,
}: Args) => { }: Args): Promise<MatchPlaylists> => {
const actions = isEmpty(selectedActions) ? null : selectedActions const actions = isEmpty(selectedActions) ? null : selectedActions
const config = { const config = {
@ -110,7 +110,19 @@ export const getMatchPlaylists = async ({
dur: fullMatchDuration, dur: fullMatchDuration,
} }
return playlist.data if (playlist.data) {
? { ...playlist.data, full_game } return { ...playlist.data, full_game }
: null }
return {
ball_in_play: {},
full_game,
goals: {},
highlights: {},
lexics: {},
players1: [],
players2: [],
score1: 0,
score2: 0,
}
} }

Loading…
Cancel
Save