mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-11 16:39:15 +08:00
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
This commit is contained in:
parent
eba370210f
commit
c3c125f3a3
@ -7,14 +7,15 @@ export const useAddOpenAIVideo = (providerId: string) => {
|
|||||||
const { addVideo } = useVideos(providerId)
|
const { addVideo } = useVideos(providerId)
|
||||||
|
|
||||||
const addOpenAIVideo = useCallback(
|
const addOpenAIVideo = useCallback(
|
||||||
(video: OpenAI.Videos.Video) => {
|
(video: OpenAI.Videos.Video, prompt: string) => {
|
||||||
switch (video.status) {
|
switch (video.status) {
|
||||||
case 'queued':
|
case 'queued':
|
||||||
addVideo({
|
addVideo({
|
||||||
id: video.id,
|
id: video.id,
|
||||||
status: video.status,
|
status: video.status,
|
||||||
type: 'openai',
|
type: 'openai',
|
||||||
metadata: video
|
metadata: video,
|
||||||
|
prompt
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
case 'in_progress':
|
case 'in_progress':
|
||||||
@ -23,7 +24,8 @@ export const useAddOpenAIVideo = (providerId: string) => {
|
|||||||
status: 'in_progress',
|
status: 'in_progress',
|
||||||
type: 'openai',
|
type: 'openai',
|
||||||
progress: video.progress,
|
progress: video.progress,
|
||||||
metadata: video
|
metadata: video,
|
||||||
|
prompt
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
case 'completed':
|
case 'completed':
|
||||||
@ -31,7 +33,9 @@ export const useAddOpenAIVideo = (providerId: string) => {
|
|||||||
id: video.id,
|
id: video.id,
|
||||||
status: 'completed',
|
status: 'completed',
|
||||||
type: 'openai',
|
type: 'openai',
|
||||||
metadata: video
|
metadata: video,
|
||||||
|
prompt,
|
||||||
|
thumbnail: null
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
case 'failed':
|
case 'failed':
|
||||||
@ -40,7 +44,8 @@ export const useAddOpenAIVideo = (providerId: string) => {
|
|||||||
status: 'failed',
|
status: 'failed',
|
||||||
type: 'openai',
|
type: 'openai',
|
||||||
error: video.error,
|
error: video.error,
|
||||||
metadata: video
|
metadata: video,
|
||||||
|
prompt
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,16 @@
|
|||||||
import { retrieveVideo } from '@renderer/services/ApiService'
|
import { retrieveVideo } from '@renderer/services/ApiService'
|
||||||
import { SystemProviderIds } from '@renderer/types'
|
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 { useProvider } from '../useProvider'
|
||||||
|
import { useAddOpenAIVideo } from './useAddOpenAIVideo'
|
||||||
|
import { useVideo } from './useVideo'
|
||||||
|
import { useVideos } from './useVideos'
|
||||||
|
|
||||||
export const useOpenAIVideo = (id: string) => {
|
export const useOpenAIVideo = (id: string) => {
|
||||||
const { provider: openai } = useProvider(SystemProviderIds.openai)
|
const providerId = SystemProviderIds.openai
|
||||||
|
const { provider: openai } = useProvider(providerId)
|
||||||
const fetcher = async () => {
|
const fetcher = async () => {
|
||||||
return retrieveVideo({
|
return retrieveVideo({
|
||||||
type: 'openai',
|
type: 'openai',
|
||||||
@ -13,12 +18,37 @@ export const useOpenAIVideo = (id: string) => {
|
|||||||
provider: openai
|
provider: openai
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const { data, isLoading, error } = useSWR(`video/openai/${id}`, fetcher, {
|
const video = useVideo(providerId, id)
|
||||||
revalidateOnFocus: false,
|
const { updateVideo } = useVideos(providerId)
|
||||||
revalidateOnMount: true
|
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 { mutate } = useSWRConfig()
|
||||||
const revalidate = () => mutate(`video/openai/${id}`)
|
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 {
|
return {
|
||||||
video: data,
|
video: data,
|
||||||
isLoading,
|
isLoading,
|
||||||
|
|||||||
7
src/renderer/src/hooks/video/useVideo.ts
Normal file
7
src/renderer/src/hooks/video/useVideo.ts
Normal file
@ -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
|
||||||
|
}
|
||||||
@ -89,7 +89,7 @@ const VideoListItem = ({ video, isActive, onClick }: { video: Video; isActive: b
|
|||||||
{/* Thumbnail placeholder */}
|
{/* Thumbnail placeholder */}
|
||||||
<div className="absolute inset-0 flex items-center justify-center bg-gradient-to-br from-default-100 to-default-200">
|
<div className="absolute inset-0 flex items-center justify-center bg-gradient-to-br from-default-100 to-default-200">
|
||||||
{showThumbnail ? (
|
{showThumbnail ? (
|
||||||
<img src={video.thumbnail} alt="Video thumbnail" className="h-full w-full object-cover" />
|
<img src={video.thumbnail ?? ''} alt="Video thumbnail" className="h-full w-full object-cover" />
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col items-center gap-2 text-default-400">
|
<div className="flex flex-col items-center gap-2 text-default-400">
|
||||||
<div className="text-2xl">🎬</div>
|
<div className="text-2xl">🎬</div>
|
||||||
|
|||||||
@ -53,7 +53,7 @@ export const VideoPanel = ({ provider, video, params, updateParams }: VideoPanel
|
|||||||
const video = result.video
|
const video = result.video
|
||||||
switch (result.type) {
|
switch (result.type) {
|
||||||
case 'openai':
|
case 'openai':
|
||||||
addOpenAIVideo(video)
|
addOpenAIVideo(video, params.params.prompt)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
logger.error(`Invalid video type ${result.type}.`)
|
logger.error(`Invalid video type ${result.type}.`)
|
||||||
|
|||||||
@ -30,7 +30,7 @@ export interface VideoInProgress extends VideoBase {
|
|||||||
export interface VideoCompleted extends VideoBase {
|
export interface VideoCompleted extends VideoBase {
|
||||||
status: 'completed'
|
status: 'completed'
|
||||||
/** When generation completed, firstly try to retrieve thumbnail. */
|
/** When generation completed, firstly try to retrieve thumbnail. */
|
||||||
thumbnail: string
|
thumbnail: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VideoDownloading extends VideoBase {
|
export interface VideoDownloading extends VideoBase {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user