feat: 添加视频预览播放器和时间联动功能
- 新增 VideoPlayer 组件,支持完整的视频播放控制 - 实现播放/暂停、快进/快退、音量控制、全屏等功能 - 添加键盘快捷键支持 (空格、方向键、M、F、R等) - 实现视频播放时间与自定义时间输入的双向联动 - 在关键帧提取工具中集成视频预览功能 - 支持单个视频选择时显示预览播放器 - 添加'使用当前播放时间'按钮,方便设置提取时间点 - 优化用户界面,提升视频处理工作流体验
This commit is contained in:
@@ -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 });
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user