import {auth} from '@/app/(auth)/auth'; import {NextResponse} from 'next/server'; import {z} from 'zod'; import {mkdtemp,readFile,rm} from 'node:fs/promises'; import {tmpdir} from 'node:os'; import {join} from 'node:path'; import {randomUUID} from 'node:crypto'; import {uploadFile} from '@/lib/storage/service'; import {downloadCanvasMedia} from '@/lib/video-canvas/download-media'; import {processVideoFile} from '@/lib/video-canvas/media-processing'; export const runtime='nodejs'; export const maxDuration=300; const running=new Set(); const schema=z.object({operation:z.enum(['crop','audio-video']),url:z.string().url(),rect:z.object({x:z.number(),y:z.number(),width:z.number(),height:z.number()}).optional()}); export async function POST(request:Request) { const session=await auth(); if(!session?.user?.id) return NextResponse.json({success:false,error:'未登录'},{status:401}); const parsed=schema.safeParse(await request.json().catch(()=>null)); if(!parsed.success) return NextResponse.json({success:false,error:'视频处理参数无效'},{status:400}); if(running.has(session.user.id)||running.size>=2) return NextResponse.json({success:false,error:'视频处理中,请稍后再提交'},{status:429}); running.add(session.user.id); let dir=''; try { dir=await mkdtemp(join(tmpdir(),'canvas-video-')); const input=join(dir,'input'); await downloadCanvasMedia(parsed.data.url,input); const save=async(name:string)=>{const buffer=await readFile(join(dir,name));return (await uploadFile(`canvas-video-${randomUUID()}-${name}`,new Uint8Array(buffer).buffer)).url;}; if(parsed.data.operation==='crop') { const metadata=await processVideoFile(input,join(dir,'crop.mp4'),'crop',parsed.data.rect); return NextResponse.json({success:true,data:{url:await save('crop.mp4'),...metadata}}); } const metadata=await processVideoFile(input,join(dir,'audio.m4a'),'extract-audio'); await processVideoFile(input,join(dir,'silent.mp4'),'silent-video'); return NextResponse.json({success:true,data:{audioUrl:await save('audio.m4a'),url:await save('silent.mp4'),...metadata}}); } catch(error) { const message=error instanceof Error ? error.message : '视频处理失败'; return NextResponse.json({success:false,error:message.startsWith('Command failed')?'视频处理失败,请确认文件完整且格式受支持':message},{status:400}); } finally {running.delete(session.user.id);if(dir)await rm(dir,{recursive:true,force:true});} }