import { getPaths } from "yt-dlp-transcript-common/lib/paths"; import { readChannelSnapshot } from "yt-dlp-transcript-common/controller/channels"; import { AUDIO_FORMAT_VALUES } from "yt-dlp-transcript-common/lib/channelConfig"; import { transcribeBucketAction } from "../../../channels/[slug]/whisperActions"; import { jobResponse, OpsInputError, oneOf, ops, optBool, optString, optSubset, reqSlug, } from "../_lib"; export const dynamic = "force-dynamic"; // POST { slug, ids?, queueKey?, audioFormat?, strictAudioFormat? } -> { ok: true, jobId } // // The channel page's "Transcribe N downloaded" button, over HTTP: the // `downloadedNoTranscript` bucket from the snapshot, handed to the same action // on the transcription queue. retry-bucket is not this — it is the DOWNLOAD // retry, and its prefilter counts a video with audio on disk as complete, so // it would skip every video in this bucket. // // `ids` NARROWS THE BUCKET, it never widens it (optSubset). With `ids` the job // carries no bucket key and is not replayable, as an ad-hoc selection is not. export async function POST(request: Request) { return ops( request, ["slug", "ids", "queueKey", "audioFormat", "strictAudioFormat"], async (body) => { const slug = reqSlug(body, "slug"); 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.downloadedNoTranscript ?? []; const subset = optSubset( body, "ids", bucketIds, `the "downloadedNoTranscript" bucket of ${slug}`, ); const audioFormat = body.audioFormat === undefined ? undefined : oneOf(body, "audioFormat", AUDIO_FORMAT_VALUES); return jobResponse( await transcribeBucketAction( slug, subset ?? bucketIds, optString(body, "queueKey"), audioFormat, optBool(body, "strictAudioFormat"), subset ? undefined : "downloadedNoTranscript", ), ); }, ); }