diff --git a/apps/desktop/src/components/FFmpegDebugPanel.tsx b/apps/desktop/src/components/FFmpegDebugPanel.tsx index 9411b4e..8fa40ef 100644 --- a/apps/desktop/src/components/FFmpegDebugPanel.tsx +++ b/apps/desktop/src/components/FFmpegDebugPanel.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import { Play, FileText, AlertCircle, CheckCircle } from 'lucide-react'; +import { Play, FileText, AlertCircle } from 'lucide-react'; import { useMaterialStore } from '../store/materialStore'; /** @@ -7,10 +7,10 @@ import { useMaterialStore } from '../store/materialStore'; * 用于测试和调试FFmpeg功能 */ export const FFmpegDebugPanel: React.FC = () => { - const { - testSceneDetection, - getFFmpegStatus, - selectMaterialFiles + const { + // testSceneDetection, + // getFFmpegStatus, + selectMaterialFiles } = useMaterialStore(); const [testFilePath, setTestFilePath] = useState(''); @@ -22,8 +22,8 @@ export const FFmpegDebugPanel: React.FC = () => { const handleGetFFmpegStatus = async () => { setIsLoading(true); try { - const status = await getFFmpegStatus(); - setFFmpegStatus(status); + // const status = await getFFmpegStatus(); + setFFmpegStatus('FFmpeg状态检查功能暂时不可用'); } catch (error) { setFFmpegStatus(`获取状态失败: ${error}`); } finally { @@ -54,8 +54,8 @@ export const FFmpegDebugPanel: React.FC = () => { setTestResult('正在测试场景检测...'); try { - const result = await testSceneDetection(testFilePath); - setTestResult(result); + // const result = await testSceneDetection(testFilePath); + setTestResult('场景检测测试功能暂时不可用'); } catch (error) { setTestResult(`测试失败: ${error}`); } finally { diff --git a/apps/desktop/src/components/MaterialCard.tsx b/apps/desktop/src/components/MaterialCard.tsx index 1bd5ac1..c678cc6 100644 --- a/apps/desktop/src/components/MaterialCard.tsx +++ b/apps/desktop/src/components/MaterialCard.tsx @@ -1,5 +1,8 @@ import React, { useState } from 'react'; -import { FileVideo, FileAudio, FileImage, File, Clock, ExternalLink, ChevronDown, ChevronUp } from 'lucide-react'; +import { + FileVideo, FileAudio, FileImage, File, Clock, ExternalLink, ChevronDown, ChevronUp, + Monitor, Volume2, Palette, Calendar, Hash, Zap, HardDrive, Film, Eye +} from 'lucide-react'; import { Material, MaterialSegment } from '../types/material'; import { useMaterialStore } from '../store/materialStore'; @@ -7,6 +10,48 @@ interface MaterialCardProps { material: Material; } +// 格式化时间(秒转为 mm:ss 格式) +const formatTime = (seconds: number): string => { + const minutes = Math.floor(seconds / 60); + const remainingSeconds = Math.floor(seconds % 60); + return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`; +}; + +// 格式化文件大小 +const formatFileSize = (bytes: number): string => { + if (bytes === 0) return '0 B'; + const k = 1024; + const sizes = ['B', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; +}; + +// 格式化比特率 +const formatBitrate = (bitrate: number): string => { + if (bitrate >= 1000000) { + return `${(bitrate / 1000000).toFixed(1)} Mbps`; + } else if (bitrate >= 1000) { + return `${(bitrate / 1000).toFixed(0)} Kbps`; + } + return `${bitrate} bps`; +}; + +// 格式化分辨率 +const formatResolution = (width: number, height: number): string => { + return `${width}×${height}`; +}; + +// 格式化日期 +const formatDate = (dateString: string): string => { + return new Date(dateString).toLocaleDateString('zh-CN', { + year: 'numeric', + month: 'short', + day: 'numeric', + hour: '2-digit', + minute: '2-digit' + }); +}; + /** * 素材卡片组件 * 显示素材信息和切分片段 @@ -47,12 +92,7 @@ export const MaterialCard: React.FC = ({ material }) => { } }; - // 格式化时间 - const formatTime = (seconds: number) => { - const minutes = Math.floor(seconds / 60); - const remainingSeconds = Math.floor(seconds % 60); - return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`; - }; + // 加载切分片段 const loadSegments = async () => { @@ -126,11 +166,144 @@ export const MaterialCard: React.FC = ({ material }) => { {/* 素材详细信息 */} -
-

类型: {material.material_type}

-

大小: {(material.file_size / 1024 / 1024).toFixed(2)} MB

- {material.segments && material.segments.length > 0 && ( -

片段数: {material.segments.length}

+
+ {/* 基本信息 */} +
+
+ + {formatFileSize(material.file_size)} +
+
+ + {formatDate(material.created_at)} +
+
+ + {/* 元数据信息 */} + {material.metadata !== 'None' && ( +
+ {/* 视频元数据 */} + {material.metadata && typeof material.metadata === 'object' && 'Video' in material.metadata && ( +
+
+ + 视频信息 +
+
+
+ + {formatTime(material.metadata.Video.duration)} +
+
+ + {formatResolution(material.metadata.Video.width, material.metadata.Video.height)} +
+
+ + {formatBitrate(material.metadata.Video.bitrate)} +
+
+ + {material.metadata.Video.fps} fps +
+
+ + {material.metadata.Video.codec} +
+ {material.metadata.Video.has_audio && ( +
+ + {material.metadata.Video.audio_codec || 'Audio'} +
+ )} +
+
+ )} + + {/* 音频元数据 */} + {material.metadata && typeof material.metadata === 'object' && 'Audio' in material.metadata && ( +
+
+ + 音频信息 +
+
+
+ + {formatTime(material.metadata.Audio.duration)} +
+
+ + {formatBitrate(material.metadata.Audio.bitrate)} +
+
+ + {material.metadata.Audio.sample_rate} Hz +
+
+ + {material.metadata.Audio.codec} +
+
+
+ )} + + {/* 图片元数据 */} + {material.metadata && typeof material.metadata === 'object' && 'Image' in material.metadata && ( +
+
+ + 图片信息 +
+
+
+ + {formatResolution(material.metadata.Image.width, material.metadata.Image.height)} +
+
+ + {material.metadata.Image.format} +
+ {material.metadata.Image.dpi && ( +
+ + {material.metadata.Image.dpi} DPI +
+ )} +
+
+ )} +
+ )} + + {/* 处理统计信息 */} + {(material.scene_detection || material.segments.length > 0) && ( +
+
+ + 处理统计 +
+
+ {material.scene_detection && ( +
+ + {material.scene_detection.total_scenes} 个场景 +
+ )} + {material.segments.length > 0 && ( +
+ + {material.segments.length} 个片段 +
+ )} + {material.processed_at && ( +
+ + 已处理 +
+ )} +
+
)}
diff --git a/apps/desktop/src/components/MaterialImportDialog.tsx b/apps/desktop/src/components/MaterialImportDialog.tsx index 4fd4725..b9b8848 100644 --- a/apps/desktop/src/components/MaterialImportDialog.tsx +++ b/apps/desktop/src/components/MaterialImportDialog.tsx @@ -21,7 +21,7 @@ export const MaterialImportDialog: React.FC = ({ onImportComplete }) => { const { - isImporting, + // isImporting, importProgress, error, importMaterials, @@ -92,12 +92,12 @@ export const MaterialImportDialog: React.FC = ({ }; // 格式化文件大小 - const formatFileSize = (bytes: number) => { - const sizes = ['B', 'KB', 'MB', 'GB']; - if (bytes === 0) return '0 B'; - const i = Math.floor(Math.log(bytes) / Math.log(1024)); - return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i]; - }; + // const formatFileSize = (bytes: number) => { + // const sizes = ['B', 'KB', 'MB', 'GB']; + // if (bytes === 0) return '0 B'; + // const i = Math.floor(Math.log(bytes) / Math.log(1024)); + // return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i]; + // }; // 获取文件名 const getFileName = (path: string) => {