feat: add batch processing with injected db and api

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
IgorVolochay
2026-10-25 10:51:07 +03:00
co-authored by Claude Sonnet 5.5
parent 97faeedca3
commit 13838cbeb7
2 changed files with 317 additions and 0 deletions
+73
View File
@@ -0,0 +1,73 @@
import { computeScore, shouldSave } from './score.js';
export const RECHECK_MS = 24 * 60 * 60 * 1000;
const watchUrl = (videoId) => `https://www.youtube.com/watch?v=${videoId}`;
function isFresh(checkedAt, nowMs) {
return checkedAt !== undefined && nowMs - Date.parse(checkedAt) < RECHECK_MS;
}
export async function processBatch(items, { settings, db, api, now = new Date() }) {
const nowMs = now.getTime();
const nowIso = now.toISOString();
const byId = new Map(items.map((item) => [item.videoId, item]));
const seen = await db.getSeen([...byId.keys()]);
const due = [...byId.values()].filter(
(item) => item.vidiqScore !== null || !isFresh(seen.get(item.videoId)?.checkedAt, nowMs),
);
if (due.length === 0) return { checked: 0, saved: 0 };
const { videos, missingIds } = await api.fetchVideos(due.map((item) => item.videoId));
const channelIds = [...new Set(videos.map((video) => video.channelId))];
const cachedChannels = await db.getChannels(channelIds);
const staleIds = channelIds.filter((id) => !isFresh(cachedChannels.get(id)?.fetchedAt, nowMs));
const fetchedChannels = staleIds.length > 0 ? await api.fetchChannels(staleIds) : [];
const channelRecords = fetchedChannels.map((channel) => ({ ...channel, fetchedAt: nowIso }));
const channels = new Map(cachedChannels);
for (const record of channelRecords) channels.set(record.channelId, record);
const existing = await db.getOutliers(videos.map((video) => video.videoId));
const outliers = [];
for (const video of videos) {
const item = byId.get(video.videoId);
const previous = existing.get(video.videoId);
const channel = channels.get(video.channelId);
const subscribers = channel?.subscribers ?? null;
const score = computeScore(video.views, subscribers);
const vidiqScore = item.vidiqScore ?? previous?.vidiqScore ?? null;
if (!previous && !shouldSave({ views: video.views, score, vidiqScore }, settings)) continue;
outliers.push({
videoId: video.videoId,
title: video.title,
url: watchUrl(video.videoId),
thumbnailUrl: video.thumbnailUrl,
channelId: video.channelId,
channelTitle: video.channelTitle,
channelUrl: channel?.url ?? `https://www.youtube.com/channel/${video.channelId}`,
subscribers,
views: video.views,
publishedAt: video.publishedAt,
durationSec: video.durationSec,
isShort: item.viaShortsUrl || previous?.isShort || false,
score,
vidiqScore,
foundOn: previous?.foundOn ?? item.page,
firstSeenAt: previous?.firstSeenAt ?? nowIso,
updatedAt: nowIso,
});
}
// Results first, `seen` last: if anything above throws, the ids are retried later.
await db.putChannels(channelRecords);
await db.putOutliers(outliers);
await db.putSeen([
...videos.map((video) => ({ videoId: video.videoId, checkedAt: nowIso, status: 'ok' })),
...missingIds.map((videoId) => ({ videoId, checkedAt: nowIso, status: 'unavailable' })),
]);
return { checked: due.length, saved: outliers.length };
}