n8n-nodes-piapi
Version:
Community n8n nodes for PiAPI - integrate generative AI capabilities (image, video, audio, 3D) into your workflows
301 lines • 13.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FaceswapImageToImage = void 0;
const n8n_workflow_1 = require("n8n-workflow");
const GenericFunctions_1 = require("../shared/GenericFunctions");
class FaceswapImageToImage {
constructor() {
this.description = {
displayName: 'PiAPI Image Faceswap (Single & Multi-Face)',
name: 'faceswapImageToImage',
icon: 'file:../piapi.svg',
group: ['transform'],
version: 1,
description: 'Swap one or multiple faces in images with precise face index control using PiAPI Faceswap API',
defaults: {
name: 'Faceswap',
},
inputs: ["main"],
outputs: ["main"],
credentials: [
{
name: 'piAPIApi',
required: true,
},
],
properties: [
{
displayName: 'Multi-Face Support',
name: 'multiFaceSupport',
type: 'notice',
default: 'This node supports swapping multiple faces between images. Use the Advanced Options to specify which faces to swap with index numbers (0,1,2, etc.).',
},
{
displayName: 'Target Image Input Method',
name: 'targetImageInputMethod',
type: 'options',
options: [
{
name: 'URL',
value: 'url',
},
{
name: 'Binary Data',
value: 'binaryData',
},
],
default: 'url',
description: 'Method to input the target image data',
},
{
displayName: 'Target Image Binary Property',
name: 'targetBinaryPropertyName',
type: 'string',
default: 'data',
required: true,
displayOptions: {
show: {
targetImageInputMethod: ['binaryData'],
},
},
description: 'Name of the binary property containing the target image data',
},
{
displayName: 'Target Image URL',
name: 'targetImageUrl',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
targetImageInputMethod: ['url'],
},
},
description: 'URL of the target image that will have faces replaced',
},
{
displayName: 'Swap Image Input Method',
name: 'swapImageInputMethod',
type: 'options',
options: [
{
name: 'URL',
value: 'url',
},
{
name: 'Binary Data',
value: 'binaryData',
},
],
default: 'url',
description: 'Method to input the swap image data',
},
{
displayName: 'Swap Image Binary Property',
name: 'swapBinaryPropertyName',
type: 'string',
default: 'data',
required: true,
displayOptions: {
show: {
swapImageInputMethod: ['binaryData'],
},
},
description: 'Name of the binary property containing the swap image data',
},
{
displayName: 'Swap Image URL',
name: 'swapImageUrl',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
swapImageInputMethod: ['url'],
},
},
description: 'URL of the image containing the face(s) to swap',
},
{
displayName: 'Enable Multi-Face Selection',
name: 'enableMultiFaceSelection',
type: 'boolean',
default: false,
description: 'Whether to manually select specific faces by index',
},
{
displayName: 'Face Indices Information',
name: 'faceIndicesInfo',
type: 'notice',
displayOptions: {
show: {
enableMultiFaceSelection: [true],
},
},
default: 'Faces are detected in order from left to right in most cases. For diagonal positioning, top-left might be 1 and bottom-right 0.',
},
{
displayName: 'Swap Faces Index',
name: 'swapFacesIndex',
type: 'string',
default: '0',
displayOptions: {
show: {
enableMultiFaceSelection: [true],
},
},
placeholder: '0 or 0,1',
description: 'Index(es) of faces to use from the swap image (e.g., "0" for first face, "0,1" for first and second faces)',
},
{
displayName: 'Target Faces Index',
name: 'targetFacesIndex',
type: 'string',
default: '0',
displayOptions: {
show: {
enableMultiFaceSelection: [true],
},
},
placeholder: '0 or 0,1',
description: 'Index(es) of faces to replace in the target image (e.g., "0" for first face, "0,1" for first and second faces)',
},
{
displayName: 'Wait For Completion',
name: 'waitForCompletion',
type: 'boolean',
default: false,
description: 'Whether to wait for the face swap process to complete before continuing',
},
{
displayName: 'Max Retries',
name: 'maxRetries',
type: 'number',
default: 20,
description: 'Maximum number of retries to check task status',
displayOptions: {
show: {
waitForCompletion: [true],
},
},
},
{
displayName: 'Retry Interval',
name: 'retryInterval',
type: 'number',
default: 3000,
description: 'Interval between retries in milliseconds',
displayOptions: {
show: {
waitForCompletion: [true],
},
},
},
],
};
}
async execute() {
var _a, _b;
const items = this.getInputData();
const returnData = [];
for (let i = 0; i < items.length; i++) {
try {
const targetImageInputMethod = this.getNodeParameter('targetImageInputMethod', i);
const swapImageInputMethod = this.getNodeParameter('swapImageInputMethod', i);
const waitForCompletion = this.getNodeParameter('waitForCompletion', i, false);
const enableMultiFaceSelection = this.getNodeParameter('enableMultiFaceSelection', i, false);
let swapFacesIndex = '';
let targetFacesIndex = '';
if (enableMultiFaceSelection) {
swapFacesIndex = this.getNodeParameter('swapFacesIndex', i, '0');
targetFacesIndex = this.getNodeParameter('targetFacesIndex', i, '0');
}
let targetImageData;
if (targetImageInputMethod === 'url') {
targetImageData = this.getNodeParameter('targetImageUrl', i);
}
else {
const targetBinaryPropertyName = this.getNodeParameter('targetBinaryPropertyName', i);
const targetBinaryData = this.helpers.assertBinaryData(i, targetBinaryPropertyName);
if (targetBinaryData.mimeType && !targetBinaryData.mimeType.includes('image')) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'The provided target binary data is not an image', { itemIndex: i });
}
const base64String = Buffer.from(await this.helpers.getBinaryDataBuffer(i, targetBinaryPropertyName)).toString('base64');
targetImageData = `data:${targetBinaryData.mimeType};base64,${base64String}`;
}
let swapImageData;
if (swapImageInputMethod === 'url') {
swapImageData = this.getNodeParameter('swapImageUrl', i);
}
else {
const swapBinaryPropertyName = this.getNodeParameter('swapBinaryPropertyName', i);
const swapBinaryData = this.helpers.assertBinaryData(i, swapBinaryPropertyName);
if (swapBinaryData.mimeType && !swapBinaryData.mimeType.includes('image')) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'The provided swap binary data is not an image', { itemIndex: i });
}
const base64String = Buffer.from(await this.helpers.getBinaryDataBuffer(i, swapBinaryPropertyName)).toString('base64');
swapImageData = `data:${swapBinaryData.mimeType};base64,${base64String}`;
}
const requestBody = {
model: 'Qubico/image-toolkit',
task_type: enableMultiFaceSelection ? 'multi-face-swap' : 'face-swap',
input: {
target_image: targetImageData,
swap_image: swapImageData,
},
};
if (enableMultiFaceSelection) {
requestBody.input.swap_faces_index = swapFacesIndex;
requestBody.input.target_faces_index = targetFacesIndex;
}
const response = await GenericFunctions_1.piApiRequest.call(this, 'POST', '/api/v1/task', requestBody);
const taskId = (_a = response.data) === null || _a === void 0 ? void 0 : _a.task_id;
if (!taskId) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to get a valid task ID from the API');
}
let executionData;
if (waitForCompletion) {
const maxRetries = this.getNodeParameter('maxRetries', i, 20);
const retryInterval = this.getNodeParameter('retryInterval', i, 3000);
executionData = await GenericFunctions_1.waitForTaskCompletion.call(this, taskId, maxRetries, retryInterval);
}
else {
executionData = {
task_id: taskId,
status: ((_b = response.data) === null || _b === void 0 ? void 0 : _b.status) || 'pending',
};
}
returnData.push({
json: executionData,
});
}
catch (error) {
if (error.message && error.message.includes('failed to get valid image')) {
const errorMessage = 'The API could not process the provided image. Please ensure the image is accessible, in a common format (JPEG, PNG), and meets the size requirements (under 2048x2048 resolution).';
if (this.continueOnFail()) {
returnData.push({
json: {
error: errorMessage,
details: error.message,
},
});
continue;
}
throw new n8n_workflow_1.NodeOperationError(this.getNode(), errorMessage);
}
if (this.continueOnFail()) {
returnData.push({
json: {
error: error.message,
},
});
continue;
}
throw error;
}
}
return [returnData];
}
}
exports.FaceswapImageToImage = FaceswapImageToImage;
//# sourceMappingURL=FaceswapImageToImage.node.js.map