Interview_AI / statics /script.js
LiamKhoaLe's picture
Rm pydub with ffmpeg, Use Whisper
243b6fb
raw
history blame
2.53 kB
/*******************************
* Interview Q&A Frontend JS *
*******************************/
// Elements
const recordBtn = document.getElementById("record-button");
const questionEl = document.getElementById("question-output");
const answerEl = document.getElementById("answer-output");
// Typing animation util
function typeEffect(el, text, speed = 30) {
el.textContent = "";
let idx = 0;
const timer = setInterval(() => {
el.textContent += text.charAt(idx);
idx++;
if (idx >= text.length) clearInterval(timer);
}, speed);
}
// Audio recording setup
let mediaRecorder, chunks = [];
// Initialise media data
async function initMedia() {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder = new MediaRecorder(stream);
// Continuously push available data
mediaRecorder.ondataavailable = e => chunks.push(e.data);
// Stop btn start write and send audio chunk
mediaRecorder.onstop = async () => {
const audioBlob = new Blob(chunks, { type: "audio/wav" });
chunks = [];
// Build form data
const form = new FormData();
form.append("file", audioBlob, "record.wav");
// UX feedback
typeEffect(questionEl, "⌛ Transcribing…");
answerEl.textContent = "";
try {
const res = await fetch("/voice-transcribe", { method: "POST", body: form });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
// render markdown after a small delay for dramatic effect
typeEffect(questionEl, data.question || "[no speech detected]");
setTimeout(() => typeEffect(answerEl, data.answer || "[no answer]"), 500);
} catch (err) {
typeEffect(answerEl, "❌ " + err.message);
}
};
} catch (err) {
alert("Microphone access denied – please allow permissions.");
}
}
// Hold‑to‑record UX
function bindRecordBtn() {
if (!mediaRecorder) return;
recordBtn.addEventListener("mousedown", () => mediaRecorder.start());
recordBtn.addEventListener("mouseup", () => mediaRecorder.stop());
// Touch devices
recordBtn.addEventListener("touchstart", e => { e.preventDefault(); mediaRecorder.start(); });
recordBtn.addEventListener("touchend", e => { e.preventDefault(); mediaRecorder.stop(); });
}
// Init on page load
window.addEventListener("DOMContentLoaded", async () => {
try { await initMedia(); bindRecordBtn(); }
catch (e) { alert("Mic permission required"); }
});