require('dotenv').config(); const express = require('express'); const http = require('http'); const socketIo = require('socket.io'); const cors = require('cors'); const path = require('path'); const multer = require('multer'); const whisperBackend = require('./backends/whisper'); const elevenLabsBackend = require('./backends/elevenlabs'); const settingsManager = require('./utils/settingsManager'); const app = express(); const server = http.createServer(app); const io = socketIo(server, { cors: { origin: "*", methods: ["GET", "POST"] } }); const upload = multer({ storage: multer.memoryStorage() }); app.use(cors()); app.use(express.json()); app.use(express.static(path.join(__dirname, '../../public'))); const activeBackend = { current: 'whisper-local', instance: null }; // Initialize settings settingsManager.loadSettings().then(() => { settingsManager.applyToEnv(); activeBackend.current = settingsManager.getSetting('selectedBackend') || 'whisper-local'; console.log('Settings loaded, active backend:', activeBackend.current); }); app.post('/api/backend', (req, res) => { const { backend } = req.body; activeBackend.current = backend; console.log(`Switched to backend: ${backend}`); res.json({ success: true, backend }); }); app.post('/api/transcribe', upload.single('audio'), async (req, res) => { try { if (!req.file) { return res.status(400).json({ error: 'No audio file provided' }); } let transcription = ''; switch (activeBackend.current) { case 'whisper-local': transcription = await whisperBackend.transcribeLocal(req.file.buffer); break; case 'whisper-remote': transcription = await whisperBackend.transcribeRemote(req.file.buffer); break; case 'elevenlabs': transcription = await elevenLabsBackend.transcribe(req.file.buffer); break; default: throw new Error(`Unknown backend: ${activeBackend.current}`); } // Only emit non-empty transcriptions if (transcription && transcription.trim()) { io.emit('transcription', { text: transcription, timestamp: Date.now() }); } res.json({ transcription }); } catch (error) { console.error('Transcription error:', error); res.status(500).json({ error: error.message }); } }); app.get('/api/backends', (req, res) => { res.json({ backends: [ { id: 'whisper-local', name: 'Whisper (Local)', available: true }, { id: 'whisper-remote', name: 'Whisper (OpenAI API)', available: !!process.env.OPENAI_API_KEY }, { id: 'elevenlabs', name: 'ElevenLabs (Scribe)', available: !!process.env.ELEVENLABS_API_KEY } ], current: activeBackend.current }); }); app.get('/api/queue-status', (req, res) => { try { const status = whisperBackend.getQueueStatus(); res.json(status); } catch (error) { res.status(500).json({ error: 'Failed to get queue status' }); } }); // Settings endpoints app.get('/api/settings', (req, res) => { try { const settings = settingsManager.getSettings(); // Don't send API keys to client for security const clientSettings = { ...settings }; if (clientSettings.openaiApiKey) { clientSettings.openaiApiKey = '***hidden***'; } if (clientSettings.elevenlabsApiKey) { clientSettings.elevenlabsApiKey = '***hidden***'; } res.json(clientSettings); } catch (error) { res.status(500).json({ error: 'Failed to load settings' }); } }); app.post('/api/settings', async (req, res) => { try { const updates = req.body; // Only update API keys if they're not the hidden placeholder if (updates.openaiApiKey === '***hidden***') { delete updates.openaiApiKey; } if (updates.elevenlabsApiKey === '***hidden***') { delete updates.elevenlabsApiKey; } const success = await settingsManager.updateSettings(updates); if (success) { // Reload settings to ensure they're current await settingsManager.loadSettings(); settingsManager.applyToEnv(); // Update active backend if changed if (updates.selectedBackend) { activeBackend.current = updates.selectedBackend; } // Log GPU changes for debugging if (updates.gpuAcceleration !== undefined) { console.log(`GPU acceleration changed to: ${updates.gpuAcceleration}`); } if (updates.whisperModel !== undefined) { console.log(`Whisper model changed to: ${updates.whisperModel}`); } res.json({ success: true }); } else { res.status(500).json({ error: 'Failed to save settings' }); } } catch (error) { console.error('Error updating settings:', error); res.status(500).json({ error: 'Failed to save settings' }); } }); app.post('/api/settings/reset', async (req, res) => { try { const success = await settingsManager.resetToDefaults(); if (success) { settingsManager.applyToEnv(); activeBackend.current = 'whisper-local'; res.json({ success: true }); } else { res.status(500).json({ error: 'Failed to reset settings' }); } } catch (error) { console.error('Error resetting settings:', error); res.status(500).json({ error: 'Failed to reset settings' }); } }); app.post('/api/detect-gpu', async (req, res) => { const { exec } = require('child_process'); const util = require('util'); const execPromise = util.promisify(exec); const result = { cuda: false, rocm: false, openvino: false, recommended: 'none' }; try { // Check for NVIDIA GPU (CUDA) try { await execPromise('nvidia-smi'); result.cuda = true; result.recommended = 'cuda'; } catch (e) { // NVIDIA not available } // Check for AMD GPU (ROCm) try { await execPromise('rocm-smi'); result.rocm = true; if (!result.cuda) { result.recommended = 'rocm'; } } catch (e) { // ROCm not available } // Check for Intel GPU/OpenVINO try { const { stdout } = await execPromise('lspci | grep -i intel | grep -i vga || echo ""'); if (stdout.includes('Intel')) { result.openvino = true; if (!result.cuda && !result.rocm) { result.recommended = 'openvino'; } } } catch (e) { // Intel GPU check failed } res.json(result); } catch (error) { console.error('GPU detection error:', error); res.status(500).json({ error: 'Failed to detect GPU' }); } }); app.post('/api/test-connections', async (req, res) => { const { openaiApiKey, elevenlabsApiKey } = req.body; const results = {}; // Test OpenAI connection if (openaiApiKey && openaiApiKey !== '***hidden***') { try { const OpenAI = require('openai'); const openai = new OpenAI({ apiKey: openaiApiKey }); await openai.models.list(); results.openai = { success: true }; } catch (error) { results.openai = { success: false, error: error.message }; } } // Test ElevenLabs connection if (elevenlabsApiKey && elevenlabsApiKey !== '***hidden***') { try { const axios = require('axios'); await axios.get('https://api.elevenlabs.io/v1/user', { headers: { 'xi-api-key': elevenlabsApiKey } }); results.elevenlabs = { success: true }; } catch (error) { results.elevenlabs = { success: false, error: error.response?.data?.detail?.message || error.message }; } } res.json(results); }); io.on('connection', (socket) => { console.log('Client connected'); socket.on('disconnect', () => { console.log('Client disconnected'); }); }); const PORT = process.env.SERVER_PORT || 3000; server.listen(PORT, () => { console.log(`Server running on port ${PORT}`); });