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:
+9
-1
@@ -777,14 +777,22 @@ footer.site-footer {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background: #6b7280; /* grau = aktuell offline */
|
||||
background: #e05260; /* rot = offline (Standard) */
|
||||
box-shadow: 0 0 8px rgba(224, 82, 96, .55);
|
||||
margin-right: .45rem;
|
||||
vertical-align: middle;
|
||||
transition: background .3s ease, box-shadow .3s ease;
|
||||
}
|
||||
.live-dot.is-live {
|
||||
background: #22c55e; /* grün = live */
|
||||
animation: live-pulse 1.6s infinite;
|
||||
}
|
||||
/* Solange der Status noch geladen wird: neutral grau, damit nie kurz
|
||||
fälschlich "offline" aufblitzt. */
|
||||
.live-dot.is-unknown {
|
||||
background: #6b7280;
|
||||
box-shadow: none;
|
||||
}
|
||||
@keyframes live-pulse {
|
||||
0% { box-shadow: 0 0 0 0 rgba(34, 197, 94, .55); }
|
||||
70% { box-shadow: 0 0 0 10px rgba(34, 197, 94, 0); }
|
||||
|
||||
+57
-7
@@ -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);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user