Live-Punkt laeuft jetzt automatisch ueber TikTok

- Neues Modul lib/tiktok-live.js liest den Live-Status direkt von der
  oeffentlichen TikTok-Live-Seite (SIGI_STATE -> liveRoom.status,
  2 = live, alles andere = offline; bestaetigt durch yt-dlp)
- Cron-Trigger im Worker prueft jede Minute und schreibt das Ergebnis
  nach app_settings; schlaegt ein Abruf fehl, bleibt der letzte gute
  Stand stehen statt faelschlich auf offline zu springen
- /live-status liefert jetzt Quelle, Titel und Zeitpunkt der Pruefung
- Punkt ist rot wenn offline, gruen+pulsierend wenn live, grau solange
  der Status laedt; Startseite aktualisiert alle 30 s ohne Neuladen
- Handschaltung bleibt als Notfall-Vorrang mit 3-Stunden-Ablauf, plus
  Knopf "Zurueck zur Automatik" im Postfach

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-07-31 03:03:31 +02:00
co-authored by Claude Opus 5
parent 597988a9a2
commit 7b4e1352ca
7 changed files with 361 additions and 36 deletions
+36 -15
View File
@@ -53,7 +53,7 @@
<p class="lead worlds">Drei Welten, eine Familie: Team Dogi.</p>
<div class="card" style="max-width:420px;margin:1.4rem auto 0;padding:1rem 1.4rem;">
<span class="badge rank-1"><span class="live-dot" id="live-dot"></span><span id="live-text">Aktuell offline</span></span>
<span class="badge rank-1"><span class="live-dot is-unknown" id="live-dot"></span><span id="live-text">Status wird geladen …</span></span>
<p class="small" style="margin:.5rem 0 0;">Dogfather geht <strong>jeden Tag ab 20 Uhr</strong> live — oft schon tagsüber, zu wechselnden Zeiten. <a href="streamplan.html">Streamplan ansehen →</a></p>
</div>
@@ -268,23 +268,44 @@ document.addEventListener("DOMContentLoaded", () => {
);
});
// Live-Status: grüner Punkt, wenn Dogfather gerade live ist
// Live-Status: grün = läuft gerade, rot = offline.
// Der Wert kommt automatisch von TikTok (Cron im Worker prüft jede Minute).
// Die Seite fragt alle 30 Sekunden nach, damit der Punkt ohne Neuladen umspringt.
const liveDot = document.getElementById("live-dot");
const liveText = document.getElementById("live-text");
if (API_BASE_URL && liveDot && liveText) {
fetch(`${API_BASE_URL}/live-status`)
.then((res) => res.json())
.then((data) => {
if (data && data.live) {
liveDot.classList.add("is-live");
liveText.textContent = "Live jetzt!";
} else {
liveText.textContent = "Aktuell offline";
}
})
.catch(() => {
liveText.textContent = "Aktuell offline";
});
liveDot.classList.add("is-unknown");
liveText.textContent = "Status wird geladen …";
const applyLiveStatus = (data) => {
const isLive = !!(data && data.live);
liveDot.classList.remove("is-unknown");
liveDot.classList.toggle("is-live", isLive);
if (isLive) {
liveText.textContent = data.title ? `Jetzt live: ${data.title}` : "Jetzt live!";
} else {
liveText.textContent = "Gerade offline";
}
};
const refreshLiveStatus = () =>
fetch(`${API_BASE_URL}/live-status`, { cache: "no-store" })
.then((res) => res.json())
.then(applyLiveStatus)
.catch(() => {
liveDot.classList.remove("is-live");
liveDot.classList.add("is-unknown");
liveText.textContent = "Status gerade nicht abrufbar";
});
refreshLiveStatus();
setInterval(refreshLiveStatus, 30000);
// Beim Zurückwechseln zum Tab sofort aktualisieren statt bis zu 30 s warten.
document.addEventListener("visibilitychange", () => {
if (!document.hidden) refreshLiveStatus();
});
}
// Stats: Team-Größe aus den Datenobjekten berechnen