新功能: - 集成Google Gemini API进行视频智能分类 - 实现任务队列系统支持批量处理 - 添加实时进度显示和状态管理 - 自动文件整理到分类文件夹 架构改进: - 遵循Tauri开发规范的分层架构设计 - 完整的数据模型和仓库层实现 - 异步任务处理和错误处理机制 - 类型安全的前后端通信接口 用户界面: - MaterialCard组件添加AI分类按钮 - VideoClassificationProgress进度显示组件 - 优美的动画效果和响应式设计 - 符合前端开发规范的UI/UX优化 数据库扩展: - 新增video_classification_records表 - 新增video_classification_tasks表 - 完整的索引优化和外键约束 技术实现: - Rust后端服务层完整实现 - React/TypeScript前端状态管理 - Zustand状态存储和API封装 - 完善的错误处理和用户提示 文档: - 完整的功能文档和API说明 - 架构设计和使用流程说明 - 开发规范遵循情况说明 Closes #AI视频分类功能开发
231 lines
6.9 KiB
Rust
231 lines
6.9 KiB
Rust
use std::path::Path;
|
|
use anyhow::{Result, anyhow};
|
|
|
|
/// 文件系统操作工具
|
|
/// 遵循 Tauri 开发规范的文件系统安全操作
|
|
pub struct FileSystemService;
|
|
|
|
impl FileSystemService {
|
|
/// 验证路径是否存在且可访问
|
|
pub fn validate_path(path: &str) -> Result<bool> {
|
|
let path = Path::new(path);
|
|
Ok(path.exists() && path.is_dir())
|
|
}
|
|
|
|
/// 获取路径的绝对路径
|
|
pub fn get_absolute_path(path: &str) -> Result<String> {
|
|
let path = Path::new(path);
|
|
let absolute_path = path.canonicalize()?;
|
|
Ok(absolute_path.to_string_lossy().to_string())
|
|
}
|
|
|
|
/// 检查路径是否为有效的项目目录
|
|
pub fn is_valid_project_directory(path: &str) -> Result<bool> {
|
|
let path = Path::new(path);
|
|
|
|
// 检查路径是否存在且为目录
|
|
if !path.exists() || !path.is_dir() {
|
|
return Ok(false);
|
|
}
|
|
|
|
// 检查是否有读写权限
|
|
let metadata = path.metadata()?;
|
|
if metadata.permissions().readonly() {
|
|
return Ok(false);
|
|
}
|
|
|
|
Ok(true)
|
|
}
|
|
|
|
/// 获取目录名称
|
|
pub fn get_directory_name(path: &str) -> Result<String> {
|
|
let path = Path::new(path);
|
|
match path.file_name() {
|
|
Some(name) => Ok(name.to_string_lossy().to_string()),
|
|
None => Err(anyhow::anyhow!("Invalid directory path")),
|
|
}
|
|
}
|
|
|
|
/// 创建项目目录结构
|
|
pub fn create_project_structure(project_path: &str) -> Result<()> {
|
|
let base_path = Path::new(project_path);
|
|
|
|
// 创建基本目录结构
|
|
let directories = [
|
|
"assets",
|
|
"output",
|
|
"temp",
|
|
"config",
|
|
];
|
|
|
|
for dir in &directories {
|
|
let dir_path = base_path.join(dir);
|
|
if !dir_path.exists() {
|
|
std::fs::create_dir_all(&dir_path)?;
|
|
}
|
|
}
|
|
|
|
// 创建项目配置文件
|
|
let config_file = base_path.join("mixvideo.project.json");
|
|
if !config_file.exists() {
|
|
let default_config = serde_json::json!({
|
|
"version": "0.1.0",
|
|
"created_at": chrono::Utc::now().to_rfc3339(),
|
|
"settings": {
|
|
"auto_save": true,
|
|
"backup_enabled": true
|
|
}
|
|
});
|
|
std::fs::write(config_file, serde_json::to_string_pretty(&default_config)?)?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// 检查是否为现有的 MixVideo 项目
|
|
pub fn is_mixvideo_project(path: &str) -> bool {
|
|
let project_file = Path::new(path).join("mixvideo.project.json");
|
|
project_file.exists()
|
|
}
|
|
|
|
/// 创建目录(如果不存在)
|
|
pub fn create_directory_if_not_exists(path: &str) -> Result<()> {
|
|
let dir_path = Path::new(path);
|
|
if !dir_path.exists() {
|
|
std::fs::create_dir_all(dir_path)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// 移动文件
|
|
pub fn move_file(source: &str, destination: &str) -> Result<()> {
|
|
let source_path = Path::new(source);
|
|
let dest_path = Path::new(destination);
|
|
|
|
// 确保源文件存在
|
|
if !source_path.exists() {
|
|
return Err(anyhow!("源文件不存在: {}", source));
|
|
}
|
|
|
|
// 确保目标目录存在
|
|
if let Some(parent) = dest_path.parent() {
|
|
std::fs::create_dir_all(parent)?;
|
|
}
|
|
|
|
// 移动文件
|
|
std::fs::rename(source_path, dest_path)?;
|
|
Ok(())
|
|
}
|
|
|
|
/// 复制文件
|
|
pub fn copy_file(source: &str, destination: &str) -> Result<()> {
|
|
let source_path = Path::new(source);
|
|
let dest_path = Path::new(destination);
|
|
|
|
// 确保源文件存在
|
|
if !source_path.exists() {
|
|
return Err(anyhow!("源文件不存在: {}", source));
|
|
}
|
|
|
|
// 确保目标目录存在
|
|
if let Some(parent) = dest_path.parent() {
|
|
std::fs::create_dir_all(parent)?;
|
|
}
|
|
|
|
// 复制文件
|
|
std::fs::copy(source_path, dest_path)?;
|
|
Ok(())
|
|
}
|
|
|
|
/// 获取文件大小
|
|
pub fn get_file_size(path: &str) -> Result<u64> {
|
|
let metadata = std::fs::metadata(path)?;
|
|
Ok(metadata.len())
|
|
}
|
|
|
|
/// 检查文件是否存在
|
|
pub fn file_exists(path: &str) -> bool {
|
|
Path::new(path).exists()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use tempfile::TempDir;
|
|
|
|
#[test]
|
|
fn test_validate_path() {
|
|
// 测试存在的目录
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let path = temp_dir.path().to_str().unwrap();
|
|
assert!(FileSystemService::validate_path(path).unwrap());
|
|
|
|
// 测试不存在的路径
|
|
assert!(!FileSystemService::validate_path("/non/existent/path").unwrap());
|
|
}
|
|
|
|
#[test]
|
|
fn test_get_absolute_path() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let path = temp_dir.path().to_str().unwrap();
|
|
let absolute_path = FileSystemService::get_absolute_path(path).unwrap();
|
|
assert!(absolute_path.len() > path.len() || absolute_path == path);
|
|
}
|
|
|
|
#[test]
|
|
fn test_is_valid_project_directory() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let path = temp_dir.path().to_str().unwrap();
|
|
|
|
// 有效目录应该返回 true
|
|
assert!(FileSystemService::is_valid_project_directory(path).unwrap());
|
|
|
|
// 不存在的路径应该返回 false
|
|
assert!(!FileSystemService::is_valid_project_directory("/non/existent/path").unwrap());
|
|
}
|
|
|
|
#[test]
|
|
fn test_get_directory_name() {
|
|
assert_eq!(
|
|
FileSystemService::get_directory_name("/path/to/project").unwrap(),
|
|
"project"
|
|
);
|
|
|
|
assert_eq!(
|
|
FileSystemService::get_directory_name("C:\\Users\\test\\project").unwrap(),
|
|
"project"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_create_project_structure() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let project_path = temp_dir.path().to_str().unwrap();
|
|
|
|
FileSystemService::create_project_structure(project_path).unwrap();
|
|
|
|
// 检查目录是否创建
|
|
assert!(temp_dir.path().join("assets").exists());
|
|
assert!(temp_dir.path().join("output").exists());
|
|
assert!(temp_dir.path().join("temp").exists());
|
|
assert!(temp_dir.path().join("config").exists());
|
|
|
|
// 检查配置文件是否创建
|
|
assert!(temp_dir.path().join("mixvideo.project.json").exists());
|
|
}
|
|
|
|
#[test]
|
|
fn test_is_mixvideo_project() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let project_path = temp_dir.path().to_str().unwrap();
|
|
|
|
// 初始状态不是 MixVideo 项目
|
|
assert!(!FileSystemService::is_mixvideo_project(project_path));
|
|
|
|
// 创建项目结构后应该是 MixVideo 项目
|
|
FileSystemService::create_project_structure(project_path).unwrap();
|
|
assert!(FileSystemService::is_mixvideo_project(project_path));
|
|
}
|
|
}
|