diff --git a/apps/desktop/src-tauri/mixvideo.db b/apps/desktop/src-tauri/mixvideo.db new file mode 100644 index 0000000..77bee66 Binary files /dev/null and b/apps/desktop/src-tauri/mixvideo.db differ diff --git a/apps/desktop/src-tauri/src/data/repositories/workflow_execution_record_repository.rs b/apps/desktop/src-tauri/src/data/repositories/workflow_execution_record_repository.rs index f31079e..9b83649 100644 --- a/apps/desktop/src-tauri/src/data/repositories/workflow_execution_record_repository.rs +++ b/apps/desktop/src-tauri/src/data/repositories/workflow_execution_record_repository.rs @@ -352,10 +352,10 @@ impl WorkflowExecutionRecordRepository { let sql = format!( "SELECT COUNT(*) as total_count, - SUM(CASE WHEN status = '\"completed\"' THEN 1 ELSE 0 END) as completed_count, - SUM(CASE WHEN status = '\"failed\"' THEN 1 ELSE 0 END) as failed_count, - SUM(CASE WHEN status = '\"running\"' THEN 1 ELSE 0 END) as running_count, - SUM(CASE WHEN status = '\"pending\"' THEN 1 ELSE 0 END) as pending_count, + COALESCE(SUM(CASE WHEN status = '\"completed\"' THEN 1 ELSE 0 END), 0) as completed_count, + COALESCE(SUM(CASE WHEN status = '\"failed\"' THEN 1 ELSE 0 END), 0) as failed_count, + COALESCE(SUM(CASE WHEN status = '\"running\"' THEN 1 ELSE 0 END), 0) as running_count, + COALESCE(SUM(CASE WHEN status = '\"pending\"' THEN 1 ELSE 0 END), 0) as pending_count, AVG(CASE WHEN duration_seconds IS NOT NULL THEN duration_seconds ELSE NULL END) as avg_duration, MIN(created_at) as earliest_execution, MAX(created_at) as latest_execution diff --git a/apps/desktop/src-tauri/src/presentation/commands/comfyui_v2_commands.rs b/apps/desktop/src-tauri/src/presentation/commands/comfyui_v2_commands.rs index 5144a3a..17629cb 100644 --- a/apps/desktop/src-tauri/src/presentation/commands/comfyui_v2_commands.rs +++ b/apps/desktop/src-tauri/src/presentation/commands/comfyui_v2_commands.rs @@ -24,6 +24,22 @@ use crate::data::models::comfyui::{ use comfyui_sdk::types::{ComfyUIWorkflow, ComfyUINode}; use crate::business::services::comfyui_manager::ComfyUIManager; +// ==================== 辅助函数 ==================== + +/// 将前端的 ComfyUIV2Config 转换为后端的 ComfyUIConfig +fn convert_v2_config_to_config(v2_config: ComfyUIV2Config) -> ComfyUIConfig { + ComfyUIConfig { + base_url: v2_config.base_url, + timeout_seconds: v2_config.timeout.unwrap_or(30000) / 1000, // 转换毫秒为秒 + retry_attempts: v2_config.retry_attempts.unwrap_or(3), + retry_delay_ms: 1000, // 默认重试延迟 + enable_websocket: true, // 默认启用 WebSocket + enable_cache: v2_config.enable_cache.unwrap_or(true), + max_concurrency: v2_config.max_concurrency.unwrap_or(4), + custom_headers: None, + } +} + // ==================== 请求类型定义 ==================== /// 创建工作流请求 @@ -128,16 +144,30 @@ pub struct HealthCheckResponse { /// 连接到 ComfyUI 服务 #[tauri::command] pub async fn comfyui_v2_connect( + config: Option, state: State<'_, AppState>, ) -> Result { info!("Command: comfyui_v2_connect"); // 获取配置管理器 let config_manager = get_config_manager(&state).await?; - let config = config_manager.get_comfyui_config().await; + + // 使用传入的配置或获取现有配置 + let final_config = if let Some(v2_config) = config { + info!("使用传入的配置: {}", v2_config.base_url); + // 转换为 ComfyUIConfig + let converted_config = convert_v2_config_to_config(v2_config); + // 先更新配置到配置管理器 + config_manager.update_comfyui_config(converted_config.clone()).await + .map_err(|e| format!("更新配置失败: {}", e))?; + converted_config + } else { + info!("使用现有配置"); + config_manager.get_comfyui_config().await + }; // 创建 ComfyUI 管理器 - let manager = ComfyUIManager::new(config) + let manager = ComfyUIManager::new(final_config) .map_err(|e| format!("创建管理器失败: {}", e))?; // 尝试连接 @@ -145,7 +175,7 @@ pub async fn comfyui_v2_connect( Ok(_) => { let stats = manager.get_connection_stats().await; info!("ComfyUI 连接成功"); - + Ok(ConnectionStatusResponse { connected: true, status: stats.status.to_string(), @@ -218,8 +248,14 @@ pub async fn comfyui_v2_get_system_info( ) -> Result { info!("Command: comfyui_v2_get_system_info"); - // TODO: 从管理器获取系统信息 - Err("功能暂未实现".to_string()) + let manager = state.comfyui_manager.read().await; + match manager.get_system_info().await { + Ok(stats) => Ok(convert_system_info(stats)), + Err(e) => { + error!("获取系统信息失败: {}", e); + Err(format!("获取系统信息失败: {}", e)) + } + } } /// 获取队列基本状态 @@ -229,15 +265,23 @@ pub async fn comfyui_v2_get_queue_basic_status( ) -> Result { info!("Command: comfyui_v2_get_queue_basic_status"); - // TODO: 从管理器获取队列状态 - Ok(QueueStatusResponse { - running_count: 0, - pending_count: 0, - running_tasks: Vec::new(), - pending_tasks: Vec::new(), - }) + let manager = state.comfyui_manager.read().await; + match manager.get_queue_status().await { + Ok(status) => Ok(QueueStatusResponse { + running_count: status.running_count, + pending_count: status.pending_count, + running_tasks: Vec::new(), // TODO: 实现详细任务信息 + pending_tasks: Vec::new(), // TODO: 实现详细任务信息 + }), + Err(e) => { + error!("获取队列状态失败: {}", e); + Err(format!("获取队列状态失败: {}", e)) + } + } } + + // ==================== 配置管理命令 ==================== /// 获取 ComfyUI 配置 @@ -256,14 +300,17 @@ pub async fn comfyui_v2_get_config( /// 更新 ComfyUI 配置 #[tauri::command] pub async fn comfyui_v2_update_config( - config: ComfyUIConfig, + config: ComfyUIV2Config, state: State<'_, AppState>, ) -> Result { info!("Command: comfyui_v2_update_config"); let config_manager = get_config_manager(&state).await?; - - match config_manager.update_comfyui_config(config).await { + + // 转换为 ComfyUIConfig + let converted_config = convert_v2_config_to_config(config); + + match config_manager.update_comfyui_config(converted_config).await { Ok(_) => { info!("ComfyUI 配置更新成功"); Ok("配置更新成功".to_string()) @@ -278,12 +325,14 @@ pub async fn comfyui_v2_update_config( /// 验证配置 #[tauri::command] pub async fn comfyui_v2_validate_config( - config: ComfyUIConfig, + config: ComfyUIV2Config, state: State<'_, AppState>, ) -> Result { info!("Command: comfyui_v2_validate_config"); - let validation = config.validate(); + // 转换为 ComfyUIConfig 进行验证 + let converted_config = convert_v2_config_to_config(config); + let validation = converted_config.validate(); Ok(validation) } diff --git a/apps/desktop/src-tauri/src/presentation/commands/comfyui_v2_queue_commands.rs b/apps/desktop/src-tauri/src/presentation/commands/comfyui_v2_queue_commands.rs index f9ca729..ffa297e 100644 --- a/apps/desktop/src-tauri/src/presentation/commands/comfyui_v2_queue_commands.rs +++ b/apps/desktop/src-tauri/src/presentation/commands/comfyui_v2_queue_commands.rs @@ -177,26 +177,28 @@ pub async fn comfyui_v2_cancel_queue_task( Ok("任务已取消".to_string()) } -/// 获取队列状态 +/// 获取队列状态 (简化版本,用于前端) #[tauri::command] pub async fn comfyui_v2_get_queue_status( state: State<'_, AppState>, -) -> Result { +) -> Result { info!("Command: comfyui_v2_get_queue_status"); - // TODO: 从队列管理器获取状态 - Ok(QueueStatisticsResponse { - total_tasks: 0, - pending_tasks: 0, - running_tasks: 0, - completed_tasks: 0, - failed_tasks: 0, - average_wait_time: 0.0, - average_execution_time: 0.0, - throughput: 0.0, - by_priority: HashMap::new(), - by_type: HashMap::new(), - }) + let manager = state.comfyui_manager.read().await; + match manager.get_queue_status().await { + Ok(status) => { + // 返回前端期望的简单格式 + Ok(serde_json::json!({ + "running_count": status.running_count, + "pending_count": status.pending_count, + "history_count": 0 // TODO: 从历史记录获取 + })) + }, + Err(e) => { + error!("获取队列状态失败: {}", e); + Err(format!("获取队列状态失败: {}", e)) + } + } } /// 获取队列中的任务列表 diff --git a/apps/desktop/src/components/comfyui/ComfyUIConnectionPanel.tsx b/apps/desktop/src/components/comfyui/ComfyUIConnectionPanel.tsx index 5f5b8a7..f77ef8e 100644 --- a/apps/desktop/src/components/comfyui/ComfyUIConnectionPanel.tsx +++ b/apps/desktop/src/components/comfyui/ComfyUIConnectionPanel.tsx @@ -35,6 +35,7 @@ export const ComfyUIConnectionPanel: React.FC = ({ refreshSystemInfo, refreshQueueStatus, updateConfig, + loadConfig, startRealtimeMonitor, subscribeRealtimeEvents, realtimeConnected, @@ -52,6 +53,11 @@ export const ComfyUIConnectionPanel: React.FC = ({ const [isConnecting, setIsConnecting] = useState(false); const [connectionError, setConnectionError] = useState(null); + // 组件挂载时加载配置 + useEffect(() => { + loadConfig(); + }, [loadConfig]); + // 初始化配置表单 useEffect(() => { if (config) { @@ -68,7 +74,7 @@ export const ComfyUIConnectionPanel: React.FC = ({ return () => clearInterval(interval); } - }, [isConnected, refreshQueueStatus]); + }, [isConnected]); // 移除 refreshQueueStatus 依赖,因为它是稳定的 Zustand action const handleConnect = async () => { setIsConnecting(true); diff --git a/apps/desktop/src/components/comfyui/QueueStatusMonitor.tsx b/apps/desktop/src/components/comfyui/QueueStatusMonitor.tsx index 8865d84..71bc369 100644 --- a/apps/desktop/src/components/comfyui/QueueStatusMonitor.tsx +++ b/apps/desktop/src/components/comfyui/QueueStatusMonitor.tsx @@ -303,7 +303,7 @@ export const QueueStatusMonitor: React.FC = ({
{queueEvents.map((event, index) => (
diff --git a/apps/desktop/src/pages/ComfyUIV2Dashboard.tsx b/apps/desktop/src/pages/ComfyUIV2Dashboard.tsx index 1909d80..05ca3a4 100644 --- a/apps/desktop/src/pages/ComfyUIV2Dashboard.tsx +++ b/apps/desktop/src/pages/ComfyUIV2Dashboard.tsx @@ -37,7 +37,7 @@ export const ComfyUIV2Dashboard: React.FC = () => { toast.error('连接断开', 'ComfyUI 服务连接已断开'); } } - }, [connectionStatus?.connected, isConnected, toast]); + }, [connectionStatus?.connected, isConnected]); // 移除 toast 依赖 const tabs = [ { diff --git a/apps/desktop/src/services/comfyuiV2Service.ts b/apps/desktop/src/services/comfyuiV2Service.ts index 3bb7e12..d57489a 100644 --- a/apps/desktop/src/services/comfyuiV2Service.ts +++ b/apps/desktop/src/services/comfyuiV2Service.ts @@ -173,7 +173,8 @@ export class ComfyUIV2Service { */ static async connect(config: ComfyUIV2Config): Promise { try { - return await invoke('comfyui_v2_connect', { config }); + const result = await invoke<{connected: boolean, status: string, base_url: string}>('comfyui_v2_connect', { config }); + return result.connected ? '连接成功' : '连接失败'; } catch (error) { console.error('Failed to connect to ComfyUI:', error); throw new Error(`连接 ComfyUI 失败: ${error}`); @@ -260,7 +261,29 @@ export class ComfyUIV2Service { */ static async updateConfig(config: Partial): Promise { try { - return await invoke('comfyui_v2_update_config', { config }); + // 获取当前配置,然后合并新配置 + let currentConfig: ComfyUIV2Config; + try { + currentConfig = await this.getConfig(); + } catch { + // 如果获取失败,使用默认配置 + currentConfig = { + base_url: 'http://192.168.0.193:8188', + timeout: 30000, + retry_attempts: 3, + enable_cache: true, + max_concurrency: 4, + websocket_url: 'ws://192.168.0.193:8188/ws', + }; + } + + // 合并配置 + const fullConfig: ComfyUIV2Config = { + ...currentConfig, + ...config, + }; + + return await invoke('comfyui_v2_update_config', { config: fullConfig }); } catch (error) { console.error('Failed to update config:', error); throw new Error(`更新配置失败: ${error}`); diff --git a/apps/desktop/src/store/comfyuiV2Store.ts b/apps/desktop/src/store/comfyuiV2Store.ts index 4bdebf9..32cf2a1 100644 --- a/apps/desktop/src/store/comfyuiV2Store.ts +++ b/apps/desktop/src/store/comfyuiV2Store.ts @@ -80,6 +80,7 @@ interface ComfyUIV2Actions { refreshConnectionStatus: () => Promise; refreshSystemInfo: () => Promise; refreshQueueStatus: () => Promise; + loadConfig: () => Promise; updateConfig: (config: Partial) => Promise; // ==================== 工作流管理 ==================== @@ -189,13 +190,15 @@ export const useComfyUIV2Store = create()( connect: async (config: ComfyUIV2Config) => { try { + // 先更新配置,然后连接 + await ComfyUIV2Service.updateConfig(config); await ComfyUIV2Service.connect(config); - set({ - isConnected: true, + set({ + isConnected: true, config, connectionStatus: await ComfyUIV2Service.getConnectionStatus() }); - + // 连接成功后加载基础数据 const { refreshSystemInfo, refreshQueueStatus, loadWorkflows, loadTemplates } = get(); await Promise.all([ @@ -257,6 +260,25 @@ export const useComfyUIV2Store = create()( } }, + loadConfig: async () => { + try { + const config = await ComfyUIV2Service.getConfig(); + set({ config }); + } catch (error) { + console.error('Failed to load config:', error); + // 如果加载失败,使用默认配置 + const defaultConfig: ComfyUIV2Config = { + base_url: 'http://192.168.0.193:8188', + timeout: 30000, + retry_attempts: 3, + enable_cache: true, + max_concurrency: 4, + websocket_url: 'ws://192.168.0.193:8188/ws', + }; + set({ config: defaultConfig }); + } + }, + updateConfig: async (configUpdate: Partial) => { try { await ComfyUIV2Service.updateConfig(configUpdate); diff --git a/cargos/comfyui-sdk/types/api.rs b/cargos/comfyui-sdk/types/api.rs index 716b87e..3dce9cb 100644 --- a/cargos/comfyui-sdk/types/api.rs +++ b/cargos/comfyui-sdk/types/api.rs @@ -80,6 +80,7 @@ pub struct SystemInfo { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DeviceInfo { pub name: String, + #[serde(default = "default_device_type")] pub device_type: String, pub vram_total: u64, pub vram_free: u64, @@ -87,6 +88,11 @@ pub struct DeviceInfo { pub torch_vram_free: u64, } +/// Default device type when not provided by ComfyUI +fn default_device_type() -> String { + "unknown".to_string() +} + /// Object info response #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ObjectInfo {