40 lines
2.5 KiB
TypeScript

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<string>();
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});}
}