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.
125 lines
2.9 KiB
HTML
125 lines
2.9 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<title>Отправить сообщение на стрим</title>
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<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;
|
||
max-width: 480px;
|
||
margin: 60px auto;
|
||
padding: 0 20px;
|
||
}
|
||
h1 {
|
||
font-size: 22px;
|
||
margin-bottom: 4px;
|
||
}
|
||
.subtitle {
|
||
color: #666;
|
||
font-size: 14px;
|
||
margin-bottom: 24px;
|
||
}
|
||
#user-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
margin-bottom: 20px;
|
||
}
|
||
#user-bar img {
|
||
width: 32px;
|
||
height: 32px;
|
||
border-radius: 50%;
|
||
}
|
||
#logout {
|
||
margin-left: auto;
|
||
cursor: pointer;
|
||
color: #888;
|
||
font-size: 14px;
|
||
}
|
||
label {
|
||
display: block;
|
||
font-size: 13px;
|
||
color: #666;
|
||
margin-bottom: 4px;
|
||
}
|
||
#display-name {
|
||
width: 100%;
|
||
font-size: 16px;
|
||
padding: 8px 10px;
|
||
box-sizing: border-box;
|
||
margin-bottom: 16px;
|
||
}
|
||
textarea {
|
||
width: 100%;
|
||
height: 100px;
|
||
font-size: 16px;
|
||
padding: 10px;
|
||
box-sizing: border-box;
|
||
resize: vertical;
|
||
}
|
||
button {
|
||
margin-top: 10px;
|
||
padding: 10px 20px;
|
||
font-size: 16px;
|
||
cursor: pointer;
|
||
}
|
||
#status {
|
||
margin-top: 10px;
|
||
font-size: 14px;
|
||
color: #888;
|
||
min-height: 20px;
|
||
}
|
||
.checkbox-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
margin-top: 10px;
|
||
font-size: 14px;
|
||
}
|
||
.checkbox-row label {
|
||
display: inline;
|
||
margin-bottom: 0;
|
||
color: inherit;
|
||
}
|
||
#hidden-until-login {
|
||
display: none;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<h1>Сообщение на стрим</h1>
|
||
<p class="subtitle">Войдите через Google и напишите сообщение — оно появится на стриме с озвучкой.</p>
|
||
|
||
<div id="login-container"></div>
|
||
|
||
<div id="hidden-until-login">
|
||
<div id="user-bar">
|
||
<img id="user-pic" src="" alt="" />
|
||
<span id="user-name"></span>
|
||
<span id="logout">выйти</span>
|
||
</div>
|
||
|
||
<label for="display-name">Ваше имя (покажется стримеру вместо имени из Google)</label>
|
||
<input id="display-name" type="text" maxlength="60" placeholder="Имя..." />
|
||
|
||
<textarea id="message" placeholder="Текст для стрима..." maxlength="500"></textarea>
|
||
|
||
<div class="checkbox-row">
|
||
<input id="ai-reply" type="checkbox" />
|
||
<label for="ai-reply" id="ai-reply-label">Задать вопрос Альтушке Ирине 🤖</label>
|
||
</div>
|
||
|
||
<button id="send">Отправить</button>
|
||
<div id="status"></div>
|
||
</div>
|
||
|
||
<script src="/socket.io/socket.io.js"></script>
|
||
<script src="/send.js"></script>
|
||
</body>
|
||
</html>
|