import type { AutoQueueOrder } from "../jobs/autoQueuePolicy"; // A plan entry that can be ordered by recency: one channel's worth of pending // work, with the newest and oldest upload dates in it. // // Both dates, not one. Reading `newestPending` for both directions would make // "oldest first" mean "the channel whose freshest video is least fresh", which // is a different and much less useful question than "the channel holding the // oldest un-done work". export type RecencyPlanEntry = { // YYYYMMDD, or "" for a channel with no dated pending work. newestPending: string; oldestPending: string; }; // Sort a sweep's CHANNEL list by recency, in place, falling back to the plan's // own weight order. // // This is the cross-channel half of "newest first". The within-channel half is // batchRecency; both exist so that the same setting means the same thing at // both levels, and this function is where the three rules that make that true // live: // // 1. `order: "listed"` sorts by weight ALONE — byte-for-byte today's plan. // Not "sorts by a recency key that happens to tie": the historical order // is reproduced by not consulting recency at all. // 2. An unknown date is "", which sorts LAST under "newest" and FIRST under // "oldest" — exactly what makeRecencyComparator does to an undatable // video. So a corpus where nothing is dated (a cold stats cache, an index // mid-rebuild) falls entirely through to the weight order and reproduces // today, rather than shuffling into an arbitrary one. // 3. Weight is the TIEBREAK, never discarded. Two channels whose freshest // pending video landed the same day are still visited heaviest-first, // which is the ordering the sweep was built on and still the right answer // once recency has nothing left to say. export function orderPlanByRecency( entries: T[], order: AutoQueueOrder, byWeight: (a: T, b: T) => number, ): T[] { if (order === "listed") return entries.sort(byWeight); return entries.sort((a, b) => { const ka = order === "newest" ? a.newestPending : a.oldestPending; const kb = order === "newest" ? b.newestPending : b.oldestPending; if (ka !== kb) { // Keys are YYYYMMDD, so lexicographic order IS chronological order. // "newest" therefore sorts DESCENDING: a smaller (older) key comes later. return order === "newest" ? (ka < kb ? 1 : -1) : ka < kb ? -1 : 1; } return byWeight(a, b); }); }