Add all bp

This commit is contained in:
KenwoodFox
2026-06-16 23:47:37 -04:00
commit ef4716d360
8 changed files with 562 additions and 0 deletions

32
css/grafana.css Normal file
View File

@@ -0,0 +1,32 @@
/*
* Grafana embed wrappers for rx.kitsunehosting.net
*
* Cross-origin iframes cannot be styled from this page — Grafana renders on
* grafana.kitsunehosting.net. Use URL params on the iframe src instead:
* theme=dark|light panel colors
* kiosk=tv hide Grafana chrome (nav, menus)
* refresh=30s auto-refresh interval
*
* Custom Grafana themes require server-side config on the Grafana instance.
*/
.grafana-embed {
margin-bottom: 12px;
background: #000000;
border: 2px inset #808080;
padding: 2px;
overflow: hidden;
}
.grafana-embed iframe {
display: block;
width: 100%;
height: 200px;
border: 0;
background: #000000;
}
/* Match the retro terminal vibe when Grafana is in dark mode */
.grafana-embed--dark {
box-shadow: inset 0 0 12px rgba(0, 255, 0, 0.08);
}

208
css/style.css Normal file
View File

@@ -0,0 +1,208 @@
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 16px;
background: #c0c0c0;
color: #000000;
font-family: "Times New Roman", Times, serif;
font-size: 16px;
}
a {
color: #0000ee;
}
a:visited {
color: #551a8b;
}
a:hover {
color: #ff0000;
}
.banner {
text-align: center;
background: #000080;
color: #ffff00;
padding: 12px;
border: 4px outset #808080;
font-size: 28px;
font-weight: bold;
letter-spacing: 2px;
text-shadow: 2px 2px #000000;
}
.nav {
text-align: center;
margin: 12px 0;
padding: 8px;
background: #d4d0c8;
border: 2px inset #808080;
}
.nav a {
margin: 0 16px;
font-weight: bold;
text-decoration: underline;
}
.nav a.active {
color: #000000;
text-decoration: none;
background: #ffffff;
padding: 2px 6px;
border: 1px inset #808080;
}
hr {
border: none;
height: 2px;
background: #808080;
margin: 16px 0;
}
.panel {
background: #d4d0c8;
border: 2px inset #808080;
padding: 12px;
}
.panel-title {
text-align: center;
font-weight: bold;
font-size: 18px;
margin: 0 0 12px 0;
text-decoration: underline;
}
.live-feed {
text-align: center;
margin: 0 auto;
max-width: 960px;
}
.live-feed img {
display: block;
margin: 0 auto;
width: 100%;
height: auto;
aspect-ratio: 16 / 9;
object-fit: contain;
border: 3px inset #808080;
background: #000000;
}
.live-label {
margin-top: 8px;
font-family: "Courier New", Courier, monospace;
font-size: 14px;
color: #008000;
}
.live-timestamp {
margin: 4px 0 0;
font-family: "Courier New", Courier, monospace;
font-size: 12px;
color: #404040;
}
.blink {
animation: blink 1s step-end infinite;
}
@keyframes blink {
50% {
opacity: 0;
}
}
.content-table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
}
.content-table td {
vertical-align: top;
padding: 0 6px;
}
.graphs-panel {
min-height: 280px;
}
.graph-placeholder {
height: 200px;
background: #000000;
border: 2px inset #808080;
display: flex;
align-items: center;
justify-content: center;
color: #00ff00;
font-family: "Courier New", Courier, monospace;
font-size: 14px;
margin-bottom: 12px;
}
.stats-panel {
min-height: 280px;
}
.stat-block {
text-align: center;
margin-bottom: 16px;
padding: 12px;
background: #ffffff;
border: 2px outset #808080;
}
.stat-value {
font-size: 48px;
font-weight: bold;
font-family: "Courier New", Courier, monospace;
color: #000080;
line-height: 1;
}
.stat-value--ok {
color: #008000;
}
.stat-value--bad {
color: #cc0000;
}
#stat-status {
font-size: 32px;
}
.stat-label {
font-size: 14px;
margin-top: 4px;
text-transform: uppercase;
}
.placeholder-page {
text-align: center;
padding: 48px 16px;
}
.placeholder-page h2 {
font-size: 24px;
}
.under-construction {
font-size: 48px;
margin: 24px 0;
}
.footer {
text-align: center;
margin-top: 24px;
font-size: 12px;
font-family: "Courier New", Courier, monospace;
color: #404040;
}

