use tauri::{State, AppHandle, Emitter}; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; use std::collections::HashMap; use uuid::Uuid; use serde::{Deserialize, Serialize}; use tvai::{ TvaiProcessor, TvaiConfig, ProcessResult, TvaiError, VideoUpscaleParams, InterpolationParams, VideoEnhanceParams, ImageUpscaleParams, ImageFormat, UpscaleModel, InterpolationModel, QualityPreset, quick_upscale_video, quick_upscale_image, quick_interpolate_video, auto_enhance_video, detect_topaz_installation, detect_gpu_support, detect_ffmpeg, get_video_info, get_image_info, estimate_processing_time, GpuInfo, FfmpegInfo, VideoInfo, }; /// TVAI处理任务状态 #[derive(Debug, Clone, Serialize, Deserialize)] pub enum TvaiTaskStatus { Pending, Processing, Completed, Failed, Cancelled, } /// TVAI处理任务 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TvaiTask { pub id: String, pub task_type: String, pub input_path: String, pub output_path: String, pub status: TvaiTaskStatus, pub progress: f32, pub error_message: Option, pub created_at: chrono::DateTime, pub started_at: Option>, pub completed_at: Option>, pub processing_time: Option, // milliseconds } /// TVAI服务状态管理 pub struct TvaiState { processor: Arc>>, tasks: Arc>>, config: Arc>>, } impl TvaiState { pub fn new() -> Self { Self { processor: Arc::new(Mutex::new(None)), tasks: Arc::new(Mutex::new(HashMap::new())), config: Arc::new(Mutex::new(None)), } } } /// 检测Topaz Video AI安装 #[tauri::command] pub async fn detect_topaz_installation_command() -> Result, String> { match detect_topaz_installation() { Some(path) => Ok(Some(path.to_string_lossy().to_string())), None => Ok(None), } } /// 检测GPU支持 #[tauri::command] pub async fn detect_gpu_support_command() -> Result { Ok(detect_gpu_support()) } /// 检测FFmpeg #[tauri::command] pub async fn detect_ffmpeg_command() -> Result { Ok(detect_ffmpeg()) } /// 初始化TVAI配置 #[tauri::command] pub async fn initialize_tvai_config( state: State<'_, TvaiState>, topaz_path: Option, use_gpu: Option, temp_dir: Option, ) -> Result<(), String> { let topaz_path = match topaz_path { Some(path) => PathBuf::from(path), None => detect_topaz_installation() .ok_or("Topaz Video AI not found. Please specify the installation path.")?, }; let mut config_builder = TvaiConfig::builder() .topaz_path(topaz_path) .use_gpu(use_gpu.unwrap_or(true)); if let Some(temp_dir) = temp_dir { config_builder = config_builder.temp_dir(PathBuf::from(temp_dir)); } let config = config_builder .build() .map_err(|e| format!("Failed to create TVAI config: {}", e))?; let processor = TvaiProcessor::new(config.clone()) .map_err(|e| format!("Failed to create TVAI processor: {}", e))?; { let mut config_guard = state.config.lock().unwrap(); *config_guard = Some(config); } { let mut processor_guard = state.processor.lock().unwrap(); *processor_guard = Some(processor); } Ok(()) } /// 获取视频信息 #[tauri::command] pub async fn get_video_info_command( video_path: String, ) -> Result { let path = Path::new(&video_path); let info = get_video_info(path).await .map_err(|e| format!("Failed to get video info: {}", e))?; let json_info = serde_json::json!({ "width": info.width, "height": info.height, "fps": info.fps, "duration": info.duration.as_secs(), "codec": info.codec, "bitrate": info.bitrate, "file_size": info.file_size, }); Ok(json_info) } /// 获取图片信息 #[tauri::command] pub async fn get_image_info_command( image_path: String, ) -> Result { let path = Path::new(&image_path); let info = get_image_info(path) .map_err(|e| format!("Failed to get image info: {}", e))?; let json_info = serde_json::json!({ "width": info.width, "height": info.height, "format": info.format, "color_depth": info.color_depth, "file_size": info.file_size, }); Ok(json_info) } /// 估算处理时间 #[tauri::command] pub async fn estimate_processing_time_command( input_path: String, scale_factor: Option, ) -> Result { let path = Path::new(&input_path); // 获取视频信息 let video_info = get_video_info(path).await .map_err(|e| format!("Failed to get video info: {}", e))?; // 创建默认的放大参数 let params = VideoUpscaleParams { scale_factor: scale_factor.unwrap_or(2.0), model: UpscaleModel::Iris3, compression: 0.0, blend: 0.0, quality_preset: QualityPreset::HighQuality, }; let time = estimate_processing_time(&video_info, ¶ms); Ok(time.as_secs()) } /// 快速视频放大 #[tauri::command] pub async fn quick_upscale_video_command( app_handle: AppHandle, state: State<'_, TvaiState>, input_path: String, output_path: String, scale_factor: f32, ) -> Result { let task_id = Uuid::new_v4().to_string(); // 创建任务记录 let task = TvaiTask { id: task_id.clone(), task_type: "video_upscale".to_string(), input_path: input_path.clone(), output_path: output_path.clone(), status: TvaiTaskStatus::Pending, progress: 0.0, error_message: None, created_at: chrono::Utc::now(), started_at: None, completed_at: None, processing_time: None, }; { let mut tasks = state.tasks.lock().unwrap(); tasks.insert(task_id.clone(), task); } // 发送任务创建事件 app_handle.emit("tvai_task_created", &task_id).unwrap(); // 异步处理 let tasks_clone = state.tasks.clone(); let app_handle_clone = app_handle.clone(); let task_id_clone = task_id.clone(); tokio::spawn(async move { // 更新任务状态为处理中 { let mut tasks = tasks_clone.lock().unwrap(); if let Some(task) = tasks.get_mut(&task_id_clone) { task.status = TvaiTaskStatus::Processing; task.started_at = Some(chrono::Utc::now()); } } app_handle_clone.emit("tvai_task_updated", &task_id_clone).unwrap(); let start_time = std::time::Instant::now(); // 执行处理 let result = quick_upscale_video( Path::new(&input_path), Path::new(&output_path), scale_factor, ).await; let processing_time = start_time.elapsed(); // 更新任务状态 { let mut tasks = tasks_clone.lock().unwrap(); if let Some(task) = tasks.get_mut(&task_id_clone) { match result { Ok(_) => { task.status = TvaiTaskStatus::Completed; task.progress = 100.0; task.completed_at = Some(chrono::Utc::now()); task.processing_time = Some(processing_time.as_millis() as u64); } Err(e) => { task.status = TvaiTaskStatus::Failed; task.error_message = Some(e.to_string()); task.completed_at = Some(chrono::Utc::now()); } } } } app_handle_clone.emit("tvai_task_updated", &task_id_clone).unwrap(); }); Ok(task_id) } /// 快速图片放大 #[tauri::command] pub async fn quick_upscale_image_command( app_handle: AppHandle, state: State<'_, TvaiState>, input_path: String, output_path: String, scale_factor: f32, ) -> Result { let task_id = Uuid::new_v4().to_string(); // 创建任务记录 let task = TvaiTask { id: task_id.clone(), task_type: "image_upscale".to_string(), input_path: input_path.clone(), output_path: output_path.clone(), status: TvaiTaskStatus::Pending, progress: 0.0, error_message: None, created_at: chrono::Utc::now(), started_at: None, completed_at: None, processing_time: None, }; { let mut tasks = state.tasks.lock().unwrap(); tasks.insert(task_id.clone(), task); } // 发送任务创建事件 app_handle.emit("tvai_task_created", &task_id).unwrap(); // 异步处理 let tasks_clone = state.tasks.clone(); let app_handle_clone = app_handle.clone(); let task_id_clone = task_id.clone(); tokio::spawn(async move { // 更新任务状态为处理中 { let mut tasks = tasks_clone.lock().unwrap(); if let Some(task) = tasks.get_mut(&task_id_clone) { task.status = TvaiTaskStatus::Processing; task.started_at = Some(chrono::Utc::now()); } } app_handle_clone.emit("tvai_task_updated", &task_id_clone).unwrap(); let start_time = std::time::Instant::now(); // 执行处理 let result = quick_upscale_image( Path::new(&input_path), Path::new(&output_path), scale_factor, ).await; let processing_time = start_time.elapsed(); // 更新任务状态 { let mut tasks = tasks_clone.lock().unwrap(); if let Some(task) = tasks.get_mut(&task_id_clone) { match result { Ok(_) => { task.status = TvaiTaskStatus::Completed; task.progress = 100.0; task.completed_at = Some(chrono::Utc::now()); task.processing_time = Some(processing_time.as_millis() as u64); } Err(e) => { task.status = TvaiTaskStatus::Failed; task.error_message = Some(e.to_string()); task.completed_at = Some(chrono::Utc::now()); } } } } app_handle_clone.emit("tvai_task_updated", &task_id_clone).unwrap(); }); Ok(task_id) } /// 快速视频插帧 #[tauri::command] pub async fn quick_interpolate_video_command( app_handle: AppHandle, state: State<'_, TvaiState>, input_path: String, output_path: String, multiplier: f32, input_fps: u32, ) -> Result { let task_id = Uuid::new_v4().to_string(); // 创建任务记录 let task = TvaiTask { id: task_id.clone(), task_type: "video_interpolation".to_string(), input_path: input_path.clone(), output_path: output_path.clone(), status: TvaiTaskStatus::Pending, progress: 0.0, error_message: None, created_at: chrono::Utc::now(), started_at: None, completed_at: None, processing_time: None, }; { let mut tasks = state.tasks.lock().unwrap(); tasks.insert(task_id.clone(), task); } // 发送任务创建事件 app_handle.emit("tvai_task_created", &task_id).unwrap(); // 异步处理 let tasks_clone = state.tasks.clone(); let app_handle_clone = app_handle.clone(); let task_id_clone = task_id.clone(); tokio::spawn(async move { // 更新任务状态为处理中 { let mut tasks = tasks_clone.lock().unwrap(); if let Some(task) = tasks.get_mut(&task_id_clone) { task.status = TvaiTaskStatus::Processing; task.started_at = Some(chrono::Utc::now()); } } app_handle_clone.emit("tvai_task_updated", &task_id_clone).unwrap(); let start_time = std::time::Instant::now(); // 执行插帧处理 let result = quick_interpolate_video( Path::new(&input_path), Path::new(&output_path), multiplier, input_fps, ).await; let processing_time = start_time.elapsed(); // 更新任务状态 { let mut tasks = tasks_clone.lock().unwrap(); if let Some(task) = tasks.get_mut(&task_id_clone) { match result { Ok(_) => { task.status = TvaiTaskStatus::Completed; task.progress = 100.0; task.completed_at = Some(chrono::Utc::now()); task.processing_time = Some(processing_time.as_millis() as u64); } Err(e) => { task.status = TvaiTaskStatus::Failed; task.error_message = Some(e.to_string()); task.completed_at = Some(chrono::Utc::now()); } } } } app_handle_clone.emit("tvai_task_updated", &task_id_clone).unwrap(); }); Ok(task_id) } /// 自动增强视频 #[tauri::command] pub async fn auto_enhance_video_command( app_handle: AppHandle, state: State<'_, TvaiState>, input_path: String, output_path: String, ) -> Result { let task_id = Uuid::new_v4().to_string(); // 创建任务记录 let task = TvaiTask { id: task_id.clone(), task_type: "video_auto_enhance".to_string(), input_path: input_path.clone(), output_path: output_path.clone(), status: TvaiTaskStatus::Pending, progress: 0.0, error_message: None, created_at: chrono::Utc::now(), started_at: None, completed_at: None, processing_time: None, }; { let mut tasks = state.tasks.lock().unwrap(); tasks.insert(task_id.clone(), task); } // 发送任务创建事件 app_handle.emit("tvai_task_created", &task_id).unwrap(); // 异步处理 let tasks_clone = state.tasks.clone(); let app_handle_clone = app_handle.clone(); let task_id_clone = task_id.clone(); tokio::spawn(async move { // 更新任务状态为处理中 { let mut tasks = tasks_clone.lock().unwrap(); if let Some(task) = tasks.get_mut(&task_id_clone) { task.status = TvaiTaskStatus::Processing; task.started_at = Some(chrono::Utc::now()); } } app_handle_clone.emit("tvai_task_updated", &task_id_clone).unwrap(); let start_time = std::time::Instant::now(); // 执行处理 let result = auto_enhance_video( Path::new(&input_path), Path::new(&output_path), ).await; let processing_time = start_time.elapsed(); // 更新任务状态 { let mut tasks = tasks_clone.lock().unwrap(); if let Some(task) = tasks.get_mut(&task_id_clone) { match result { Ok(_) => { task.status = TvaiTaskStatus::Completed; task.progress = 100.0; task.completed_at = Some(chrono::Utc::now()); task.processing_time = Some(processing_time.as_millis() as u64); } Err(e) => { task.status = TvaiTaskStatus::Failed; task.error_message = Some(e.to_string()); task.completed_at = Some(chrono::Utc::now()); } } } } app_handle_clone.emit("tvai_task_updated", &task_id_clone).unwrap(); }); Ok(task_id) } /// 自动增强图片(使用快速放大作为替代) #[tauri::command] pub async fn auto_enhance_image_command( app_handle: AppHandle, state: State<'_, TvaiState>, input_path: String, output_path: String, ) -> Result { // 使用快速图片放大作为自动增强的替代 quick_upscale_image_command(app_handle, state, input_path, output_path, 2.0).await } /// 高级视频放大(支持自定义参数) #[tauri::command] pub async fn upscale_video_advanced( app_handle: AppHandle, state: State<'_, TvaiState>, input_path: String, output_path: String, scale_factor: f32, model: String, compression: f32, blend: f32, quality_preset: String, ) -> Result { let task_id = Uuid::new_v4().to_string(); // 解析模型 let upscale_model = match model.as_str() { "aaa-9" => UpscaleModel::Aaa9, "ahq-12" => UpscaleModel::Ahq12, "alq-13" => UpscaleModel::Alq13, "alqs-2" => UpscaleModel::Alqs2, "amq-13" => UpscaleModel::Amq13, "amqs-2" => UpscaleModel::Amqs2, "ghq-5" => UpscaleModel::Ghq5, "iris-2" => UpscaleModel::Iris2, "iris-3" => UpscaleModel::Iris3, "nyx-3" => UpscaleModel::Nyx3, "prob-4" => UpscaleModel::Prob4, "thf-4" => UpscaleModel::Thf4, "thd-3" => UpscaleModel::Thd3, "thm-2" => UpscaleModel::Thm2, "rhea-1" => UpscaleModel::Rhea1, "rxl-1" => UpscaleModel::Rxl1, _ => return Err(format!("Unsupported upscale model: {}", model)), }; // 解析质量预设 let preset = match quality_preset.as_str() { "fast" => QualityPreset::Fast, "balanced" => QualityPreset::Balanced, "high_quality" => QualityPreset::HighQuality, "maximum" => QualityPreset::Maximum, _ => return Err(format!("Unsupported quality preset: {}", quality_preset)), }; // 创建任务记录 let task = TvaiTask { id: task_id.clone(), task_type: "video_upscale_advanced".to_string(), input_path: input_path.clone(), output_path: output_path.clone(), status: TvaiTaskStatus::Pending, progress: 0.0, error_message: None, created_at: chrono::Utc::now(), started_at: None, completed_at: None, processing_time: None, }; { let mut tasks = state.tasks.lock().unwrap(); tasks.insert(task_id.clone(), task); } // 发送任务创建事件 app_handle.emit("tvai_task_created", &task_id).unwrap(); // 异步处理 let processor_clone = state.processor.clone(); let tasks_clone = state.tasks.clone(); let app_handle_clone = app_handle.clone(); let task_id_clone = task_id.clone(); tokio::spawn(async move { // 更新任务状态为处理中 { let mut tasks = tasks_clone.lock().unwrap(); if let Some(task) = tasks.get_mut(&task_id_clone) { task.status = TvaiTaskStatus::Processing; task.started_at = Some(chrono::Utc::now()); } } app_handle_clone.emit("tvai_task_updated", &task_id_clone).unwrap(); let start_time = std::time::Instant::now(); // 执行处理 let result: Result = { let processor_guard = processor_clone.lock().unwrap(); if let Some(ref mut processor) = processor_guard.as_ref() { let params = VideoUpscaleParams { scale_factor, model: upscale_model, compression, blend, quality_preset: preset, }; // 注意:这里需要克隆processor,因为我们不能在异步块中持有锁 // 实际实现中可能需要重新设计架构 Err(TvaiError::InvalidParameter("Processor architecture needs redesign for async usage".to_string())) } else { Err(TvaiError::ConfigurationError("TVAI processor not initialized".to_string())) } }; let processing_time = start_time.elapsed(); // 更新任务状态 { let mut tasks = tasks_clone.lock().unwrap(); if let Some(task) = tasks.get_mut(&task_id_clone) { match result { Ok(_) => { task.status = TvaiTaskStatus::Completed; task.progress = 100.0; task.completed_at = Some(chrono::Utc::now()); task.processing_time = Some(processing_time.as_millis() as u64); } Err(e) => { task.status = TvaiTaskStatus::Failed; task.error_message = Some(e.to_string()); task.completed_at = Some(chrono::Utc::now()); } } } } app_handle_clone.emit("tvai_task_updated", &task_id_clone).unwrap(); }); Ok(task_id) } /// 高级图片放大(支持自定义参数) #[tauri::command] pub async fn upscale_image_advanced( app_handle: AppHandle, state: State<'_, TvaiState>, input_path: String, output_path: String, scale_factor: f32, model: String, compression: f32, blend: f32, output_format: String, ) -> Result { let task_id = Uuid::new_v4().to_string(); // 解析模型 let upscale_model = match model.as_str() { "aaa-9" => UpscaleModel::Aaa9, "ahq-12" => UpscaleModel::Ahq12, "alq-13" => UpscaleModel::Alq13, "alqs-2" => UpscaleModel::Alqs2, "amq-13" => UpscaleModel::Amq13, "amqs-2" => UpscaleModel::Amqs2, "ghq-5" => UpscaleModel::Ghq5, "iris-2" => UpscaleModel::Iris2, "iris-3" => UpscaleModel::Iris3, "nyx-3" => UpscaleModel::Nyx3, "prob-4" => UpscaleModel::Prob4, "thf-4" => UpscaleModel::Thf4, "thd-3" => UpscaleModel::Thd3, "thm-2" => UpscaleModel::Thm2, "rhea-1" => UpscaleModel::Rhea1, "rxl-1" => UpscaleModel::Rxl1, _ => return Err(format!("Unsupported upscale model: {}", model)), }; // 解析输出格式 let format = match output_format.as_str() { "png" => ImageFormat::Png, "jpg" | "jpeg" => ImageFormat::Jpg, "tiff" => ImageFormat::Tiff, "bmp" => ImageFormat::Bmp, _ => return Err(format!("Unsupported output format: {}", output_format)), }; // 创建任务记录 let task = TvaiTask { id: task_id.clone(), task_type: "image_upscale_advanced".to_string(), input_path: input_path.clone(), output_path: output_path.clone(), status: TvaiTaskStatus::Pending, progress: 0.0, error_message: None, created_at: chrono::Utc::now(), started_at: None, completed_at: None, processing_time: None, }; { let mut tasks = state.tasks.lock().unwrap(); tasks.insert(task_id.clone(), task); } // 发送任务创建事件 app_handle.emit("tvai_task_created", &task_id).unwrap(); // 异步处理(使用快速函数,因为高级处理器需要重新设计架构) let tasks_clone = state.tasks.clone(); let app_handle_clone = app_handle.clone(); let task_id_clone = task_id.clone(); tokio::spawn(async move { // 更新任务状态为处理中 { let mut tasks = tasks_clone.lock().unwrap(); if let Some(task) = tasks.get_mut(&task_id_clone) { task.status = TvaiTaskStatus::Processing; task.started_at = Some(chrono::Utc::now()); } } app_handle_clone.emit("tvai_task_updated", &task_id_clone).unwrap(); let start_time = std::time::Instant::now(); // 执行处理(暂时使用快速函数) let result = quick_upscale_image( Path::new(&input_path), Path::new(&output_path), scale_factor, ).await; let processing_time = start_time.elapsed(); // 更新任务状态 { let mut tasks = tasks_clone.lock().unwrap(); if let Some(task) = tasks.get_mut(&task_id_clone) { match result { Ok(_) => { task.status = TvaiTaskStatus::Completed; task.progress = 100.0; task.completed_at = Some(chrono::Utc::now()); task.processing_time = Some(processing_time.as_millis() as u64); } Err(e) => { task.status = TvaiTaskStatus::Failed; task.error_message = Some(e.to_string()); task.completed_at = Some(chrono::Utc::now()); } } } } app_handle_clone.emit("tvai_task_updated", &task_id_clone).unwrap(); }); Ok(task_id) } /// 获取任务状态 #[tauri::command] pub async fn get_tvai_task_status( state: State<'_, TvaiState>, task_id: String, ) -> Result, String> { let tasks = state.tasks.lock().unwrap(); Ok(tasks.get(&task_id).cloned()) } /// 获取所有任务 #[tauri::command] pub async fn get_all_tvai_tasks( state: State<'_, TvaiState>, ) -> Result, String> { let tasks = state.tasks.lock().unwrap(); Ok(tasks.values().cloned().collect()) } /// 取消任务 #[tauri::command] pub async fn cancel_tvai_task( app_handle: AppHandle, state: State<'_, TvaiState>, task_id: String, ) -> Result<(), String> { { let mut tasks = state.tasks.lock().unwrap(); if let Some(task) = tasks.get_mut(&task_id) { if matches!(task.status, TvaiTaskStatus::Pending | TvaiTaskStatus::Processing) { task.status = TvaiTaskStatus::Cancelled; task.completed_at = Some(chrono::Utc::now()); } else { return Err("Task cannot be cancelled in current state".to_string()); } } else { return Err("Task not found".to_string()); } } app_handle.emit("tvai_task_updated", &task_id).unwrap(); Ok(()) } /// 清理已完成的任务 #[tauri::command] pub async fn cleanup_completed_tvai_tasks( state: State<'_, TvaiState>, ) -> Result { let mut tasks = state.tasks.lock().unwrap(); let initial_count = tasks.len(); tasks.retain(|_, task| { !matches!(task.status, TvaiTaskStatus::Completed | TvaiTaskStatus::Failed | TvaiTaskStatus::Cancelled) }); let removed_count = initial_count - tasks.len(); Ok(removed_count) }