UNPKG

n8n-nodes-piapi

Version:

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

276 lines 9.98 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.KlingLipSync = void 0; exports.loadTtsVoices = loadTtsVoices; exports.loadTtsEmotions = loadTtsEmotions; const GenericFunctions_1 = require("../shared/GenericFunctions"); const VOICE_EMOTIONS_MAP = { "Rock": { name: "Rock", emotions: ["Happy", "Sad", "Angry", "Neutral"] }, "Funny": { name: "Funny", emotions: ["Happy", "Excited", "Neutral"] }, "Gentle": { name: "Gentle", emotions: ["Calm", "Sad", "Neutral"] }, }; async function loadTtsVoices() { try { const response = await this.helpers.request({ method: 'GET', url: 'https://klingai.com/api/lip/sync/ttsList?type=', json: true, }); if (!response || !Array.isArray(response)) { return Object.keys(VOICE_EMOTIONS_MAP).map(voice => ({ name: voice, value: voice, })); } return response.map((voice) => ({ name: voice, value: voice, })); } catch (error) { return Object.keys(VOICE_EMOTIONS_MAP).map(voice => ({ name: voice, value: voice, })); } } async function loadTtsEmotions() { const currentVoice = this.getCurrentNodeParameter('ttsVoice'); if (!currentVoice || !VOICE_EMOTIONS_MAP[currentVoice]) { return [{ name: 'Neutral', value: 'Neutral' }]; } const emotions = VOICE_EMOTIONS_MAP[currentVoice].emotions; return emotions.map(emotion => ({ name: emotion, value: emotion })); } class KlingLipSync { constructor() { this.methods = { loadOptions: { loadTtsVoices, loadTtsEmotions, }, }; this.description = { displayName: 'PiAPI Kling Lip Sync', name: 'klingLipSync', icon: 'file:../piapi.svg', group: ['transform'], version: 1, description: 'Apply lip sync to a Kling video with text-to-speech or audio', defaults: { name: 'Kling Lip Sync', }, inputs: ["main"], outputs: ["main"], credentials: [ { name: 'piAPIApi', required: true, }, ], properties: [ { displayName: 'Original Task ID', name: 'originTaskId', type: 'string', default: '', required: true, description: 'The task ID of the original Kling video', }, { displayName: 'Audio Source', name: 'audioSource', type: 'options', options: [ { name: 'Text-to-Speech', value: 'tts', description: 'Generate audio from text using TTS', }, { name: 'Audio URL', value: 'audioUrl', description: 'Use existing audio file from URL', }, ], default: 'tts', description: 'Source of audio for lip syncing', }, { displayName: 'TTS Text', name: 'ttsText', type: 'string', typeOptions: { rows: 4, }, default: '', required: true, displayOptions: { show: { audioSource: ['tts'], }, }, description: 'Text to convert to speech', }, { displayName: 'Voice', name: 'ttsVoice', type: 'options', typeOptions: { loadOptionsMethod: 'loadTtsVoices', }, default: 'Rock', required: true, displayOptions: { show: { audioSource: ['tts'], }, }, description: 'Voice to use for text-to-speech', }, { displayName: 'Emotion', name: 'ttsEmotion', type: 'options', typeOptions: { loadOptionsMethod: 'loadTtsEmotions', }, default: 'Neutral', required: false, displayOptions: { show: { audioSource: ['tts'], }, }, description: 'Emotional style for the selected voice', }, { displayName: 'Speech Speed', name: 'ttsSpeed', type: 'number', typeOptions: { minValue: 0.8, maxValue: 2, numberPrecision: 1, }, default: 1, displayOptions: { show: { audioSource: ['tts'], }, }, description: 'Speed of the speech (0.8-2)', }, { displayName: 'Audio URL', name: 'audioUrl', type: 'string', default: '', required: true, displayOptions: { show: { audioSource: ['audioUrl'], }, }, description: 'URL to your audio file (mp3, wav, flac, ogg)', }, { displayName: 'Wait for Completion', name: 'waitForCompletion', type: 'boolean', default: false, description: 'Whether to wait for the task to complete before returning', }, ], }; } async execute() { const items = this.getInputData(); const returnData = []; for (let i = 0; i < items.length; i++) { try { const originTaskId = this.getNodeParameter('originTaskId', i); const audioSource = this.getNodeParameter('audioSource', i); const waitForCompletion = this.getNodeParameter('waitForCompletion', i, false); const body = { model: 'kling', task_type: 'lip_sync', input: { origin_task_id: originTaskId, }, config: { webhook_config: { endpoint: '', secret: '', }, }, }; if (audioSource === 'tts') { const ttsText = this.getNodeParameter('ttsText', i); const ttsVoice = this.getNodeParameter('ttsVoice', i); const ttsEmotion = this.getNodeParameter('ttsEmotion', i, 'Neutral'); const ttsSpeed = this.getNodeParameter('ttsSpeed', i); let voiceWithEmotion = ttsVoice; if (ttsEmotion && ttsEmotion !== 'Neutral') { voiceWithEmotion = `${ttsVoice}-${ttsEmotion}`; } body.input.tts_text = ttsText; body.input.tts_timbre = voiceWithEmotion; body.input.tts_speed = ttsSpeed; body.input.local_dubbing_url = ''; } else { const audioUrl = this.getNodeParameter('audioUrl', i); try { new URL(audioUrl); } catch (error) { throw new Error(`Invalid audio URL: ${error.message}`); } body.input.tts_text = ''; body.input.tts_timbre = ''; body.input.tts_speed = 1; body.input.local_dubbing_url = audioUrl; } const response = await GenericFunctions_1.piApiRequest.call(this, 'POST', '/api/v1/task', body); if (response.code !== 200) { throw new Error(`API Error: ${response.message}`); } const taskId = response.data.task_id; let taskData = response.data; if (waitForCompletion) { taskData = await GenericFunctions_1.waitForTaskCompletion.call(this, taskId); } returnData.push({ json: taskData, }); } catch (error) { if (this.continueOnFail()) { returnData.push({ json: { error: error.message, }, }); continue; } throw error; } } return [returnData]; } } exports.KlingLipSync = KlingLipSync; //# sourceMappingURL=KlingLipSync.node.js.map