ai-chatbot/lib/video-canvas/video-edit-contract.ts

32 lines
1.6 KiB
TypeScript

import { z } from "zod";
export const videoEditRequestSchema = z.object({
operation: z.enum(["repaint", "erase", "subtitle", "intro"]),
sourceUrl: z.string().url().refine((value) => /^https?:\/\//i.test(value)),
prompt: z.string().trim().max(800).optional(),
sourceNodeId: z.string().min(1).max(180),
durationSeconds: z.number().positive().max(600),
width: z.number().int().min(64).max(8192),
height: z.number().int().min(64).max(8192),
regions: z.array(z.object({
x: z.number().min(0).max(1),
y: z.number().min(0).max(1),
width: z.number().positive().max(1),
height: z.number().positive().max(1),
})).max(12).optional(),
}).superRefine((value, context) => {
if (value.durationSeconds > (value.operation === "repaint" ? 10 : 5)) {
context.addIssue({ code: "custom", message: value.operation === "repaint" ? "片段重拍最多处理 10 秒,请先剪辑目标片段" : "区域编辑和片头最多处理 5 秒,请先剪辑目标片段", path: ["durationSeconds"] });
}
if (value.width * value.height > 8_388_608) {
context.addIssue({ code: "custom", message: "视频分辨率过高,请先缩小素材", path: ["width"] });
}
if (value.operation === "erase" && !value.regions?.length) {
context.addIssue({ code: "custom", message: "请先框选要消除的主体", path: ["regions"] });
}
if (value.operation === "intro" && value.durationSeconds > 3) {
context.addIssue({ code: "custom", message: "创意片头需要不超过 3 秒的尾段视频,请先剪辑尾段", path: ["durationSeconds"] });
}
});
export type VideoEditRequest = z.infer<typeof videoEditRequestSchema>;