UNPKG

n8n-nodes-piapi

Version:

Community n8n nodes for PiAPI - integrate generative AI capabilities (image, video, audio, 3D) into your workflows

323 lines 15 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FaceswapVideoToVideo = void 0; const n8n_workflow_1 = require("n8n-workflow"); const GenericFunctions_1 = require("../shared/GenericFunctions"); class FaceswapVideoToVideo { constructor() { this.description = { displayName: 'PiAPI Video Faceswap (Single & Multi-Face)', name: 'faceswapVideoToVideo', icon: 'file:../piapi.svg', group: ['transform'], version: 1, description: 'Swap one or multiple faces from an image to a video with precise face index control using PiAPI Video Faceswap API', defaults: { name: 'Video 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 from an image to a video. Use the Advanced Options to specify which faces to swap with index numbers (0,1,2, etc.).', }, { 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: 'Target Video Input Method', name: 'targetVideoInputMethod', type: 'options', options: [ { name: 'URL', value: 'url', }, { name: 'Binary Data', value: 'binaryData', }, ], default: 'url', description: 'Method to input the target video data', }, { displayName: 'Target Video Binary Property', name: 'targetVideoBinaryPropertyName', type: 'string', default: 'data', required: true, displayOptions: { show: { targetVideoInputMethod: ['binaryData'], }, }, description: 'Name of the binary property containing the target video data', }, { displayName: 'Target Video URL', name: 'targetVideoUrl', type: 'string', default: '', required: true, displayOptions: { show: { targetVideoInputMethod: ['url'], }, }, description: 'URL of the target video that will have faces replaced (MP4 only, max 10MB, max 720p)', }, { displayName: 'Video Requirements', name: 'videoRequirements', type: 'notice', default: 'Video must be MP4 format, maximum 10MB size, maximum 720p resolution, and maximum 600 frames.', }, { 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 video (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 swapImageInputMethod = this.getNodeParameter('swapImageInputMethod', i); const targetVideoInputMethod = this.getNodeParameter('targetVideoInputMethod', 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 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}`; } let targetVideoData; if (targetVideoInputMethod === 'url') { targetVideoData = this.getNodeParameter('targetVideoUrl', i); } else { const targetVideoBinaryPropertyName = this.getNodeParameter('targetVideoBinaryPropertyName', i); const targetVideoBinaryData = this.helpers.assertBinaryData(i, targetVideoBinaryPropertyName); if (targetVideoBinaryData.mimeType && !targetVideoBinaryData.mimeType.includes('video/mp4')) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'The provided target binary data is not an MP4 video', { itemIndex: i }); } const base64String = Buffer.from(await this.helpers.getBinaryDataBuffer(i, targetVideoBinaryPropertyName)).toString('base64'); targetVideoData = `data:${targetVideoBinaryData.mimeType};base64,${base64String}`; } const requestBody = { model: 'Qubico/video-toolkit', task_type: 'face-swap', input: { swap_image: swapImageData, target_video: targetVideoData, }, config: { service_mode: 'public' } }; 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 (error.message && error.message.includes('failed to get valid video')) { const errorMessage = 'The API could not process the provided video. Please ensure the video is accessible, in MP4 format, under 10MB, maximum 720p resolution, and maximum 600 frames.'; 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.FaceswapVideoToVideo = FaceswapVideoToVideo; //# sourceMappingURL=FaceswapVideoToVideo.node.js.map