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.
Streamers log in with Google on a new landing page and get a
dashboard with two auto-generated, permanent links: one for their
OBS/StreamLabs Browser Source, one to share with viewers. Messages
are now scoped to the right streamer via Socket.IO rooms instead of
broadcasting globally. Streamer records and tokens are persisted in
SQLite (node:sqlite) so links survive restarts/redeploys.
Also adds a Forgejo Actions pipeline mirroring t2sTelegramBot's:
build, smoke-test, push to the local registry, then deploy to the
prod host over SSH.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>