n8n-nodes-elevenlabs-langfix
Version:
Complete implementation of ElevenLabs AI voice generation into n8n workflows.
420 lines • 16.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SpeechOperations = void 0;
exports.SpeechOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['speech'],
},
},
options: [
{
name: 'Text to Speech',
value: 'text-to-speech',
action: 'Generate speech from text',
description: 'Generate a speech from a text',
routing: {
send: {
preSend: [preSendText],
},
},
},
{
name: 'Speech to Speech',
value: 'speech-to-speech',
action: 'Generate speech from speech',
description: 'Generate a speech from a speech',
routing: {
send: {
preSend: [preSendUploadAudio],
},
request: {
headers: {
'Content-Type': 'multipart/form-data',
},
},
output: {
postReceive: [returnBinary],
},
},
},
],
default: 'text-to-speech',
routing: {
request: {
url: '={{"/text-to-speech/" + $parameter["voice_id"]}}',
qs: {
optimize_streaming_latency: '={{$parameter["additionalFields"]["optimize_streaming_latency"]}}',
output_format: '={{$parameter["additionalFields"]["output_format"]}}',
},
returnFullResponse: true,
encoding: 'arraybuffer',
},
output: {
postReceive: [returnBinary],
},
},
},
{
displayName: 'Text',
description: 'The Text to transform into Speech',
required: true,
name: 'text',
type: 'string',
default: 'Be good to people!',
displayOptions: {
show: {
operation: ['text-to-speech'],
},
},
},
{
displayName: 'Voice Name or ID',
name: 'voice_id',
type: 'options',
description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'listVoices',
},
default: '',
required: true,
displayOptions: {
show: {
resource: ['speech'],
},
},
},
{
displayName: 'Model Name or ID',
name: 'model_id',
type: 'options',
description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>',
typeOptions: {
loadOptionsMethod: 'listModels',
},
default: '',
required: true,
displayOptions: {
show: {
resource: ['speech'],
},
},
},
{
displayName: 'Language Support Notice',
name: 'languageNotice',
type: 'notice',
default: 'Language codes are only supported by the Turbo v2.5 model. Other models will return an error if a language code is provided.',
displayOptions: {
show: {
resource: ['speech'],
},
},
},
{
displayName: 'Options',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
resource: ['speech'],
},
},
options: [
{
displayName: 'Binary Name',
description: 'Name of the binary property to which to write the data',
name: 'binary_name',
type: 'string',
default: 'data',
},
{
displayName: 'File Name',
description: 'Name of the generated audio file',
name: 'file_name',
type: 'string',
default: 'voice',
},
{
displayName: 'Output Format',
description: 'Audio format of the generated speech',
name: 'output_format',
type: 'options',
options: [
{
name: 'FLAC (16kHz)',
value: 'flac_16000',
},
{
name: 'FLAC (24kHz)',
value: 'flac_24000',
},
{
name: 'MP3 (44.1KHz, 128kbps)',
value: 'mp3_44100_128',
},
{
name: 'MP3 (44.1KHz, 64kbps)',
value: 'mp3_44100_64',
},
{
name: 'MULAW (16kHz)',
value: 'mulaw_16000',
},
{
name: 'WAVE (16kHz)',
value: 'pcm_16000',
},
{
name: 'WAVE (24kHz)',
value: 'pcm_24000',
},
],
default: 'mp3_44100_128',
},
{
displayName: 'Voice Settings',
name: 'voiceSettings',
type: 'fixedCollection',
default: {},
placeholder: 'Add Voice Setting',
options: [
{
name: 'values',
displayName: 'Voice Settings',
values: [
{
displayName: 'Similarity Boost',
description: 'How much the voice should match the original voice (0-1)',
name: 'similarity_boost',
type: 'number',
default: 0.75,
typeOptions: {
maxValue: 1,
minValue: 0,
numberStepSize: 0.01,
},
hint: '0 = more freedom, 1 = more similar',
},
{
displayName: 'Stability',
description: 'How much the voice should vary across re-generations (0-1)',
name: 'stability',
type: 'number',
default: 0.5,
typeOptions: {
maxValue: 1,
minValue: 0,
numberStepSize: 0.01,
},
hint: '0 = more variable, 1 = more stable',
},
{
displayName: 'Style',
description: 'Amount of style to apply to the voice (0-1)',
name: 'style',
type: 'number',
default: 0,
typeOptions: {
maxValue: 1,
minValue: 0,
numberStepSize: 0.01,
},
hint: '0 = neutral style, 1 = maximum style',
},
{
displayName: 'Speaker Boost',
description: 'Whether to enhance voice clarity and target background noise',
name: 'use_speaker_boost',
type: 'boolean',
default: true,
},
],
},
],
},
{
displayName: 'Streaming Latency',
description: 'Optimize for streaming with lower latency but potentially reduced quality',
name: 'optimize_streaming_latency',
type: 'options',
options: [
{
name: 'Default (No Optimization)',
value: 0,
description: 'Best quality, highest latency',
},
{
name: 'Normal Optimization',
value: 1,
description: '~50% latency improvement',
},
{
name: 'Strong Optimization',
value: 2,
description: '~75% latency improvement',
},
{
name: 'Maximum Optimization',
value: 3,
description: 'Best possible latency',
},
{
name: 'Maximum + No Text Normalization',
value: 4,
description: 'Fastest but may mispronounce numbers/dates',
},
],
default: 0,
},
{
displayName: 'Text Normalization',
description: 'Controls how text is normalized before generation',
name: 'apply_text_normalization',
type: 'options',
options: [
{
name: 'Auto',
value: 'auto',
description: 'System decides whether to normalize text',
},
{
name: 'On',
value: 'on',
description: 'Always normalize text',
},
{
name: 'Off',
value: 'off',
description: 'Never normalize text',
},
],
default: 'auto',
hint: 'Cannot be turned on for Turbo v2.5 model',
},
{
displayName: 'Language Code',
description: 'ISO 639-1 language code (e.g., en, de, fr)',
name: 'language_code',
type: 'string',
default: '',
placeholder: 'en',
hint: 'Only works with Turbo v2.5 model',
},
{
displayName: 'Next Text',
description: 'Text that comes after the current text',
name: 'next_text',
type: 'string',
default: '',
typeOptions: {
rows: 2,
},
hint: 'Helps improve prosody when concatenating generations',
},
{
displayName: 'Previous Text',
description: 'Text that came before the current text',
name: 'previous_text',
type: 'string',
default: '',
typeOptions: {
rows: 2,
},
hint: 'Helps improve prosody when concatenating generations',
},
{
displayName: 'Seed',
description: 'Fix the random seed for consistent results (0-4294967295)',
name: 'seed',
type: 'number',
typeOptions: {
maxValue: 4294967295,
minValue: 0,
},
default: 0,
hint: 'Use the same seed to get the same voice output',
},
],
},
];
async function preSendUploadAudio(requestOptions) {
const formData = new FormData();
const audioBuffer = await this.helpers.getBinaryDataBuffer('data');
formData.append('audio', new Blob([audioBuffer]));
requestOptions.body = formData;
return requestOptions;
}
async function preSendText(requestOptions) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
const text = this.getNodeParameter('text');
const model_id = this.getNodeParameter('model_id');
const voice_id = this.getNodeParameter('voice_id');
let data = {
text,
model_id,
voice_settings: {},
};
try {
const voiceDetails = await this.helpers.httpRequestWithAuthentication.call(this, 'elevenLabsApi', {
method: 'GET',
url: `https://api.elevenlabs.io/v1/voices/${voice_id}`,
qs: {
with_settings: true,
},
});
const voiceSettings = this.getNodeParameter('additionalFields.voiceSettings.values', {});
data.voice_settings = {
stability: (_c = (_a = voiceSettings.stability) !== null && _a !== void 0 ? _a : (_b = voiceDetails.settings) === null || _b === void 0 ? void 0 : _b.stability) !== null && _c !== void 0 ? _c : 0.5,
similarity_boost: (_f = (_d = voiceSettings.similarity_boost) !== null && _d !== void 0 ? _d : (_e = voiceDetails.settings) === null || _e === void 0 ? void 0 : _e.similarity_boost) !== null && _f !== void 0 ? _f : 0.75,
style: (_j = (_g = voiceSettings.style) !== null && _g !== void 0 ? _g : (_h = voiceDetails.settings) === null || _h === void 0 ? void 0 : _h.style) !== null && _j !== void 0 ? _j : 0,
use_speaker_boost: (_m = (_k = voiceSettings.use_speaker_boost) !== null && _k !== void 0 ? _k : (_l = voiceDetails.settings) === null || _l === void 0 ? void 0 : _l.use_speaker_boost) !== null && _m !== void 0 ? _m : true,
};
if ((_o = voiceDetails.labels) === null || _o === void 0 ? void 0 : _o.language) {
data.language_code = this.getNodeParameter('additionalFields.language_code', voiceDetails.labels.language);
}
}
catch (error) {
console.error('Failed to get voice details:', error);
const voiceSettings = this.getNodeParameter('additionalFields.voiceSettings.values', {});
data.voice_settings = {
stability: (_p = voiceSettings.stability) !== null && _p !== void 0 ? _p : 0.5,
similarity_boost: (_q = voiceSettings.similarity_boost) !== null && _q !== void 0 ? _q : 0.75,
style: (_r = voiceSettings.style) !== null && _r !== void 0 ? _r : 0,
use_speaker_boost: (_s = voiceSettings.use_speaker_boost) !== null && _s !== void 0 ? _s : true,
};
data.language_code = this.getNodeParameter('additionalFields.language_code', '');
}
const textNormalization = this.getNodeParameter('additionalFields.apply_text_normalization', 'auto');
if (textNormalization !== 'auto') {
data.apply_text_normalization = textNormalization;
}
const seed = this.getNodeParameter('additionalFields.seed', undefined);
if (seed !== undefined) {
data.seed = seed;
}
const previousText = this.getNodeParameter('additionalFields.previous_text', undefined);
if (previousText) {
data.previous_text = previousText;
}
const nextText = this.getNodeParameter('additionalFields.next_text', undefined);
if (nextText) {
data.next_text = nextText;
}
requestOptions.body = data;
return requestOptions;
}
async function returnBinary(items, responseData) {
const binary_name = this.getNodeParameter('additionalFields["binary_name"]', 'data');
const file_name = this.getNodeParameter('additionalFields["file_name"]', 'voice');
const binaryData = await this.helpers.prepareBinaryData(responseData.body, file_name, 'audio/mp3');
return items.map(() => ({ json: responseData.headers, binary: { [binary_name]: binaryData } }));
}
//# sourceMappingURL=speech.js.map