Ported 'Twitch Ad Countdown Timer'

This commit is contained in:
nuttylmao
2025-06-03 22:33:21 +10:00
parent 492486b0b7
commit ec8e986412
9 changed files with 597 additions and 422 deletions
+28
View File
@@ -0,0 +1,28 @@
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<link rel="shortcut icon" href="#" />
<link rel="stylesheet" href="./style.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xz/fonts@1/serve/metropolis.min.css" />
<script type="text/javascript" src="https://unpkg.com/@streamerbot/client/dist/streamerbot-client.js"></script>
</head>
<body>
<div id="statusContainer">Connecting...</div>
<div id="mainContainer">
<div id="timer-bar"></div>
<div id="timer-label">
<span id="ad-label">Ad</span>
&nbsp;&nbsp;
<span id="ads-remaining-label">1 of 3 • </span>
<span id="time-remaining-label">1:20</span>
</div>
<div id="upcoming-ad-warning-container">
<div>Ads starting in...</div>
<div id="countdown-label">69</div>
</div>
</div>
</body>
<script src="./script.js"></script>
+397
View File
@@ -0,0 +1,397 @@
////////////////
// PARAMETERS //
////////////////
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
let sbDebugMode = true;
const sbServerAddress = urlParams.get("address") || "127.0.0.1";
const sbServerPort = urlParams.get("port") || "8080";
///////////////////
// PAGE ELEMENTS //
///////////////////
const timerBar = document.getElementById('timer-bar');
const adLabel = document.getElementById('ad-label');
const timerLabel = document.getElementById('timer-label');
const adsRemainingLabel = document.getElementById('ads-remaining-label');
const timeRemainingLabel = document.getElementById('time-remaining-label');
const upcomingAdWarningContainer = document.getElementById('upcoming-ad-warning-container');
const countdownLabel = document.getElementById('countdown-label');
/////////////
// OPTIONS //
/////////////
// Appearance
const barColor = urlParams.get("barColor") || "#FFCC00";
const barThickness = GetIntParam("barThickness", 4);
const barPosition = urlParams.get("barPosition") || "bottom";
const timerColor = urlParams.get("timerColor") || "#FFCC00";
const timerPosition = urlParams.get("timerPosition") || "top-left";
// General
const showUpcomingAdWarning = GetBooleanParam("showUpcomingAdWarning", true);
const warningSeconds = GetIntParam("warningSeconds", 10);
/////////////////
// GLOBAL VARS //
/////////////////
let singleAdLength = 30;
let timerBarLocked = false;
let timerLabelLocked = false;
let upcomingAdWarningLocked = false;
/////////////////
// PAGEE SETUP //
/////////////////
// Set the colors
timerBar.style.background = barColor;
adLabel.style.background = timerColor;
// Set the position of the bar
switch (barPosition) {
case "none":
timerBar.style.display = "none";
break;
case "bottom":
timerBar.style.height = barThickness + "px";
timerBar.style.bottom = "0px";
timerBar.style.left = "0px";
break;
case "top":
timerBar.style.height = barThickness + "px";
timerBar.style.top = "0px";
timerBar.style.left = "0px";
break;
case "left":
timerBar.style.width = barThickness + "px";
timerBar.style.bottom = "0px";
timerBar.style.left = "0px";
break;
case "right":
timerBar.style.width = barThickness + "px";
timerBar.style.bottom = "0px";
timerBar.style.right = "0px";
break;
}
// Set the position of the timer
switch (timerPosition) {
case "none":
timerLabel.style.display = "none";
break;
case "top-left":
timerLabel.style.top = "0px";
timerLabel.style.left = "0px";
break;
case "top-right":
timerLabel.style.top = "0px";
timerLabel.style.right = "0px";
break;
case "bottom-left":
timerLabel.style.bottom = "0px";
timerLabel.style.left = "0px";
break;
case "bottom-right":
timerLabel.style.bottom = "0px";
timerLabel.style.right = "0px";
break;
}
/////////////////////////
// STREAMER.BOT CLIENT //
/////////////////////////
const client = new StreamerbotClient({
host: sbServerAddress,
port: sbServerPort,
onConnect: (data) => {
console.log(`Streamer.bot successfully connected to ${sbServerAddress}:${sbServerPort}`)
console.debug(data);
SetConnectionStatus(true);
},
onDisconnect: () => {
console.error(`Streamer.bot disconnected from ${sbServerAddress}:${sbServerPort}`)
SetConnectionStatus(false);
}
});
client.on('Twitch.AdRun', (response) => {
console.debug(response.data);
TwitchAdRun(response.data);
})
client.on('Twitch.UpcomingAd', (response) => {
console.debug(response.data);
TwitchUpcomingAd(response.data);
})
/////////////////////
// TWITCH AD TIMER //
/////////////////////
function TwitchAdRun(data) {
const duration = data.length_seconds;
TimerBarAnimation(duration);
TimerLabelAnimation(duration);
}
function TwitchUpcomingAd(data) {
// TODO: Need to scrape UpcomingAd data to continue this
const warningMinute = 5;
const delayStart = warningMinute * 60 - warningSeconds;
setTimeout(() => {
UpcomingAdWarning(warningSeconds);
}, delayStart * 1000);
}
function TimerBarAnimation(duration) {
// Check if the widget is in the middle of an animation
if (timerBarLocked)
return;
timerBarLocked = true;
// If the bar is top/bottom, animate horizontally
// If the bar is left/right, animate vertically
switch (barPosition) {
case "bottom":
case "top":
timerBar.style.transition = 'width 0.5s ease-in-out';
timerBar.style.width = '100%';
setTimeout(() => {
timerBar.style.transition = `width ${duration}s linear`;
timerBar.style.width = '0%';
}, 500);
break;
case "left":
case "right":
timerBar.style.transition = 'height 0.5s ease-in-out';
timerBar.style.height = '100%';
setTimeout(() => {
timerBar.style.transition = `height ${duration}s linear`;
timerBar.style.height = '0%';
}, 500);
break;
}
// Unlock the widget
setTimeout(() => {
timerBarLocked = false;
}, duration * 1000);
}
function TimerLabelAnimation(duration) {
// Check if the widget is in the middle of an animation
if (timerLabelLocked)
return;
timerLabelLocked = true;
// Calculate starting time
let startingTime = duration % singleAdLength;
if (startingTime == 0)
startingTime = singleAdLength;
// Estimate how many ads there should be
let adsTotal = Math.ceil(duration / singleAdLength);
let adsRemaining = 1;
function updateTimer() {
// Show the labels BEFORE decrementing
adsRemainingLabel.innerText = `${adsRemaining} of ${adsTotal}`;
timeRemainingLabel.innerText = startingTime.toString().toHHMMSS();
timerLabel.style.opacity = 1;
// Decrement time
startingTime--;
// Check for transitions
if (startingTime < 0 && adsRemaining < adsTotal) {
adsRemaining++;
startingTime = singleAdLength - 1; // -1 since 1 second has already passed
}
if (startingTime < 0 && adsRemaining === adsTotal) {
clearInterval(timerThingy);
timerLabel.style.opacity = 0;
}
}
// Run immediately
updateTimer();
// Then every second
var timerThingy = setInterval(updateTimer, 1000);
// Unlock the widget
setTimeout(() => {
timerLabelLocked = false;
}, duration * 1000);
}
function UpcomingAdWarning(warningSeconds) {
if (!showUpcomingAdWarning)
return;
// Check if the widget is in the middle of an animation
if (upcomingAdWarningLocked)
return;
upcomingAdWarningLocked = true;
let width = upcomingAdWarningContainer.getBoundingClientRect().width;
// Set the starting position of the countdown box
upcomingAdWarningContainer.style.right = -width + "px";
countdownLabel.innerHTML = warningSeconds;
// Slide the countdown box on screen
upcomingAdWarningContainer.style.right = "0px";
// Start the countdown timer
let startingTime = warningSeconds;
var timerThingy = setInterval(function () {
startingTime--;
countdownLabel.innerText = startingTime;
if (startingTime == 0) {
clearInterval(timerThingy);
upcomingAdWarningContainer.style.right = -width + "px";
return;
}
}, 1000)
// Unlock the widget
setTimeout(() => {
upcomingAdWarningLocked = false;
}, warningSeconds * 1000);
}
//////////////////////
// HELPER FUNCTIONS //
//////////////////////
function GetBooleanParam(paramName, defaultValue) {
const urlParams = new URLSearchParams(window.location.search);
const paramValue = urlParams.get(paramName);
if (paramValue === null) {
return defaultValue; // Parameter not found
}
const lowercaseValue = paramValue.toLowerCase(); // Handle case-insensitivity
if (lowercaseValue === 'true') {
return true;
} else if (lowercaseValue === 'false') {
return false;
} else {
return paramValue; // Return original string if not 'true' or 'false'
}
}
function GetIntParam(paramName, defaultValue) {
const urlParams = new URLSearchParams(window.location.search);
const paramValue = urlParams.get(paramName);
if (paramValue === null) {
return defaultValue; // or undefined, or a default value, depending on your needs
}
console.log(paramValue);
const intValue = parseInt(paramValue, 10); // Parse as base 10 integer
if (isNaN(intValue)) {
return null; // or handle the error in another way, e.g., throw an error
}
return intValue;
}
String.prototype.toHHMMSS = function () {
var sec_num = parseInt(this, 10); // don't forget the second param
var hours = Math.floor(sec_num / 3600);
var minutes = Math.floor((sec_num - (hours * 3600)) / 60);
var seconds = sec_num - (hours * 3600) - (minutes * 60);
if (hours < 10) { hours = "0" + hours; }
//if (minutes < 10) {minutes = "0"+minutes;}
if (seconds < 10) { seconds = "0" + seconds; }
//return hours+':'+minutes+':'+seconds;
return minutes + ':' + seconds;
}
function IsNullOrWhitespace(str) {
return /^\s*$/.test(str);
}
////////////////////
// TEST FUNCTIONS //
////////////////////
function testAd() {
const data = {
"length_seconds": 15,
"timestamp": "0001-01-01T00:00:00",
"is_automatic": true,
"requester_user_id": "54983562",
"requester_user_login": "nutty",
"requester_user_name": "nutty",
"is_test": false
}
TwitchAdRun(data)
}
function testUpcomingAdWarning() {
UpcomingAdWarning(10);
}
///////////////////////////////////
// STREAMER.BOT WEBSOCKET STATUS //
///////////////////////////////////
// This function sets the visibility of the Streamer.bot status label on the overlay
function SetConnectionStatus(connected) {
let statusContainer = document.getElementById("statusContainer");
if (connected) {
statusContainer.style.background = "#2FB774";
statusContainer.innerText = "Connected!";
statusContainer.style.opacity = 1;
setTimeout(() => {
statusContainer.style.transition = "all 2s ease";
statusContainer.style.opacity = 0;
}, 10);
}
else {
statusContainer.style.background = "#D12025";
statusContainer.innerText = "Connecting...";
statusContainer.style.transition = "";
statusContainer.style.opacity = 1;
}
}
@@ -0,0 +1,140 @@
{
"settings": [
{
"id": "testAd",
"label": "Send Test Ad",
"description": "",
"type": "button",
"callFunction": "testAd",
"group": ""
},
{
"id": "testUpcomingAdWarning",
"label": "Send Test Upcoming Ad Warning",
"description": "",
"type": "button",
"callFunction": "testUpcomingAdWarning",
"group": ""
},
{
"id": "barColor",
"label": "Bar Color",
"description": "",
"type": "color",
"defaultValue": "#FFCC00",
"group": "Appearance"
},
{
"id": "barThickness",
"label": "Bar Thickness",
"description": "",
"type": "number",
"defaultValue": "4",
"min": 1,
"group": "Appearance"
},
{
"id": "barPosition",
"label": "Bar Position",
"description": "",
"type": "select",
"options": [
{
"value": "none",
"label": "None"
},
{
"value": "bottom",
"label": "Bottom"
},
{
"value": "top",
"label": "Top"
},
{
"value": "left",
"label": "Left"
},
{
"value": "right",
"label": "Right"
}
],
"defaultValue": "bottom",
"group": "Appearance"
},
{
"id": "timerColor",
"label": "Timer Color",
"description": "",
"type": "color",
"defaultValue": "#FFCC00",
"group": "Appearance"
},
{
"id": "timerPosition",
"label": "Timer Position",
"description": "",
"type": "select",
"options": [
{
"value": "none",
"label": "None"
},
{
"value": "top-left",
"label": "Top Left"
},
{
"value": "top-right",
"label": "Top Right"
},
{
"value": "bottom-left",
"label": "Bottom Left"
},
{
"value": "bottom-right",
"label": "Bottom Right"
}
],
"defaultValue": "top-left",
"group": "Appearance"
},
{
"id": "showUpcomingAdWarning",
"label": "Show Upcoming Ad Warning",
"description": "",
"type": "checkbox",
"defaultValue": true,
"group": "General"
},
{
"id": "warningSeconds",
"label": "Warning Time",
"description": "Warn X seconds before ad",
"type": "number",
"defaultValue": "10",
"min": 1,
"max": 300,
"showIf": "showUpcomingAdWarning",
"group": "General"
},
{
"id": "address",
"label": "Address",
"description": "Streamer.bot Websocket Server Address",
"type": "text",
"defaultValue": "127.0.0.1",
"group": "Advanced"
},
{
"id": "port",
"label": "Port",
"description": "Streamer.bot Websocket Server Port",
"type": "text",
"defaultValue": "8080",
"group": "Advanced"
}
]
}
@@ -3,14 +3,20 @@
padding: 0; padding: 0;
} }
body {
overflow: hidden;
}
html {
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
/* color: white;
text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.5);
overflow-wrap: break-word; */
}
#statusContainer { #statusContainer {
text-transform: uppercase; font-weight: 500;
font-family: 'Metropolis';
font-style: italic;
font-weight: bold;
font-size: 30px; font-size: 30px;
width: 400px;
max-width: fit-content;
text-align: center; text-align: center;
background-color: #D12025; background-color: #D12025;
color: white; color: white;
@@ -22,25 +28,28 @@
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
} }
#hugeTittiesContainer { #timer-bar {
font-family: 'Metropolis';
font-weight: bold;
background-color: rgba(0, 0, 0, .4);
padding: 12px;
border-radius: 10px;
position: absolute;
margin: 50px;
opacity: 0;
display: table;
}
#timerBar {
width: 0px; width: 0px;
height: 0px;
background-color: #ffcc00; background-color: #ffcc00;
position: absolute; position: absolute;
} }
#adLabel { #timer-label {
font-family: 'Metropolis';
font-weight: bold;
background-color: rgba(0, 0, 0, .4);
color: white;
padding: 12px;
border-radius: 10px;
position: absolute;
margin: 50px;
display: table;
opacity: 0;
transition: opacity 0.5s linear;
}
#ad-label {
font-size: 20px; font-size: 20px;
font-weight: bold; font-weight: bold;
max-width: fit-content; max-width: fit-content;
@@ -53,17 +62,7 @@
display: table-cell; display: table-cell;
} }
#timerLabel { #upcoming-ad-warning-container {
font-size: 20px;
max-width: fit-content;
text-align: center;
display: inline-block;
color: white;
padding: 10px 0px 10px 10px;
display: table-cell;
}
#midRollContainer {
font-family: 'Metropolis'; font-family: 'Metropolis';
font-size: 20px; font-size: 20px;
max-width: fit-content; max-width: fit-content;
@@ -77,9 +76,10 @@
position: absolute; position: absolute;
bottom: 15%; bottom: 15%;
right: -100%; right: -100%;
transition: right 0.5s ease-in-out;
} }
#midRollCountdownContainer { #countdown-label {
font-size: 40px; font-size: 40px;
font-weight: bold; font-weight: bold;
margin: 10px 0px 0px 0px; margin: 10px 0px 0px 0px;
-30
View File
@@ -1,30 +0,0 @@
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<link rel="shortcut icon" href="#" />
<link rel="stylesheet" href="./style.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xz/fonts@1/serve/metropolis.min.css" />
</head>
<body>
<div id="statusContainer">Connecting...</div>
<div id="mainContainer">
<div id="timerBar"></div>
</div>
<div id="hugeTittiesContainer">
<div id="adLabel">
Ad
</div>
<div id="timerLabel">
<span id="adsRemainingContainer"></span><span id="timerContainer"></span>
</div>
</div>
<div id="midRollContainer">
<div id="midRollStartingInLabel">Ads starting in...</div>
<div id="midRollCountdownContainer"></div>
</div>
</body>
<script src='https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.0/TweenMax.min.js'></script>
<script src="./script.js"></script>
-340
View File
@@ -1,340 +0,0 @@
////////////////
// PARAMETERS //
////////////////
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
let sbDebugMode = true;
const sbServerAddress = urlParams.get("address") || "127.0.0.1";
const sbServerPort = urlParams.get("port") || "8080";
const lineThickness = urlParams.get("lineThickness") || 4;
const barPosition = urlParams.get("barPosition") || "bottom"; // none, bottom, top, left, right
const timerPosition = urlParams.get("timerPosition") || "topLeft"; // none, topLeft, topRight, bottomLeft, bottomRight
const showMidRollCountdown = urlParams.get("timerPosition") || true;
/////////////////
// GLOBAL VARS //
/////////////////
let ws;
let singleAdLength = 30;
///////////////////////////////////
// SRTEAMER.BOT WEBSOCKET SERVER //
///////////////////////////////////
// This is the main function that connects to the Streamer.bot websocket server
function connectws() {
if ("WebSocket" in window) {
ws = new WebSocket("ws://" + sbServerAddress + ":" + sbServerPort + "/");
// Reconnect
ws.onclose = function () {
SetConnectionStatus(false);
setTimeout(connectws, 5000);
};
// Connect
ws.onopen = async function () {
SetConnectionStatus(true);
console.log("Subscribe to events");
ws.send(
JSON.stringify({
request: "Subscribe",
id: "subscribe-events-id",
events: {
// This is the list of Streamer.bot websocket events to subscribe to
// See full list of events here:
// https://wiki.streamer.bot/en/Servers-Clients/WebSocket-Server/Requests
twitch: [
"AdRun",
"AdMidRoll"
]
}
})
);
ws.onmessage = function (event) {
// Grab message and parse JSON
const msg = event.data;
const wsdata = JSON.parse(msg);
if (typeof wsdata.event == "undefined") {
return;
}
// Print data to log for debugging purposes
if (sbDebugMode)
{
console.log(wsdata.data);
console.log(wsdata.event.type);
}
// Check for events to trigger
// See documentation for all events here:
// https://wiki.streamer.bot/en/Servers-Clients/WebSocket-Server/Events
switch (wsdata.event.source) {
// Twitch Events
case 'Twitch':
switch (wsdata.event.type) {
case ('AdRun'):
AdRun(wsdata.data);
break;
case ('AdMidRoll'):
AdMidRoll(wsdata.data);
break;
}
break;
}
};
}
}
}
///////////////////////
// TWITCH AD OVERLAY //
///////////////////////
function AdRun(data) {
if (data.length_seconds <= 0)
{
TimerBarAnimation(singleAdLength);
HugeTittiesAnimation(singleAdLength);
}
else
{
TimerBarAnimation(data.length_seconds);
HugeTittiesAnimation(data.length_seconds);
}
}
function AdMidRoll(data) {
if (!showMidRollCountdown)
return;
//MidRollAnimation(data.length);
MidRollAnimation(5);
}
function TimerBarAnimation(adLength) {
let timerBar = document.getElementById("timerBar");
switch (barPosition) {
case "none":
timerBar.style.display = "none";
break;
case "bottom":
timerBar.style.height = lineThickness + "px";
timerBar.style.bottom = "0px";
timerBar.style.left = "0px";
// Start Animation
tl = new TimelineMax();
tl
.to(timerBar, 0.5, { width: window.innerWidth + "px", ease: Cubic.ease })
.to(timerBar, adLength, { width: "0px", ease: Linear.easeNone })
break;
case "top":
timerBar.style.height = lineThickness + "px";
timerBar.style.top = "0px";
timerBar.style.left = "0px";
// Start Animation
tl = new TimelineMax();
tl
.to(timerBar, 0.5, { width: window.innerWidth + "px", ease: Cubic.ease })
.to(timerBar, adLength, { width: "0px", ease: Linear.easeNone })
break;
case "left":
timerBar.style.width = lineThickness + "px";
timerBar.style.bottom = "0px";
timerBar.style.left = "0px";
// Start Animation
tl = new TimelineMax();
tl
.to(timerBar, 0.5, { height: window.innerHeight + "px", ease: Cubic.ease })
.to(timerBar, adLength, { height: "0px", ease: Linear.easeNone })
break;
case "right":
timerBar.style.width = lineThickness + "px";
timerBar.style.bottom = "0px";
timerBar.style.right = "0px";
// Start Animation
tl = new TimelineMax();
tl
.to(timerBar, 0.5, { height: window.innerHeight + "px", ease: Cubic.ease })
.to(timerBar, adLength, { height: "0px", ease: Linear.easeNone })
break;
}
}
function HugeTittiesAnimation(adLength) {
let hugeTittiesContainer = document.getElementById("hugeTittiesContainer");
switch (timerPosition) {
case "none":
hugeTittiesContainer.style.display = "none";
break;
case "topLeft":
hugeTittiesContainer.style.top = "0px";
hugeTittiesContainer.style.left = "0px";
break;
case "topRight":
hugeTittiesContainer.style.top = "0px";
hugeTittiesContainer.style.right = "0px";
break;
case "bottomLeft":
hugeTittiesContainer.style.bottom = "0px";
hugeTittiesContainer.style.left = "0px";
break;
case "bottomRight":
hugeTittiesContainer.style.bottom = "0px";
hugeTittiesContainer.style.right = "0px";
break;
}
// Calculate starting time
let startingTime = adLength % singleAdLength;
if (startingTime == 0)
startingTime = singleAdLength;
// Estimate how many ads there should be
let adsTotal = Math.ceil(adLength / singleAdLength);
let adsRemaining = 1;
// Start the countdown timer
let adsRemainingContainer = document.getElementById("adsRemainingContainer");
let timerContainer = document.getElementById("timerContainer");
var timerThingy = setInterval(function () {
startingTime--;
if (startingTime == 0 && adsRemaining < adsTotal) {
adsRemaining++;
startingTime = singleAdLength;
}
if (startingTime == 0 && adsRemaining == adsTotal) {
clearInterval(timerThingy);
SetVisibility(false);
return;
}
adsRemainingContainer.innerText = adsRemaining + " of " + adsTotal;
timerContainer.innerText = startingTime.toString().toHHMMSS();
// Show the widget
SetVisibility(true);
}, 1000)
}
function MidRollAnimation(countdownLength) {
let midRollContainer = document.getElementById("midRollContainer");
let midRollCountdownContainer = document.getElementById("midRollCountdownContainer");
let width = midRollContainer.getBoundingClientRect().width;
// Set the starting position of the countdown box
midRollContainer.style.right = -width + "px";
midRollCountdownContainer.innerHTML = countdownLength;
// Slide the countdown box on screen
ShowMidRollCountdown(true);
// Start the countdown timer
let startingTime = countdownLength;
var timerThingy = setInterval(function () {
startingTime--;
midRollCountdownContainer.innerText = startingTime;
if (startingTime == 0) {
clearInterval(timerThingy);
ShowMidRollCountdown(false);
return;
}
}, 1000)
}
function SetVisibility(isVisible) {
let hugeTittiesContainer = document.getElementById("hugeTittiesContainer");
var tl = new TimelineMax();
tl
.to(hugeTittiesContainer, 0.5, { opacity: isVisible, ease: Linear.easeNone });
}
function ShowMidRollCountdown(isVisible) {
let midRollContainer = document.getElementById("midRollContainer");
let width = midRollContainer.getBoundingClientRect().width;
var tl = new TimelineMax();
if (isVisible)
{
tl
.to(midRollContainer, 0.5, { right: "-10px", ease: Power1.easeInOut })
}
else
{
tl
.to(midRollContainer, 0.5, { right: -width + "px", ease: Power1.easeInOut })
}
}
//////////////////////
// HELPER FUNCTIONS //
//////////////////////
String.prototype.toHHMMSS = function () {
var sec_num = parseInt(this, 10); // don't forget the second param
var hours = Math.floor(sec_num / 3600);
var minutes = Math.floor((sec_num - (hours * 3600)) / 60);
var seconds = sec_num - (hours * 3600) - (minutes * 60);
if (hours < 10) { hours = "0" + hours; }
//if (minutes < 10) {minutes = "0"+minutes;}
if (seconds < 10) { seconds = "0" + seconds; }
//return hours+':'+minutes+':'+seconds;
return minutes + ':' + seconds;
}
function IsNullOrWhitespace(str) {
return /^\s*$/.test(str);
}
///////////////////////////////////
// STREAMER.BOT WEBSOCKET STATUS //
///////////////////////////////////
function SetConnectionStatus(connected) {
let statusContainer = document.getElementById("statusContainer");
if (connected) {
statusContainer.style.background = "#2FB774";
statusContainer.innerText = "Connected!";
var tl = new TimelineMax();
tl
.to(statusContainer, 2, { opacity: 0, ease: Linear.easeNone })
//.call(removeElement, [div]);
}
else {
statusContainer.style.background = "#D12025";
statusContainer.innerText = "Connecting...";
statusContainer.style.opacity = 1;
}
}
connectws();
-20
View File
@@ -1,20 +0,0 @@
{
"settings": [
{
"id": "address",
"label": "Address",
"description": "Streamer.bot Websocket Server Address",
"type": "text",
"defaultValue": "127.0.0.1",
"group": "Advanced"
},
{
"id": "port",
"label": "Port",
"description": "Streamer.bot Websocket Server Port",
"type": "text",
"defaultValue": "8080",
"group": "Advanced"
}
]
}