feat: 实现文件夹递归扫描功能
- 启用文件夹选择时的递归扫描,遍历所有子文件夹 - 添加递归扫描选项复选框,用户可以选择是否扫描子文件夹 - 动态更新按钮文本显示当前扫描模式 - 改进扫描状态反馈,显示扫描进度和结果统计 - 添加状态显示区域,实时显示扫描状态和结果 - 优化用户体验,提供清晰的视觉反馈 现在用户可以: - 选择文件夹时自动扫描所有子文件夹中的视频文件 - 通过复选框控制是否递归扫描 - 查看扫描进度和结果统计信息
This commit is contained in:
parent
fb173cb70c
commit
d77a3b244c
|
|
@ -55,6 +55,7 @@ const FrameExtractorTool: React.FC = () => {
|
||||||
const [ffmpegAvailable, setFfmpegAvailable] = useState<boolean | null>(null);
|
const [ffmpegAvailable, setFfmpegAvailable] = useState<boolean | null>(null);
|
||||||
const [selectedVideoForPreview, setSelectedVideoForPreview] = useState<VideoFileInfo | null>(null);
|
const [selectedVideoForPreview, setSelectedVideoForPreview] = useState<VideoFileInfo | null>(null);
|
||||||
const [currentPlayTime, setCurrentPlayTime] = useState<number>(0);
|
const [currentPlayTime, setCurrentPlayTime] = useState<number>(0);
|
||||||
|
const [recursiveScan, setRecursiveScan] = useState<boolean>(true);
|
||||||
|
|
||||||
// 检查 FFmpeg 可用性
|
// 检查 FFmpeg 可用性
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -107,7 +108,7 @@ const FrameExtractorTool: React.FC = () => {
|
||||||
|
|
||||||
if (selected && typeof selected === 'string') {
|
if (selected && typeof selected === 'string') {
|
||||||
setSelectedFolder(selected);
|
setSelectedFolder(selected);
|
||||||
await scanVideoFiles(selected, false);
|
await scanVideoFiles(selected, recursiveScan);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('选择文件夹失败:', error);
|
console.error('选择文件夹失败:', error);
|
||||||
|
|
@ -117,6 +118,8 @@ const FrameExtractorTool: React.FC = () => {
|
||||||
// 扫描视频文件
|
// 扫描视频文件
|
||||||
const scanVideoFiles = useCallback(async (folderPath: string, recursive: boolean) => {
|
const scanVideoFiles = useCallback(async (folderPath: string, recursive: boolean) => {
|
||||||
setIsScanning(true);
|
setIsScanning(true);
|
||||||
|
setCurrentFile(`正在扫描文件夹: ${folderPath}${recursive ? ' (递归)' : ''}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const files = await invoke<VideoFileInfo[]>('scan_video_files', {
|
const files = await invoke<VideoFileInfo[]>('scan_video_files', {
|
||||||
request: {
|
request: {
|
||||||
|
|
@ -125,10 +128,29 @@ const FrameExtractorTool: React.FC = () => {
|
||||||
supported_formats: SUPPORTED_VIDEO_FORMATS,
|
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) {
|
} catch (error) {
|
||||||
console.error('扫描视频文件失败:', error);
|
console.error('扫描视频文件失败:', error);
|
||||||
|
setCurrentFile('扫描失败,请重试');
|
||||||
|
setTimeout(() => {
|
||||||
|
setCurrentFile('');
|
||||||
|
}, 3000);
|
||||||
} finally {
|
} finally {
|
||||||
setIsScanning(false);
|
setIsScanning(false);
|
||||||
}
|
}
|
||||||
|
|
@ -266,9 +288,10 @@ const FrameExtractorTool: React.FC = () => {
|
||||||
onClick={handleSelectFolder}
|
onClick={handleSelectFolder}
|
||||||
disabled={isScanning || isExtracting}
|
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"
|
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" />
|
<FolderOpen className="w-4 h-4" />
|
||||||
选择文件夹
|
选择文件夹{recursiveScan ? ' (递归)' : ''}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|
@ -279,6 +302,19 @@ const FrameExtractorTool: React.FC = () => {
|
||||||
<Trash2 className="w-4 h-4" />
|
<Trash2 className="w-4 h-4" />
|
||||||
清除选择
|
清除选择
|
||||||
</button>
|
</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>
|
||||||
|
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
|
|
@ -306,6 +342,18 @@ const FrameExtractorTool: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<div className="flex items-center gap-2 bg-white rounded-lg p-1 shadow-sm border w-fit">
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue