import { getPaths } from "yt-dlp-transcript-common/lib/paths"; import { readChannelSnapshot } from "yt-dlp-transcript-common/controller/channels"; import { isReplayBucket } from "yt-dlp-transcript-common/jobs/jobSpec"; import { retryBucketAction } from "../../../channels/[slug]/pipelineActions"; import { jobResponse, OpsInputError, ops, optBool, optString, optSubset, reqSlug, reqString, } from "../_lib"; export const dynamic = "force-dynamic"; // POST { slug, bucket, ids?, queueKey?, abortOnError?, handlingOverride?, // forceCookies?, replaceAutoSubs? } -> { ok: true, jobId } // // THE BUCKET IS RESOLVED FROM THE SNAPSHOT HERE, and that is not new logic: the // bucket controls in the UI pass `snapshot.buckets[key]` to the same action, and // the action's own argument is a list of ids. An HTTP caller naming a bucket // rather than pasting ids is the same gesture — and `bucketKey` travelling with // it is what makes the job replayable against the CURRENT bucket, exactly as a // clicked one is. // // `ids` NARROWS THE BUCKET, it never widens it: every id must be in the named // bucket, as a checkbox selection on the bucket's card is. With `ids` the job // carries no `bucketKey` — like the UI's ad-hoc selections it is not // replayable, because a replay re-reads the WHOLE current bucket. export async function POST(request: Request) { return ops( request, [ "slug", "bucket", "ids", "queueKey", "abortOnError", "handlingOverride", "forceCookies", "replaceAutoSubs", ], async (body) => { const slug = reqSlug(body, "slug"); const bucket = reqString(body, "bucket"); const snapshot = await readChannelSnapshot(getPaths(), slug); if (!snapshot) { throw new OpsInputError( `Channel "${slug}" has no report yet — run /api/ops/refresh-report first.`, ); } const bucketIds = (snapshot.buckets as Record)[bucket]; if (!Array.isArray(bucketIds)) { throw new OpsInputError( `"${bucket}" is not a bucket on this channel's report — known buckets: ${Object.keys( snapshot.buckets, ).join(", ")}`, ); } const subset = optSubset( body, "ids", bucketIds as string[], `the "${bucket}" bucket of ${slug}`, ); return jobResponse( await retryBucketAction( slug, subset ?? (bucketIds as string[]), optString(body, "queueKey"), optBool(body, "abortOnError"), optString(body, "handlingOverride"), // Replayable only for the buckets a replay can re-derive; an // ad-hoc bucket name still runs, it just carries no spec — the // same distinction the UI's named vs checkbox controls make. !subset && isReplayBucket(bucket) ? bucket : undefined, optBool(body, "forceCookies"), optBool(body, "replaceAutoSubs"), ), ); }, ); }