feat: 实现文件夹递归扫描功能

- 启用文件夹选择时的递归扫描,遍历所有子文件夹
- 添加递归扫描选项复选框,用户可以选择是否扫描子文件夹
- 动态更新按钮文本显示当前扫描模式
- 改进扫描状态反馈,显示扫描进度和结果统计
- 添加状态显示区域,实时显示扫描状态和结果
- 优化用户体验,提供清晰的视觉反馈

现在用户可以:
- 选择文件夹时自动扫描所有子文件夹中的视频文件
- 通过复选框控制是否递归扫描
- 查看扫描进度和结果统计信息
This commit is contained in:
imeepos 2025-08-11 13:26:08 +08:00
parent fb173cb70c
commit d77a3b244c
1 changed files with 52 additions and 4 deletions

View File

@ -55,6 +55,7 @@ const FrameExtractorTool: React.FC = () => {
const [ffmpegAvailable, setFfmpegAvailable] = useState<boolean | null>(null);
const [selectedVideoForPreview, setSelectedVideoForPreview] = useState<VideoFileInfo | null>(null);
const [currentPlayTime, setCurrentPlayTime] = useState<number>(0);
const [recursiveScan, setRecursiveScan] = useState<boolean>(true);
// 检查 FFmpeg 可用性
useEffect(() => {
@ -107,7 +108,7 @@ const FrameExtractorTool: React.FC = () => {
if (selected && typeof selected === 'string') {
setSelectedFolder(selected);
await scanVideoFiles(selected, false);
await scanVideoFiles(selected, recursiveScan);
}
} catch (error) {
console.error('选择文件夹失败:', error);
@ -117,6 +118,8 @@ const FrameExtractorTool: React.FC = () => {
// 扫描视频文件
const scanVideoFiles = useCallback(async (folderPath: string, recursive: boolean) => {
setIsScanning(true);
setCurrentFile(`正在扫描文件夹: ${folderPath}${recursive ? ' (递归)' : ''}`);
try {
const files = await invoke<VideoFileInfo[]>('scan_video_files', {
request: {
@ -125,10 +128,29 @@ const FrameExtractorTool: React.FC = () => {
supported_formats: SUPPORTED_VIDEO_FORMATS,
}
});
setSelectedVideos(files.filter(f => f.is_valid));
const validFiles = files.filter(f => f.is_valid);
setSelectedVideos(validFiles);
// 显示扫描结果
const totalFiles = files.length;
const validCount = validFiles.length;
const invalidCount = totalFiles - validCount;
console.log(`扫描完成: 找到 ${totalFiles} 个文件,其中 ${validCount} 个有效,${invalidCount} 个无效`);
setCurrentFile(`扫描完成: 找到 ${validCount} 个有效视频文件`);
// 2秒后清除状态信息
setTimeout(() => {
setCurrentFile('');
}, 2000);
} catch (error) {
console.error('扫描视频文件失败:', error);
setCurrentFile('扫描失败,请重试');
setTimeout(() => {
setCurrentFile('');
}, 3000);
} finally {
setIsScanning(false);
}
@ -266,9 +288,10 @@ const FrameExtractorTool: React.FC = () => {
onClick={handleSelectFolder}
disabled={isScanning || isExtracting}
className="flex items-center gap-2 px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
title={recursiveScan ? "选择文件夹并递归扫描所有子文件夹中的视频文件" : "选择文件夹并扫描其中的视频文件"}
>
<FolderOpen className="w-4 h-4" />
{recursiveScan ? ' (递归)' : ''}
</button>
<button
@ -279,6 +302,19 @@ const FrameExtractorTool: React.FC = () => {
<Trash2 className="w-4 h-4" />
</button>
{/* 递归扫描选项 */}
<div className="flex items-center gap-2 ml-4 pl-4 border-l border-gray-200">
<label className="flex items-center gap-2 text-sm text-gray-700 cursor-pointer">
<input
type="checkbox"
checked={recursiveScan}
onChange={(e) => setRecursiveScan(e.target.checked)}
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
/>
<span></span>
</label>
</div>
</div>
<div className="flex items-center gap-3">
@ -306,6 +342,18 @@ const FrameExtractorTool: React.FC = () => {
</div>
</div>
{/* 状态显示区域 */}
{(isScanning || currentFile) && (
<div className="bg-blue-50 border border-blue-200 rounded-lg p-3">
<div className="flex items-center gap-2">
{isScanning && <RefreshCw className="w-4 h-4 text-blue-600 animate-spin" />}
<span className="text-sm text-blue-800">
{currentFile || '正在处理...'}
</span>
</div>
</div>
)}
{/* 视图切换 */}
<div className="flex items-center gap-2 bg-white rounded-lg p-1 shadow-sm border w-fit">
<button