UNPKG

@sheriff1980/n8n-nodes-comfyuirunner

Version:
142 lines (133 loc) 5.93 kB
import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription, } from 'n8n-workflow'; // We might need uuid for client ID generation later // import { v4 as uuidv4 } from 'uuid'; export class ComfyUiRunner implements INodeType { description: INodeTypeDescription = { displayName: 'ComfyUI Runner', name: 'comfyUiRunner', // Changed group to something more appropriate group: ['External APIs'], version: 1, description: 'Runs a ComfyUI workflow via its API', // Updated description defaults: { name: 'ComfyUI Runner', // Updated default name }, inputs: ['main'] as any, outputs: ['main'] as any, properties: [ // Property for the ComfyUI API URL { displayName: 'ComfyUI URL', name: 'comfyUiUrl', type: 'string', default: 'http://127.0.0.1:8188', placeholder: 'http://127.0.0.1:8188', description: 'The base URL of your running ComfyUI instance (e.g., http://localhost:8188)', required: true, }, // Property for the Workflow JSON { displayName: 'Workflow JSON', name: 'workflowJson', type: 'json', // Use 'json' type for better input validation and expression support default: '{}', placeholder: 'Paste your ComfyUI API format workflow here', description: 'The ComfyUI workflow in API JSON format. Use expressions to insert dynamic data.', required: true, // Optional: Enhance the JSON editor experience // typeOptions: { // editor: 'json', // language: 'json', // }, }, ], }; // The execute method will be defined below async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> { const items = this.getInputData(); const returnData: INodeExecutionData[] = []; // Loop through each input item for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { try { // Get node parameters for the current item const comfyUiUrl = this.getNodeParameter('comfyUiUrl', itemIndex, '') as string; // Get the workflow JSON, allowing for expressions const workflowJsonString = this.getNodeParameter('workflowJson', itemIndex, '{}') as string; // --- Placeholder for ComfyUI API Interaction --- // This section will contain the logic to: // 1. Parse workflowJsonString into a JavaScript object. // let workflowObject = JSON.parse(workflowJsonString); // 2. Generate a unique client ID (e.g., using uuid). // const clientId = uuidv4(); // 3. Establish a WebSocket connection to `${comfyUiUrl}/ws?clientId=${clientId}`. // - Use a library like 'ws'. Remember to add it as a dependency if needed. // 4. Send the workflow via a POST request to `${comfyUiUrl}/prompt`. // - The body should be { prompt: workflowObject, client_id: clientId }. // - Use `this.helpers.httpRequest` for the POST request. // 5. Listen for messages on the WebSocket connection: // - Handle 'status' messages (queue updates). // - Handle 'execution_start' message. // - Handle 'executing' messages (current node, check for errors). // - Handle 'progress' messages (optional). // - Wait for the 'executed' message which contains output data, including image details (filename, subfolder, type). // - Close the WebSocket connection when done or on error. // 6. Once the 'executed' message with image details is received: // - Construct the image URL: `${comfyUiUrl}/view?filename=${filename}&subfolder=${subfolder}&type=${type}`. // - Fetch the image data using `this.helpers.httpRequest`. Crucially, set `returnBinaryData: true` and `encoding: 'arraybuffer'`. // const imageDataBuffer = await this.helpers.httpRequest({ ...options... }); // 7. Prepare the output item for n8n: // - Create binary data using `this.helpers.prepareBinaryData`. // const binaryOutput: IBinaryData = await this.helpers.prepareBinaryData( // Buffer.from(imageDataBuffer), // Convert ArrayBuffer to Buffer // 'output.png' // Or use the filename from ComfyUI // ); // - Construct the final INodeExecutionData object. // const newItem: INodeExecutionData = { // json: { success: true, message: "Workflow executed", outputFilename: filename }, // Example JSON output // binary: { data: binaryOutput }, // Attach the binary data under a key (e.g., 'data') // pairedItem: { item: itemIndex }, // }; // returnData.push(newItem); // --- End Placeholder --- // For now, just return a success message and pass through input JSON. // We will replace this with the actual API logic later. const newItem: INodeExecutionData = { json: { ...items[itemIndex].json, // Pass original JSON through _comfyRunnerStatus: 'Placeholder - API call not implemented yet', _receivedUrl: comfyUiUrl, _receivedWorkflow: workflowJsonString, // Show what was received }, binary: {}, // No binary data yet pairedItem: { item: itemIndex }, }; returnData.push(newItem); } catch (error: any) { // Handle errors for individual items if (this.continueOnFail()) { const errorItem: INodeExecutionData = { json: { error: error.message, stack: error.stack, // Include stack trace for debugging itemIndex: itemIndex, inputJson: items[itemIndex].json, // Include original input for context }, binary: {}, pairedItem: { item: itemIndex }, }; returnData.push(errorItem); continue; // Continue to the next item } // If not continuing on fail, re-throw the error to stop the workflow throw error; } } // Return the processed data return this.prepareOutputData(returnData); } }