91
index.html Normal file
View File

@@ -0,0 +1,91 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RX.KITSUNEHOSTING.NET - Live</title>
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/grafana.css">
</head>
<body>
<div class="banner">RX.KITSUNEHOSTING.NET</div>
<nav class="nav">
<a href="index.html" class="active">Home/Live</a>
<a href="tools.html">Tools</a>
<a href="radio.html">Radio</a>
</nav>
<hr>
<section class="panel live-feed">
<h2 class="panel-title">Live</h2>
<img id="live-feed" src="live/live.png" alt="Live feed" width="1920" height="1080">
<p class="live-label"><span class="blink">&#9679;</span> LIVE</p>
<p id="live-timestamp" class="live-timestamp"></p>
</section>
<hr>
<table class="content-table" role="presentation">
<tr>
<td width="60%">
<section class="panel graphs-panel">
<h2 class="panel-title">Graphs</h2>
<div class="grafana-embed">
<iframe
src="https://grafana.kitsunehosting.net/d-solo/ffp84u9sdyccgb/amateur-radio?orgId=2&timezone=browser&refresh=auto&theme=light&panelId=1&__feature.dashboardSceneSolo"
width="450"
height="200"
title="Amateur radio graph 1"
loading="lazy"
></iframe>
</div>
<div class="grafana-embed">
<iframe
src="https://grafana.kitsunehosting.net/d-solo/ffp84u9sdyccgb/amateur-radio?orgId=2&timezone=browser&refresh=auto&theme=light&panelId=1&__feature.dashboardSceneSolo"
width="450"
height="200"
title="Amateur radio graph 2"
loading="lazy"
></iframe>
</div>
<div class="grafana-embed">
<iframe
src="https://grafana.kitsunehosting.net/d-solo/ffp84u9sdyccgb/amateur-radio?orgId=2&timezone=browser&refresh=auto&theme=light&panelId=1&__feature.dashboardSceneSolo"
width="450"
height="200"
title="Amateur radio graph 3"
loading="lazy"
></iframe>
</div>
</section>
</td>
<td width="40%">
<section class="panel stats-panel">
<h2 class="panel-title">Overall Stats</h2>
<div class="stat-block">
<div id="stat-uptime" class="stat-value">---</div>
<div class="stat-label">Uptime</div>
</div>
<div class="stat-block">
<div id="stat-listeners" class="stat-value">---</div>
<div class="stat-label">Live Listeners</div>
</div>
<div class="stat-block">
<div id="stat-status" class="stat-value">---</div>
<div class="stat-label">Status</div>
</div>
</section>
</td>
</tr>
</table>
<div class="footer">
rx.kitsunehosting.net
</div>
<script src="js/live.js"></script>
</body>
</html>

161
js/live.js Normal file
View File

