feat(#168): add video download process

VID-168-donwload-video
andreidekterev 2 years ago
parent 93e2716817
commit 9271894154
  1. 4
      src/config/lexics/matchDownload.tsx
  2. 1
      src/config/queries.tsx
  3. 96
      src/features/MatchPage/components/MatchDownloadPopup/index.tsx
  4. 23
      src/features/MatchPage/components/MatchDownloadPopup/styled.tsx
  5. 62
      src/features/MatchSidePlaylists/components/DownloadNotification/DownloadNotification.tsx
  6. 48
      src/features/MatchSidePlaylists/components/DownloadNotification/styled.tsx
  7. 45
      src/features/MatchSidePlaylists/components/MatchDownloadButton/index.tsx
  8. 35
      src/features/MatchSidePlaylists/components/MatchPlaylists/index.tsx
  9. 17
      src/features/MatchSidePlaylists/styled.tsx
  10. 19
      src/helpers/downloadFile/index.tsx
  11. 1
      src/helpers/index.tsx
  12. 38
      src/requests/downloadPlaylist.tsx

@ -1,8 +1,12 @@
export const matchDownload = { export const matchDownload = {
can_close: 20238,
choose_what_to_download: 20193, choose_what_to_download: 20193,
download_files_for_periods: 20197, download_files_for_periods: 20197,
download_full_match: 20198, download_full_match: 20198,
download_single_file: 20196, download_single_file: 20196,
entire_record: 20195, entire_record: 20195,
error_message: 20240,
in_game_time_only: 20194, in_game_time_only: 20194,
processed: 20200,
will_notified: 20239,
} }

@ -1,5 +1,6 @@
export const querieKeys = { export const querieKeys = {
ads: 'ads', ads: 'ads',
downloadPlaylist: 'downloadPlaylist',
liveMatchScores: 'liveMatchScores', liveMatchScores: 'liveMatchScores',
matchScore: 'matchScore', matchScore: 'matchScore',
sportsList: 'sportsList', sportsList: 'sportsList',

@ -1,7 +1,18 @@
import { useState } from 'react' import { useState } from 'react'
import { useQuery } from 'react-query'
import { T9n } from 'features/T9n' import { T9n } from 'features/T9n'
import { usePageParams } from 'hooks'
import { querieKeys } from 'config'
import {
downloadPlaylist,
type DownloadPlaylistProps,
} from 'requests/downloadPlaylist'
import { Radio } from 'features/Common'
import { import {
Header, Header,
Footer, Footer,
@ -13,20 +24,65 @@ import {
FirstTitle, FirstTitle,
SecondTitle, SecondTitle,
RadioButtonsWrapper, RadioButtonsWrapper,
Input,
Label, Label,
} from './styled' } from './styled'
type Props = { type Props = {
closePopup: () => void, closePopup: () => void,
isModalOpen: boolean, isModalOpen: boolean,
openDownloadNotification: () => void,
}
const fileType: Record<'download_single_file'| 'download_files_for_periods', string> = {
download_files_for_periods: 'download_files_for_periods',
download_single_file: 'download_single_file',
}
const initialRadioBtns: Record<string, boolean> = {
entire_record: true,
in_game_time_only: false,
} }
export const MatchDownloadPopup = ({ export const MatchDownloadPopup = ({
closePopup, closePopup,
isModalOpen, isModalOpen,
openDownloadNotification,
}: Props) => { }: Props) => {
const [selected, setSelected] = useState(true) const [downloadConfig, setDownloadConfig] = useState<typeof initialRadioBtns>(initialRadioBtns)
const { profileId: match_id, sportType: sport_id } = usePageParams()
const config: DownloadPlaylistProps = {
ball_in_play: downloadConfig.in_game_time_only,
match_id,
sport_id,
}
const { refetch } = useQuery(
querieKeys.downloadPlaylist,
() => downloadPlaylist(config),
{
enabled: false, // disable this query from automatically running
refetchOnWindowFocus: false,
},
)
const handleDownload = (id = fileType.download_single_file) => {
if (downloadConfig.in_game_time_only) {
config.concat = fileType.download_single_file === id
}
refetch()
closePopup()
openDownloadNotification()
}
const handleChange = (id: string) => {
setDownloadConfig((prev) => Object.keys(prev)
.reduce((result: typeof initialRadioBtns, key: keyof typeof initialRadioBtns) => ({
...result,
[key]: id === key,
}), {} as typeof initialRadioBtns))
}
return ( return (
<Modal <Modal
@ -42,28 +98,28 @@ export const MatchDownloadPopup = ({
</HeaderTitle> </HeaderTitle>
</Header> </Header>
<RadioButtonsWrapper> <RadioButtonsWrapper>
<Label> {Object.entries(downloadConfig).map(([id, checked], index) => (
<Input <Label>
defaultChecked={selected} <Radio
name='in_game_time_only' name={id}
onClick={() => setSelected(true)} key={id}
/> checked={checked}
<T9n t='in_game_time_only' /> onChange={(e) => handleChange(e.target.name)}
</Label> />
<Label> <T9n t={id} />
<Input </Label>
name='entire_record' ))}
defaultChecked={!selected}
onClick={() => setSelected(false)}
/>
<T9n t='entire_record' />
</Label>
</RadioButtonsWrapper> </RadioButtonsWrapper>
<Footer> <Footer>
<ScApplyButton> <ScApplyButton
onClick={() => handleDownload(fileType.download_single_file)}
>
<T9n t='download_single_file' /> <T9n t='download_single_file' />
</ScApplyButton> </ScApplyButton>
<ScApplyButton disabled={!selected}> <ScApplyButton
disabled={!downloadConfig.in_game_time_only}
onClick={() => handleDownload(fileType.download_files_for_periods)}
>
<T9n t='download_files_for_periods' /> <T9n t='download_files_for_periods' />
</ScApplyButton> </ScApplyButton>
</Footer> </Footer>

@ -100,29 +100,6 @@ export const Label = styled.label`
font-size: 14px;` font-size: 14px;`
: ''}; : ''};
` `
export const Input = styled.input.attrs(() => ({
type: 'radio',
}))`
margin: 0 25px 0 0;
appearance: none;
width: 25px;
height: 25px;
cursor: pointer;
background-image: url(/images/${({ defaultChecked }) => (
defaultChecked
? 'checkedRadiobutton.png'
: 'radiobutton.png'
)});
${isMobileDevice
? css`
margin-right: 10px;`
: ''};
`
export const Footer = styled.div`` export const Footer = styled.div``
export const ScApplyButton = styled(ApplyButton)` export const ScApplyButton = styled(ApplyButton)`

@ -0,0 +1,62 @@
import { useQueryClient } from 'react-query'
import { T9n } from 'features/T9n'
import { querieKeys } from 'config'
import { DownloadResponse } from 'requests/downloadPlaylist'
import {
Container,
Description,
Title,
CloseBtn,
Header,
MainContainer,
Error,
} from './styled'
type TDownloadNotification = {
close: () => void,
}
export const DownloadNotification = ({ close }:TDownloadNotification) => {
const client = useQueryClient()
const data = client.getQueryData<DownloadResponse>(querieKeys.downloadPlaylist)
if (!data) {
return null
}
return (
<Container>
<Header>
<CloseBtn
onClick={close}
/>
</Header>
<MainContainer>
<Title>
<T9n t='processed' />
</Title>
<Description>
{data?.status === 'ERROR'
? (
<Error>
<T9n t='error_message' />
</Error>
) : (
<>
<T9n t='can_close' />&nbsp;
<T9n t='will_notified' />&nbsp;
{/* <LinkToDownload href='/useraccount/downloads'> */}
{/* My Videos */}
{/* </LinkToDownload> */}
</>
)}
</Description>
</MainContainer>
</Container>
)
}

@ -0,0 +1,48 @@
import styled from 'styled-components/macro'
import { CloseButton } from 'features/PopupComponents'
export const Container = styled.section`
width: 100%;
border-radius: 0.125rem;
background: #333;
padding: 0.5rem 0.5rem ;
color: #FFF;
font-weight: 400;
line-height: normal;
font-size: 0.75rem;
margin-bottom: 0.45rem;
`
export const Title = styled.h3`
font-size: 0.875rem;
font-style: normal;
font-weight: 700;
margin-bottom: 0.56rem;
`
export const Description = styled.span`
`
export const CloseBtn = styled(CloseButton)`
padding: 4px;
`
export const Header = styled.header`
display: flex;
justify-content: end;
`
export const MainContainer = styled.main`
padding: 0.4rem 1.1rem 2rem 1.3rem;
`
export const LinkToDownload = styled.a`
text-decoration: underline;
cursor: pointer;
color: white;
font-weight: 600;
`
export const Error = styled.span`
color: red;
`

@ -11,10 +11,22 @@ import { MatchDownloadPopup } from 'features/MatchPage/components/MatchDownloadP
import { usePageParams } from 'hooks/usePageParams' import { usePageParams } from 'hooks/usePageParams'
import { Item } from '../MatchPlaylists' import { useQueryClient } from 'react-query'
import { DownloadResponse } from 'requests/downloadPlaylist'
import { querieKeys } from 'config'
import { useInterval } from 'hooks'
import { useEffect } from 'react'
import { downloadFile } from 'helpers'
import { DownloadButton, Title } from '../../styled' import { DownloadButton, Title } from '../../styled'
import { Item } from '../MatchPlaylists'
type TMatchDownloadButton = {
open: () => void,
}
export const MatchDownloadButton = () => { const INTERVAL_CHECK_DOWNLOAD_STATUS = 60 * 1000
export const MatchDownloadButton = ({ open: openDownloadNotification }:TMatchDownloadButton) => {
const { user, userInfo } = useAuthStore() const { user, userInfo } = useAuthStore()
const { profile } = useMatchPageStore() const { profile } = useMatchPageStore()
const { sportType } = usePageParams() const { sportType } = usePageParams()
@ -47,6 +59,34 @@ export const MatchDownloadButton = () => {
return null return null
} }
const client = useQueryClient()
const data = client.getQueryData<DownloadResponse>(querieKeys.downloadPlaylist)
const handleRefetch = () => {
client.refetchQueries(querieKeys.downloadPlaylist)
}
const { start, stop } = useInterval({
callback: handleRefetch,
intervalDuration: INTERVAL_CHECK_DOWNLOAD_STATUS,
startImmediate: false,
})
useEffect(() => {
start()
if (data?.status === 'COMPLETED' && data.urls) {
downloadFile(data.urls)
stop()
} else if (data?.status === 'ERROR') {
stop()
}
return () => stop()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [data?.status])
const isNeedDownloadButton = user && (isAvailableTournaments() || isAvailableTeams()) const isNeedDownloadButton = user && (isAvailableTournaments() || isAvailableTeams())
if (!isNeedDownloadButton) return null if (!isNeedDownloadButton) return null
@ -56,6 +96,7 @@ export const MatchDownloadButton = () => {
<MatchDownloadPopup <MatchDownloadPopup
isModalOpen={isOpen} isModalOpen={isOpen}
closePopup={close} closePopup={close}
openDownloadNotification={openDownloadNotification}
/> />
<DownloadButton onClick={open}> <DownloadButton onClick={open}>
<Title> <Title>

@ -1,5 +1,6 @@
import type { ForwardedRef } from 'react' import type { ForwardedRef } from 'react'
import { forwardRef } from 'react' import { forwardRef, useEffect } from 'react'
import { useQueryClient } from 'react-query'
import styled, { css } from 'styled-components/macro' import styled, { css } from 'styled-components/macro'
@ -7,7 +8,9 @@ import isEmpty from 'lodash/isEmpty'
import map from 'lodash/map' import map from 'lodash/map'
import some from 'lodash/some' import some from 'lodash/some'
import { isMobileDevice } from 'config' import { isMobileDevice, querieKeys } from 'config'
import { DownloadResponse } from 'requests/downloadPlaylist'
import type { import type {
MatchPlaylistOption, MatchPlaylistOption,
@ -22,6 +25,9 @@ import { useLexicsStore } from 'features/LexicsStore'
import { MATCH_ADS } from 'components/Ads/types' import { MATCH_ADS } from 'components/Ads/types'
import { usePageParams, useToggle } from 'hooks'
import { DownloadNotification } from '../DownloadNotification/DownloadNotification'
import { PlayButton } from '../PlayButton' import { PlayButton } from '../PlayButton'
import { MatchDownloadButton } from '../MatchDownloadButton' import { MatchDownloadButton } from '../MatchDownloadButton'
@ -71,6 +77,13 @@ export const MatchPlaylists = forwardRef(
) => { ) => {
const { ads, setEpisodeInfo } = useMatchPageStore() const { ads, setEpisodeInfo } = useMatchPageStore()
const { translate } = useLexicsStore() const { translate } = useLexicsStore()
const {
close,
isOpen,
open,
} = useToggle()
const { profileId } = usePageParams()
const handleButtonClick = (playlist: MatchPlaylistOption) => { const handleButtonClick = (playlist: MatchPlaylistOption) => {
onSelect?.(playlist) onSelect?.(playlist)
@ -82,13 +95,22 @@ export const MatchPlaylists = forwardRef(
}) })
} }
const client = useQueryClient()
const data = client.getQueryData<DownloadResponse>(querieKeys.downloadPlaylist)
useEffect(() => {
close()
}, [profileId, close])
return ( return (
<List <List
ref={ref} ref={ref}
isAdsExist={some(ads, ({ position }) => position.id === MATCH_ADS.WATCH_TOP)} isAdsExist={some(ads, ({ position }) => position.id === MATCH_ADS.WATCH_TOP)}
> >
{ {isOpen && data
map(playlists, (playlist) => ( ? (<DownloadNotification close={close} />)
: map(playlists, (playlist) => (
<Item <Item
key={playlist.id} key={playlist.id}
id={`match_watch_${playlist.id}${live ? '_live' : ''}`} id={`match_watch_${playlist.id}${live ? '_live' : ''}`}
@ -103,9 +125,8 @@ export const MatchPlaylists = forwardRef(
<T9n t={playlist.lexic} /> <T9n t={playlist.lexic} />
</PlayButton> </PlayButton>
</Item> </Item>
)) ))}
} <MatchDownloadButton open={open} />
<MatchDownloadButton />
</List> </List>
) )
}, },

@ -6,7 +6,7 @@ import {
devices, devices,
} from 'config' } from 'config'
import { customScrollbar } from 'features/Common' import { ButtonOutline, customScrollbar } from 'features/Common'
import { T9n } from 'features/T9n' import { T9n } from 'features/T9n'
type WrapperProps = { type WrapperProps = {
@ -219,19 +219,16 @@ export const Button = styled.button<ButtonProps>`
}} }}
` `
export const DownloadButton = styled(Button)` export const DownloadButton = styled(ButtonOutline)`
width: 100%;
height: 2.25rem;
font-size: 0.875rem;
font-weight: 600;
line-height: 1rem;
:hover { :hover {
background-color: ${({ theme }) => theme.colors.buttonHover}; background-color: ${({ theme }) => theme.colors.buttonHover};
} }
${({ active }) => (active
? css`
border: 1px solid #FFFFFF;
border-radius: 2px;
background-color: black;
`
: '')}
` `
export const Title = styled.span` export const Title = styled.span`

@ -0,0 +1,19 @@
/* скачивание файлов без клика */
export const downloadFile = (urls: Array<string>) => {
if (!urls.length) return
urls.forEach((url) => {
const link = document.createElement('a')
link.href = url
link.download = 'video.mp4'
link.target = '_blank'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
})
}

@ -17,3 +17,4 @@ export * from './languageUrlParam'
export * from './bodyScrollLock' export * from './bodyScrollLock'
export * from './getLocalStorage' export * from './getLocalStorage'
export * from './checkPage' export * from './checkPage'
export * from './downloadFile'

@ -0,0 +1,38 @@
import { API_ROOT } from 'config'
import { callApi } from 'helpers'
export type DownloadPlaylistProps = {
ball_in_play: boolean,
concat?: boolean,
match_id: number,
sport_id: number,
}
type Status = 'COMPLETED' | 'IN PROGRESS' | 'ERROR'
export type DownloadResponse = {
status:Status,
urls?: Array<string>,
}
export const downloadPlaylist = async (
{
ball_in_play,
concat,
match_id,
sport_id,
}: DownloadPlaylistProps,
)
: Promise<DownloadResponse> => {
const config = {
body: {
ball_in_play,
concat,
},
}
return callApi({
config,
url: `${API_ROOT}/v1/matches/${sport_id}/${match_id}/download`,
})
}
Loading…
Cancel
Save