feat: add batch processing with injected db and api
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
97faeedca3
commit
13838cbeb7
@@ -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 };
|
||||
}
|
||||
Reference in New Issue
Block a user