import { preparePlayableAction } from "../../../channels/[slug]/playableActions"; import { OpsInputError, jobResponse, ops, optBool, optString, reqSlug, reqStringArray, reqVideoId, type OpsBody } from "../_lib"; export const dynamic = "force-dynamic"; // POST { slug, ids?: string[], trackers?: string[], root?, dryRun? } // -> { ok: true, jobId } // // Playable copies and their torrents (release 21 D3): each of the channel's // saved containers remuxed losslessly (`-c copy`) into mp4 (+faststart) or // webm, and one single-file torrent per copy, under the playable root — // `playable/` beside the saved-video store, unless `root` names another — as // one `prepare-playable` job (controller/preparePlayable.ts). `ids` narrows // it to those videos; `trackers` is the announce list written into each // .torrent (none by default; it is outside the info dict, so the infohash // does not depend on it). A video already prepared from the same source // (by sha256) is skipped. `dryRun` logs each decision and writes nothing. function optIds(body: OpsBody): string[] | undefined { if (body.ids === undefined) return undefined; return reqStringArray(body, "ids").map((id) => reqVideoId({ id }, "id")); } function optTrackers(body: OpsBody): string[] | undefined { if (body.trackers === undefined) return undefined; if (!Array.isArray(body.trackers)) throw new OpsInputError('"trackers" must be an array of announce URLs'); if (body.trackers.length === 0) return []; return reqStringArray(body, "trackers"); } export async function POST(request: Request) { return ops(request, ["slug", "ids", "trackers", "root", "dryRun"], async (body) => jobResponse( await preparePlayableAction(reqSlug(body, "slug"), { ids: optIds(body), trackers: optTrackers(body), root: optString(body, "root"), dryRun: optBool(body, "dryRun"), }), ), ); }