Yayın durdurma, kanal duraklat/devam (tekli+toplu), şimdi kontrol et, manuel URL ile zorla kayıt başlatma, canlı player+log önizleme, oturum süre sayacı, başarısız/takılı render için tekrar dene ve iptal, poll aralığı + ham segment TTL'yi panelden canlı değiştirme, cookie bayatlık uyarısı, Telegram bildirim aç/kapa, ve kullanım/maliyet özeti (/stats). api-daemon'ın yeni state-değiştiren endpoint'leri (stop/force-start/ render/cancel) INTERNAL_API_TOKEN ile korunuyor — bu daemon'ın portu Coolify'de public'e açık olduğu için korumasız bırakmak güvenlik açığı olurdu. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
24 lines
751 B
TypeScript
24 lines
751 B
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
|
|
function formatElapsed(ms: number): string {
|
|
const totalSec = Math.max(0, Math.floor(ms / 1000));
|
|
const h = Math.floor(totalSec / 3600);
|
|
const m = Math.floor((totalSec % 3600) / 60);
|
|
const s = totalSec % 60;
|
|
const pad = (n: number) => String(n).padStart(2, "0");
|
|
return h > 0 ? `${h}:${pad(m)}:${pad(s)}` : `${m}:${pad(s)}`;
|
|
}
|
|
|
|
export function SessionTimer({ startedAt }: { startedAt: string }) {
|
|
const [now, setNow] = useState(() => Date.now());
|
|
|
|
useEffect(() => {
|
|
const timer = setInterval(() => setNow(Date.now()), 1000);
|
|
return () => clearInterval(timer);
|
|
}, []);
|
|
|
|
return <span className="mono">{formatElapsed(now - new Date(startedAt).getTime())}</span>;
|
|
}
|