fix(channels): address R14 code-review findings

- list_channels no longer computes the Normal/Shorts/Live aggregation the
  frontend stopped reading when R14 dropped the N/S/L table column; the dead
  ManagedChannel.count_* fields are gone too.
- Immediate "get full history" backfill now coordinates with the paced
  scheduler via a small in-flight set: capped at 2 concurrent + per-channel
  dedup, and run_deep_backfill skips a channel an immediate backfill owns —
  killing the cursor race and bounding the quota-drain burst, without losing
  the speed win.
- Add test_channel_backfill.py: the channelDetail N/S/L counts + the guard's
  no-op / cap / scheduler-skip behaviour.
- Inline the syncedChannels useMemo (its dep is a fresh array each render, so
  it never cached); fix the stale "~2-min ticks" comment (it's 10 minutes).
This commit is contained in:
2026-08-15 06:38:19 +02:00
parent a342e15e05
commit 2cc77757d9
7 changed files with 179 additions and 42 deletions
+3 -2
View File
@@ -81,8 +81,9 @@ export default function ChannelPage({
const [bannerAttempt, setBannerAttempt] = useState(0);
// A LIVE query: while THIS user's full-history backfill is running, poll so the stored count (and
// the N/S/L breakdown) climb on their own — the scheduler adds videos in ~2-min ticks — then stop
// polling once the back-catalogue is in. Same queryKey as before, so mutation invalidations still hit.
// the N/S/L breakdown) climb on their own — the immediate backfill fills it in seconds, and the
// scheduler tops up any tail on its ~10-minute ticks — then stop polling once the back-catalogue is
// in. Same queryKey as before, so mutation invalidations still hit.
const detail = useLiveQuery(qk.channel(channelId), () => api.channelDetail(channelId), {
intervalMs: (d) => (d?.deep_requested && !d.backfill_done ? 4000 : false),
});
+3 -4
View File
@@ -249,10 +249,9 @@ export default function Channels() {
(c) => !q || `${c.title ?? ""} ${c.handle ?? ""}`.toLowerCase().includes(q),
);
// Sync-status pre-filter (top chips → `filters.sync`), applied before the table/cards see the rows.
const syncedChannels = useMemo(
() => applySyncFilter(visibleChannels, filters.sync),
[visibleChannels, filters.sync],
);
// (Inline, not memoised: `visibleChannels` is a fresh array each render, so a useMemo keyed on it
// could never hit its cache — and applySyncFilter is a cheap single pass anyway.)
const syncedChannels = applySyncFilter(visibleChannels, filters.sync);
const s = statusQuery.data;
const onView = (c: ManagedChannel) =>
@@ -19,9 +19,6 @@ const ch = (over: Partial<ManagedChannel>): ManagedChannel => ({
stored_videos: 0,
last_video_at: null,
total_duration_seconds: 0,
count_normal: 0,
count_short: 0,
count_live: 0,
priority: 0,
hidden: false,
deep_requested: false,
-3
View File
@@ -73,9 +73,6 @@ export interface ManagedChannel {
stored_videos: number;
last_video_at: string | null;
total_duration_seconds: number;
count_normal: number;
count_short: number;
count_live: number;
priority: number;
hidden: boolean;
deep_requested: boolean;