update calling overlap
This commit is contained in:
parent
97e014105c
commit
bc392b3f27
@ -224,85 +224,174 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const announcementQueue = [];
|
||||||
|
const queuedAnnouncementKeys = new Set();
|
||||||
|
let announcementPlaying = false;
|
||||||
|
let activeAnnouncementAudio = null;
|
||||||
|
let activeSpeechUtterance = null;
|
||||||
|
let audioLoadTimeoutId = null;
|
||||||
|
|
||||||
|
const clearAnnouncementPlayback = () => {
|
||||||
|
if (audioLoadTimeoutId) {
|
||||||
|
window.clearTimeout(audioLoadTimeoutId);
|
||||||
|
audioLoadTimeoutId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeAnnouncementAudio) {
|
||||||
|
activeAnnouncementAudio.onended = null;
|
||||||
|
activeAnnouncementAudio.onerror = null;
|
||||||
|
activeAnnouncementAudio.pause();
|
||||||
|
activeAnnouncementAudio.removeAttribute('src');
|
||||||
|
activeAnnouncementAudio.load();
|
||||||
|
activeAnnouncementAudio = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeSpeechUtterance) {
|
||||||
|
activeSpeechUtterance.onend = null;
|
||||||
|
activeSpeechUtterance.onerror = null;
|
||||||
|
activeSpeechUtterance = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const queueAnnouncement = (card) => {
|
||||||
|
if (!card) return;
|
||||||
|
const key = card.dataset.announcementKey || '';
|
||||||
|
const text = locale === 'ar' ? (card.dataset.announcementAr || '') : (card.dataset.announcementEn || '');
|
||||||
|
if (!key || !text || queuedAnnouncementKeys.has(key)) return;
|
||||||
|
announcementQueue.push({ key, text });
|
||||||
|
queuedAnnouncementKeys.add(key);
|
||||||
|
playNextAnnouncement();
|
||||||
|
};
|
||||||
|
|
||||||
|
const playNextAnnouncement = () => {
|
||||||
|
if (announcementPlaying || announcementQueue.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextAnnouncement = announcementQueue.shift();
|
||||||
|
if (!nextAnnouncement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
announcementPlaying = true;
|
||||||
|
const videoPlayer = document.getElementById('adsVideoPlayer');
|
||||||
|
if (videoPlayer) videoPlayer.volume = 0.1;
|
||||||
|
|
||||||
|
const finishAnnouncement = () => {
|
||||||
|
clearAnnouncementPlayback();
|
||||||
|
if ('speechSynthesis' in window && (window.speechSynthesis.speaking || window.speechSynthesis.pending)) {
|
||||||
|
window.speechSynthesis.cancel();
|
||||||
|
}
|
||||||
|
if (videoPlayer) videoPlayer.volume = 1.0;
|
||||||
|
queuedAnnouncementKeys.delete(nextAnnouncement.key);
|
||||||
|
announcementPlaying = false;
|
||||||
|
playNextAnnouncement();
|
||||||
|
};
|
||||||
|
|
||||||
|
playChime();
|
||||||
|
|
||||||
|
window.setTimeout(() => {
|
||||||
|
const tl = locale === 'ar' ? 'ar' : 'en';
|
||||||
|
const ttsUrl = window.location.pathname.replace(/\/[^\/]*$/, '/api/tts.php') + '?lang=' + tl + '&text=' + encodeURIComponent(nextAnnouncement.text);
|
||||||
|
const audioObj = new Audio(ttsUrl);
|
||||||
|
audioObj.preload = 'auto';
|
||||||
|
activeAnnouncementAudio = audioObj;
|
||||||
|
|
||||||
|
let finished = false;
|
||||||
|
const done = () => {
|
||||||
|
if (finished) return;
|
||||||
|
finished = true;
|
||||||
|
finishAnnouncement();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFallback = (err) => {
|
||||||
|
if (finished) return;
|
||||||
|
console.warn('External TTS failed, falling back to built-in speech', err);
|
||||||
|
|
||||||
|
if (activeAnnouncementAudio === audioObj) {
|
||||||
|
audioObj.onended = null;
|
||||||
|
audioObj.onerror = null;
|
||||||
|
audioObj.pause();
|
||||||
|
audioObj.removeAttribute('src');
|
||||||
|
audioObj.load();
|
||||||
|
activeAnnouncementAudio = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('speechSynthesis' in window) {
|
||||||
|
const utterance = new SpeechSynthesisUtterance(nextAnnouncement.text);
|
||||||
|
utterance.lang = locale === 'ar' ? 'ar-SA' : 'en-US';
|
||||||
|
const voices = availableVoices.length > 0 ? availableVoices : window.speechSynthesis.getVoices();
|
||||||
|
const langPrefix = locale === 'ar' ? 'ar' : 'en';
|
||||||
|
const langVoices = voices.filter((voice) => voice.lang.toLowerCase().startsWith(langPrefix));
|
||||||
|
|
||||||
|
if (langVoices.length > 0) {
|
||||||
|
const bestVoice = langVoices.find((voice) =>
|
||||||
|
voice.name.includes('Google') || voice.name.includes('Natural') || voice.name.includes('Premium') || voice.name.includes('Online')
|
||||||
|
) || langVoices.find((voice) => voice.name.includes('Microsoft')) || langVoices[0];
|
||||||
|
if (bestVoice) utterance.voice = bestVoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
activeSpeechUtterance = utterance;
|
||||||
|
utterance.onend = done;
|
||||||
|
utterance.onerror = done;
|
||||||
|
if (window.speechSynthesis.speaking || window.speechSynthesis.pending) {
|
||||||
|
window.speechSynthesis.cancel();
|
||||||
|
}
|
||||||
|
window.speechSynthesis.speak(utterance);
|
||||||
|
} else {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
audioObj.onended = done;
|
||||||
|
audioObj.onerror = handleFallback;
|
||||||
|
|
||||||
|
const playPromise = audioObj.play();
|
||||||
|
if (playPromise !== undefined) {
|
||||||
|
playPromise.catch(handleFallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
audioLoadTimeoutId = window.setTimeout(() => {
|
||||||
|
if (!finished && (audioObj.networkState === HTMLMediaElement.NETWORK_NO_SOURCE || audioObj.error)) {
|
||||||
|
handleFallback(new Error('Audio load timeout'));
|
||||||
|
}
|
||||||
|
}, 2500);
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
|
||||||
const checkAnnouncements = () => {
|
const checkAnnouncements = () => {
|
||||||
const cards = Array.from(document.querySelectorAll('.announcement-card'));
|
const cards = Array.from(document.querySelectorAll('.announcement-card'));
|
||||||
const latest = cards[0];
|
const latest = cards[0];
|
||||||
const audioEnabled = window.localStorage.getItem('hospitalQueue:audioEnabled') !== 'false';
|
const audioEnabled = window.localStorage.getItem('hospitalQueue:audioEnabled') !== 'false';
|
||||||
|
|
||||||
if (latest) {
|
if (!latest) {
|
||||||
const announcementKey = latest.dataset.announcementKey || '';
|
return;
|
||||||
const storageKey = `hospitalQueue:lastAnnouncement:${locale}`;
|
}
|
||||||
const storedKey = window.localStorage.getItem(storageKey) || '';
|
|
||||||
|
const storageKey = `hospitalQueue:lastAnnouncement:${locale}`;
|
||||||
if (announcementKey && announcementKey !== storedKey) {
|
const storedKey = window.localStorage.getItem(storageKey) || '';
|
||||||
window.localStorage.setItem(storageKey, announcementKey);
|
const latestKey = latest.dataset.announcementKey || '';
|
||||||
|
if (!latestKey || latestKey === storedKey) {
|
||||||
if (audioEnabled) {
|
return;
|
||||||
const videoPlayer = document.getElementById('adsVideoPlayer');
|
}
|
||||||
if (videoPlayer) videoPlayer.volume = 0.1;
|
|
||||||
|
let newCards = [];
|
||||||
const text = locale === 'ar' ? (latest.dataset.announcementAr || '') : (latest.dataset.announcementEn || '');
|
if (!storedKey) {
|
||||||
let audioObj = null;
|
newCards = [latest];
|
||||||
if (text) {
|
} else {
|
||||||
const tl = locale === 'ar' ? 'ar' : 'en';
|
for (const card of cards) {
|
||||||
const ttsUrl = window.location.pathname.replace(/\/[^\/]*$/, '/api/tts.php') + '?lang=' + tl + '&text=' + encodeURIComponent(text);
|
const key = card.dataset.announcementKey || '';
|
||||||
audioObj = new Audio(ttsUrl);
|
if (!key) continue;
|
||||||
audioObj.preload = 'auto'; // Force browser to start downloading
|
if (key === storedKey) break;
|
||||||
}
|
newCards.push(card);
|
||||||
|
|
||||||
playChime();
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
if (audioObj) {
|
|
||||||
audioObj.onended = () => { if (videoPlayer) videoPlayer.volume = 1.0; };
|
|
||||||
|
|
||||||
let fallbackPlayed = false;
|
|
||||||
const handleFallback = (err) => {
|
|
||||||
if (fallbackPlayed) return;
|
|
||||||
fallbackPlayed = true;
|
|
||||||
console.warn("External TTS failed, falling back to built-in speech", err);
|
|
||||||
if ('speechSynthesis' in window) {
|
|
||||||
const utterance = new SpeechSynthesisUtterance(text);
|
|
||||||
utterance.lang = locale === 'ar' ? 'ar-SA' : 'en-US';
|
|
||||||
const voices = availableVoices.length > 0 ? availableVoices : window.speechSynthesis.getVoices();
|
|
||||||
const langPrefix = locale === 'ar' ? 'ar' : 'en';
|
|
||||||
const langVoices = voices.filter(v => v.lang.toLowerCase().startsWith(langPrefix));
|
|
||||||
|
|
||||||
if (langVoices.length > 0) {
|
|
||||||
const bestVoice = langVoices.find(v =>
|
|
||||||
v.name.includes('Google') || v.name.includes('Natural') || v.name.includes('Premium') || v.name.includes('Online')
|
|
||||||
) || langVoices.find(v => v.name.includes('Microsoft')) || langVoices[0];
|
|
||||||
if (bestVoice) utterance.voice = bestVoice;
|
|
||||||
}
|
|
||||||
|
|
||||||
utterance.onend = () => { if (videoPlayer) videoPlayer.volume = 1.0; };
|
|
||||||
utterance.onerror = () => { if (videoPlayer) videoPlayer.volume = 1.0; };
|
|
||||||
window.speechSynthesis.cancel(); // Clear any stuck queue
|
|
||||||
window.speechSynthesis.speak(utterance);
|
|
||||||
} else {
|
|
||||||
if (videoPlayer) videoPlayer.volume = 1.0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
audioObj.onerror = handleFallback;
|
|
||||||
|
|
||||||
const playPromise = audioObj.play();
|
|
||||||
if (playPromise !== undefined) {
|
|
||||||
playPromise.catch(handleFallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Failsafe timeout in case audioObj hangs (e.g., blocked by adblocker, bad network)
|
|
||||||
setTimeout(() => {
|
|
||||||
if (audioObj.networkState === HTMLMediaElement.NETWORK_NO_SOURCE || audioObj.error) {
|
|
||||||
handleFallback(new Error("Audio load timeout"));
|
|
||||||
}
|
|
||||||
}, 2500);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (videoPlayer) videoPlayer.volume = 1.0;
|
|
||||||
}
|
|
||||||
}, 500); // reduced timeout from 1200ms to 500ms for faster playback
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
newCards.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.localStorage.setItem(storageKey, latestKey);
|
||||||
|
|
||||||
|
if (audioEnabled) {
|
||||||
|
newCards.forEach(queueAnnouncement);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user