diff --git a/apps/api-daemon/src/youtubePolling.ts b/apps/api-daemon/src/youtubePolling.ts index 4c0434f..087ec7d 100644 --- a/apps/api-daemon/src/youtubePolling.ts +++ b/apps/api-daemon/src/youtubePolling.ts @@ -155,12 +155,31 @@ export async function checkChannel(channel: { export async function pollOnce(): Promise { const channels = await prisma.channel.findMany({ where: { isActive: true } }); + const openSessions = await prisma.streamSession.findMany({ + where: { endedAt: null, channelId: { in: channels.map((c) => c.id) } }, + select: { channelId: true }, + }); + const recordingChannelIds = new Set(openSessions.map((s) => s.channelId)); + for (const channel of channels) { + // Already capturing this channel — we already know it's live, so skip + // the bot-check-prone /live lookup entirely. With several channels + // sharing one cookie session + one proxy IP, re-checking channels that + // don't need it multiplies request volume through that single shared + // identity for no reason — a likely contributor to multiple channels + // failing detection at once once a few are live simultaneously. + if (recordingChannelIds.has(channel.id)) continue; + try { await checkChannel(channel); } catch (err) { console.error(`[youtube-polling] error polling channel ${channel.name}:`, err); } + + // Stagger checks instead of firing them back-to-back — a tight burst of + // near-simultaneous requests through the same cookie/IP looks more + // automated than the same requests spread out. + await new Promise((resolve) => setTimeout(resolve, 2000)); } }