/* ===================================================================== lib/tiktok-live.js — Automatische Live-Erkennung für TikTok TikTok bietet KEINE offizielle API, die einem sagt "ist Nutzer X gerade live?". Was aber öffentlich funktioniert: die Seite https://www.tiktok.com/@/live liefert im HTML ein JSON-Objekt (", open); if (end === -1) return null; try { return JSON.parse(html.slice(open + 1, end)); } catch { return null; } } /** * Fragt bei TikTok nach, ob der Kanal gerade live ist. * @returns {Promise<{ok:boolean, live?:boolean, title?:string, roomId?:string, * followers?:number, rawStatus?:number, reason?:string}>} */ export async function checkTikTokLive(username) { const handle = String(username || "").replace(/^@/, "").trim(); if (!handle) return { ok: false, reason: "kein-benutzername" }; let res; try { res = await fetch(`https://www.tiktok.com/@${encodeURIComponent(handle)}/live`, { headers: BROWSER_HEADERS, redirect: "follow", cf: { cacheTtl: 0, cacheEverything: false }, }); } catch (err) { return { ok: false, reason: `netzwerkfehler: ${String(err).slice(0, 120)}` }; } if (!res.ok) return { ok: false, reason: `http-${res.status}` }; const html = await res.text(); if (html.length < 5000) return { ok: false, reason: "seite-zu-klein-vermutlich-blockiert" }; // Bevorzugt SIGI_STATE, ersatzweise das neuere Rehydration-Objekt. const sigi = extractScriptJson(html, "SIGI_STATE"); let info = sigi?.LiveRoom?.liveRoomUserInfo; if (!info) { const universal = extractScriptJson(html, "__UNIVERSAL_DATA_FOR_REHYDRATION__"); info = universal?.__DEFAULT_SCOPE__?.["webapp.live-detail"]?.liveRoomUserInfo; } if (!info) return { ok: false, reason: "struktur-nicht-gefunden" }; const status = info.liveRoom?.status; if (typeof status !== "number") return { ok: false, reason: "kein-status-feld" }; return { ok: true, live: status === LIVE_STATUS_RUNNING, rawStatus: status, title: info.liveRoom?.title || null, roomId: info.user?.roomId ? String(info.user.roomId) : null, followers: info.stats?.followerCount ?? null, }; }