n8n-nodes-piapi
Version:
Community n8n nodes for PiAPI - integrate generative AI capabilities (image, video, audio, 3D) into your workflows
493 lines • 21.9 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.WanXImageToVideo = void 0;
const GenericFunctions_1 = require("../shared/GenericFunctions");
const Constants_1 = require("../shared/Constants");
class WanXImageToVideo {
constructor() {
this.description = {
displayName: 'PiAPI WanX Image to Video',
name: 'wanXImageToVideo',
icon: 'file:../piapi.svg',
group: ['transform'],
version: 1,
description: 'Generate videos from images using PiAPI WanX',
defaults: {
name: 'WanX Image to Video',
},
inputs: ["main"],
outputs: ["main"],
credentials: [
{
name: 'piAPIApi',
required: true,
},
],
properties: [
{
displayName: 'Prompt',
name: 'prompt',
type: 'string',
typeOptions: {
rows: 4,
},
default: '',
required: true,
description: 'Descriptive prompt for video generation',
},
{
displayName: 'Negative Prompt',
name: 'negativePrompt',
type: 'string',
typeOptions: {
rows: 2,
},
default: '',
description: 'Things to exclude from the video generation',
},
{
displayName: 'Model',
name: 'model',
type: 'options',
options: Constants_1.WANX_MODELS.filter(model => model.value === 'img2video-14b' ||
model.value === 'img2video-14b-keyframe' ||
model.value === 'img2video-14b-lora'),
default: 'img2video-14b',
description: 'The WanX model to use for image-to-video generation',
},
{
displayName: 'Image URL',
name: 'imageUrl',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
imageSource: ['url'],
model: ['img2video-14b', 'img2video-14b-lora'],
},
},
description: 'Direct URL to the image (must be a publicly accessible image file)',
},
{
displayName: 'First Frame Source',
name: 'firstFrameSource',
type: 'options',
options: [
{
name: 'URL',
value: 'url',
},
{
name: 'Binary Data',
value: 'binaryData',
},
],
default: 'url',
description: 'The source of the first frame image',
displayOptions: {
show: {
model: ['img2video-14b-keyframe'],
},
},
},
{
displayName: 'Last Frame Source',
name: 'lastFrameSource',
type: 'options',
options: [
{
name: 'URL',
value: 'url',
},
{
name: 'Binary Data',
value: 'binaryData',
},
],
default: 'url',
description: 'The source of the last frame image',
displayOptions: {
show: {
model: ['img2video-14b-keyframe'],
},
},
},
{
displayName: 'Binary Property',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
required: true,
displayOptions: {
show: {
imageSource: ['binaryData'],
model: ['img2video-14b', 'img2video-14b-lora'],
},
},
description: 'Name of the binary property containing the image data',
},
{
displayName: 'First Frame URL',
name: 'firstFrameUrl',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
firstFrameSource: ['url'],
model: ['img2video-14b-keyframe'],
},
},
description: 'Direct URL to the first frame image (must be a publicly accessible image file)',
},
{
displayName: 'Last Frame URL',
name: 'lastFrameUrl',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
lastFrameSource: ['url'],
model: ['img2video-14b-keyframe'],
},
},
description: 'Direct URL to the last frame image (must be a publicly accessible image file)',
},
{
displayName: 'First Frame Binary Property',
name: 'firstFrameBinaryPropertyName',
type: 'string',
default: 'firstFrame',
required: true,
displayOptions: {
show: {
firstFrameSource: ['binaryData'],
model: ['img2video-14b-keyframe'],
},
},
description: 'Name of the binary property containing the first frame image data',
},
{
displayName: 'Last Frame Binary Property',
name: 'lastFrameBinaryPropertyName',
type: 'string',
default: 'lastFrame',
required: true,
displayOptions: {
show: {
lastFrameSource: ['binaryData'],
model: ['img2video-14b-keyframe'],
},
},
description: 'Name of the binary property containing the last frame image data',
},
{
displayName: 'Aspect Ratio',
name: 'aspectRatio',
type: 'options',
options: [
{
name: 'Landscape (16:9)',
value: '16:9',
},
{
name: 'Portrait (9:16)',
value: '9:16',
}
],
default: '16:9',
description: 'Aspect ratio of the generated video',
},
{
displayName: 'Use LoRA',
name: 'useLoRA',
type: 'boolean',
default: true,
description: 'Whether to use LoRA for style control',
displayOptions: {
show: {
model: ['img2video-14b-lora'],
},
},
},
{
displayName: 'LoRA Type',
name: 'loraType',
type: 'options',
options: Constants_1.GHIBLI_STYLE_OPTIONS,
default: 'ghibli',
description: 'Style type to apply using LoRA',
displayOptions: {
show: {
model: ['img2video-14b-lora'],
useLoRA: [true],
},
},
},
{
displayName: 'LoRA Strength',
name: 'loraStrength',
type: 'number',
typeOptions: {
minValue: 0,
maxValue: 1,
stepSize: 0.1,
},
default: 1,
description: 'Controls the intensity of LoRA influence (0.0-1.0)',
displayOptions: {
show: {
model: ['img2video-14b-lora'],
useLoRA: [true],
},
},
},
{
displayName: 'Image Source',
name: 'imageSource',
type: 'options',
options: [
{
name: 'URL',
value: 'url',
},
{
name: 'Binary Data',
value: 'binaryData',
},
],
default: 'url',
description: 'The source of the input image',
displayOptions: {
show: {
model: ['img2video-14b', 'img2video-14b-lora'],
},
},
},
{
displayName: 'Wait for Completion',
name: 'waitForCompletion',
type: 'boolean',
default: false,
description: 'Wait for task to complete and return results',
},
],
};
}
async execute() {
var _a;
const items = this.getInputData();
const returnData = [];
for (let i = 0; i < items.length; i++) {
const prompt = this.getNodeParameter('prompt', i);
const negativePrompt = this.getNodeParameter('negativePrompt', i, '');
const model = this.getNodeParameter('model', i, 'img2video-14b');
const aspectRatio = this.getNodeParameter('aspectRatio', i);
const waitForCompletion = this.getNodeParameter('waitForCompletion', i, false);
let body;
body = {
model: 'Qubico/wanx',
task_type: model,
input: {
prompt,
negative_prompt: negativePrompt,
aspect_ratio: aspectRatio,
video_resolution: '480P',
},
};
if (model === 'img2video-14b' || model === 'img2video-14b-lora') {
const imageSource = this.getNodeParameter('imageSource', i);
let imageBase64 = '';
if (imageSource === 'url') {
const imageUrl = this.getNodeParameter('imageUrl', i);
try {
new URL(imageUrl);
const imageResponse = await this.helpers.request({
method: 'GET',
url: imageUrl,
encoding: null,
resolveWithFullResponse: true,
});
const buffer = Buffer.from(imageResponse.body);
const contentType = imageResponse.headers['content-type'] || 'image/png';
imageBase64 = `data:${contentType};base64,${buffer.toString('base64')}`;
}
catch (error) {
throw new Error(`Failed to download image from URL: ${error.message}`);
}
}
else {
const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i);
const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName);
if (binaryData.mimeType && !binaryData.mimeType.includes('image/')) {
throw new Error('The provided binary data is not an image. Please provide valid image data.');
}
if (binaryData.data) {
imageBase64 = `data:${binaryData.mimeType};base64,${binaryData.data}`;
}
else if (binaryData.url) {
try {
const imageResponse = await this.helpers.request({
method: 'GET',
url: binaryData.url,
encoding: null,
resolveWithFullResponse: true,
});
const buffer = Buffer.from(imageResponse.body);
const contentType = imageResponse.headers['content-type'] || 'image/png';
imageBase64 = `data:${contentType};base64,${buffer.toString('base64')}`;
}
catch (error) {
throw new Error(`Failed to download image from binary URL: ${error.message}`);
}
}
else {
throw new Error('Could not extract image from binary data. Missing URL or data.');
}
}
body.input.image = imageBase64;
}
if (model === 'img2video-14b-lora') {
const useLoRA = this.getNodeParameter('useLoRA', i, true);
if (useLoRA) {
const loraType = this.getNodeParameter('loraType', i);
const loraStrength = this.getNodeParameter('loraStrength', i, 1);
body.input.lora_settings = [
{
lora_type: loraType,
lora_strength: loraStrength,
},
];
}
}
else if (model === 'img2video-14b-keyframe') {
const firstFrameSource = this.getNodeParameter('firstFrameSource', i);
const lastFrameSource = this.getNodeParameter('lastFrameSource', i);
let firstFrameBase64 = '';
if (firstFrameSource === 'url') {
const firstFrameUrl = this.getNodeParameter('firstFrameUrl', i);
try {
new URL(firstFrameUrl);
const imageResponse = await this.helpers.request({
method: 'GET',
url: firstFrameUrl,
encoding: null,
resolveWithFullResponse: true,
});
const buffer = Buffer.from(imageResponse.body);
const contentType = imageResponse.headers['content-type'] || 'image/png';
firstFrameBase64 = `data:${contentType};base64,${buffer.toString('base64')}`;
}
catch (error) {
throw new Error(`Failed to download first frame image: ${error.message}`);
}
}
else {
const binaryPropertyName = this.getNodeParameter('firstFrameBinaryPropertyName', i);
const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName);
if (binaryData.mimeType && !binaryData.mimeType.includes('image/')) {
throw new Error('The provided first frame binary data is not an image.');
}
if (binaryData.data) {
firstFrameBase64 = `data:${binaryData.mimeType};base64,${binaryData.data}`;
}
else if (binaryData.url) {
try {
const imageResponse = await this.helpers.request({
method: 'GET',
url: binaryData.url,
encoding: null,
resolveWithFullResponse: true,
});
const buffer = Buffer.from(imageResponse.body);
const contentType = imageResponse.headers['content-type'] || 'image/png';
firstFrameBase64 = `data:${contentType};base64,${buffer.toString('base64')}`;
}
catch (error) {
throw new Error(`Failed to download first frame from binary URL: ${error.message}`);
}
}
else {
throw new Error('Could not extract first frame from binary data.');
}
}
let lastFrameBase64 = '';
if (lastFrameSource === 'url') {
const lastFrameUrl = this.getNodeParameter('lastFrameUrl', i);
try {
new URL(lastFrameUrl);
const imageResponse = await this.helpers.request({
method: 'GET',
url: lastFrameUrl,
encoding: null,
resolveWithFullResponse: true,
});
const buffer = Buffer.from(imageResponse.body);
const contentType = imageResponse.headers['content-type'] || 'image/png';
lastFrameBase64 = `data:${contentType};base64,${buffer.toString('base64')}`;
}
catch (error) {
throw new Error(`Failed to download last frame image: ${error.message}`);
}
}
else {
const binaryPropertyName = this.getNodeParameter('lastFrameBinaryPropertyName', i);
const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName);
if (binaryData.mimeType && !binaryData.mimeType.includes('image/')) {
throw new Error('The provided last frame binary data is not an image.');
}
if (binaryData.data) {
lastFrameBase64 = `data:${binaryData.mimeType};base64,${binaryData.data}`;
}
else if (binaryData.url) {
try {
const imageResponse = await this.helpers.request({
method: 'GET',
url: binaryData.url,
encoding: null,
resolveWithFullResponse: true,
});
const buffer = Buffer.from(imageResponse.body);
const contentType = imageResponse.headers['content-type'] || 'image/png';
lastFrameBase64 = `data:${contentType};base64,${buffer.toString('base64')}`;
}
catch (error) {
throw new Error(`Failed to download last frame from binary URL: ${error.message}`);
}
}
else {
throw new Error('Could not extract last frame from binary data.');
}
}
body.input.first_frame = firstFrameBase64;
body.input.last_frame = lastFrameBase64;
}
try {
const response = await GenericFunctions_1.piApiRequest.call(this, 'POST', '/api/v1/task', body);
let taskResult = response;
if (waitForCompletion && ((_a = response.data) === null || _a === void 0 ? void 0 : _a.task_id)) {
taskResult = await GenericFunctions_1.waitForTaskCompletion.call(this, response.data.task_id);
}
returnData.push({
json: taskResult,
});
}
catch (error) {
if (this.continueOnFail()) {
returnData.push({
json: {
error: error.message,
},
});
continue;
}
throw error;
}
}
return [returnData];
}
}
exports.WanXImageToVideo = WanXImageToVideo;
//# sourceMappingURL=WanXImageToVideo.node.js.map
;