85 lines · 3.2 KB
#!/usr/bin/env bun
/**
 * Uploads a prepared video directory to R2 under `videos/<id>/<version>/`:
 *   bun scripts/video/upload.ts --directory .artifacts/videos/intro/v1 [--bucket gitfor] [--local]
 * Media goes first and master.m3u8 last, so a half-finished upload is never playable. An existing version is
 * refused: published versions are immutable (the edge caches them forever), so re-encode as a new version.
 */
import { readFile, readdir, stat } from "node:fs/promises";
import { join, resolve } from "node:path";
import { parseArgs } from "node:util";
import { contentTypeForAsset, parseVideoPath, videoR2Key } from "../../src/lib/video";

const CONCURRENCY = 6;
const ATTEMPTS = 3;

async function wrangler(args: string[]): Promise<number> {
  const proc = Bun.spawn(["bunx", "wrangler", ...args], { stdout: "ignore", stderr: "pipe" });
  const code = await proc.exited;
  if (code !== 0 && !args.includes("--pipe")) console.error(await new Response(proc.stderr).text());
  return code;
}

function uploadOrder(asset: string): number {
  if (asset === "master.m3u8") return 3;
  if (asset.endsWith(".m3u8")) return 2;
  if (asset.endsWith(".json")) return 1;
  return 0;
}

const { values } = parseArgs({
  options: { directory: { type: "string" }, bucket: { type: "string", default: "gitfor" }, local: { type: "boolean" } },
});
if (!values.directory) {
  console.error("upload --directory <prepared dir> [--bucket gitfor] [--local]");
  process.exit(1);
}
const dir = resolve(values.directory);
const { id, version } = JSON.parse(await readFile(join(dir, "prepared.json"), "utf8")) as {
  id: string;
  version: string;