fix: poll hatasına zaman damgası ekle + STT'yi panelden kapatılabilir yap

- lastPollErrors artık {message, at} tutuyor — panelde hangi hatanın ne
  zaman oluştuğu görünmüyordu, eski/güncel ayrımı yapılamıyordu.
- "This live event will begin in N minutes" gibi zararsız mesajlar da
  hata rozetine düşüyordu, "is not currently live" ile aynı gruba alındı.
- Yeni stt_enabled ayarı (Ayarlar sayfası): kapatılınca stt_scoring.py
  OpenAI Whisper çağrısı yapmadan adayı PENDING_STT'de bırakıp çıkıyor —
  konuşmasız/ambiyans yayınlarda (bkz. NASA testi) boşa API maliyeti
  önleniyor.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-02 14:52:15 +03:00
co-authored by Claude Sonnet 5
parent 7d504d1938
commit cbb16697ca
7 changed files with 74 additions and 16 deletions
+16 -8
View File
@@ -10,9 +10,18 @@ const execFileAsync = promisify(execFile);
/**
* Live detection failures are kept per channel and surfaced via GET /status
* for debugging.
* for debugging. `at` lets the panel show how stale an error is — a message
* on its own doesn't say whether it's from the last poll or from hours ago
* before the channel started passing again.
*/
export const lastPollErrors = new Map<string, string>();
export interface PollError {
message: string;
at: string;
}
export const lastPollErrors = new Map<string, PollError>();
/** Non-error outcomes yt-dlp still reports as a non-zero exit — not worth surfacing as a scary error badge in the panel. */
const BENIGN_POLL_MESSAGE = /is not currently live|will begin in/;
/**
* A few approaches were tried before landing here:
@@ -57,11 +66,10 @@ async function findLiveVideoId(channelId: string): Promise<string | null> {
return videoId || null;
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
// yt-dlp reports "not live" as a non-zero exit too — that's a normal,
// expected outcome on every check where the channel isn't broadcasting,
// not a failure worth surfacing as an error in the panel.
if (!/is not currently live/.test(message)) {
lastPollErrors.set(channelId, message.slice(0, 500));
// yt-dlp reports "not live" / "starts in N minutes" as a non-zero exit
// too — normal, expected outcomes, not failures worth a red badge.
if (!BENIGN_POLL_MESSAGE.test(message)) {
lastPollErrors.set(channelId, { message: message.slice(0, 500), at: new Date().toISOString() });
} else {
lastPollErrors.delete(channelId);
}
@@ -107,7 +115,7 @@ export async function startCaptureSession(
/** Runs the live-check for a single channel and applies its result — the unit both `pollOnce` and the panel's manual "şimdi kontrol et" action reuse. */
export async function checkChannel(channel: { id: string; name: string; channelId: string }): Promise<{
liveVideoId: string | null;
error: string | null;
error: PollError | null;
}> {
const liveVideoId = await findLiveVideoId(channel.channelId);
await prisma.channel.update({