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>
161 lines
6.5 KiB
TypeScript
161 lines
6.5 KiB
TypeScript
import { prisma } from "@streamclipper/db";
|
||
import { updateYtdlpCookies, updateSystemSettings, updateNotificationPrefs } from "../actions";
|
||
|
||
export const dynamic = "force-dynamic";
|
||
|
||
const STALE_COOKIE_HOURS = 4;
|
||
|
||
export default async function SettingsPage() {
|
||
const [setting, pollSetting, ttlSetting, notifyStart, notifyRender] = await Promise.all([
|
||
prisma.appSetting.findUnique({ where: { key: "ytdlp_cookies" } }),
|
||
prisma.appSetting.findUnique({ where: { key: "poll_interval_ms" } }),
|
||
prisma.appSetting.findUnique({ where: { key: "raw_segment_ttl_hours" } }),
|
||
prisma.appSetting.findUnique({ where: { key: "notify_stream_start" } }),
|
||
prisma.appSetting.findUnique({ where: { key: "notify_render_done" } }),
|
||
]);
|
||
|
||
const cookieAgeHours = setting ? (Date.now() - setting.updatedAt.getTime()) / 3_600_000 : null;
|
||
const cookieStale = cookieAgeHours !== null && cookieAgeHours > STALE_COOKIE_HOURS;
|
||
const pollIntervalSec = pollSetting ? Math.round(Number(pollSetting.value) / 1000) : 60;
|
||
const ttlHours = ttlSetting ? Number(ttlSetting.value) : 24;
|
||
const notifyStreamStart = notifyStart?.value !== "false";
|
||
const notifyRenderDone = notifyRender?.value !== "false";
|
||
|
||
return (
|
||
<>
|
||
<h1>Ayarlar</h1>
|
||
|
||
<div className="card">
|
||
<div className="card-head">
|
||
<span>YouTube Cookie'leri</span>
|
||
<span className="mono">
|
||
{setting ? `son güncelleme: ${setting.updatedAt.toLocaleString("tr-TR")}` : "hiç ayarlanmadı"}
|
||
</span>
|
||
</div>
|
||
{cookieStale && (
|
||
<p style={{ marginBottom: "0.5rem" }}>
|
||
<span className="badge warn">
|
||
⚠️ {Math.round(cookieAgeHours!)} saattir güncellenmedi — büyük/popüler kanallarda bot-check
|
||
hatası görülebilir, taze bir cookies.txt ile güncelle
|
||
</span>
|
||
</p>
|
||
)}
|
||
<p className="empty" style={{ marginBottom: "0.5rem" }}>
|
||
YouTube canlı yayın kontrolü ve kayıt için kullanılıyor. Google, oturum çerezlerinin bir
|
||
kısmını birkaç saatte bir yeniliyor — burada eskidiğini fark edersen (kanal detay
|
||
sayfasında "son poll hatası" olarak görürsün) taze bir cookies.txt ile
|
||
güncelle. Ana hesabın yerine ayrı, önemsiz bir Google hesabı kullanman önerilir.
|
||
</p>
|
||
<form action={updateYtdlpCookies}>
|
||
<textarea
|
||
name="cookies"
|
||
required
|
||
placeholder="Netscape formatlı cookies.txt içeriğini buraya yapıştır"
|
||
rows={10}
|
||
style={{
|
||
width: "100%",
|
||
background: "var(--bg)",
|
||
border: "1px solid var(--border)",
|
||
borderRadius: "6px",
|
||
padding: "0.6rem 0.7rem",
|
||
color: "var(--text)",
|
||
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
||
fontSize: "0.75rem",
|
||
resize: "vertical",
|
||
}}
|
||
/>
|
||
<button
|
||
type="submit"
|
||
style={{
|
||
marginTop: "0.6rem",
|
||
background: "var(--accent)",
|
||
border: "none",
|
||
borderRadius: "6px",
|
||
padding: "0.5rem 1rem",
|
||
color: "#fff",
|
||
fontWeight: 600,
|
||
fontSize: "0.9rem",
|
||
cursor: "pointer",
|
||
}}
|
||
>
|
||
Kaydet
|
||
</button>
|
||
</form>
|
||
</div>
|
||
|
||
<div className="card">
|
||
<div className="card-head">
|
||
<span>Sistem Parametreleri</span>
|
||
</div>
|
||
<p className="empty" style={{ marginBottom: "0.5rem" }}>
|
||
Bir sonraki döngüden itibaren geçerli olur, redeploy gerekmez.
|
||
</p>
|
||
<form action={updateSystemSettings} style={{ display: "flex", flexDirection: "column", gap: "0.6rem" }}>
|
||
<label className="mono" style={{ fontSize: "0.8rem" }}>
|
||
Poll aralığı (saniye)
|
||
<input name="pollIntervalSec" type="number" min={5} defaultValue={pollIntervalSec} style={{ display: "block", marginTop: "0.25rem" }} />
|
||
</label>
|
||
<label className="mono" style={{ fontSize: "0.8rem" }}>
|
||
Ham segment TTL (saat)
|
||
<input name="ttlHours" type="number" min={1} defaultValue={ttlHours} style={{ display: "block", marginTop: "0.25rem" }} />
|
||
</label>
|
||
<p className="empty" style={{ fontSize: "0.75rem", margin: 0 }}>
|
||
Eşzamanlı capture limiti ve render eşzamanlılığı BullMQ worker'ları başlatılırken
|
||
sabitleniyor — bunları değiştirmek için Coolify'deki `MAX_CONCURRENT_CAPTURES` /
|
||
`VIDEO_RENDER_CONCURRENCY` env var'larını güncelleyip redeploy etmek gerekiyor.
|
||
</p>
|
||
<button
|
||
type="submit"
|
||
style={{
|
||
alignSelf: "flex-start",
|
||
background: "var(--accent)",
|
||
border: "none",
|
||
borderRadius: "6px",
|
||
padding: "0.5rem 1rem",
|
||
color: "#fff",
|
||
fontWeight: 600,
|
||
fontSize: "0.9rem",
|
||
cursor: "pointer",
|
||
}}
|
||
>
|
||
Kaydet
|
||
</button>
|
||
</form>
|
||
</div>
|
||
|
||
<div className="card">
|
||
<div className="card-head">
|
||
<span>Bildirimler</span>
|
||
</div>
|
||
<form action={updateNotificationPrefs} style={{ display: "flex", flexDirection: "column", gap: "0.5rem" }}>
|
||
<label className="mono" style={{ fontSize: "0.85rem", display: "flex", alignItems: "center", gap: "0.4rem" }}>
|
||
<input type="checkbox" name="notifyStreamStart" defaultChecked={notifyStreamStart} />
|
||
Yayın başladığında Telegram bildirimi
|
||
</label>
|
||
<label className="mono" style={{ fontSize: "0.85rem", display: "flex", alignItems: "center", gap: "0.4rem" }}>
|
||
<input type="checkbox" name="notifyRenderDone" defaultChecked={notifyRenderDone} />
|
||
9:16 render tamamlandığında Telegram bildirimi
|
||
</label>
|
||
<button
|
||
type="submit"
|
||
style={{
|
||
alignSelf: "flex-start",
|
||
marginTop: "0.3rem",
|
||
background: "var(--accent)",
|
||
border: "none",
|
||
borderRadius: "6px",
|
||
padding: "0.5rem 1rem",
|
||
color: "#fff",
|
||
fontWeight: 600,
|
||
fontSize: "0.9rem",
|
||
cursor: "pointer",
|
||
}}
|
||
>
|
||
Kaydet
|
||
</button>
|
||
</form>
|
||
</div>
|
||
</>
|
||
);
|
||
}
|