@@ -0,0 +1,161 @@
(function () {
var LIVE_IMAGE = "live/live.png";
var LIVE_DATA = "live/live.json";
var REFRESH_MS = 60 * 1000;
var TICK_MS = 1000;
var img = document.getElementById("live-feed");
var timestampEl = document.getElementById("live-timestamp");
var statUptime = document.getElementById("stat-uptime");
var statListeners = document.getElementById("stat-listeners");
var statStatus = document.getElementById("stat-status");
var lastCapturedAt = null;
if (!img) {
return;
}
function formatTimeAgo(unixSeconds) {
var diff = Math.floor(Date.now() / 1000) - unixSeconds;
if (diff < 0) {
diff = 0;
}
if (diff < 60) {
return diff === 1 ? "1 second ago" : diff + " seconds ago";
}
var minutes = Math.floor(diff / 60);
if (minutes < 60) {
return minutes === 1 ? "1 minute ago" : minutes + " minutes ago";
}
var hours = Math.floor(diff / 3600);
if (hours < 24) {
return hours === 1 ? "1 hour ago" : hours + " hours ago";
}
var days = Math.floor(diff / 86400);
return days === 1 ? "1 day ago" : days + " days ago";
}
function formatDuration(seconds) {
var diff = Math.max(0, Math.floor(seconds));
var days = Math.floor(diff / 86400);
var hours = Math.floor((diff % 86400) / 3600);
var minutes = Math.floor((diff % 3600) / 60);
if (days > 0) {
return days + "d " + hours + "h";
}
if (hours > 0) {
return hours + "h " + minutes + "m";
}
if (minutes > 0) {
return minutes + "m";
}
return diff + "s";
}
function formatUptime(value) {
if (value === null || value === undefined || value === "") {
return "---";
}
if (typeof value === "number") {
return formatDuration(value);
}
return String(value);
}
function formatListeners(value) {
if (value === null || value === undefined || value === "") {
return "---";
}
return String(value);
}
function formatStatus(value) {
if (value === null || value === undefined || value === "") {
return "---";
}
return String(value);
}
function updateTimeAgo() {
if (!timestampEl) {
return;
}
if (!lastCapturedAt) {
timestampEl.textContent = "";
return;
}
timestampEl.textContent = "Captured " + formatTimeAgo(lastCapturedAt);
}
function updateStats(data) {
if (statUptime) {
statUptime.textContent = formatUptime(data.uptime);
}
if (statListeners) {
var listeners = data.listeners;
if (listeners === undefined) {
listeners = data.live_listeners;
}
statListeners.textContent = formatListeners(listeners);
}
if (statStatus) {
statStatus.textContent = formatStatus(data.status);
statStatus.classList.remove("stat-value--ok", "stat-value--bad");
var status = String(data.status || "").toLowerCase();
if (status === "online" || status === "ok" || status === "up") {
statStatus.classList.add("stat-value--ok");
} else if (status === "offline" || status === "down" || status === "error") {
statStatus.classList.add("stat-value--bad");
}
}
}
function clearStats() {
updateStats({});
}
function fetchLiveData() {
fetch(LIVE_DATA + "?t=" + Date.now())
.then(function (response) {
if (!response.ok) {
throw new Error("live data unavailable");
}
return response.json();
})
.then(function (data) {
if (data && data.timestamp) {
lastCapturedAt = data.timestamp;
updateTimeAgo();
} else {
lastCapturedAt = null;
updateTimeAgo();
}
updateStats(data || {});
})
.catch(function () {
lastCapturedAt = null;
updateTimeAgo();
clearStats();
});
}
function refreshLiveImage() {
img.src = LIVE_IMAGE + "?t=" + Date.now();
fetchLiveData();
}
img.addEventListener("error", function () {
img.alt = "Live feed unavailable";
});
refreshLiveImage();
setInterval(refreshLiveImage, REFRESH_MS);
setInterval(updateTimeAgo, TICK_MS);
})();

6
live/live.json Normal file
View File

@@ -0,0 +1,6 @@
{
"timestamp": 1781667629,
"uptime": 86412,
"listeners": 0,
"status": "online"
}

BIN
live/live.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

32
radio.html Normal file
View File

@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RX.KITSUNEHOSTING.NET - Radio</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="banner">RX.KITSUNEHOSTING.NET</div>
<nav class="nav">
<a href="index.html">Home/Live</a>
<a href="tools.html">Tools</a>
<a href="radio.html" class="active">Radio</a>
</nav>
<hr>
<section class="panel placeholder-page">
<div class="under-construction">&#9835;</div>
<h2>Radio</h2>
<p>Coming soon.</p>
</section>
<div class="footer">
rx.kitsunehosting.net
</div>
</body>
</html>

32
tools.html Normal file
View File

@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RX.KITSUNEHOSTING.NET - Tools</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="banner">RX.KITSUNEHOSTING.NET</div>
<nav class="nav">
<a href="index.html">Home/Live</a>
<a href="tools.html" class="active">Tools</a>
<a href="radio.html">Radio</a>
</nav>
<hr>
<section class="panel placeholder-page">
<div class="under-construction">&#9881;</div>
<h2>Tools</h2>
<p>Coming soon.</p>
</section>
<div class="footer">
rx.kitsunehosting.net
</div>
</body>
</html>