debug: yt-dlp live-check hatalarını /status'ta görünür kıl

yt-dlp hem "kanal canlı değil" hem de gerçek bir hata (ağ engeli,
bot tespiti, extractor kırılması) durumunda non-zero exit code
veriyor; ikisi ayırt edilemiyordu. Son hata mesajı artık kanal
başına saklanıp GET /status içinde lastPollError alanında dönüyor.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-30 15:54:51 +03:00
co-authored by Claude Sonnet 5
parent bae115f497
commit ad419f38b0
2 changed files with 14 additions and 2 deletions
+2
View File
@@ -1,6 +1,7 @@
import express from "express"; import express from "express";
import { prisma } from "@streamclipper/db"; import { prisma } from "@streamclipper/db";
import { env } from "./env"; import { env } from "./env";
import { lastPollErrors } from "./youtubePolling";
export function startServer() { export function startServer() {
const app = express(); const app = express();
@@ -26,6 +27,7 @@ export function startServer() {
lastCheckedAt: c.lastCheckedAt, lastCheckedAt: c.lastCheckedAt,
recording: c.sessions.length > 0, recording: c.sessions.length > 0,
activeSessionId: c.sessions[0]?.id ?? null, activeSessionId: c.sessions[0]?.id ?? null,
lastPollError: lastPollErrors.get(c.channelId) ?? null,
})), })),
}); });
}); });
+12 -2
View File
@@ -7,6 +7,14 @@ import { sendTelegramMessage } from "./telegram";
const execFileAsync = promisify(execFile); const execFileAsync = promisify(execFile);
/**
* yt-dlp exits non-zero both when a channel isn't live and when the check
* itself fails (network block, bot detection, extractor breakage). Those two
* cases must not be indistinguishable, so the last failure per channel is
* kept here and surfaced via GET /status for debugging.
*/
export const lastPollErrors = new Map<string, string>();
/** /**
* Live detection goes through yt-dlp itself instead of the YouTube Data API. * Live detection goes through yt-dlp itself instead of the YouTube Data API.
* `search.list?eventType=live` costs 100 quota units per call against a * `search.list?eventType=live` costs 100 quota units per call against a
@@ -23,10 +31,12 @@ async function findLiveVideoId(channelId: string): Promise<string | null> {
["--simulate", "--no-warnings", "--print", "%(id)s", liveUrl], ["--simulate", "--no-warnings", "--print", "%(id)s", liveUrl],
{ timeout: 20_000 }, { timeout: 20_000 },
); );
lastPollErrors.delete(channelId);
const videoId = stdout.trim().split("\n")[0]; const videoId = stdout.trim().split("\n")[0];
return videoId || null; return videoId || null;
} catch { } catch (err) {
// yt-dlp exits non-zero when the channel isn't currently live const message = err instanceof Error ? err.message : String(err);
lastPollErrors.set(channelId, message.slice(0, 500));
return null; return null;
} }
} }