import * as z from "zod";
export const VIDEO_PREFIX = "/videos/";
export const VIDEO_R2_PREFIX = "videos/";
export const SCRUB_MANIFEST = "scrub/manifest.json";
const idPattern = /^[a-z0-9][a-z0-9-]{0,63}$/;
const assetPattern =
/^(?:master\.m3u8|poster\.jpg|captions\/[a-z]{2}\.vtt|scrub\/(?:manifest\.json|sheet-[0-9]{3}\.webp)|[1-9][0-9]{0,3}\/(?:index\.m3u8|init\.mp4|[0-9]{5}\.m4s))$/;
export interface VideoPath {
id: string;
version: string;
asset: string;
}
/** `/videos/<id>/<version>/<asset>`; anything outside the prepared layout is refused before touching R2. */
export function parseVideoPath(pathname: string): VideoPath | null {
if (!pathname.startsWith(VIDEO_PREFIX)) return null;
const [id, version, ...rest] = pathname.slice(VIDEO_PREFIX.length).split("/");
const asset = rest.join("/");
if (!id || !version || !idPattern.test(id) || !idPattern.test(version) || !assetPattern.test(asset)) return null;
return { id, version, asset };
}
export function contentTypeForAsset(asset: string): string {
if (asset.endsWith(".m3u8")) return "application/vnd.apple.mpegurl";
if (asset.endsWith(".jpg")) return "image/jpeg";
if (asset.endsWith(".webp")) return "image/webp";
if (asset.endsWith(".vtt")) return "text/vtt; charset=utf-8";
if (asset.endsWith(".json")) return "application/json";
return "video/mp4";
}
export function videoR2Key({ id, version, asset }: VideoPath): string {
return `${VIDEO_R2_PREFIX}${id}/${version}/${asset}`;
}
export function videoUrl(id: string, version: string, asset: string): string {
return `${VIDEO_PREFIX}${id}/${version}/${asset}`;