feat(video): add video download functionality

implement video download feature with progress tracking and error handling
update video status and thumbnail types to support null values
add download error message to i18n
This commit is contained in:
icarus 2025-10-13 14:44:16 +08:00
parent b68a0ffaba
commit 1c89262929
5 changed files with 38 additions and 9 deletions

View File

@ -144,6 +144,7 @@ export const useVideos = (providerId: string) => {
addVideo,
updateVideo,
setVideos,
setVideo,
removeVideo
}
}

View File

@ -4651,6 +4651,7 @@
"video": {
"error": {
"create": "Failed to create video",
"download": "Failed to download video.",
"invalid": "Invalid video",
"load": {
"message": "Failed to load the video",

View File

@ -1,7 +1,8 @@
import { Button, cn, Image, Skeleton, Textarea, Tooltip } from '@heroui/react'
import { loggerService } from '@logger'
import { useAddOpenAIVideo } from '@renderer/hooks/video/useAddOpenAIVideo'
import { createVideo } from '@renderer/services/ApiService'
import { useVideos } from '@renderer/hooks/video/useVideos'
import { createVideo, retrieveVideoContent } from '@renderer/services/ApiService'
import { Provider } from '@renderer/types'
import { CreateVideoParams, Video } from '@renderer/types/video'
import { getErrorMessage } from '@renderer/utils'
@ -26,14 +27,20 @@ const logger = loggerService.withContext('VideoPanel')
export const VideoPanel = ({ provider, video, params, updateParams }: VideoPanelProps) => {
const { t } = useTranslation()
const addOpenAIVideo = useAddOpenAIVideo(provider.id)
const { setVideo } = useVideos(provider.id)
const [isProcessing, setIsProcessing] = useState(false)
const fileInputRef = useRef<HTMLInputElement>(null)
const inputReference = params.params.input_reference
const couldCreateVideo = useMemo(
() => !isProcessing && !isEmpty(params.params.prompt),
[isProcessing, params.params.prompt]
() =>
!isProcessing &&
!isEmpty(params.params.prompt) &&
video?.status !== 'queued' &&
video?.status !== 'downloading' &&
video?.status !== 'in_progress',
[isProcessing, params.params.prompt, video?.status]
)
useEffect(() => {
@ -69,8 +76,28 @@ export const VideoPanel = ({ provider, video, params, updateParams }: VideoPanel
}
}, [addOpenAIVideo, couldCreateVideo, params, t, video])
const handleDownloadVideo = (videoId: string) => {
window.toast.info('Not implemented')
const handleDownloadVideo = async () => {
if (!video) return
if (video.status === 'completed' || video.status === 'downloaded') {
setVideo({
...video,
status: 'downloading',
progress: 0
})
const promise = retrieveVideoContent({ type: 'openai', videoId: video.id, provider })
promise
.then((result) => result.response)
.then((response) => {
// TODO: implement download
logger.debug('download response', response)
})
promise.catch((e) => {
logger.error(`Failed to download video ${video.id}.`, e as Error)
window.toast.error(t('video.error.download'))
// rollback
setVideo(video)
})
}
}
const handleUploadFile = useCallback(() => {

View File

@ -21,7 +21,7 @@ export type VideoViewerProps =
}
| {
video: Video
onDownload: (videoId: string) => void
onDownload: () => void
}
export const VideoViewer = ({ video, onDownload }: VideoViewerProps) => {
@ -33,7 +33,7 @@ export const VideoViewer = ({ video, onDownload }: VideoViewerProps) => {
{video === undefined && t('video.undefined')}
{video && video.status === 'queued' && <QueuedVideo />}
{video && video.status === 'in_progress' && <InProgressVideo progress={video.progress} />}
{video && video.status === 'completed' && <CompletedVideo onDownload={() => onDownload(video.id)} />}
{video && video.status === 'completed' && <CompletedVideo onDownload={onDownload} />}
{video && video.status === 'downloading' && <DownloadingVideo progress={video.progress} />}
{video && video.status === 'downloaded' && loadSuccess !== false && (
<video

View File

@ -47,13 +47,13 @@ export interface VideoCompleted extends VideoBase {
export interface VideoDownloading extends VideoBase {
readonly status: 'downloading'
thumbnail: string
thumbnail: string | null
/** integer percent */
progress: number
}
export interface VideoDownloaded extends VideoBase {
readonly status: 'downloaded'
thumbnail: string
thumbnail: string | null
/** Managed by fileManager */
fileId: string
}