UNPKG

n8n-nodes-salutespeech

Version:

User-friendly SaluteSpeech (Sber) community node

385 lines 16.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SaluteSpeech = void 0; const n8n_workflow_1 = require("n8n-workflow"); const constants_1 = require("./constants"); const SaluteSpeechApiClient_1 = require("../shared/SaluteSpeechApiClient"); const node_https_1 = require("node:https"); class SaluteSpeech { constructor() { this.description = { displayName: 'SaluteSpeech', name: 'saluteSpeech', icon: 'file:temp-salute.svg', group: ['input'], version: 1, defaults: { name: 'SaluteSpeech API', }, description: 'Transform your voice into text', inputs: ["main"], outputs: ["main"], credentials: [ { name: 'saluteSpeechApi', required: true, }, ], requestDefaults: { skipSslCertificateValidation: true, }, properties: [ { displayName: 'Ресурс', name: 'resource', type: 'options', noDataExpression: true, options: [ { name: 'Речь', value: 'speech', description: 'Работа с готовой речью', }, { name: 'Текст', value: 'text', description: 'Работа с текстом' }, ], default: 'speech', }, { displayName: 'Операция', name: 'operation', type: 'options', noDataExpression: true, displayOptions: { show: { resource: ['speech'], }, }, options: [ { name: 'Распознать речь', value: 'recognize', description: 'Распознать речь в аудио файле', action: 'Speech recognition', }, ], default: 'recognize', }, { displayName: 'Операция', name: 'operation', type: 'options', noDataExpression: true, displayOptions: { show: { resource: ['text'], }, }, options: [ { name: 'Синтезировать речь', value: 'synthesize', action: 'Speech synthesis', }, ], default: 'synthesize', }, { displayName: 'Voice Binary Field', name: 'binaryPropertyName', type: 'string', default: 'data', required: true, hint: 'Имя поля с аудио, которое надо отправить.', displayOptions: { show: { operation: ['recognize'], resource: ['speech'], }, }, placeholder: '', description: 'Name of the binary property that contains the data to upload', }, { displayName: 'Language', name: 'language', type: 'options', description: 'Source language', options: constants_1.smartSpeechLanguages, default: 'ru-RU', displayOptions: { show: { operation: ['recognize'], resource: ['speech'], }, }, required: true, }, { displayName: 'Модель', name: 'model', type: 'options', description: 'Модель для анализа аудио', options: constants_1.smartSpeechModels, default: 'general', displayOptions: { show: { operation: ['recognize'], resource: ['speech'], }, }, required: true, }, { displayName: 'Фильтр цензуры', name: 'profanityFilter', type: 'boolean', default: false, displayOptions: { show: { operation: ['recognize'], resource: ['speech'], }, }, description: 'Включить/выключить фильтр обсценной лексики', }, { displayName: 'Частота дискретизации', name: 'sampleRate', type: 'number', default: 16000, displayOptions: { show: { operation: ['recognize'], resource: ['speech'], }, }, description: 'Частота дискретизации. Максимально возможное значение — до 96000Гц. Меняйте, только если знаете, что делаете.', }, { displayName: 'Кол-во каналов', name: 'channelsCount', type: 'number', default: 1, displayOptions: { show: { operation: ['recognize'], resource: ['speech'], }, }, description: 'Количество каналов в многоканальном аудио. Для OGG войсов - 1 канал. Меняйте, только если знаете, что делаете.', }, { displayName: 'Соединить ответ', name: 'combineArray', type: 'boolean', default: false, displayOptions: { show: { operation: ['recognize'], resource: ['speech'], }, }, description: 'Соединить ответ в одну строку?', }, { displayName: 'Символ соединения', name: 'combineSymbol', type: 'string', default: '\n', displayOptions: { show: { operation: ['recognize'], resource: ['speech'], combineArray: [true], }, }, typeOptions: { rows: 2, }, description: 'Символ соединения строк. По умолчанию - перенос строки.', hint: 'Переключите на Expression, чтобы добавить переносы строк', }, { displayName: 'Voice', name: 'voiceId', type: 'options', description: 'Voice to use', options: constants_1.smartSpeechVoices, default: 'Nec', displayOptions: { show: { operation: ['synthesize'], }, }, required: true, }, { displayName: 'Format', name: 'format', type: 'options', description: 'Output audio format (codec)', options: constants_1.smartSpeechFormats, default: 'wav16', displayOptions: { show: { operation: ['synthesize'], }, }, required: true, }, { displayName: 'Frequency', name: 'freq', type: 'options', description: 'Frequency of audio (Hz)', options: constants_1.smartSpeechHertz, default: '24000', displayOptions: { show: { operation: ['synthesize'], }, }, required: true, }, { displayName: 'Текст', name: 'textInput', type: 'string', description: 'Текст, который нужно озвучить', default: '', placeholder: 'Внимание, говорит Москва...', displayOptions: { show: { operation: ['synthesize'], }, }, typeOptions: { rows: 4, }, required: true, }, { displayName: 'Output Binary Property Name', name: 'outputPropertyName', type: 'string', description: 'Название переменной, в которой выложить аудио файл', default: 'data', displayOptions: { show: { operation: ['synthesize'], }, }, required: true, }, ], }; } async execute() { const items = this.getInputData(); const returnData = []; const resource = this.getNodeParameter('resource', 0); const operation = this.getNodeParameter('operation', 0); const credentials = await this.getCredentials('saluteSpeechApi'); const scope = credentials.scope ? String(credentials.scope) : 'SALUTE_SPEECH_PERS'; await SaluteSpeechApiClient_1.SaluteSpeechApiClient.updateConfig({ authKey: credentials.authorizationKey, scope, baseUrl: String(credentials.base_url) || 'https://smartspeech.sber.ru/rest/v1', authUrl: credentials.auth_url ? `${credentials.auth_url}/api/v2/oauth` : 'https://ngw.devices.sberbank.ru:9443/api/v2/oauth', httpsAgent: new node_https_1.Agent({ rejectUnauthorized: false, }), }); for (let i = 0; i < items.length; i++) { try { if (resource === 'speech' && operation === 'recognize') { const binaryPropertyName = this.getNodeParameter('binaryPropertyName', 0); const model = this.getNodeParameter('model', 0); const profanity = this.getNodeParameter('profanityFilter', 0); const lang = this.getNodeParameter('language', 0); const channelsCount = this.getNodeParameter('channelsCount', 0); const combineArray = this.getNodeParameter('combineArray', 0); const itemBinaryData = items[i].binary[binaryPropertyName]; const itemBuffer = Buffer.from(itemBinaryData.data, n8n_workflow_1.BINARY_ENCODING); let mimeType = itemBinaryData.mimeType; if (mimeType.includes('ogg')) { mimeType = 'audio/ogg;codecs=opus'; } const speechRecognized = await SaluteSpeechApiClient_1.SaluteSpeechApiClient.speechRecognize(new File([itemBuffer], itemBinaryData.fileName || `file${itemBinaryData.fileExtension || 'bin'}`, { type: mimeType, }), lang, profanity, model, channelsCount); let final = speechRecognized; if (combineArray) { const combineSymbol = this.getNodeParameter('combineSymbol', 0); final = final.join(combineSymbol); } const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray({ response: final }), { itemData: { item: i }, }); if (Array.isArray(executionData)) { returnData.push(...executionData); } else { returnData.push(executionData); } } if (resource === 'text' && operation === 'synthesize') { const voiceId = this.getNodeParameter('voiceId', 0); const format = this.getNodeParameter('format', 0); const freq = this.getNodeParameter('freq', 0); const text = this.getNodeParameter('textInput', 0); const outputPropertyName = this.getNodeParameter('outputPropertyName', 0); const synthesis = await SaluteSpeechApiClient_1.SaluteSpeechApiClient.textSynthesize(text, voiceId, format, freq); const bin = await this.helpers.prepareBinaryData(synthesis.buffer, synthesis.fileName, synthesis.mime); returnData.push({ json: { format: bin.format }, binary: { [outputPropertyName]: bin }, }); } } catch (error) { let errorMessage; if (error instanceof Error) { errorMessage = error.message; } else if (typeof error === 'object' && error !== null) { if ('response' in error && typeof error.response === 'object' && error.response !== null) { const response = error.response; if (response.data && typeof response.data === 'object') { errorMessage = JSON.stringify(response.data); } else if (response.statusText) { errorMessage = `HTTP ${response.status}: ${response.statusText}`; } else { errorMessage = `HTTP Error: ${response.status}`; } } else { errorMessage = JSON.stringify(error); } } else { errorMessage = String(error); } if (this.continueOnFail()) { returnData.push({ json: { error: errorMessage, }, pairedItem: { item: i }, }); continue; } throw new n8n_workflow_1.NodeOperationError(this.getNode(), errorMessage, { itemIndex: i }); } } return [returnData]; } } exports.SaluteSpeech = SaluteSpeech; //# sourceMappingURL=SaluteSpeech.node.js.map