UNPKG

n8n-nodes-groq-speech

Version:

N8N Community Node for Groq Text-to-Speech API integration

364 lines (363 loc) 16.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GroqTts = void 0; const n8n_workflow_1 = require("n8n-workflow"); // Using fetch directly for TTS API calls class GroqTts { constructor() { this.description = { displayName: 'Groq TTS', name: 'groqTts', icon: 'file:groq-icon-logo-png_seeklogo-605779.png', group: ['transform'], version: 1, subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', description: 'Convert text to speech using Groq API', defaults: { name: 'Groq TTS', }, inputs: ["main" /* NodeConnectionType.Main */], outputs: ["main" /* NodeConnectionType.Main */], credentials: [ { name: 'groqApi', required: true, }, ], requestDefaults: { baseURL: 'https://api.groq.com/openai/v1', headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, }, properties: [ { displayName: '⚠️ First Time Setup Required', name: 'setupNotice', type: 'notice', default: '', displayOptions: { show: {}, }, typeOptions: { theme: 'warning', }, }, { displayName: 'Before first use, you must accept model terms at: https://console.groq.com/playground?model=playai-tts (one-time setup per Groq account)', name: 'setupInstructions', type: 'notice', default: '', displayOptions: { show: {}, }, }, { displayName: 'Resource', name: 'resource', type: 'options', noDataExpression: true, options: [ { name: 'Audio', value: 'audio', }, ], default: 'audio', }, { displayName: 'Operation', name: 'operation', type: 'options', noDataExpression: true, displayOptions: { show: { resource: ['audio'], }, }, options: [ { name: 'Text to Speech', value: 'textToSpeech', action: 'Convert text to speech', description: 'Convert text to speech using Groq TTS', }, ], default: 'textToSpeech', }, { displayName: 'Text', name: 'text', type: 'string', displayOptions: { show: { resource: ['audio'], operation: ['textToSpeech'], }, }, default: '', placeholder: 'Enter the text to convert to speech', description: 'The text to convert to speech', required: true, }, { displayName: 'Model', name: 'model', type: 'options', displayOptions: { show: { resource: ['audio'], operation: ['textToSpeech'], }, }, options: [ { name: 'PlayAI TTS', value: 'playai-tts', }, { name: 'PlayAI TTS Arabic', value: 'playai-tts-arabic', }, ], default: 'playai-tts', description: 'The TTS model to use. Note: You must accept the model terms at https://console.groq.com/playground before first use.', }, { displayName: 'Voice', name: 'voice', type: 'options', displayOptions: { show: { resource: ['audio'], operation: ['textToSpeech'], }, }, options: [ { name: 'Arista', value: 'Arista-PlayAI' }, { name: 'Atlas', value: 'Atlas-PlayAI' }, { name: 'Basil', value: 'Basil-PlayAI' }, { name: 'Briggs', value: 'Briggs-PlayAI' }, { name: 'Calum', value: 'Calum-PlayAI' }, { name: 'Celeste', value: 'Celeste-PlayAI' }, { name: 'Cheyenne', value: 'Cheyenne-PlayAI' }, { name: 'Chip', value: 'Chip-PlayAI' }, { name: 'Cillian', value: 'Cillian-PlayAI' }, { name: 'Deedee', value: 'Deedee-PlayAI' }, { name: 'Fritz', value: 'Fritz-PlayAI' }, { name: 'Gail', value: 'Gail-PlayAI' }, { name: 'Indigo', value: 'Indigo-PlayAI' }, { name: 'Mamaw', value: 'Mamaw-PlayAI' }, { name: 'Mason', value: 'Mason-PlayAI' }, { name: 'Mikail', value: 'Mikail-PlayAI' }, { name: 'Mitch', value: 'Mitch-PlayAI' }, { name: 'Quinn', value: 'Quinn-PlayAI' }, { name: 'Thunder', value: 'Thunder-PlayAI' }, ], default: 'Fritz-PlayAI', description: 'The voice to use for speech synthesis', }, { displayName: 'Response Format', name: 'responseFormat', type: 'options', displayOptions: { show: { resource: ['audio'], operation: ['textToSpeech'], }, }, options: [ { name: 'MP3', value: 'mp3', }, { name: 'WAV', value: 'wav', }, { name: 'FLAC', value: 'flac', }, { name: 'OGG', value: 'ogg', }, ], default: 'mp3', description: 'The audio format for the output', }, { displayName: 'Speed', name: 'speed', type: 'number', displayOptions: { show: { resource: ['audio'], operation: ['textToSpeech'], }, }, default: 1.0, typeOptions: { minValue: 0.25, maxValue: 4.0, numberStepSize: 0.25, }, description: 'The speed of the generated audio. Select a value from 0.25 to 4.0. 1.0 is the default.', }, { displayName: 'Additional Fields', name: 'additionalFields', type: 'collection', placeholder: 'Add Field', default: {}, displayOptions: { show: { resource: ['audio'], operation: ['textToSpeech'], }, }, options: [ { displayName: 'Output File Name', name: 'fileName', type: 'string', default: 'speech', description: 'Name for the output audio file (without extension)', }, { displayName: 'Include Base64 Output', name: 'includeBase64', type: 'boolean', default: false, description: 'Include base64 encoded audio in JSON output (useful for WhatsApp APIs)', }, ], }, ], }; } async execute() { var _a, _b; const items = this.getInputData(); const returnData = []; const resource = this.getNodeParameter('resource', 0); const operation = this.getNodeParameter('operation', 0); for (let i = 0; i < items.length; i++) { try { if (resource === 'audio') { if (operation === 'textToSpeech') { // Get credentials const credentials = await this.getCredentials('groqApi'); // Note: We use fetch directly instead of groq-sdk for TTS // Get parameters const text = this.getNodeParameter('text', i); const model = this.getNodeParameter('model', i); const voice = this.getNodeParameter('voice', i); const responseFormat = this.getNodeParameter('responseFormat', i); const speed = this.getNodeParameter('speed', i); const additionalFields = this.getNodeParameter('additionalFields', i); if (!text) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Text is required for text-to-speech conversion'); } // Prepare request body const requestBody = { model: model, voice: voice, input: text, }; // Add optional parameters if (responseFormat !== 'mp3') { requestBody.response_format = responseFormat; } if (speed !== 1.0) { requestBody.speed = speed; } // Make API call using fetch directly const response = await fetch('https://api.groq.com/openai/v1/audio/speech', { method: 'POST', headers: { 'Authorization': `Bearer ${credentials.apiKey}`, 'Content-Type': 'application/json', }, body: JSON.stringify(requestBody), }); if (!response.ok) { const errorText = await response.text(); let errorMessage = `Groq API error: ${response.status} ${response.statusText}`; try { const errorData = JSON.parse(errorText); if (((_a = errorData.error) === null || _a === void 0 ? void 0 : _a.code) === 'model_terms_required') { errorMessage = `🔐 SETUP REQUIRED: You need to accept terms for the "${model}" model.\n\n` + `👉 Click here: https://console.groq.com/playground?model=${model}\n` + `📋 Steps: 1) Login to Groq 2) Select the model 3) Accept terms when prompted\n` + `✅ This is a one-time setup per Groq account.`; } else if ((_b = errorData.error) === null || _b === void 0 ? void 0 : _b.message) { errorMessage = `Groq API error: ${errorData.error.message}`; } } catch { errorMessage += ` - ${errorText}`; } throw new n8n_workflow_1.NodeOperationError(this.getNode(), errorMessage); } // Convert response to buffer const arrayBuffer = await response.arrayBuffer(); const buffer = Buffer.from(arrayBuffer); // Prepare file name const fileName = additionalFields.fileName || 'speech'; const fullFileName = `${fileName}.${responseFormat}`; // Create binary data const binaryData = await this.helpers.prepareBinaryData(buffer, fullFileName, `audio/${responseFormat}`); // Prepare JSON data const jsonData = { success: true, fileName: fullFileName, format: responseFormat, voice: voice, model: model, speed: speed, textLength: text.length, binaryDataSize: buffer.length, }; // Add base64 if requested if (additionalFields.includeBase64 === true) { const base64Audio = buffer.toString('base64'); jsonData.audioBase64 = base64Audio; jsonData.audioDataUri = `data:audio/${responseFormat};base64,${base64Audio}`; jsonData.base64Enabled = true; } else { jsonData.base64Enabled = false; } // Ensure binary data is properly set const newItem = { json: jsonData, binary: { data: binaryData, }, }; returnData.push(newItem); } } } catch (error) { if (this.continueOnFail()) { returnData.push({ json: { error: error.message, success: false, }, }); continue; } throw error; } } return [returnData]; } } exports.GroqTts = GroqTts;