Your Name be5b9aef12
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
chore: sync latest workspace changes
2026-04-05 02:08:26 +08:00

113 lines
3.4 KiB
TypeScript

import { NextResponse } from "next/server";
import { auth } from "@/app/(auth)/auth";
import { executeDirectVideoGeneration } from "@/lib/ai/tools/generate-video";
import { filterReachableRemoteUrls } from "@/lib/net/url-access";
import type { VideoGenerationMode, VideoRatio, VideoResolution } from "@/types/ai";
export const maxDuration = 300;
interface GenerateVideoRequest {
prompt?: string;
mode?: VideoGenerationMode;
images?: string[];
resolution?: VideoResolution;
ratio?: VideoRatio;
duration?: number;
watermark?: boolean;
cameraFixed?: boolean;
seed?: number;
returnLastFrame?: boolean;
preferredModelRef?: string;
}
function inferMode(images: string[]): VideoGenerationMode {
if (images.length >= 3) return "reference-images";
if (images.length >= 2) return "first-last-frame";
if (images.length === 1) return "first-frame";
return "text-to-video";
}
export async function POST(request: Request) {
try {
const session = await auth();
if (!session?.user?.id) {
return NextResponse.json(
{ success: false, error: "未登录" },
{ status: 401 }
);
}
const body = (await request.json().catch(() => ({}))) as GenerateVideoRequest;
const prompt = String(body.prompt || "").trim();
if (!prompt) {
return NextResponse.json(
{ success: false, error: "请输入视频提示词" },
{ status: 400 }
);
}
const inputImages = Array.isArray(body.images)
? body.images.map((item) => String(item || "").trim()).filter(Boolean).slice(0, 4)
: [];
const { kept: reachableImages, dropped: unreachableImages } =
await filterReachableRemoteUrls(inputImages, { timeoutMs: 3000 });
if (inputImages.length > 0 && reachableImages.length === 0) {
return NextResponse.json(
{ success: false, error: "参考图片链接不可访问,请重新上传后再试" },
{ status: 400 }
);
}
const mode = body.mode || inferMode(reachableImages);
const result = await executeDirectVideoGeneration({
prompt,
mode,
images: reachableImages,
resolution: body.resolution,
ratio: body.ratio,
duration:
typeof body.duration === "number" && Number.isFinite(body.duration)
? body.duration
: undefined,
watermark: body.watermark,
cameraFixed: body.cameraFixed,
seed:
typeof body.seed === "number" && Number.isFinite(body.seed) ? body.seed : undefined,
returnLastFrame: body.returnLastFrame,
preferredModelRef:
typeof body.preferredModelRef === "string" ? body.preferredModelRef : undefined,
});
if (!result.success || !result.videoUrl) {
return NextResponse.json(
{ success: false, error: "视频生成失败,请稍后重试" },
{ status: 502 }
);
}
return NextResponse.json({
success: true,
data: {
url: result.videoUrl,
metadata: result.metadata || null,
modelDisplayName: result.modelDisplayName,
modelId: result.modelId,
modelRef: result.modelRef,
mode,
images: reachableImages,
droppedImages: unreachableImages,
},
});
} catch (error) {
console.error("[Canvas Generate Video] Error:", error);
return NextResponse.json(
{
success: false,
error: error instanceof Error ? error.message : "生成视频失败",
},
{ status: 500 }
);
}
}