Fix TTS overlap deterministically, preserve message order, fix flaky Google login button
All checks were successful
CI/CD Pipeline / build-and-deploy (push) Successful in 29s

Overlay display duration is now computed server-side from estimated
reading time (text length / speaking rate), not from the audio
element's 'ended' event — edge-tts streams MP3 without a duration
header, and OBS's embedded browser fires 'ended' unreliably for that,
which is why the earlier event-based fix didn't hold up.

Per-streamer broadcasts are now serialized through a queue, so a
message waiting on an AI reply can no longer be overtaken by a later
message from a different viewer that doesn't need one.

Also fix the Google Sign-In button intermittently not rendering: the
GSI script tag is async/defer, and both index.html and send.html/js
called google.accounts.id.initialize() without waiting for it to
actually finish loading — a race that failed silently until the
script was cached from a previous load.
This commit is contained in:
vrubelroman 2026-07-08 18:21:57 +00:00
parent b669c2d0b7
commit 8aeca230bc
6 changed files with 81 additions and 66 deletions

View file

@ -4,7 +4,10 @@
<meta charset="UTF-8" />
<title>Send2Streamer — сообщения от зрителей прямо на стрим</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="https://accounts.google.com/gsi/client" async defer></script>
<script>
window.googleGsiLoaded = new Promise((resolve) => { window.resolveGoogleGsiLoaded = resolve; });
</script>
<script src="https://accounts.google.com/gsi/client" async defer onload="resolveGoogleGsiLoaded()"></script>
<style>
body {
font-family: system-ui, sans-serif;
@ -87,6 +90,12 @@
const configRes = await fetch('/config');
const { googleClientId } = await configRes.json();
// The GSI script tag is async/defer — without waiting for it to actually
// finish loading, this can run before `google` exists (a race that fails
// silently), which is why the button sometimes only appeared after a
// reload (once the script was already cached).
await window.googleGsiLoaded;
google.accounts.id.initialize({
client_id: googleClientId,
callback: handleCredentialResponse,