Files
screenclipper/apps/frontend/app/settings/page.tsx
T
ayrisdevandClaude Sonnet 5 43ccbd9ec7 feat: interaktif panel kontrolleri (Faz 3)
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>
2026-09-02 13:38:47 +03:00

161 lines
6.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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&apos;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 &quot;son poll hatası&quot; 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&apos;ları başlatılırken
sabitleniyor bunları değiştirmek için Coolify&apos;deki `MAX_CONCURRENT_CAPTURES` /
`VIDEO_RENDER_CONCURRENCY` env var&apos;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>
</>
);
}