Update web/templates/entry_view.html
This commit is contained in:
parent
6408d0a589
commit
3ad80494a0
@ -285,47 +285,38 @@
|
||||
const ttsBtn = document.getElementById('ttsBtn');
|
||||
if (!ttsBtn) return;
|
||||
|
||||
const TTS_URL = "{{ tts_url|default:'' }}"; // staff-only when present
|
||||
let ttsAudio = null; // holds current playback (Audio or shim)
|
||||
const TTS_URL = "{{ tts_url|default:'' }}"; // empty for non-staff → browser TTS
|
||||
|
||||
function stopPlayback(){
|
||||
try {
|
||||
if (window.speechSynthesis) speechSynthesis.cancel();
|
||||
} catch(e){}
|
||||
try {
|
||||
if (ttsAudio && typeof ttsAudio.pause === 'function') {
|
||||
ttsAudio.pause();
|
||||
ttsAudio.currentTime = 0;
|
||||
}
|
||||
if (ttsAudio && typeof ttsAudio.stop === 'function') {
|
||||
ttsAudio.stop();
|
||||
}
|
||||
} catch(e){}
|
||||
ttsAudio = null;
|
||||
// -------- persistent audio + helpers --------
|
||||
let audioEl = null;
|
||||
let currentURL = null; // objectURL for OpenAI response
|
||||
let playing = false;
|
||||
let fetchCtrl = null;
|
||||
|
||||
function ensureAudio(){
|
||||
if (!audioEl) {
|
||||
audioEl = new Audio();
|
||||
audioEl.setAttribute('playsinline',''); // iOS
|
||||
audioEl.preload = 'auto';
|
||||
audioEl.addEventListener('ended', () => { playing = false; cleanupURL(); });
|
||||
audioEl.addEventListener('pause', () => { /* keep playing=false only if really paused */ });
|
||||
}
|
||||
return audioEl;
|
||||
}
|
||||
|
||||
async function playOpenAITTS() {
|
||||
const r = await fetch(TTS_URL, { credentials: 'same-origin', cache: 'no-store' });
|
||||
if (!r.ok) {
|
||||
const msg = await r.text().catch(()=> String(r.status));
|
||||
throw new Error(`HTTP ${r.status}: ${msg.slice(0,200)}`);
|
||||
}
|
||||
const ct = (r.headers.get('content-type') || '').toLowerCase();
|
||||
if (!ct.startsWith('audio/')) {
|
||||
const preview = await r.text().catch(()=> '(non-audio response)');
|
||||
throw new Error(`Unexpected content-type "${ct}". Preview: ${preview.slice(0,200)}`);
|
||||
}
|
||||
const blob = await r.blob();
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
const audio = new Audio(url);
|
||||
audio.preload = 'auto';
|
||||
audio.setAttribute('playsinline','');
|
||||
audio.onended = () => { URL.revokeObjectURL(url); ttsAudio = null; };
|
||||
await audio.play();
|
||||
ttsAudio = audio;
|
||||
function cleanupURL(){
|
||||
if (currentURL) { URL.revokeObjectURL(currentURL); currentURL = null; }
|
||||
}
|
||||
|
||||
function stopAll(){
|
||||
try { if (fetchCtrl) fetchCtrl.abort(); } catch(_){}
|
||||
fetchCtrl = null;
|
||||
if (audioEl) { audioEl.pause(); audioEl.currentTime = 0; }
|
||||
cleanupURL();
|
||||
playing = false;
|
||||
}
|
||||
|
||||
// -------- text builders --------
|
||||
function buildCombinedText(){
|
||||
const ill = (document.getElementById('illustration-text')?.innerText || '').trim();
|
||||
const app = (document.getElementById('application-text')?.innerText || '').trim();
|
||||
@ -338,43 +329,83 @@
|
||||
if (!('speechSynthesis' in window) || !('SpeechSynthesisUtterance' in window)) {
|
||||
throw new Error('Browser TTS not supported.');
|
||||
}
|
||||
speechSynthesis.cancel();
|
||||
window.speechSynthesis.cancel();
|
||||
const u = new SpeechSynthesisUtterance(text);
|
||||
u.rate = 1.0; u.pitch = 1.0; u.volume = 1.0;
|
||||
u.onend = () => { ttsAudio = null; };
|
||||
speechSynthesis.speak(u);
|
||||
|
||||
// Create a tiny shim so our toggle can "stop" it
|
||||
ttsAudio = {
|
||||
pause: () => speechSynthesis.cancel(),
|
||||
currentTime: 0
|
||||
};
|
||||
playing = true;
|
||||
}
|
||||
|
||||
ttsBtn.addEventListener('click', async () => {
|
||||
try {
|
||||
// Toggle: if already playing, stop instead
|
||||
if (ttsAudio) {
|
||||
stopPlayback();
|
||||
showToast("Playback stopped");
|
||||
return;
|
||||
}
|
||||
async function playOpenAITTS(){
|
||||
stopAll();
|
||||
fetchCtrl = new AbortController();
|
||||
|
||||
const r = await fetch(TTS_URL, { credentials:'same-origin', cache:'no-store', signal: fetchCtrl.signal });
|
||||
if (!r.ok) {
|
||||
const preview = await r.text().catch(()=> String(r.status));
|
||||
throw new Error(`HTTP ${r.status}: ${preview.slice(0,200)}`);
|
||||
}
|
||||
const ct = (r.headers.get('content-type') || '').toLowerCase();
|
||||
if (!ct.startsWith('audio/')) {
|
||||
const preview = await r.text().catch(()=> '(non-audio response)');
|
||||
throw new Error(`Unexpected content-type "${ct}". Preview: ${preview.slice(0,200)}`);
|
||||
}
|
||||
|
||||
const blob = await r.blob();
|
||||
currentURL = URL.createObjectURL(blob);
|
||||
|
||||
const a = ensureAudio();
|
||||
a.src = currentURL;
|
||||
a.currentTime = 0;
|
||||
a.muted = false;
|
||||
|
||||
try {
|
||||
await a.play(); // <- may throw NotAllowedError on some iOS/Safari cases
|
||||
playing = true;
|
||||
} catch (err) {
|
||||
// Autoplay/permission style block (Safari/iOS or Chrome heuristics)
|
||||
if (err && (err.name === 'NotAllowedError' ||
|
||||
/not allowed|denied permission/i.test(err.message))) {
|
||||
showToast("Audio was blocked. Make sure the phone isn't on Silent and tap the speaker again.");
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------- button: toggle behavior --------
|
||||
ttsBtn.addEventListener('click', async () => {
|
||||
// Toggle OFF if something is already playing (either engine)
|
||||
if (playing) {
|
||||
try {
|
||||
stopAll();
|
||||
if ('speechSynthesis' in window) window.speechSynthesis.cancel();
|
||||
} finally {
|
||||
playing = false;
|
||||
showToast("Stopped.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ttsBtn.disabled = true;
|
||||
|
||||
if (TTS_URL) {
|
||||
await playOpenAITTS();
|
||||
showToast("Using OpenAI TTS");
|
||||
if (playing) showToast("Playing with OpenAI TTS");
|
||||
} else {
|
||||
speakBrowserTTS();
|
||||
showToast("Using Browser TTS");
|
||||
showToast("Playing with Browser TTS");
|
||||
}
|
||||
} catch (err) {
|
||||
alert('TTS error: ' + (err && err.message ? err.message : String(err)));
|
||||
showToast("TTS error: " + (err?.message || String(err)));
|
||||
} finally {
|
||||
ttsBtn.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Safety: stop audio when navigating away
|
||||
window.addEventListener('pagehide', stopAll);
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user