UNPKG

n8n-nodes-assemblyai-stream

Version:

n8n node for streaming audio to AssemblyAI using node-microphone

169 lines 7.22 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AssemblyAIStream = void 0; const n8n_workflow_1 = require("n8n-workflow"); const ws_1 = __importDefault(require("ws")); const node_microphone_1 = __importDefault(require("node-microphone")); class AssemblyAIStream { constructor() { this.description = { displayName: 'AssemblyAI Stream', name: 'assemblyAIStream', group: ['transform'], version: 1, description: 'Stream microphone audio to AssemblyAI for real-time transcription', defaults: { name: 'AssemblyAI Stream', }, inputs: ['main'], outputs: ['main'], credentials: [ { name: 'assemblyAIApi', required: true, }, ], properties: [ { displayName: 'Operation', name: 'operation', type: 'options', noDataExpression: true, options: [ { name: 'Start Streaming', value: 'startStreaming', description: 'Start streaming audio from microphone', action: 'Start streaming audio from microphone', }, ], default: 'startStreaming', }, { displayName: 'Duration (seconds)', name: 'duration', type: 'number', default: 30, description: 'Duration to record in seconds (0 for continuous)', }, { displayName: 'Device', name: 'device', type: 'string', default: 'default', description: 'Microphone device to use (leave as default for system default)', }, ], }; } async execute() { const operation = this.getNodeParameter('operation', 0); const duration = this.getNodeParameter('duration', 0); const device = this.getNodeParameter('device', 0); const credentials = await this.getCredentials('assemblyAIApi'); if (!credentials) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No credentials provided!'); } const apiKey = credentials.apiKey; if (operation === 'startStreaming') { const response = await fetch('https://api.assemblyai.com/v2/realtime/token', { method: 'POST', headers: { 'Authorization': apiKey, 'Content-Type': 'application/json', }, body: JSON.stringify({ expires_in: 3600 }), }); if (!response.ok) { const errorText = await response.text(); throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to get AssemblyAI token: ${response.status} ${response.statusText} - ${errorText}`); } const data = await response.json(); if (!data.token) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid response from AssemblyAI: No token received'); } const token = data.token; const ws = new ws_1.default(`wss://api.assemblyai.com/v2/realtime/ws?token=${token}`); return new Promise((resolve, reject) => { const results = []; const mic = new node_microphone_1.default({ device: device === 'default' ? undefined : device, rate: 16000, channels: 1, debug: false, }); let micStream = null; const handleMicError = (error) => { ws.close(); reject(new n8n_workflow_1.NodeOperationError(this.getNode(), `Microphone error: ${error.message}`)); }; ws.on('open', () => { console.log('WebSocket connection established'); ws.send(JSON.stringify({ "message_type": "StartRecognition", "end_utterance_silence_threshold": 700, "sample_rate": 16000, })); micStream = mic.startRecording(); micStream.on('error', handleMicError); micStream.on('data', (data) => { if (ws.readyState === ws_1.default.OPEN) { ws.send(JSON.stringify({ "audio_data": data.toString('base64'), })); } }); if (duration > 0) { setTimeout(() => { mic.stopRecording(); ws.close(); }, duration * 1000); } }); ws.on('message', (message) => { const data = JSON.parse(message.toString()); switch (data.message_type) { case 'FinalTranscript': results.push({ json: { text: data.text, timestamp: new Date().toISOString(), confidence: data.confidence, message_type: 'final' }, }); break; case 'PartialTranscript': results.push({ json: { text: data.text, timestamp: new Date().toISOString(), message_type: 'partial' }, }); break; case 'Error': console.error('AssemblyAI Error:', data.error); break; } }); ws.on('error', (error) => { mic.stopRecording(); reject(new n8n_workflow_1.NodeOperationError(this.getNode(), `WebSocket error: ${error.message}`)); }); ws.on('close', () => { mic.stopRecording(); resolve([results]); }); }); } return [[]]; } } exports.AssemblyAIStream = AssemblyAIStream; //# sourceMappingURL=AssemblyAIStream.node.js.map