v7: fix speech recognition repetition, HTML injection, long-session guard
- Fix word repetition: use sessionFinals pattern so each browser SR session starts fresh; no overlap when recognition auto-restarts - Fix HTML injection / '>' parse error: escape < > & in live transcript innerHTML before inserting speech recognition text - Add 24 MB blob guard: fall back to live SR transcript if audio file is too large for Whisper API (long sessions) - Bump version to 7.0.0, update docker-compose image tag to v7
This commit is contained in:
parent
689d5e9028
commit
f397ca7725
5 changed files with 56 additions and 11 deletions
|
|
@ -1,6 +1,6 @@
|
|||
services:
|
||||
pediatric-scribe:
|
||||
image: danielonyejesi/pediatric-ai-scribe-v3:v3.1
|
||||
image: danielonyejesi/pediatric-ai-scribe-v3:v7
|
||||
ports:
|
||||
- "3552:3000"
|
||||
env_file:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "pediatric-ai-scribe",
|
||||
"version": "3.1.0",
|
||||
"version": "7.0.0",
|
||||
"description": "AI-powered pediatric clinical documentation platform",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -20,23 +20,32 @@
|
|||
var isPaused = false;
|
||||
var recognition = createSpeechRecognition();
|
||||
var finalText = '';
|
||||
var sessionFinals = '';
|
||||
|
||||
function escHtml(s) { return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }
|
||||
|
||||
if (recognition) {
|
||||
recognition.onresult = function(e) {
|
||||
var interim = '';
|
||||
for (var i = e.resultIndex; i < e.results.length; i++) {
|
||||
if (e.results[i].isFinal) finalText += e.results[i][0].transcript + ' ';
|
||||
if (e.results[i].isFinal) sessionFinals += e.results[i][0].transcript + ' ';
|
||||
else interim = e.results[i][0].transcript;
|
||||
}
|
||||
transcript.innerHTML = finalText + (interim ? '<span style="color:#9ca3af;">' + interim + '</span>' : '');
|
||||
var combined = finalText + sessionFinals;
|
||||
transcript.innerHTML = escHtml(combined) + (interim ? '<span style="color:#9ca3af;">' + escHtml(interim) + '</span>' : '');
|
||||
};
|
||||
recognition.onend = function() {
|
||||
finalText += sessionFinals;
|
||||
sessionFinals = '';
|
||||
if (isRecording && !isPaused) try { recognition.start(); } catch(e) {}
|
||||
};
|
||||
recognition.onend = function() { if (isRecording && !isPaused) try { recognition.start(); } catch(e) {} };
|
||||
recognition.onerror = function() {};
|
||||
}
|
||||
|
||||
recordBtn.addEventListener('click', function() {
|
||||
if (!isRecording) {
|
||||
finalText = '';
|
||||
sessionFinals = '';
|
||||
recorder.start().then(function() {
|
||||
isRecording = true;
|
||||
isPaused = false;
|
||||
|
|
@ -63,6 +72,12 @@
|
|||
showLoading('Transcribing...');
|
||||
recorder.stop().then(function(blob) {
|
||||
if (!blob || blob.size === 0) { hideLoading(); if (finalText.trim()) transcript.textContent = finalText.trim(); return; }
|
||||
if (blob.size > 24 * 1024 * 1024) {
|
||||
hideLoading();
|
||||
transcript.textContent = finalText.trim();
|
||||
showToast('Recording too large for AI transcription — using live transcript', 'info');
|
||||
return;
|
||||
}
|
||||
return transcribeAudio(blob).then(function(data) {
|
||||
hideLoading();
|
||||
if (data.success) { transcript.textContent = data.text; showToast('Transcribed ' + dur + 's', 'success'); }
|
||||
|
|
|
|||
|
|
@ -18,22 +18,31 @@
|
|||
var isRecording = false;
|
||||
var recognition = createSpeechRecognition();
|
||||
var finalText = '';
|
||||
var sessionFinals = '';
|
||||
|
||||
function escHtml(s) { return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }
|
||||
|
||||
if (recognition) {
|
||||
recognition.onresult = function(e) {
|
||||
var interim = '';
|
||||
for (var i = e.resultIndex; i < e.results.length; i++) {
|
||||
if (e.results[i].isFinal) finalText += e.results[i][0].transcript + ' ';
|
||||
if (e.results[i].isFinal) sessionFinals += e.results[i][0].transcript + ' ';
|
||||
else interim = e.results[i][0].transcript;
|
||||
}
|
||||
transcript.innerHTML = finalText + (interim ? '<span style="color:#9ca3af;">' + interim + '</span>' : '');
|
||||
var combined = finalText + sessionFinals;
|
||||
transcript.innerHTML = escHtml(combined) + (interim ? '<span style="color:#9ca3af;">' + escHtml(interim) + '</span>' : '');
|
||||
};
|
||||
recognition.onend = function() {
|
||||
finalText += sessionFinals;
|
||||
sessionFinals = '';
|
||||
if (isRecording) try { recognition.start(); } catch(e) {}
|
||||
};
|
||||
recognition.onend = function() { if (isRecording) try { recognition.start(); } catch(e) {} };
|
||||
}
|
||||
|
||||
recordBtn.addEventListener('click', function() {
|
||||
if (!isRecording) {
|
||||
finalText = '';
|
||||
sessionFinals = '';
|
||||
recorder.start().then(function() {
|
||||
isRecording = true;
|
||||
recordBtn.classList.add('recording');
|
||||
|
|
@ -53,6 +62,12 @@
|
|||
showLoading('Transcribing...');
|
||||
recorder.stop().then(function(blob) {
|
||||
if (!blob) { hideLoading(); if (finalText.trim()) transcript.textContent = finalText.trim(); return; }
|
||||
if (blob.size > 24 * 1024 * 1024) {
|
||||
hideLoading();
|
||||
transcript.textContent = finalText.trim();
|
||||
showToast('Recording too large for AI transcription — using live transcript', 'info');
|
||||
return;
|
||||
}
|
||||
return transcribeAudio(blob).then(function(data) {
|
||||
hideLoading();
|
||||
if (data.success) transcript.textContent = data.text;
|
||||
|
|
|
|||
|
|
@ -20,22 +20,31 @@
|
|||
var isPaused = false;
|
||||
var recognition = createSpeechRecognition();
|
||||
var finalText = '';
|
||||
var sessionFinals = '';
|
||||
|
||||
function escHtml(s) { return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }
|
||||
|
||||
if (recognition) {
|
||||
recognition.onresult = function(e) {
|
||||
var interim = '';
|
||||
for (var i = e.resultIndex; i < e.results.length; i++) {
|
||||
if (e.results[i].isFinal) finalText += e.results[i][0].transcript + ' ';
|
||||
if (e.results[i].isFinal) sessionFinals += e.results[i][0].transcript + ' ';
|
||||
else interim = e.results[i][0].transcript;
|
||||
}
|
||||
transcript.innerHTML = finalText + (interim ? '<span style="color:#9ca3af;">' + interim + '</span>' : '');
|
||||
var combined = finalText + sessionFinals;
|
||||
transcript.innerHTML = escHtml(combined) + (interim ? '<span style="color:#9ca3af;">' + escHtml(interim) + '</span>' : '');
|
||||
};
|
||||
recognition.onend = function() {
|
||||
finalText += sessionFinals;
|
||||
sessionFinals = '';
|
||||
if (isRecording && !isPaused) try { recognition.start(); } catch(e) {}
|
||||
};
|
||||
recognition.onend = function() { if (isRecording && !isPaused) try { recognition.start(); } catch(e) {} };
|
||||
}
|
||||
|
||||
recordBtn.addEventListener('click', function() {
|
||||
if (!isRecording) {
|
||||
finalText = '';
|
||||
sessionFinals = '';
|
||||
recorder.start().then(function() {
|
||||
isRecording = true;
|
||||
isPaused = false;
|
||||
|
|
@ -61,6 +70,12 @@
|
|||
showLoading('Transcribing...');
|
||||
recorder.stop().then(function(blob) {
|
||||
if (!blob || blob.size === 0) { hideLoading(); if (finalText.trim()) transcript.textContent = finalText.trim(); return; }
|
||||
if (blob.size > 24 * 1024 * 1024) {
|
||||
hideLoading();
|
||||
transcript.textContent = finalText.trim();
|
||||
showToast('Recording too large for AI transcription — using live transcript', 'info');
|
||||
return;
|
||||
}
|
||||
return transcribeAudio(blob).then(function(data) {
|
||||
hideLoading();
|
||||
if (data.success) { transcript.textContent = data.text; showToast('Transcribed ' + dur + 's', 'success'); }
|
||||
|
|
|
|||
Loading…
Reference in a new issue