Files
mixvideo-v2/apps/desktop/src-tauri/src/infrastructure/file_system.rs
imeepos b8dfaf8af8 feat: 实现AI视频分类功能
新功能:
- 集成Google Gemini API进行视频智能分类
- 实现任务队列系统支持批量处理
- 添加实时进度显示和状态管理
- 自动文件整理到分类文件夹

 架构改进:
- 遵循Tauri开发规范的分层架构设计
- 完整的数据模型和仓库层实现
- 异步任务处理和错误处理机制
- 类型安全的前后端通信接口

 用户界面:
- MaterialCard组件添加AI分类按钮
- VideoClassificationProgress进度显示组件
- 优美的动画效果和响应式设计
- 符合前端开发规范的UI/UX优化

 数据库扩展:
- 新增video_classification_records表
- 新增video_classification_tasks表
- 完整的索引优化和外键约束

 技术实现:
- Rust后端服务层完整实现
- React/TypeScript前端状态管理
- Zustand状态存储和API封装
- 完善的错误处理和用户提示

 文档:
- 完整的功能文档和API说明
- 架构设计和使用流程说明
- 开发规范遵循情况说明

Closes #AI视频分类功能开发
2025-07-14 12:52:30 +08:00

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));
}
}