import { NextResponse } from "next/server"; import { auth } from "@/app/(auth)/auth"; import { executeDirectVideoGeneration } from "@/lib/ai/tools/generate-video"; import { probeRemoteUrl } from "@/lib/net/url-access"; import { validateVideoReferences } from "@/lib/video-canvas/reference-input"; import type { VideoGenerationMode, VideoRatio, VideoResolution } from "@/types/ai"; export const maxDuration = 300; interface GenerateVideoRequest { prompt?: string; mode?: VideoGenerationMode; images?: string[]; audioUrls?: string[]; generateAudio?: boolean; 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 referenceCheck = await validateVideoReferences(body.images, url => probeRemoteUrl(url, { timeoutMs: 8000 })); if (referenceCheck.error) { return NextResponse.json({ success: false, error: referenceCheck.error }, { status: referenceCheck.status }); } const reachableImages = referenceCheck.images; const mode = body.mode || inferMode(reachableImages); const result = await executeDirectVideoGeneration({ prompt, mode, audioUrls: Array.isArray(body.audioUrls) ? body.audioUrls.filter(url => typeof url === "string" && /^https?:\/\//i.test(url)).slice(0, 3) : undefined, generateAudio: body.generateAudio, 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, temporaryVideoUrl: result.videoUrl, storageStatus: result.metadata?.permanentVideoUrl === result.videoUrl ? "archived" : "temporary" }, modelDisplayName: result.modelDisplayName, modelId: result.modelId, modelRef: result.modelRef, mode, images: reachableImages, droppedImages: [], }, }); } catch (error) { console.error("[Canvas Generate Video] Error:", error); return NextResponse.json( { success: false, error: error instanceof Error ? error.message : "生成视频失败", }, { status: 500 } ); } }