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:
icarus 2025-10-13 15:23:48 +08:00
parent 8518734e48
commit c20394f460

View File

@ -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>
)
}