feat: 添加视频预览播放器和时间联动功能

- 新增 VideoPlayer 组件,支持完整的视频播放控制
- 实现播放/暂停、快进/快退、音量控制、全屏等功能
- 添加键盘快捷键支持 (空格、方向键、M、F、R等)
- 实现视频播放时间与自定义时间输入的双向联动
- 在关键帧提取工具中集成视频预览功能
- 支持单个视频选择时显示预览播放器
- 添加'使用当前播放时间'按钮,方便设置提取时间点
- 优化用户界面,提升视频处理工作流体验
This commit is contained in:
imeepos
2025-08-11 13:19:16 +08:00
parent 332648d29e
commit 28c778e3ae
5 changed files with 562 additions and 18 deletions

View File

@@ -35,6 +35,7 @@ import { VideoFileList } from '../../components/frame-extractor/VideoFileList';
import { ExtractionProgress } from '../../components/frame-extractor/ExtractionProgress';
import { FramePreview } from '../../components/frame-extractor/FramePreview';
import { ExtractionResults } from '../../components/frame-extractor/ExtractionResults';
import { VideoPlayer } from '../../components/frame-extractor/VideoPlayer';
/**
* 视频关键帧提取工具主组件
@@ -52,6 +53,8 @@ const FrameExtractorTool: React.FC = () => {
const [viewMode, setViewMode] = useState<'config' | 'progress' | 'results'>('config');
const [selectedFolder, setSelectedFolder] = useState<string>('');
const [ffmpegAvailable, setFfmpegAvailable] = useState<boolean | null>(null);
const [selectedVideoForPreview, setSelectedVideoForPreview] = useState<VideoFileInfo | null>(null);
const [currentPlayTime, setCurrentPlayTime] = useState<number>(0);
// 检查 FFmpeg 可用性
useEffect(() => {
@@ -347,23 +350,58 @@ const FrameExtractorTool: React.FC = () => {
<VideoFileList
videos={selectedVideos}
isScanning={isScanning}
onRemoveVideo={(index) => {
onRemoveVideo={(index: number) => {
const newVideos = [...selectedVideos];
newVideos.splice(index, 1);
setSelectedVideos(newVideos);
// 如果删除的是当前预览的视频,清除预览
if (selectedVideoForPreview && selectedVideos[index]?.path === selectedVideoForPreview.path) {
setSelectedVideoForPreview(null);
}
}}
onPreviewVideo={(video: VideoFileInfo) => {
setSelectedVideoForPreview(video);
setViewMode('config'); // 切换到配置视图以显示预览
}}
/>
</div>
{/* 右侧:主要内容 */}
<div className="lg:col-span-2">
<div className="lg:col-span-2 space-y-6">
{viewMode === 'config' && (
<FrameExtractionConfigPanel
config={config}
onConfigChange={setConfig}
onApplyPreset={handleApplyPreset}
presets={FRAME_EXTRACTION_PRESETS}
/>
<>
{/* 视频预览播放器 */}
{selectedVideoForPreview && (
<div className="bg-white rounded-lg shadow-sm border p-4">
<div className="flex items-center justify-between mb-4">
<h3 className="font-semibold text-gray-900"></h3>
<button
onClick={() => setSelectedVideoForPreview(null)}
className="text-gray-400 hover:text-gray-600"
>
<Eye className="w-5 h-5" />
</button>
</div>
<VideoPlayer
video={selectedVideoForPreview}
currentTime={currentPlayTime}
onTimeUpdate={setCurrentPlayTime}
className="w-full h-64"
/>
</div>
)}
{/* 配置面板 */}
<FrameExtractionConfigPanel
config={config}
onConfigChange={setConfig}
onApplyPreset={handleApplyPreset}
presets={FRAME_EXTRACTION_PRESETS}
currentVideo={selectedVideoForPreview || undefined}
currentTime={currentPlayTime}
onTimeChange={setCurrentPlayTime}
/>
</>
)}
{viewMode === 'progress' && (
@@ -378,7 +416,7 @@ const FrameExtractorTool: React.FC = () => {
{viewMode === 'results' && (
<ExtractionResults
results={extractionResults}
onOpenResult={(result) => {
onOpenResult={(result: FrameExtractionResult) => {
// 打开结果文件所在目录
invoke('open_file_directory', { path: result.output_path });
}}