88 lines
2.7 KiB
TypeScript
88 lines
2.7 KiB
TypeScript
|
|
import { useEffect, useState } from 'react'
|
|||
|
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
|||
|
|
import type { FeedVideoDto } from '../api/client'
|
|||
|
|
import { downloadVideo, getDownloadStatus } from '../api/client'
|
|||
|
|
|
|||
|
|
const ACTIVE_STATUSES = ['queued', 'downloading', 'postprocessing']
|
|||
|
|
|
|||
|
|
const STATUS_LABELS: Record<string, string> = {
|
|||
|
|
queued: 'В очереди...',
|
|||
|
|
postprocessing: 'Обработка...',
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
interface Props {
|
|||
|
|
video: FeedVideoDto
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function DownloadButton({ video }: Props) {
|
|||
|
|
const queryClient = useQueryClient()
|
|||
|
|
const [polling, setPolling] = useState(ACTIVE_STATUSES.includes(video.local.status))
|
|||
|
|
|
|||
|
|
const statusQuery = useQuery({
|
|||
|
|
queryKey: ['download-status', video.youtube_video_id],
|
|||
|
|
queryFn: () => getDownloadStatus(video.youtube_video_id),
|
|||
|
|
enabled: polling,
|
|||
|
|
refetchInterval: (query) => (query.state.data && ACTIVE_STATUSES.includes(query.state.data.status) ? 2000 : false),
|
|||
|
|
initialData: polling
|
|||
|
|
? {
|
|||
|
|
status: video.local.status,
|
|||
|
|
progress_percent: video.local.progress_percent,
|
|||
|
|
media_url: video.local.media_url,
|
|||
|
|
error_message: null,
|
|||
|
|
}
|
|||
|
|
: undefined,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const downloadMutation = useMutation({
|
|||
|
|
mutationFn: () => downloadVideo(video.youtube_video_id),
|
|||
|
|
onSuccess: (data) => {
|
|||
|
|
queryClient.setQueryData(['download-status', video.youtube_video_id], data)
|
|||
|
|
setPolling(true)
|
|||
|
|
},
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const current = polling ? statusQuery.data : undefined
|
|||
|
|
const status = current?.status ?? video.local.status
|
|||
|
|
const percent = current?.progress_percent ?? video.local.progress_percent
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
if (current && !ACTIVE_STATUSES.includes(current.status)) {
|
|||
|
|
setPolling(false)
|
|||
|
|
if (current.status === 'completed' || current.status === 'failed') {
|
|||
|
|
queryClient.invalidateQueries({ queryKey: ['feed'] })
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}, [current?.status, queryClient])
|
|||
|
|
|
|||
|
|
if (status === 'completed') {
|
|||
|
|
return <span className="download-badge completed">✓ На сервере</span>
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (status === 'downloading') {
|
|||
|
|
return <span className="download-badge">{percent ?? 0}%</span>
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (status === 'queued' || status === 'postprocessing') {
|
|||
|
|
return <span className="download-badge">{STATUS_LABELS[status]}</span>
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (status === 'failed') {
|
|||
|
|
return (
|
|||
|
|
<span className="download-error">
|
|||
|
|
<span className="download-badge error">Ошибка</span>
|
|||
|
|
<button onClick={() => downloadMutation.mutate()} disabled={downloadMutation.isPending}>
|
|||
|
|
Повторить
|
|||
|
|
</button>
|
|||
|
|
</span>
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<button onClick={() => downloadMutation.mutate()} disabled={downloadMutation.isPending}>
|
|||
|
|
⬇ Скачать
|
|||
|
|
</button>
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export default DownloadButton
|