import type { Metadata } from "next"; import Link from "next/link"; import { DEFAULT_JOBS_LIMIT } from "yt-dlp-transcript-common/jobs/listJobs"; import { getRegistry } from "yt-dlp-transcript-common/jobs/registry"; import { getWorkerPool } from "yt-dlp-transcript-common/jobs/workerPool"; import { getPaths } from "yt-dlp-transcript-common/lib/paths"; import { buildActiveJobsPayload, listJobRows, } from "./active/buildActiveJobs"; import { ClearLogsMenu } from "./components/ClearLogsMenu"; import { DrainAllButton } from "./components/DrainAllButton"; import { JobsTable } from "./components/JobsTable"; import { RetryAllFailedButton } from "./components/RetryAllFailedButton"; import { PauseLaneButton } from "../components/lanes/pauseControl"; export const dynamic = "force-dynamic"; export const metadata: Metadata = { title: "Jobs" }; // ONE LIST, TWO SOURCES. The HEAD is the live payload — what the registry and // the scheduler say is running, queued, stuck or freshly finished — and the // client polls it. The TAIL is a page of the `.jobs` directory listing, which // rides the global pulse like every other server-rendered page. `Load more` // grows the TAIL only: the head is never paged, because it is always small. // Clamp so a hand-edited ?limit= can't ask the server to hydrate an unbounded // page. "Load more" grows the limit a page at a time. const MAX_JOBS_LIMIT = 2000; function parseLimit(raw: string | string[] | undefined): number { const n = Number(Array.isArray(raw) ? raw[0] : raw); if (!Number.isFinite(n) || n <= 0) return DEFAULT_JOBS_LIMIT; return Math.min(Math.floor(n), MAX_JOBS_LIMIT); } export default async function JobsPage({ searchParams, }: { searchParams: Promise<{ limit?: string | string[] }>; }) { const limit = parseLimit((await searchParams).limit); const [{ rows, hasMore, total }, initial] = await Promise.all([ listJobRows(getPaths(), { limit }), buildActiveJobsPayload(), ]); // Base the Retry-all affordance on the registry (what retryAllFailedAction // actually acts on), not just the loaded page. const hasRetryableFailed = getRegistry() .list() .some((j) => j.status === "failed" && Boolean(j.spec)); const paused = getWorkerPool().isPaused(); return (

Jobs

{hasRetryableFailed && } {/* No onChange, so the button falls back to router.refresh() — this page is force-dynamic, so that re-reads the live pool. */}
{hasMore && (
Showing {rows.length} of {total} Load more
)}
); }