n8n-nodes-elevenlabs-enhanced
Version:
Complete implementation of ElevenLabs AI voice generation into n8n workflows.
950 lines • 35 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: 'Text to speech',
description: 'Generate a speech from a text',
routing: {
send: {
preSend: [preSendText],
},
},
},
{
name: 'Voice changer',
value: 'voice-changer',
action: 'Voice Changer',
description: 'Transform audio from one voice to another',
routing: {
send: {
preSend: [preSendUploadAudio],
},
request: {
headers: {
'Content-Type': 'multipart/form-data',
},
},
output: {
postReceive: [returnBinary],
},
},
},
{
name: 'Create Transcript',
value: 'speech-to-text',
action: 'Create transcript',
description: 'Transcribe an audio or video file',
routing: {
request: {
url: '/speech-to-text',
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data',
},
},
send: {
preSend: [preSendTranscript],
},
},
},
{
name: 'Create Sound Effects',
value: 'sound-generation',
action: 'Create sound effects',
description: 'Generate realistic sound effects from text descriptions',
routing: {
request: {
url: '/sound-generation',
method: 'POST',
returnFullResponse: true,
encoding: 'arraybuffer',
},
send: {
preSend: [preSendSoundEffects],
},
output: {
postReceive: [returnBinary],
},
},
},
{
name: 'Audio Isolation',
value: 'audio-isolation',
action: 'Audio isolation',
description: 'Removes background noise from audio and isolates vocals/speech',
routing: {
request: {
url: '/audio-isolation',
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data',
},
returnFullResponse: true,
encoding: 'arraybuffer',
},
send: {
preSend: [preSendAudioIsolation],
},
output: {
postReceive: [returnBinary],
},
},
},
],
default: 'text-to-speech',
},
{
displayName: 'Request Configuration',
name: 'requestConfiguration',
type: 'hidden',
default: '',
displayOptions: {
show: {
operation: ['text-to-speech', 'voice-changer'],
},
},
routing: {
request: {
url: '={{"/"+$parameter["operation"]+"/"+$parameter["voice_id"]+($parameter["include_timing"] ? "/with-timestamps" : "")}}',
qs: {
optimize_streaming_latency: '={{$parameter["additionalFields"]["optimize_streaming_latency"]}}',
output_format: '={{$parameter["additionalFields"]["output_format"]}}',
enable_logging: '={{$parameter["additionalFields"]["enable_logging"]}}',
},
returnFullResponse: true,
encoding: 'arraybuffer',
},
output: {
postReceive: [returnBinaryOrTiming],
},
},
},
{
displayName: 'Request Configuration',
name: 'requestConfigurationTranscript',
type: 'hidden',
default: '',
displayOptions: {
show: {
operation: ['speech-to-text'],
},
},
routing: {
request: {
url: '/speech-to-text',
qs: {
enable_logging: '={{$parameter["additionalFields"]["enable_logging"]}}',
},
returnFullResponse: true,
},
},
},
{
displayName: 'Request Configuration',
name: 'requestConfigurationSoundEffects',
type: 'hidden',
default: '',
displayOptions: {
show: {
operation: ['sound-generation'],
},
},
routing: {
request: {
url: '/sound-generation',
qs: {
output_format: '={{$parameter["additionalFields"]["output_format"]}}',
},
returnFullResponse: true,
encoding: 'arraybuffer',
},
output: {
postReceive: [returnBinary],
},
},
},
{
displayName: 'Request Configuration',
name: 'requestConfigurationAudioIsolation',
type: 'hidden',
default: '',
displayOptions: {
show: {
operation: ['audio-isolation'],
},
},
routing: {
request: {
url: '/audio-isolation',
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!',
typeOptions: {
rows: 5,
},
displayOptions: {
show: {
operation: ['text-to-speech'],
},
},
},
{
displayName: 'Include Character Timing',
description: 'Whether to include character-level timing information in the response. When enabled, returns JSON with base64 audio and timing data instead of binary audio.',
name: 'include_timing',
type: 'boolean',
default: false,
displayOptions: {
show: {
operation: ['text-to-speech'],
},
},
},
{
displayName: 'Sound Effect Description',
description: 'Describe the sound effect you want to generate',
required: true,
name: 'soundEffectText',
type: 'string',
default: 'Spacious braam suitable for high-impact movie trailer moments',
typeOptions: {
rows: 3,
},
displayOptions: {
show: {
operation: ['sound-generation'],
},
},
},
{
displayName: 'Binary Input Field',
name: 'binaryInputField',
type: 'string',
default: 'data',
required: true,
description: 'Name of the binary property that contains the audio file to transform',
displayOptions: {
show: {
operation: ['voice-changer'],
},
},
},
{
displayName: 'Binary Input Field',
name: 'binaryInputField',
type: 'string',
default: 'data',
required: true,
description: 'Name of the binary property that contains the audio/video file to transcribe',
displayOptions: {
show: {
operation: ['speech-to-text'],
},
},
},
{
displayName: 'Binary Input Field',
name: 'binaryInputField',
type: 'string',
default: 'data',
required: true,
description: 'Name of the binary property that contains the audio file to isolate vocals/speech from',
displayOptions: {
show: {
operation: ['audio-isolation'],
},
},
},
{
displayName: 'Voice ID',
description: 'The voice you want to use',
name: 'voice_id',
type: 'resourceLocator',
default: { mode: 'list', value: null },
modes: [
{
displayName: 'From list',
name: 'list',
type: 'list',
hint: 'Choose from list',
typeOptions: {
searchListMethod: 'listVoices',
searchable: true,
},
},
{
displayName: 'ID',
name: 'id',
type: 'string',
hint: 'Enter an ID',
placeholder: 'XYZ123',
},
],
displayOptions: {
show: {
resource: ['speech'],
operation: ['text-to-speech', 'voice-changer'],
},
},
required: true,
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
default: {},
placeholder: 'Add Fields',
displayOptions: {
show: {
resource: ['speech'],
},
},
options: [
{
displayName: 'Binary Name',
description: 'Change the output binary name.',
name: 'binary_name',
type: 'string',
default: 'data',
displayOptions: {
show: {
'/operation': ['text-to-speech', 'voice-changer', 'sound-generation'],
},
},
},
{
displayName: 'File Name',
description: 'Change the output file name.',
name: 'file_name',
type: 'string',
default: 'voice',
displayOptions: {
show: {
'/operation': ['text-to-speech', 'voice-changer', 'sound-generation'],
},
},
},
{
displayName: 'Streaming Latency',
description: 'Turn on latency optimizations at some cost of quality. Values: 0 (default - no optimizations), 1 (normal - 50% improvement), 2 (strong - 75% improvement), 3 (max), 4 (max with text normalizer off).',
name: 'optimize_streaming_latency',
type: 'number',
default: 0,
typeOptions: {
maxValue: 4,
minValue: 0,
numberStepSize: 1,
},
displayOptions: {
show: {
'/operation': ['text-to-speech', 'voice-changer'],
},
},
},
{
displayName: 'Output Format',
description: 'Output format of the generated audio',
name: 'output_format',
type: 'options',
options: [
{ name: 'MP3 (44.1kHz, 128kbps)', value: 'mp3_44100_128' },
{ name: 'MP3 (44.1kHz, 192kbps)', value: 'mp3_44100_192' },
{ name: 'PCM (16-bit, 22.05kHz)', value: 'pcm_22050' },
{ name: 'PCM (16-bit, 24kHz)', value: 'pcm_24000' },
{ name: 'PCM (16-bit, 44.1kHz)', value: 'pcm_16000' },
{ name: 'PCM (24-bit, 44.1kHz)', value: 'pcm_24000_24' },
{ name: 'μ-law (8-bit, 8kHz)', value: 'ulaw_8000' },
],
default: 'mp3_44100_128',
displayOptions: {
show: {
'/operation': ['text-to-speech', 'voice-changer', 'sound-generation'],
},
},
},
{
displayName: 'Language Code',
description: 'Language code (ISO 639-1) used to enforce a language for the model. IMPORTANT: Currently ONLY works with Turbo v2.5 and Flash v2.5 models!.',
name: 'language_code',
type: 'string',
default: '',
placeholder: 'en',
displayOptions: {
show: {
'/operation': ['text-to-speech'],
},
},
},
{
displayName: 'Model Name or ID',
description: 'Identifier of the model that will be used. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
name: 'model_id',
type: 'options',
typeOptions: {
loadOptionsMethod: 'listModels',
},
default: '',
displayOptions: {
show: {
'/operation': ['text-to-speech'],
},
},
},
{
displayName: 'Model Name or ID',
description: 'Identifier of the model that will be used. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
name: 'model_id',
type: 'options',
typeOptions: {
loadOptionsMethod: 'listModels',
},
default: '',
displayOptions: {
show: {
'/operation': ['voice-changer'],
},
},
},
{
displayName: 'Stability',
description: 'Define voice stability',
name: 'stability',
type: 'number',
default: 0.5,
typeOptions: {
maxValue: 1,
minValue: 0,
numberStepSize: 0.01,
},
displayOptions: {
show: {
'/operation': ['text-to-speech', 'voice-changer'],
},
},
},
{
displayName: 'Similarity Boost',
description: 'Define voice similarity boost',
name: 'similarity_boost',
type: 'number',
default: 0.75,
typeOptions: {
maxValue: 1,
minValue: 0,
numberStepSize: 0.01,
},
displayOptions: {
show: {
'/operation': ['text-to-speech', 'voice-changer'],
},
},
},
{
displayName: 'Style',
description: 'Exaggerate voice style',
name: 'style',
type: 'number',
default: 0,
typeOptions: {
maxValue: 1,
minValue: 0,
numberStepSize: 0.01,
},
displayOptions: {
show: {
'/operation': ['text-to-speech', 'voice-changer'],
},
},
},
{
displayName: 'Speaker Boost',
description: 'Whether speaker boost is activated',
name: 'use_speaker_boost',
type: 'boolean',
default: false,
displayOptions: {
show: {
'/operation': ['text-to-speech', 'voice-changer'],
},
},
},
{
displayName: 'Seed',
description: 'Makes TTS deterministic. Providing the same seed with the same text will result in the same audio output. Values between 0-4294967295',
name: 'seed',
type: 'number',
default: 0,
typeOptions: {
minValue: 0,
maxValue: 4294967295,
},
displayOptions: {
show: {
'/operation': ['text-to-speech', 'voice-changer'],
},
},
},
{
displayName: 'Enable Logging',
description: 'Whether to enable logging. False means zero retention mode (history features unavailable)',
name: 'enable_logging',
type: 'boolean',
default: true,
displayOptions: {
show: {
'/operation': ['text-to-speech', 'voice-changer', 'speech-to-text'],
},
},
},
{
displayName: 'Text Normalization',
description: 'Controls text normalization. Auto (system decides), On (always applied), Off (skipped)',
name: 'apply_text_normalization',
type: 'options',
options: [
{ name: 'Auto', value: 'auto' },
{ name: 'On', value: 'on' },
{ name: 'Off', value: 'off' },
],
default: 'auto',
displayOptions: {
show: {
'/operation': ['text-to-speech'],
},
},
},
{
displayName: 'Use PVC as IVC',
description: 'If true, won\'t use PVC version of the voice for generation but the IVC version',
name: 'use_pvc_as_ivc',
type: 'boolean',
default: false,
displayOptions: {
show: {
'/operation': ['text-to-speech'],
},
},
},
{
displayName: 'Stitching',
description: 'Whether stitching is activated (give the model context by passing past and previous text)',
name: 'stitching',
type: 'boolean',
default: true,
displayOptions: {
show: {
'/operation': ['text-to-speech'],
},
},
},
{
displayName: 'Previous Request IDs',
description: 'A list of request_ids of samples generated before this generation (max 3)',
name: 'previous_request_ids',
type: 'string',
default: '',
placeholder: 'id1,id2,id3',
displayOptions: {
show: {
stitching: [true],
'/operation': ['text-to-speech'],
},
},
},
{
displayName: 'Next Request IDs',
description: 'A list of request_ids of samples that come after this generation (max 3)',
name: 'next_request_ids',
type: 'string',
default: '',
placeholder: 'id1,id2,id3',
displayOptions: {
show: {
stitching: [true],
'/operation': ['text-to-speech'],
},
},
},
{
displayName: 'Remove Background Noise',
description: 'Whether to remove background noise from the audio input',
name: 'remove_background_noise',
type: 'boolean',
default: false,
displayOptions: {
show: {
'/operation': ['voice-changer'],
},
},
},
{
displayName: 'Transcript Model ID',
description: 'Model to use for transcription. Currently only "scribe_v1" is available',
name: 'transcript_model_id',
type: 'string',
default: 'scribe_v1',
displayOptions: {
show: {
'/operation': ['speech-to-text'],
},
},
},
{
displayName: 'Language Code',
description: 'ISO-639-1 or ISO-639-3 language code of the audio. If not provided, language is auto-detected',
name: 'transcript_language_code',
type: 'string',
default: '',
placeholder: 'en',
displayOptions: {
show: {
'/operation': ['speech-to-text'],
},
},
},
{
displayName: 'Tag Audio Events',
description: 'Whether to tag audio events like (laughter), (footsteps), etc. in the transcription',
name: 'tag_audio_events',
type: 'boolean',
default: true,
displayOptions: {
show: {
'/operation': ['speech-to-text'],
},
},
},
{
displayName: 'Number of Speakers',
description: 'Maximum amount of speakers in the audio (helps with prediction)',
name: 'num_speakers',
type: 'number',
default: '',
placeholder: '2',
typeOptions: {
minValue: 1,
maxValue: 32,
},
displayOptions: {
show: {
'/operation': ['speech-to-text'],
},
},
},
{
displayName: 'Timestamps Granularity',
description: 'Granularity of timestamps in the transcription',
name: 'timestamps_granularity',
type: 'options',
options: [
{ name: 'None', value: 'none' },
{ name: 'Word', value: 'word' },
{ name: 'Character', value: 'character' },
],
default: 'word',
displayOptions: {
show: {
'/operation': ['speech-to-text'],
},
},
},
{
displayName: 'Speaker Diarization',
description: 'Whether to annotate which speaker is talking in the uploaded file.',
name: 'diarize',
type: 'boolean',
default: false,
displayOptions: {
show: {
'/operation': ['speech-to-text'],
},
},
},
{
displayName: 'Duration (Seconds)',
description: 'Duration of the generated sound effect in seconds (0.5-22 seconds)',
name: 'duration_seconds',
type: 'number',
default: '',
placeholder: 'Auto',
typeOptions: {
minValue: 5,
maxValue: 10,
numberStepSize: 1,
},
displayOptions: {
show: {
'/operation': ['sound-generation'],
},
},
},
{
displayName: 'Prompt Influence',
description: 'Controls how closely the sound follows the prompt. Higher values = less variation but more faithful to prompt',
name: 'prompt_influence',
type: 'number',
default: 0.7,
typeOptions: {
minValue: 0,
maxValue: 1,
numberStepSize: 0.1,
},
displayOptions: {
show: {
'/operation': ['sound-generation'],
},
},
},
],
},
];
async function preSendUploadAudio(requestOptions) {
const binaryInputField = this.getNodeParameter('binaryInputField', 'data');
const additionalFields = this.getNodeParameter('additionalFields', {});
const audioBuffer = await this.helpers.getBinaryDataBuffer(binaryInputField);
const formData = new FormData();
formData.append('audio', new Blob([audioBuffer]));
formData.append('model_id', additionalFields.model_id || 'eleven_english_sts_v2');
if (additionalFields.seed && additionalFields.seed !== 0) {
formData.append('seed', additionalFields.seed);
}
if (additionalFields.remove_background_noise !== undefined) {
formData.append('remove_background_noise', String(additionalFields.remove_background_noise));
}
const voiceSettings = {
stability: additionalFields.stability || 0.5,
similarity_boost: additionalFields.similarity_boost || 0.75,
style: additionalFields.style || 0,
use_speaker_boost: additionalFields.use_speaker_boost || false,
};
formData.append('voice_settings', JSON.stringify(voiceSettings));
if (additionalFields.enable_logging !== undefined) {
requestOptions.qs = {
...requestOptions.qs,
enable_logging: additionalFields.enable_logging,
};
}
if (additionalFields.output_format) {
requestOptions.qs = {
...requestOptions.qs,
output_format: additionalFields.output_format,
};
}
if (additionalFields.optimize_streaming_latency !== undefined) {
requestOptions.qs = {
...requestOptions.qs,
optimize_streaming_latency: additionalFields.optimize_streaming_latency,
};
}
requestOptions.body = formData;
return requestOptions;
}
async function preSendText(requestOptions) {
var _a;
const text = this.getNodeParameter('text');
const additionalFields = this.getNodeParameter('additionalFields', {});
const model_id = additionalFields.model_id || 'eleven_monolingual_v1';
const seed = additionalFields.seed !== undefined ? additionalFields.seed : 0;
const language_code = additionalFields.language_code;
const apply_text_normalization = additionalFields.apply_text_normalization;
const use_pvc_as_ivc = additionalFields.use_pvc_as_ivc;
const stitching = additionalFields.stitching;
const stability = additionalFields.stability !== undefined ? additionalFields.stability : 0.5;
const similarity_boost = additionalFields.similarity_boost !== undefined ? additionalFields.similarity_boost : 0.75;
const style = additionalFields.style !== undefined ? additionalFields.style : 0;
const use_speaker_boost = additionalFields.use_speaker_boost;
const data = {
text,
model_id,
voice_settings: {
stability,
similarity_boost,
style,
use_speaker_boost,
},
};
if (language_code && (model_id.includes('turbo_v2') || model_id.includes('flash_v2'))) {
data.language_code = language_code;
}
if (seed !== 0)
data.seed = seed;
if (apply_text_normalization)
data.apply_text_normalization = apply_text_normalization;
if (use_pvc_as_ivc !== undefined)
data.use_pvc_as_ivc = use_pvc_as_ivc;
if (additionalFields.enable_logging !== undefined) {
requestOptions.qs = {
...requestOptions.qs,
enable_logging: additionalFields.enable_logging,
};
}
if (additionalFields.output_format) {
requestOptions.qs = {
...requestOptions.qs,
output_format: additionalFields.output_format,
};
}
if (additionalFields.optimize_streaming_latency !== undefined) {
requestOptions.qs = {
...requestOptions.qs,
optimize_streaming_latency: additionalFields.optimize_streaming_latency,
};
}
if (stitching) {
if (seed)
data.seed = seed;
if (model_id)
data.model_id = model_id;
const runIndex = this.getItemIndex();
const texts = [];
(_a = this.getExecuteData().data.main[0]) === null || _a === void 0 ? void 0 : _a.forEach((item) => {
texts.push(item.json.text);
});
if (runIndex > 0)
data.previous_text = texts[runIndex - 1];
if (runIndex < texts.length - 1)
data.next_text = texts[runIndex + 1];
const previousRequestIds = (additionalFields.previous_request_ids || '').split(',').filter(Boolean);
const nextRequestIds = (additionalFields.next_request_ids || '').split(',').filter(Boolean);
if (previousRequestIds.length > 0) {
data.previous_request_ids = previousRequestIds.slice(0, 3);
}
if (nextRequestIds.length > 0) {
data.next_request_ids = nextRequestIds.slice(0, 3);
}
}
requestOptions.body = data;
return requestOptions;
}
async function preSendSoundEffects(requestOptions) {
var _a;
const soundEffectText = this.getNodeParameter('soundEffectText');
const additionalFields = this.getNodeParameter('additionalFields', {});
const duration_seconds = additionalFields.duration_seconds;
const prompt_influence = (_a = additionalFields.prompt_influence) !== null && _a !== void 0 ? _a : 0.3;
const output_format = additionalFields.output_format;
const data = {
text: soundEffectText,
};
if (duration_seconds) {
data.duration_seconds = duration_seconds;
}
if (prompt_influence !== undefined) {
data.prompt_influence = prompt_influence;
}
if (output_format) {
requestOptions.qs = {
...requestOptions.qs,
output_format,
};
}
requestOptions.body = data;
return requestOptions;
}
async function preSendAudioIsolation(requestOptions) {
const binaryInputField = this.getNodeParameter('binaryInputField', 'data');
const audioBuffer = await this.helpers.getBinaryDataBuffer(binaryInputField);
const formData = new FormData();
formData.append('audio', new Blob([audioBuffer]));
requestOptions.body = formData;
return requestOptions;
}
async function preSendTranscript(requestOptions) {
const binaryInputField = this.getNodeParameter('binaryInputField', 'data');
const additionalFields = this.getNodeParameter('additionalFields', {});
const audioBuffer = await this.helpers.getBinaryDataBuffer(binaryInputField);
const model_id = additionalFields.transcript_model_id || 'scribe_v1';
const language_code = additionalFields.transcript_language_code;
const tag_audio_events = additionalFields.tag_audio_events;
const num_speakers = additionalFields.num_speakers;
const timestamps_granularity = additionalFields.timestamps_granularity;
const diarize = additionalFields.diarize;
const enable_logging = additionalFields.enable_logging;
const formData = new FormData();
formData.append('file', new Blob([audioBuffer]));
formData.append('model_id', model_id);
if (language_code) {
formData.append('language_code', language_code);
}
if (tag_audio_events !== undefined) {
formData.append('tag_audio_events', String(tag_audio_events));
}
if (num_speakers) {
formData.append('num_speakers', String(num_speakers));
}
if (timestamps_granularity) {
formData.append('timestamps_granularity', timestamps_granularity);
}
if (diarize !== undefined) {
formData.append('diarize', String(diarize));
}
if (enable_logging !== undefined) {
requestOptions.qs = {
...requestOptions.qs,
enable_logging,
};
}
requestOptions.body = formData;
return requestOptions;
}
async function returnBinary(items, responseData) {
const binary_name = this.getNodeParameter('additionalFields["binary_name"]', 'data');
let file_name = this.getNodeParameter('additionalFields["file_name"]', 'voice');
const operation = this.getNodeParameter('operation');
if (operation === 'sound-generation' && file_name === 'voice') {
file_name = 'sound_effect';
}
else if (operation === 'audio-isolation' && file_name === 'voice') {
file_name = 'isolated_audio';
}
const binaryData = await this.helpers.prepareBinaryData(responseData.body, file_name, 'audio/mp3');
return items.map(() => ({ json: responseData.headers, binary: { [binary_name]: binaryData } }));
}
async function returnBinaryOrTiming(items, responseData) {
const include_timing = this.getNodeParameter('include_timing', false);
if (include_timing) {
const jsonResponse = JSON.parse(responseData.body);
const binary_name = this.getNodeParameter('additionalFields["binary_name"]', 'data');
let file_name = this.getNodeParameter('additionalFields["file_name"]', 'voice');
const audioBuffer = Buffer.from(jsonResponse.audio_base64, 'base64');
const binaryData = await this.helpers.prepareBinaryData(audioBuffer, file_name, 'audio/mp3');
return items.map(() => ({
json: {
...responseData.headers,
alignment: jsonResponse.alignment,
normalized_alignment: jsonResponse.normalized_alignment,
},
binary: { [binary_name]: binaryData }
}));
}
else {
return returnBinary.call(this, items, responseData);
}
}
//# sourceMappingURL=speech.js.map