n8n-nodes-comfyui-media
Version:
n8n node to integrate with ComfyUI stable diffusion workflows for image to video conversion
216 lines • 9.64 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ComfyuiVideoToVideo = void 0;
const n8n_workflow_1 = require("n8n-workflow");
const form_data_1 = __importDefault(require("form-data"));
const apiClient_1 = require("./apiClient");
const inputProviders_1 = require("./inputProviders");
const workflowService_1 = require("./workflowService");
const poller_1 = require("./poller");
class ComfyuiVideoToVideo {
constructor() {
this.description = {
displayName: 'ComfyUI Video to Video',
name: 'comfyuiVideoToVideo',
icon: 'file:comfyui.svg',
group: ['transform'],
version: 1,
description: 'Convert Video to videos using ComfyUI workflow',
defaults: {
name: 'ComfyUI Video to Video',
},
credentials: [
{
name: 'comfyUIApi',
required: true,
},
],
inputs: ['main'],
outputs: ['main'],
properties: [
{
displayName: 'Workflow JSON',
name: 'workflow',
type: 'string',
typeOptions: {
rows: 10,
},
default: '',
required: true,
description: 'The ComfyUI workflow in JSON format',
},
{
displayName: 'Input Type',
name: 'inputType',
type: 'options',
options: [
{ name: 'URL', value: 'url' },
{ name: 'Base64', value: 'base64' },
{ name: 'Binary', value: 'binary' }
],
default: 'url',
required: true,
},
{
displayName: 'Input Image',
name: 'inputImage',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
inputType: ['url', 'base64'],
},
},
description: 'URL or base64 data of the input image',
},
{
displayName: 'Binary Property',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
required: true,
displayOptions: {
show: {
inputType: ['binary'],
},
},
description: 'Name of the binary property containing the image',
},
{
displayName: 'Timeout',
name: 'timeout',
type: 'number',
default: 30,
description: 'Maximum time in minutes to wait for video generation',
},
],
};
}
async execute() {
var _a;
const api = new apiClient_1.N8nApiClient(this.helpers);
const credentials = await this.getCredentials('comfyUIApi');
const apiUrl = credentials.apiUrl;
const apiKey = credentials.apiKey;
const headers = { 'Content-Type': 'application/json' };
if (apiKey)
headers['Authorization'] = `Bearer ${apiKey}`;
try {
const inputType = this.getNodeParameter('inputType', 0);
const workflow = this.getNodeParameter('workflow', 0);
const timeout = this.getNodeParameter('timeout', 0);
let provider;
if (inputType === 'url') {
const inputImage = this.getNodeParameter('inputImage', 0);
provider = new inputProviders_1.UrlInputProvider(api, inputImage);
}
else if (inputType === 'base64') {
const inputImage = this.getNodeParameter('inputImage', 0);
provider = new inputProviders_1.Base64InputProvider(inputImage);
}
else {
const binaryPropertyName = this.getNodeParameter('binaryPropertyName', 0);
provider = new inputProviders_1.BinaryInputProvider(this.helpers, binaryPropertyName, this.getInputData());
}
const buffer = await provider.getBuffer();
const formData = new form_data_1.default();
formData.append('image', buffer, 'input.png');
formData.append('subfolder', '');
formData.append('overwrite', 'true');
const uploadResponse = await api.request({
method: 'POST',
url: `${apiUrl}/upload/image`,
headers: { ...headers, ...formData.getHeaders() },
body: formData,
});
const imageInfo = JSON.parse(uploadResponse);
const wf = workflowService_1.WorkflowService.parse(workflow);
workflowService_1.WorkflowService.injectImage(wf, imageInfo);
const response = await api.request({
method: 'POST',
url: `${apiUrl}/prompt`,
headers,
body: { prompt: wf },
json: true,
});
if (!response.prompt_id)
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Failed to get prompt ID' });
const poller = new poller_1.Poller(api, apiUrl, headers);
const promptResult = await poller.waitForCompletion(response.prompt_id, timeout);
console.log('[ComfyUI] Video generation completed');
if (((_a = promptResult.status) === null || _a === void 0 ? void 0 : _a.status_str) === 'error') {
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: '[ComfyUI] Video generation failed' });
}
console.log('[ComfyUI] Raw outputs structure:', JSON.stringify(promptResult.outputs, null, 2));
const mediaOutputs = Object.values(promptResult.outputs)
.flatMap((nodeOutput) => nodeOutput.images || nodeOutput.gifs || [])
.filter((image) => image.type === 'output' || image.type === 'temp')
.map((img) => ({
...img,
url: `${apiUrl}/view?filename=${img.filename}&subfolder=${img.subfolder || ''}&type=${img.type}`
}));
console.log('[ComfyUI] Found media outputs:', mediaOutputs);
if (mediaOutputs.length === 0) {
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: '[ComfyUI] No media outputs found in results' });
}
const videoOutputs = mediaOutputs.filter(output => output.filename.endsWith('.webp') ||
output.filename.endsWith('.mp4') ||
output.filename.endsWith('.gif'));
if (videoOutputs.length === 0) {
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: '[ComfyUI] No video outputs found in results' });
}
console.log('[ComfyUI] Found video outputs:', videoOutputs);
const videoOutput = videoOutputs[0];
const videoResponse = await this.helpers.request({
method: 'GET',
url: videoOutput.url,
encoding: null,
resolveWithFullResponse: true
});
if (videoResponse.statusCode === 404) {
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: `Video file not found at ${videoOutput.url}` });
}
console.log('[ComfyUI] Using media directly from ComfyUI');
const videoBuffer = Buffer.from(videoResponse.body);
const base64Data = videoBuffer.toString('base64');
const fileSize = Math.round(videoBuffer.length / 1024 * 10) / 10 + " kB";
let mimeType = 'image/webp';
let fileExtension = 'webp';
if (videoOutput.filename.endsWith('.mp4')) {
mimeType = 'video/mp4';
fileExtension = 'mp4';
}
else if (videoOutput.filename.endsWith('.gif')) {
mimeType = 'image/gif';
fileExtension = 'gif';
}
return [[{
json: {
mimeType,
fileName: videoOutput.filename,
data: base64Data,
status: promptResult.status,
},
binary: {
data: {
fileName: videoOutput.filename,
data: base64Data,
fileType: 'video',
fileSize,
fileExtension,
mimeType
}
}
}]];
}
catch (err) {
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: err.message });
}
}
}
exports.ComfyuiVideoToVideo = ComfyuiVideoToVideo;
//# sourceMappingURL=ComfyuiVideoToVideo.node.js.map