From c3c125f3a3063b60dd90c54f3c2e31d6a1f5bee8 Mon Sep 17 00:00:00 2001 From: icarus Date: Sun, 12 Oct 2025 17:37:56 +0800 Subject: [PATCH] feat(video): add video status tracking and thumbnail handling - Implement useVideo hook for single video retrieval - Make thumbnail optional in VideoCompleted interface - Add prompt parameter to addOpenAIVideo and handle progress updates - Add auto-refresh for in-progress videos and update progress --- .../src/hooks/video/useAddOpenAIVideo.ts | 15 ++++--- .../src/hooks/video/useOpenAIVideo.ts | 42 ++++++++++++++++--- src/renderer/src/hooks/video/useVideo.ts | 7 ++++ src/renderer/src/pages/video/VideoList.tsx | 2 +- src/renderer/src/pages/video/VideoPanel.tsx | 2 +- src/renderer/src/types/video.ts | 2 +- 6 files changed, 56 insertions(+), 14 deletions(-) create mode 100644 src/renderer/src/hooks/video/useVideo.ts diff --git a/src/renderer/src/hooks/video/useAddOpenAIVideo.ts b/src/renderer/src/hooks/video/useAddOpenAIVideo.ts index 0aceb9f074..354e883700 100644 --- a/src/renderer/src/hooks/video/useAddOpenAIVideo.ts +++ b/src/renderer/src/hooks/video/useAddOpenAIVideo.ts @@ -7,14 +7,15 @@ export const useAddOpenAIVideo = (providerId: string) => { const { addVideo } = useVideos(providerId) const addOpenAIVideo = useCallback( - (video: OpenAI.Videos.Video) => { + (video: OpenAI.Videos.Video, prompt: string) => { switch (video.status) { case 'queued': addVideo({ id: video.id, status: video.status, type: 'openai', - metadata: video + metadata: video, + prompt }) break case 'in_progress': @@ -23,7 +24,8 @@ export const useAddOpenAIVideo = (providerId: string) => { status: 'in_progress', type: 'openai', progress: video.progress, - metadata: video + metadata: video, + prompt }) break case 'completed': @@ -31,7 +33,9 @@ export const useAddOpenAIVideo = (providerId: string) => { id: video.id, status: 'completed', type: 'openai', - metadata: video + metadata: video, + prompt, + thumbnail: null }) break case 'failed': @@ -40,7 +44,8 @@ export const useAddOpenAIVideo = (providerId: string) => { status: 'failed', type: 'openai', error: video.error, - metadata: video + metadata: video, + prompt }) break } diff --git a/src/renderer/src/hooks/video/useOpenAIVideo.ts b/src/renderer/src/hooks/video/useOpenAIVideo.ts index f67e3fd87e..17579b78cf 100644 --- a/src/renderer/src/hooks/video/useOpenAIVideo.ts +++ b/src/renderer/src/hooks/video/useOpenAIVideo.ts @@ -1,11 +1,16 @@ import { retrieveVideo } from '@renderer/services/ApiService' import { SystemProviderIds } from '@renderer/types' -import useSWR, { useSWRConfig } from 'swr' +import { useEffect } from 'react' +import useSWR, { SWRConfiguration, useSWRConfig } from 'swr' import { useProvider } from '../useProvider' +import { useAddOpenAIVideo } from './useAddOpenAIVideo' +import { useVideo } from './useVideo' +import { useVideos } from './useVideos' export const useOpenAIVideo = (id: string) => { - const { provider: openai } = useProvider(SystemProviderIds.openai) + const providerId = SystemProviderIds.openai + const { provider: openai } = useProvider(providerId) const fetcher = async () => { return retrieveVideo({ type: 'openai', @@ -13,12 +18,37 @@ export const useOpenAIVideo = (id: string) => { provider: openai }) } - const { data, isLoading, error } = useSWR(`video/openai/${id}`, fetcher, { - revalidateOnFocus: false, - revalidateOnMount: true - }) + const video = useVideo(providerId, id) + const { updateVideo } = useVideos(providerId) + const addOpenAIVideo = useAddOpenAIVideo(providerId) + let options: SWRConfiguration = {} + switch (video?.status) { + case 'in_progress': + options = { + refreshInterval: 3000 + } + break + default: + options = { + revalidateOnFocus: false, + revalidateOnMount: true + } + } + const { data, isLoading, error } = useSWR(`video/openai/${id}`, fetcher, options) const { mutate } = useSWRConfig() const revalidate = () => mutate(`video/openai/${id}`) + + useEffect(() => { + // update progress + if (data && data.video.status === 'in_progress' && data.video.progress) { + if (video) { + updateVideo({ id: video.id, progress: data.video.progress }) + } else { + addOpenAIVideo(data.video, 'Prompt lost') + } + } + }, [addOpenAIVideo, data, updateVideo, video]) + return { video: data, isLoading, diff --git a/src/renderer/src/hooks/video/useVideo.ts b/src/renderer/src/hooks/video/useVideo.ts new file mode 100644 index 0000000000..378746158f --- /dev/null +++ b/src/renderer/src/hooks/video/useVideo.ts @@ -0,0 +1,7 @@ +import { useVideos } from './useVideos' + +export const useVideo = (providerId: string, id: string) => { + const { videos } = useVideos(providerId) + const video = videos.find((v) => v.id === id) + return video +} diff --git a/src/renderer/src/pages/video/VideoList.tsx b/src/renderer/src/pages/video/VideoList.tsx index e6c7572dbe..f3cc63d137 100644 --- a/src/renderer/src/pages/video/VideoList.tsx +++ b/src/renderer/src/pages/video/VideoList.tsx @@ -89,7 +89,7 @@ const VideoListItem = ({ video, isActive, onClick }: { video: Video; isActive: b {/* Thumbnail placeholder */}
{showThumbnail ? ( - Video thumbnail + Video thumbnail ) : (
🎬
diff --git a/src/renderer/src/pages/video/VideoPanel.tsx b/src/renderer/src/pages/video/VideoPanel.tsx index 34c5159cb1..a10d3a9b97 100644 --- a/src/renderer/src/pages/video/VideoPanel.tsx +++ b/src/renderer/src/pages/video/VideoPanel.tsx @@ -53,7 +53,7 @@ export const VideoPanel = ({ provider, video, params, updateParams }: VideoPanel const video = result.video switch (result.type) { case 'openai': - addOpenAIVideo(video) + addOpenAIVideo(video, params.params.prompt) break default: logger.error(`Invalid video type ${result.type}.`) diff --git a/src/renderer/src/types/video.ts b/src/renderer/src/types/video.ts index f4482c4284..905647f288 100644 --- a/src/renderer/src/types/video.ts +++ b/src/renderer/src/types/video.ts @@ -30,7 +30,7 @@ export interface VideoInProgress extends VideoBase { export interface VideoCompleted extends VideoBase { status: 'completed' /** When generation completed, firstly try to retrieve thumbnail. */ - thumbnail: string + thumbnail: string | null } export interface VideoDownloading extends VideoBase {