// server.js if (require('dotenv')) { const envFile = process.env.NODE_ENV === 'development' ? '.env.development' : '.env'; require('dotenv').config({ path: envFile }); } const express = require('express'); const bodyParser = require('body-parser'); const WebSocket = require('ws'); const storage = require('node-persist'); const request = require('request'); const fs = require('fs'); const path = require('path'); const app = express(); app.use(bodyParser.json()); const PORT_HTTP = process.env.SERVER_PORT_HTTP || 3000; const PORT_WS = process.env.SERVER_PORT_WS || 8080; const TTS_API_URL = process.env.TTS_API_URL; let language = "en"; let storeRecordings = false; let queueCounter = 0; const sessions = new Map(); const users = new Map(); const chats = new Map(); // Store chat rooms storage.init().then(() => { storage.getItem('language').then((value) => { if (value !== undefined) language = value; else storage.setItem('language', language); }); storage.getItem('storeRecordings').then((value) => { if (value !== undefined) storeRecordings = value; else storage.setItem('storeRecordings', storeRecordings); }); }); // WebSocket Server const wss = new WebSocket.Server({ port: PORT_WS }); wss.on('connection', (ws) => { ws.sessionId = Math.random().toString(36).substring(2); sessions.set(ws.sessionId, { language: 'en' }); ws.send(JSON.stringify({ sessionId: ws.sessionId, language, storeRecordings })); ws.on('message', (message) => { try { const data = JSON.parse(message); if (data.type === 'join') { const { username } = data; users.set(ws.sessionId, { username, sessionId: ws.sessionId }); broadcastUserList(); } else if (data.type === 'startChat') { const { users: chatUsers } = data; const chatId = Math.random().toString(36).substring(2); chats.set(chatId, { participants: [ws.sessionId, ...chatUsers], messages: [] }); broadcastUserList(); } else if (data.type === 'audio') { handleAudioData(ws, data.audio); } } catch (err) { console.error('Failed to parse message', err); } }); ws.on('close', () => { users.delete(ws.sessionId); sessions.delete(ws.sessionId); broadcastUserList(); }); }); function handleAudioData(ws, data) { const sessionData = sessions.get(ws.sessionId); let language = sessionData.language || 'en'; let task = sessionData.task || 'transcribe'; const formData = { task, language, vad_filter: 'true', output: 'json', audio_file: { value: data, options: { filename: 'audio.ogg', contentType: 'audio/ogg' } } }; if (language === 'auto' || language === '') { detectLanguage(ws, formData); } else { transcribeAudio(ws, formData, sessionData); } } function detectLanguage(ws, formData) { request.post({ url: TTS_API_URL.replace('/asr', '/detect-language'), formData }, (err, httpResponse, body) => { if (err) return console.error('Language detection failed:', err); const result = JSON.parse(body); if (result && result.language_code) { const language = result.language_code; const sessionData = sessions.get(ws.sessionId); sessionData.language = language; ws.send(JSON.stringify({ languageDetected: result.detected_language })); transcribeAudio(ws, formData, sessionData); } }); } function transcribeAudio(ws, formData, sessionData) { const start = new Date().getTime(); queueCounter++; request.post({ url: TTS_API_URL, formData }, (err, httpResponse, body) => { queueCounter--; if (err) return console.error('Transcription failed:', err); const duration = new Date().getTime() - start; ws.send(JSON.stringify({ queueCounter, duration, language: sessionData.language, text: body })); }); if (storeRecordings) { const timestamp = Date.now(); fs.mkdir('rec', { recursive: true }, (err) => { if (err) throw err; }); fs.writeFile(`rec/audio${timestamp}.ogg`, formData.audio_file.value, (err) => { if (err) console.log(err); else console.log('Audio data saved to rec/audio' + timestamp + '.ogg'); }); } } function broadcastUserList() { const userList = Array.from(users.values()).map(user => ({ username: user.username, sessionId: user.sessionId })); wss.clients.forEach(client => { if (client.readyState === WebSocket.OPEN) { client.send(JSON.stringify({ type: 'userList', users: userList })); } }); } // HTTP Server app.get('/', (req, res) => { res.sendFile(path.join(__dirname, 'chat-client.html')); }); app.post('/log', (req, res) => { console.log(`[LOG ${new Date().toISOString()}] ${req.body.message}`); res.status(200).send('OK'); }); app.get('/wsurl', (req, res) => { res.status(200).send(process.env.WS_URL); }); app.get('/settings', (req, res) => { if (req.query.language) { language = req.query.language; storage.setItem('language', language); } if (req.query.storeRecordings) { storeRecordings = req.query.storeRecordings; storage.setItem('storeRecordings', storeRecordings); } res.status(200).send({ language, storeRecordings }); }); app.post('/settings', (req, res) => { const { sessionId, language, storeRecordings, task } = req.body; const sessionData = sessions.get(sessionId); if (language) sessionData.language = language; if (storeRecordings) sessionData.storeRecordings = storeRecordings; if (task) sessionData.task = task; res.status(200).send('OK'); }); app.post('/upload', (req, res) => { const timestamp = Date.now(); fs.mkdir('rec', { recursive: true }, (err) => { if (err) return res.status(500).send('ERROR'); const file = fs.createWriteStream(`rec/audio_slice_${timestamp}.ogg`); req.pipe(file); file.on('finish', () => res.status(200).send('OK')); }); }); app.get('/chats', (req, res) => { const { username } = req.query; const userChats = Array.from(chats.values()).filter(chat => chat.participants.includes(username)); res.status(200).send({ chats: userChats }); }); app.listen(PORT_HTTP, () => { console.log(`Server listening on port ${PORT_HTTP}`); });