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
+57 -7
View File
@@ -162,17 +162,56 @@ async function loadNotes(applicationId) {
.join("") || `<p class="small muted">Noch keine Notizen.</p>`;
}
/* ---------------- Live-Status ---------------- */
function renderLiveStatus(live) {
$("admin-live-dot").classList.toggle("is-live", !!live);
$("admin-live-text").textContent = live ? "Live jetzt!" : "Aktuell offline";
/* ---------------- Live-Status ----------------
Der Status kommt automatisch von TikTok (Cron im Worker, jede Minute).
Die Handschaltung ist nur ein zeitlich begrenzter Vorrang.
------------------------------------------------------------------- */
function formatSince(iso) {
if (!iso) return null;
const diffMin = Math.floor((Date.now() - new Date(iso).getTime()) / 60000);
if (diffMin < 1) return "gerade eben";
if (diffMin === 1) return "vor 1 Minute";
if (diffMin < 60) return `vor ${diffMin} Minuten`;
const h = Math.floor(diffMin / 60);
return h === 1 ? "vor 1 Stunde" : `vor ${h} Stunden`;
}
function renderLiveStatus(data) {
const live = !!(data && data.live);
const dot = $("admin-live-dot");
dot.classList.remove("is-unknown");
dot.classList.toggle("is-live", live);
$("admin-live-text").textContent = live ? "Jetzt live" : "Gerade offline";
const manual = data && data.source === "manuell";
$("go-auto-btn").style.display = manual ? "inline-flex" : "none";
const lines = [];
if (manual) {
lines.push("⚠️ Von Hand gesetzt — die Automatik ist gerade übersteuert.");
if (data.overrideUntil) {
const until = new Date(data.overrideUntil);
lines.push(`Läuft aus um ${until.toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit" })} Uhr.`);
}
if (data.autoLive !== null && data.autoLive !== undefined) {
lines.push(`TikTok meldet gerade: ${data.autoLive ? "live" : "offline"}.`);
}
} else {
if (data && data.title && live) lines.push(`Titel: ${data.title}`);
const since = formatSince(data && data.autoCheckedAt);
if (since) lines.push(`Automatisch geprüft ${since}.`);
if (data && data.autoHealthy === false) {
lines.push("⚠️ Der letzte Abruf bei TikTok hat nicht geklappt — angezeigt wird der letzte bekannte Stand.");
}
}
$("admin-live-detail").innerHTML = lines.join("<br>");
}
async function loadLiveStatus() {
try {
const res = await fetch(`${API_BASE_URL}/live-status`);
const res = await fetch(`${API_BASE_URL}/live-status`, { cache: "no-store" });
const data = await res.json();
if (res.ok && data.ok) renderLiveStatus(data.live);
if (res.ok && data.ok) renderLiveStatus(data);
} catch {
/* keine Blockade */
}
@@ -180,7 +219,12 @@ async function loadLiveStatus() {
async function setLiveStatus(live) {
const res = await apiCall(API_BASE_URL, "/live-status/set", { live });
if (res.ok) renderLiveStatus(res.live);
if (res.ok) renderLiveStatus(res);
}
async function backToAutoLive() {
const res = await apiCall(API_BASE_URL, "/live-status/set", { auto: true });
if (res.ok) renderLiveStatus(res);
}
/* ---------------- Login / Logout ---------------- */
@@ -213,4 +257,10 @@ document.addEventListener("DOMContentLoaded", () => {
$("show-archived").addEventListener("change", loadInbox);
$("go-live-btn").addEventListener("click", () => setLiveStatus(true));
$("go-offline-btn").addEventListener("click", () => setLiveStatus(false));
$("go-auto-btn").addEventListener("click", backToAutoLive);
// Anzeige alle 30 Sekunden auffrischen, damit man die Automatik arbeiten sieht.
setInterval(() => {
if (!document.hidden && getSession()) loadLiveStatus();
}, 30000);
});