n8n-nodes-piapi
Version:
Community n8n nodes for PiAPI - integrate generative AI capabilities (image, video, audio, 3D) into your workflows
335 lines • 15.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PiAPISkyreels = void 0;
const n8n_workflow_1 = require("n8n-workflow");
const GenericFunctions_1 = require("../shared/GenericFunctions");
class PiAPISkyreels {
constructor() {
this.description = {
displayName: 'PiAPI Skyreels',
name: 'piAPISkyreels',
icon: 'file:../piapi.svg',
group: ['transform'],
version: 1,
description: 'Create videos from images using Skyreels (ONLY works with human subjects)',
defaults: {
name: 'PiAPI Skyreels',
},
inputs: ["main"],
outputs: ["main"],
credentials: [
{
name: 'piAPIApi',
required: true,
},
],
properties: [
{
displayName: 'Important Image Requirements',
name: 'imageRequirements',
type: 'notice',
default: '⚠️ This model ONLY works properly with human faces and subjects. Animals, objects, and landscapes will likely fail. For best results, provide clear images of people with good lighting and minimal background distractions.',
displayOptions: {
show: {
operation: ['imageToVideo'],
},
},
},
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Image to Video',
value: 'imageToVideo',
description: 'Convert a static image to video',
action: 'Convert a static image to video',
},
],
default: 'imageToVideo',
},
{
displayName: 'Image Source',
name: 'imageSource',
type: 'options',
options: [
{
name: 'URL',
value: 'url',
description: 'Use an image URL',
},
{
name: 'Binary Data',
value: 'binaryData',
description: 'Use binary data from previous node',
},
],
default: 'url',
description: 'The source of the image',
},
{
displayName: 'Image URL',
name: 'imageUrl',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
imageSource: ['url'],
},
},
description: 'URL of the image to use',
},
{
displayName: 'Binary Property',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
required: true,
displayOptions: {
show: {
imageSource: ['binaryData'],
},
},
description: 'Name of the binary property containing the image data',
},
{
displayName: 'Prompt',
name: 'prompt',
type: 'string',
default: 'FPS-24, a person with natural movements and facial expressions',
required: true,
description: 'Description to guide the video generation process',
},
{
displayName: 'Negative Prompt',
name: 'negativePrompt',
type: 'string',
default: 'chaotic, distortion, morphing',
description: 'What to exclude from the video generation',
},
{
displayName: 'Aspect Ratio',
name: 'aspectRatio',
type: 'options',
options: [
{
name: 'Landscape (16:9)',
value: '16:9',
},
{
name: 'Portrait (9:16)',
value: '9:16',
},
{
name: 'Square (1:1)',
value: '1:1',
},
],
default: '16:9',
description: 'The aspect ratio of the generated video',
},
{
displayName: 'Guidance Scale',
name: 'guidanceScale',
type: 'number',
typeOptions: {
minValue: 0.1,
maxValue: 10,
},
default: 3.5,
description: 'Controls how closely the video adheres to your prompt',
},
{
displayName: 'Wait For Task Completion',
name: 'waitForTaskCompletion',
type: 'boolean',
default: false,
description: 'Whether to wait for the task to complete before returning',
},
{
displayName: 'Maximum Retry Count',
name: 'maxRetries',
type: 'number',
displayOptions: {
show: {
waitForTaskCompletion: [true],
},
},
default: 20,
description: 'Maximum number of times to check for task completion (with 3-second intervals)',
},
{
displayName: 'Return Only Video URL',
name: 'returnOnlyVideoUrl',
type: 'boolean',
default: false,
description: 'Whether to return only the video URL instead of the full task data',
},
{
displayName: 'Return Binary Data',
name: 'returnBinaryData',
type: 'boolean',
default: false,
description: 'Whether to return the video as binary data',
},
{
displayName: 'Binary Property (For Output)',
name: 'binaryPropertyOutput',
type: 'string',
default: 'data',
required: true,
displayOptions: {
show: {
returnBinaryData: [true],
},
},
description: 'Name of the binary property to which to write the video data',
},
],
};
}
async execute() {
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
const items = this.getInputData();
const returnData = [];
for (let i = 0; i < items.length; i++) {
try {
const operation = this.getNodeParameter('operation', i);
if (operation === 'imageToVideo') {
const imageSource = this.getNodeParameter('imageSource', i);
let imageUrl = '';
if (imageSource === 'url') {
imageUrl = this.getNodeParameter('imageUrl', i);
}
else {
const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i);
const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName);
if (binaryData.mimeType && !binaryData.mimeType.includes('image/')) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'The provided binary data is not an image', { itemIndex: i });
}
const dataBuffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
const base64Image = dataBuffer.toString('base64');
const mimeType = binaryData.mimeType || 'image/jpeg';
imageUrl = `data:${mimeType};base64,${base64Image}`;
}
const prompt = this.getNodeParameter('prompt', i);
const negativePrompt = this.getNodeParameter('negativePrompt', i, '');
const aspectRatio = this.getNodeParameter('aspectRatio', i);
const guidanceScale = this.getNodeParameter('guidanceScale', i);
const waitForTaskCompletion = this.getNodeParameter('waitForTaskCompletion', i, true);
const maxRetries = waitForTaskCompletion ? this.getNodeParameter('maxRetries', i, 20) : 0;
const returnOnlyVideoUrl = this.getNodeParameter('returnOnlyVideoUrl', i, false);
const returnBinaryData = this.getNodeParameter('returnBinaryData', i, false);
const binaryPropertyOutput = returnBinaryData
? this.getNodeParameter('binaryPropertyOutput', i)
: '';
const requestBody = {
model: 'Qubico/skyreels',
task_type: 'img2video',
input: {
prompt,
image: imageUrl,
aspect_ratio: aspectRatio,
guidance_scale: guidanceScale,
},
};
if (negativePrompt) {
requestBody.input.negative_prompt = negativePrompt;
}
const response = await GenericFunctions_1.piApiRequest.call(this, 'POST', '/api/v1/task', requestBody);
let taskResponse = response;
if (waitForTaskCompletion) {
const taskId = (_a = response.data) === null || _a === void 0 ? void 0 : _a.task_id;
if (!taskId) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No task ID returned from API');
}
taskResponse = await GenericFunctions_1.waitForTaskCompletion.call(this, taskId, maxRetries);
}
if (returnOnlyVideoUrl) {
const videoUrl = (_c = (_b = taskResponse.data) === null || _b === void 0 ? void 0 : _b.output) === null || _c === void 0 ? void 0 : _c.video_url;
if (videoUrl) {
returnData.push({
json: {
videoUrl,
taskId: (_d = taskResponse.data) === null || _d === void 0 ? void 0 : _d.task_id,
status: (_e = taskResponse.data) === null || _e === void 0 ? void 0 : _e.status,
},
});
}
else {
returnData.push({
json: {
error: 'Video URL not available',
taskId: (_f = taskResponse.data) === null || _f === void 0 ? void 0 : _f.task_id,
status: (_g = taskResponse.data) === null || _g === void 0 ? void 0 : _g.status,
},
});
}
}
else if (returnBinaryData && waitForTaskCompletion) {
const videoUrl = (_j = (_h = taskResponse.data) === null || _h === void 0 ? void 0 : _h.output) === null || _j === void 0 ? void 0 : _j.video_url;
if (videoUrl) {
const videoData = await this.helpers.request({
method: 'GET',
url: videoUrl,
encoding: null,
resolveWithFullResponse: true,
});
const newItem = {
json: taskResponse.data,
binary: {},
};
if (newItem.binary) {
newItem.binary[binaryPropertyOutput] = await this.helpers.prepareBinaryData(Buffer.from(videoData.body), videoUrl.split('/').pop() || 'video.mp4', videoData.headers['content-type']);
}
returnData.push(newItem);
}
else {
returnData.push({
json: {
error: 'Video URL not available',
...taskResponse.data,
},
});
}
}
else {
returnData.push({
json: taskResponse.data,
});
}
}
}
catch (error) {
if (error.message && (error.message.includes('failed to get valid image') || error.message.includes('invalid request'))) {
const errorMessage = 'The API could not process the provided image. Skyreels ONLY works with human faces and subjects. Animals, objects, and landscapes will likely fail. Please provide a clear image of a person with good lighting and minimal background distractions.';
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.PiAPISkyreels = PiAPISkyreels;
//# sourceMappingURL=PiAPISkyreels.node.js.map