40 lines
2.5 KiB
TypeScript
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});}
|
|
}
|