mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-04 11:49:02 +08:00
feat(video): add VideoPlayer component with file loading
Implement VideoPlayer component that fetches video file path using FileManager and displays it with loading skeleton. This improves video loading reliability by handling file existence checks and error states.
This commit is contained in:
parent
8518734e48
commit
c20394f460
@ -6,14 +6,17 @@ import {
|
|||||||
ModalContent,
|
ModalContent,
|
||||||
ModalFooter,
|
ModalFooter,
|
||||||
Progress,
|
Progress,
|
||||||
|
Skeleton,
|
||||||
Spinner,
|
Spinner,
|
||||||
useDisclosure
|
useDisclosure
|
||||||
} from '@heroui/react'
|
} from '@heroui/react'
|
||||||
import { Video, VideoFailed } from '@renderer/types/video'
|
import FileManager from '@renderer/services/FileManager'
|
||||||
|
import { Video, VideoDownloaded, VideoFailed } from '@renderer/types/video'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { CheckCircleIcon, CircleXIcon, Clock9Icon } from 'lucide-react'
|
import { CheckCircleIcon, CircleXIcon, Clock9Icon } from 'lucide-react'
|
||||||
import { useMemo, useState } from 'react'
|
import { useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import useSWRImmutable from 'swr/immutable'
|
||||||
|
|
||||||
export type VideoViewerProps =
|
export type VideoViewerProps =
|
||||||
| {
|
| {
|
||||||
@ -41,13 +44,7 @@ export const VideoViewer = ({ video, onDownload, onRegenerate }: VideoViewerProp
|
|||||||
)}
|
)}
|
||||||
{video && video.status === 'downloading' && <DownloadingVideo progress={video.progress} />}
|
{video && video.status === 'downloading' && <DownloadingVideo progress={video.progress} />}
|
||||||
{video && video.status === 'downloaded' && loadSuccess !== false && (
|
{video && video.status === 'downloaded' && loadSuccess !== false && (
|
||||||
<video
|
<VideoPlayer video={video} setLoadSuccess={setLoadSuccess} />
|
||||||
controls
|
|
||||||
className="h-full w-full"
|
|
||||||
onLoadedData={() => setLoadSuccess(true)}
|
|
||||||
onError={() => setLoadSuccess(false)}>
|
|
||||||
<source src="video.mp4" type="video/mp4" />
|
|
||||||
</video>
|
|
||||||
)}
|
)}
|
||||||
{video && video.status === 'failed' && <FailedVideo error={video.error} />}
|
{video && video.status === 'failed' && <FailedVideo error={video.error} />}
|
||||||
{video && video.status === 'downloaded' && loadSuccess === false && (
|
{video && video.status === 'downloaded' && loadSuccess === false && (
|
||||||
@ -176,3 +173,36 @@ const LoadFailedVideo = ({ onRedownload }: { onRedownload: () => void }) => {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const VideoPlayer = ({
|
||||||
|
video,
|
||||||
|
setLoadSuccess
|
||||||
|
}: {
|
||||||
|
video: VideoDownloaded
|
||||||
|
setLoadSuccess: (value: boolean) => void
|
||||||
|
}) => {
|
||||||
|
const fetcher = async () => {
|
||||||
|
const file = await FileManager.getFile(video.fileId)
|
||||||
|
if (!file) {
|
||||||
|
throw new Error(`Video file ${video.fileId} not exist.`)
|
||||||
|
}
|
||||||
|
return FileManager.getFilePath(file)
|
||||||
|
}
|
||||||
|
const { data: src, isLoading, error } = useSWRImmutable(`video/file/${video.id}`, fetcher)
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
setLoadSuccess(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Skeleton isLoaded={!isLoading}>
|
||||||
|
<video
|
||||||
|
controls
|
||||||
|
className="h-full w-full"
|
||||||
|
onLoadedData={() => setLoadSuccess(true)}
|
||||||
|
onError={() => setLoadSuccess(false)}>
|
||||||
|
<source src={src} type="video/mp4" />
|
||||||
|
</video>
|
||||||
|
</Skeleton>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user