193 lines
6 KiB
Text
193 lines
6 KiB
Text
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<style>
|
|
:root {
|
|
--chat-left: <%= layout.chat_left %>%;
|
|
--chat-top: <%= layout.chat_top %>%;
|
|
--chat-width: <%= layout.chat_width %>%;
|
|
--chat-height: <%= layout.chat_height %>%;
|
|
--chat-font-size: <%= layout.chat_font_size %>px;
|
|
--chat-font-family: <%= layout.chat_font_family %>;
|
|
--chat-bg-opacity: <%= layout.chat_bg_opacity %>;
|
|
--chat-text-color: <%= layout.chat_text_color %>;
|
|
--chat-border-radius: <%= layout.chat_border_radius %>px;
|
|
--alert-left: <%= layout.alert_left %>%;
|
|
--alert-top: <%= layout.alert_top %>%;
|
|
--alert-font-size: <%= layout.alert_font_size %>px;
|
|
--alert-bg-opacity: <%= layout.alert_bg_opacity %>;
|
|
--alert-text-color: <%= layout.alert_text_color %>;
|
|
}
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body { background: transparent; overflow: hidden; width: 1920px; height: 1080px; }
|
|
|
|
#chat {
|
|
position: fixed;
|
|
left: var(--chat-left);
|
|
top: var(--chat-top);
|
|
width: var(--chat-width);
|
|
max-height: var(--chat-height);
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
font-family: var(--chat-font-family);
|
|
font-size: var(--chat-font-size);
|
|
color: var(--chat-text-color);
|
|
}
|
|
.chat-msg {
|
|
background: rgba(0, 0, 0, var(--chat-bg-opacity));
|
|
border-radius: var(--chat-border-radius);
|
|
padding: 5px 10px;
|
|
word-break: break-word;
|
|
animation: fadeIn 0.25s ease;
|
|
transition: opacity 0.5s ease;
|
|
}
|
|
.chat-msg .username { font-weight: 700; margin-right: 4px; }
|
|
|
|
#alert-box {
|
|
position: fixed;
|
|
left: var(--alert-left);
|
|
top: var(--alert-top);
|
|
transform: translate(-50%, -50%);
|
|
font-family: var(--chat-font-family);
|
|
font-size: var(--alert-font-size);
|
|
color: var(--alert-text-color);
|
|
background: rgba(0, 0, 0, var(--alert-bg-opacity));
|
|
border-radius: 12px;
|
|
padding: 16px 28px;
|
|
text-align: center;
|
|
display: none;
|
|
}
|
|
#alert-video {
|
|
position: fixed;
|
|
left: var(--alert-left);
|
|
top: var(--alert-top);
|
|
transform: translate(-50%, -50%);
|
|
max-width: 640px;
|
|
max-height: 480px;
|
|
border-radius: 12px;
|
|
display: none;
|
|
}
|
|
|
|
@keyframes fadeIn {
|
|
from { opacity: 0; transform: translateY(6px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
<%- settings.custom_css %>
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="chat"></div>
|
|
<div id="alert-box"></div>
|
|
<video id="alert-video" autoplay></video>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/tmi.js@1.8.5/build/tmi.min.js"></script>
|
|
<script>
|
|
const S = <%- JSON.stringify(settings) %>;
|
|
const chat = document.getElementById('chat');
|
|
const alertBox = document.getElementById('alert-box');
|
|
const alertVideo = document.getElementById('alert-video');
|
|
|
|
let lastMsgTime = 0;
|
|
|
|
// --- Chat via tmi.js ---
|
|
if (S.channel) {
|
|
const client = new tmi.Client({ channels: [S.channel] });
|
|
client.connect().catch(console.error);
|
|
|
|
client.on('message', (channel, tags, message) => {
|
|
const now = Date.now();
|
|
const silence = (now - lastMsgTime) / 1000;
|
|
|
|
if (S.gap_audio_enabled && S.gap_audio_url && lastMsgTime > 0 && silence >= S.gap_audio_min_silence) {
|
|
const t = Math.min(
|
|
(silence - S.gap_audio_min_silence) / Math.max(S.gap_audio_loud_after - S.gap_audio_min_silence, 1),
|
|
1
|
|
);
|
|
const vol = S.gap_audio_min_volume + t * (S.gap_audio_max_volume - S.gap_audio_min_volume);
|
|
playAudio(S.gap_audio_url, vol);
|
|
}
|
|
|
|
lastMsgTime = now;
|
|
addMessage(tags['display-name'] || tags.username, tags.color || '#b4a0d4', message);
|
|
});
|
|
}
|
|
|
|
function addMessage(username, color, text) {
|
|
const div = document.createElement('div');
|
|
div.className = 'chat-msg';
|
|
div.innerHTML = `<span class="username" style="color:${esc(color)}">${esc(username)}</span>: ${esc(text)}`;
|
|
chat.appendChild(div);
|
|
|
|
while (chat.children.length > S.max_messages) chat.removeChild(chat.firstChild);
|
|
|
|
if (S.message_timeout > 0) {
|
|
setTimeout(() => {
|
|
div.style.opacity = '0';
|
|
setTimeout(() => div.remove(), 500);
|
|
}, S.message_timeout * 1000);
|
|
}
|
|
}
|
|
|
|
// --- Alert WebSocket ---
|
|
function connectWS() {
|
|
const proto = location.protocol === 'https:' ? 'wss' : 'ws';
|
|
const ws = new WebSocket(`${proto}://${location.host}/ws/overlay`);
|
|
|
|
ws.onmessage = (e) => {
|
|
const msg = JSON.parse(e.data);
|
|
document.dispatchEvent(new CustomEvent(`bashyoverlay:${msg.event_type}`, { detail: msg }));
|
|
handleAction(msg);
|
|
};
|
|
|
|
ws.onclose = () => setTimeout(connectWS, 3000);
|
|
}
|
|
connectWS();
|
|
|
|
function handleAction(msg) {
|
|
if (msg.action_type === 'sound' && msg.media_url) {
|
|
playAudio(msg.media_url, 1.0);
|
|
} else if (msg.action_type === 'video' && msg.media_url) {
|
|
showVideo(msg.media_url);
|
|
} else if (msg.action_type === 'alert') {
|
|
showAlert(msg.alert_text || '', msg.username);
|
|
}
|
|
}
|
|
|
|
function playAudio(url, volume) {
|
|
const a = new Audio(url);
|
|
a.volume = Math.max(0, Math.min(1, volume));
|
|
a.play().catch(() => {});
|
|
}
|
|
|
|
function showAlert(text, username) {
|
|
alertBox.textContent = text.replace('{username}', username || '');
|
|
alertBox.style.display = 'block';
|
|
setTimeout(() => { alertBox.style.display = 'none'; }, 5000);
|
|
}
|
|
|
|
function showVideo(url) {
|
|
alertVideo.src = url;
|
|
alertVideo.style.display = 'block';
|
|
alertVideo.play();
|
|
alertVideo.onended = () => {
|
|
alertVideo.style.display = 'none';
|
|
alertVideo.src = '';
|
|
};
|
|
}
|
|
|
|
function esc(str) {
|
|
return String(str)
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"');
|
|
}
|
|
|
|
<%- settings.custom_js %>
|
|
</script>
|
|
</body>
|
|
</html>
|