const axios = require("axios"); const fs = require("fs"); const { exec } = require("child_process"); require("dotenv").config(); const path = require("path"); const FFMPEG_PATH = require("ffmpeg-static"); // Get path to ffmpeg binary const OPENAI_API_KEY = process.env.OPENAI_API_KEY; const DEEPGRAM_API_KEY = process.env.DEEPGRAM_API_KEY; const CHUNK_DURATION = process.env.CHUNK_DURATION || 20; // Default to 20 seconds if not set const { Translate } = require("@google-cloud/translate").v2; const MAX_FILES = 20; // Maximum number of files to store at once const TEMP_DIR = "uploads/transcription/"; // ✅ Set Google Credentials Path process.env.GOOGLE_APPLICATION_CREDENTIALS = "uploads/nds-app-451914-61743ae92ad4.json"; const translate = new Translate(); // Ensure the directory exists if (!fs.existsSync(TEMP_DIR)) { fs.mkdirSync(TEMP_DIR, { recursive: true }); } // Process command-line arguments const args = process.argv.slice(2); if (args.length < 3) { console.error("❌ Not enough arguments. Expected: RTMP URL, Session ID, Event ID."); process.exit(1); } const providedRtmpUrl = args[0]; const sessionId = args[1]; const eventId = args[2]; console.log(`📢 Starting audio processing for Session ID: ${sessionId}, Event ID: ${eventId}`); console.log(`📡 Using RTMP URL: ${providedRtmpUrl}`); // ✅ Capture 20-sec chunks & send to Deepgram function processAudioChunk() { console.log("🎙 Capturing 20-sec audio chunk..."); const uniqueFileName = `${Date.now()}_temp_audio.wav`; // Create unique file name const tempFilePath = path.join(TEMP_DIR, uniqueFileName); // Build the ffmpeg command using the provided RTMP URL const ffmpegCommand = `${FFMPEG_PATH} -y -rtmp_live live -i "${providedRtmpUrl}" -t ${CHUNK_DURATION} -acodec pcm_s16le -ac 1 -ar 16000 "${tempFilePath}"`; console.log("Executing ffmpeg command:", ffmpegCommand); exec(ffmpegCommand, async (error, stdout, stderr) => { if (error) { console.error("❌ Error capturing audio chunk:", error); console.error("ffmpeg stderr:", stderr); return; } console.log("✅ Audio chunk saved. Sending to Deepgram..."); // Send audio to Deepgram const transcript = await transcribeWithDeepgram(tempFilePath); if (transcript) { console.log("✅ Transcription Received:", transcript); await saveToDatabase(transcript); fs.unlinkSync(tempFilePath); } // Manage file count: delete oldest files if needed manageFiles(); }); } // ✅ Send Audio to Deepgram async function transcribeWithDeepgram(audioFile) { try { const audioData = fs.createReadStream(audioFile); const response = await axios.post( "https://api.deepgram.com/v1/listen", audioData, { headers: { Authorization: `Token ${DEEPGRAM_API_KEY}`, "Content-Type": "audio/wav" }, params: { model: "nova", language: "en", smart_format: true, detect_language: true } } ); if (response.data && response.data.results) { return response.data.results.channels[0].alternatives[0].transcript; } console.error("❌ Deepgram response error: No results found."); return null; } catch (error) { console.error("❌ Deepgram API Error:", error.response?.data || error.message); return null; } } // ✅ Save Transcription to Database async function saveToDatabase(transcript) { try { // Detect if the transcript contains Arabic characters using regex const isArabic = /[\u0600-\u06FF]/.test(transcript); let transcript_ar, transcript_en; if (isArabic) { // If Arabic, store the raw transcript in Arabic field and translate to English transcript_ar = transcript; transcript_en = await translateToEnglish(transcript); } else { // Otherwise, assume English; store it and translate to Arabic transcript_en = transcript; transcript_ar = await translateToArabic(transcript); } await axios.post("http://localhost:5000/api/transcriptions", { event_id: eventId, session_id: sessionId, transcript: transcript_en, transcript_ar: transcript_ar, transcription_status: "pending" }); console.log("✅ Transcription saved to database!"); } catch (error) { console.error("❌ Error saving to database:", error.message); } } // ✅ Translation Functions async function translateToArabic(text) { try { const [translation] = await translate.translate(text, "ar"); console.log("✅ Arabic Translation:", translation); return translation; } catch (error) { console.error("❌ Google Translate API Error:", error.message); return text; } } async function translateToEnglish(text) { try { const [translation] = await translate.translate(text, "en"); console.log("✅ English Translation:", translation); return translation; } catch (error) { console.error("❌ Google Translate API Error:", error.message); return text; } } // ✅ Manage Files - Delete oldest if the number exceeds MAX_FILES function manageFiles() { fs.readdir(TEMP_DIR, (err, files) => { if (err) { console.error("❌ Error reading directory:", err.message); return; } if (files.length > MAX_FILES) { const sortedFiles = files.sort((a, b) => fs.statSync(path.join(TEMP_DIR, a)).mtime - fs.statSync(path.join(TEMP_DIR, b)).mtime ); const filesToDelete = sortedFiles.slice(0, files.length - MAX_FILES); filesToDelete.forEach(file => { const filePath = path.join(TEMP_DIR, file); console.log(`❌ Deleting old file: ${filePath}`); fs.unlinkSync(filePath); }); } }); } // ✅ Process Audio Every 20 Seconds setInterval(processAudioChunk, CHUNK_DURATION * 1000);