Archived
271 lines
7.6 KiB
JavaScript
271 lines
7.6 KiB
JavaScript
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}`);
|
|
}); |