initial commit
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
const HEALTH_CONFIG = {
|
||||
warningThreshold: 60,
|
||||
dangerThreshold: 30,
|
||||
|
||||
staleWarningMs: 2500,
|
||||
staleDangerMs: 5000,
|
||||
|
||||
bitrateLow: 1000,
|
||||
bitrateGood: 1001,
|
||||
|
||||
autoSceneSwitch: false,
|
||||
sceneLow: "Low",
|
||||
sceneOffline: "Offline",
|
||||
|
||||
enableAlerts: false
|
||||
};
|
||||
|
||||
// ================= STATE =================
|
||||
let lastHealthState = {
|
||||
live1: "UNKNOWN",
|
||||
live2: "UNKNOWN"
|
||||
};
|
||||
|
||||
// ================= MAIN ENTRY =================
|
||||
function evaluateStreams(status) {
|
||||
|
||||
if (!status?.streams) return;
|
||||
|
||||
const results = {};
|
||||
|
||||
for (const key of Object.keys(status.streams)) {
|
||||
|
||||
const stream = status.streams[key];
|
||||
|
||||
const health = calculateHealth(stream);
|
||||
|
||||
results[key] = health;
|
||||
|
||||
handleStateChange(key, health, status);
|
||||
updateHealthUI(key, health);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// ================= HEALTH CALC =================
|
||||
function calculateHealth(stream) {
|
||||
|
||||
const now = Date.now();
|
||||
|
||||
const bitrate = stream.bitrate || 0;
|
||||
const staleness = now - (stream.time || now);
|
||||
|
||||
let score = 100;
|
||||
|
||||
// 🔴 STALENESS (most important)
|
||||
if (staleness > HEALTH_CONFIG.staleDangerMs) score -= 60;
|
||||
else if (staleness > HEALTH_CONFIG.staleWarningMs) score -= 30;
|
||||
|
||||
// 📉 BITRATE
|
||||
if (bitrate === 0) score -= 50;
|
||||
else if (bitrate < HEALTH_CONFIG.bitrateLow) score -= 30;
|
||||
else if (bitrate < HEALTH_CONFIG.bitrateGood) score -= 10;
|
||||
|
||||
// clamp
|
||||
score = Math.max(0, Math.min(100, score));
|
||||
|
||||
return {
|
||||
score,
|
||||
bitrate,
|
||||
staleness,
|
||||
status: getStatus(score, bitrate, staleness)
|
||||
};
|
||||
}
|
||||
|
||||
// ================= STATUS =================
|
||||
function getStatus(score, bitrate, staleness) {
|
||||
|
||||
if (bitrate === 0 && staleness > HEALTH_CONFIG.staleDangerMs) {
|
||||
return "DOWN";
|
||||
}
|
||||
|
||||
if (score < HEALTH_CONFIG.dangerThreshold) {
|
||||
return "DANGER";
|
||||
}
|
||||
|
||||
if (score < HEALTH_CONFIG.warningThreshold) {
|
||||
return "WEAK";
|
||||
}
|
||||
|
||||
return "GOOD";
|
||||
}
|
||||
|
||||
// ================= STATE HANDLER =================
|
||||
function handleStateChange(streamName, health, status) {
|
||||
|
||||
const prev = lastHealthState[streamName];
|
||||
const current = health.status;
|
||||
|
||||
if (prev === current) return;
|
||||
|
||||
lastHealthState[streamName] = current;
|
||||
|
||||
console.log(`[HEALTH] ${streamName}: ${prev} → ${current}`);
|
||||
|
||||
// 🚨 ALERTS
|
||||
if (HEALTH_CONFIG.enableAlerts) {
|
||||
sendAlert(streamName, health, status);
|
||||
}
|
||||
|
||||
// 🎛 AUTO SCENE SWITCH
|
||||
if (HEALTH_CONFIG.autoSceneSwitch) {
|
||||
handleAutoScene(streamName, health, status);
|
||||
}
|
||||
}
|
||||
|
||||
// ================= AUTO SCENE =================
|
||||
function handleAutoScene(streamName, health, status) {
|
||||
|
||||
if (streamName !== status.activeStream) return;
|
||||
|
||||
if (health.status === "DOWN") {
|
||||
setSceneSafe(HEALTH_CONFIG.sceneOffline);
|
||||
}
|
||||
|
||||
if (health.status === "DANGER") {
|
||||
setSceneSafe(HEALTH_CONFIG.sceneLow);
|
||||
}
|
||||
}
|
||||
|
||||
// ================= SAFE SCENE SWITCH =================
|
||||
function setSceneSafe(scene) {
|
||||
|
||||
fetch("https://api.anjuma-crew.de/scene", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ scene })
|
||||
}).catch(console.error);
|
||||
}
|
||||
|
||||
// ================= ALERT SYSTEM =================
|
||||
function sendAlert(streamName, health, status) {
|
||||
|
||||
const msg = {
|
||||
stream: streamName,
|
||||
status: health.status,
|
||||
score: health.score,
|
||||
bitrate: health.bitrate,
|
||||
staleness: health.staleness
|
||||
};
|
||||
|
||||
console.warn("[ALERT]", msg);
|
||||
|
||||
// 👉 optional webhook
|
||||
/*
|
||||
fetch("YOUR_WEBHOOK_URL", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(msg)
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
// ================= UI =================
|
||||
function updateHealthUI(streamName, health) {
|
||||
|
||||
const el = document.getElementById(`health-${streamName}`);
|
||||
if (el) {
|
||||
el.innerText = health.score;
|
||||
|
||||
el.style.color =
|
||||
health.status === "GOOD" ? "green" :
|
||||
health.status === "WEAK" ? "orange" :
|
||||
"red";
|
||||
}
|
||||
|
||||
const label = document.getElementById(`status-${streamName}`);
|
||||
if (label) {
|
||||
label.innerText = health.status;
|
||||
}
|
||||
|
||||
const staleEl = document.getElementById(`stale-${streamName}`);
|
||||
if (staleEl) {
|
||||
staleEl.innerText = Math.round(health.staleness / 1000) + "s";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user