use serde::{Deserialize, Serialize}; use chrono::{DateTime, Utc}; /// 素材类型枚举 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum MaterialType { Video, Audio, Image, Document, Other, } impl MaterialType { pub fn from_extension(ext: &str) -> Self { match ext.to_lowercase().as_str() { "mp4" | "avi" | "mov" | "mkv" | "wmv" | "flv" | "webm" | "m4v" => MaterialType::Video, "mp3" | "wav" | "flac" | "aac" | "ogg" | "wma" | "m4a" => MaterialType::Audio, "jpg" | "jpeg" | "png" | "gif" | "bmp" | "tiff" | "webp" | "svg" => MaterialType::Image, "pdf" | "doc" | "docx" | "txt" | "md" | "rtf" => MaterialType::Document, _ => MaterialType::Other, } } } /// 素材处理状态枚举 #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] pub enum ProcessingStatus { Pending, // 待处理 Processing, // 处理中 Completed, // 处理完成 Failed, // 处理失败 Skipped, // 跳过(重复文件) } /// 视频元数据结构 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct VideoMetadata { pub duration: f64, // 时长(秒) pub width: u32, // 宽度 pub height: u32, // 高度 pub fps: f64, // 帧率 pub bitrate: u64, // 比特率 pub codec: String, // 编码格式 pub format: String, // 容器格式 pub has_audio: bool, // 是否包含音频 pub audio_codec: Option, // 音频编码格式 pub audio_bitrate: Option, // 音频比特率 pub audio_sample_rate: Option, // 音频采样率 } /// 音频元数据结构 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AudioMetadata { pub duration: f64, // 时长(秒) pub bitrate: u64, // 比特率 pub codec: String, // 编码格式 pub sample_rate: u32, // 采样率 pub channels: u32, // 声道数 pub format: String, // 音频格式 } /// 图片元数据结构 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ImageMetadata { pub width: u32, // 宽度 pub height: u32, // 高度 pub format: String, // 图片格式 pub color_space: Option, // 色彩空间 pub has_alpha: bool, // 是否有透明通道 pub dpi: Option, // DPI } /// 素材元数据联合体 #[derive(Debug, Clone, Serialize, Deserialize)] pub enum MaterialMetadata { Video(VideoMetadata), Audio(AudioMetadata), Image(ImageMetadata), None, } /// 场景检测结果 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SceneDetection { pub scenes: Vec, pub total_scenes: u32, pub detection_method: String, pub threshold: f64, } /// 场景片段 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SceneSegment { pub start_time: f64, // 开始时间(秒) pub end_time: f64, // 结束时间(秒) pub duration: f64, // 片段时长(秒) pub scene_id: u32, // 场景ID pub confidence: f64, // 检测置信度 } /// 素材实体模型 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Material { pub id: String, pub project_id: String, pub name: String, pub original_path: String, pub file_size: u64, pub md5_hash: String, pub material_type: MaterialType, pub processing_status: ProcessingStatus, pub metadata: MaterialMetadata, pub scene_detection: Option, pub segments: Vec, pub created_at: DateTime, pub updated_at: DateTime, pub processed_at: Option>, pub error_message: Option, } /// 素材片段(切分后的片段) #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MaterialSegment { pub id: String, pub material_id: String, pub segment_index: u32, pub start_time: f64, pub end_time: f64, pub duration: f64, pub file_path: String, pub file_size: u64, pub created_at: DateTime, } /// 创建素材请求 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CreateMaterialRequest { pub project_id: String, pub file_paths: Vec, pub auto_process: bool, pub max_segment_duration: Option, // 最大片段时长(秒) } /// 素材处理配置 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MaterialProcessingConfig { pub max_segment_duration: f64, // 最大片段时长(秒) pub scene_detection_threshold: f64, // 场景检测阈值 pub enable_scene_detection: bool, // 是否启用场景检测 pub video_quality: String, // 视频质量设置 pub audio_quality: String, // 音频质量设置 pub output_format: String, // 输出格式 pub auto_process: Option, // 是否自动处理 } impl Default for MaterialProcessingConfig { fn default() -> Self { Self { max_segment_duration: 300.0, // 5分钟 scene_detection_threshold: 0.3, enable_scene_detection: true, video_quality: "medium".to_string(), audio_quality: "medium".to_string(), output_format: "mp4".to_string(), auto_process: Some(true), } } } /// 素材导入结果 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MaterialImportResult { pub total_files: u32, pub processed_files: u32, pub skipped_files: u32, pub failed_files: u32, pub created_materials: Vec, pub errors: Vec, pub processing_time: f64, // 处理时间(秒) } /// 素材统计信息 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MaterialStats { pub total_materials: u32, pub video_count: u32, pub audio_count: u32, pub image_count: u32, pub other_count: u32, pub total_size: u64, pub total_duration: f64, // 总时长(秒) pub processing_status_counts: std::collections::HashMap, } impl Material { /// 创建新的素材实例 pub fn new( project_id: String, name: String, original_path: String, file_size: u64, md5_hash: String, material_type: MaterialType, ) -> Self { let now = Utc::now(); Self { id: uuid::Uuid::new_v4().to_string(), project_id, name, original_path, file_size, md5_hash, material_type, processing_status: ProcessingStatus::Pending, metadata: MaterialMetadata::None, scene_detection: None, segments: Vec::new(), created_at: now, updated_at: now, processed_at: None, error_message: None, } } /// 更新处理状态 pub fn update_status(&mut self, status: ProcessingStatus, error_message: Option) { self.processing_status = status; self.error_message = error_message; self.updated_at = Utc::now(); if matches!(status, ProcessingStatus::Completed | ProcessingStatus::Failed) { self.processed_at = Some(Utc::now()); } } /// 设置元数据 pub fn set_metadata(&mut self, metadata: MaterialMetadata) { self.metadata = metadata; self.updated_at = Utc::now(); } /// 添加场景检测结果 pub fn set_scene_detection(&mut self, scene_detection: SceneDetection) { self.scene_detection = Some(scene_detection); self.updated_at = Utc::now(); } /// 添加片段 pub fn add_segment(&mut self, segment: MaterialSegment) { self.segments.push(segment); self.updated_at = Utc::now(); } /// 获取总时长 pub fn get_duration(&self) -> Option { match &self.metadata { MaterialMetadata::Video(video) => Some(video.duration), MaterialMetadata::Audio(audio) => Some(audio.duration), _ => None, } } /// 检查是否需要切分 pub fn needs_segmentation(&self, max_duration: f64) -> bool { if let Some(duration) = self.get_duration() { duration > max_duration } else { false } } } impl MaterialSegment { /// 创建新的素材片段 pub fn new( material_id: String, segment_index: u32, start_time: f64, end_time: f64, file_path: String, file_size: u64, ) -> Self { Self { id: uuid::Uuid::new_v4().to_string(), material_id, segment_index, start_time, end_time, duration: end_time - start_time, file_path, file_size, created_at: Utc::now(), } } }