139 lines
4.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { NextRequest, NextResponse } from 'next/server';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
import { prisma } from '@/lib/prisma';
import {
createZipFromFiles,
generatePreviewImages,
cleanupTempFiles,
cleanupOnError,
saveAssets,
} from '@/lib/task-material-processor';
// POST - 完成视频截图上传
export async function POST(request: NextRequest) {
let materialId: string | null = null;
const tempFiles: string[] = []; // 需要清理的临时文件路径
let zipFilePath: string | null = null;
const uploadedPreviewUrls: string[] = []; // 新生成的S3预览图URLs
const uploadIds: string[] = []; // 需要清理的uploadId列表
try {
const session = await getServerSession(authOptions);
if (!session) {
return NextResponse.json({ error: '请先登录' }, { status: 401 });
}
const body = await request.json();
const { materialId: matId, assets } = body;
materialId = matId;
if (!materialId || !assets) {
return NextResponse.json(
{ error: '缺少必填字段' },
{ status: 400 }
);
}
if (!assets.main || assets.main.length === 0) {
return NextResponse.json(
{ error: '至少需要上传一张截图' },
{ status: 400 }
);
}
console.log('[Video Screenshot Complete] Starting, materialId:', materialId);
console.log('[Video Screenshot Complete] Screenshot count:', assets.main.length);
// 验证素材是否存在且属于当前用户
const material: any = await prisma.material.findUnique({
where: { id: materialId },
});
if (!material) {
return NextResponse.json(
{ success: false, error: '素材不存在' },
{ status: 404 }
);
}
if (material.userId !== (session.user as any).id) {
return NextResponse.json(
{ success: false, error: '无权限操作此素材' },
{ status: 403 }
);
}
// 步骤1: 从chunk临时目录读取截图文件
console.log(`[Video Screenshot] Processing ${assets.main.length} screenshots...`);
const { getUploadedFile } = await import('@/lib/upload-sessions');
const screenshotFiles: Array<{ path: string; name: string; size: number }> = [];
for (const asset of assets.main) {
try {
const filePath = await getUploadedFile(asset.uploadId);
if (filePath) {
screenshotFiles.push({
path: filePath,
name: asset.fileName,
size: asset.fileSize,
});
tempFiles.push(filePath);
uploadIds.push(asset.uploadId);
} else {
throw new Error(`找不到uploadId对应的文件: ${asset.uploadId}`);
}
} catch (error) {
console.error(`[Video Screenshot] Failed to get file for uploadId ${asset.uploadId}:`, error);
throw new Error(`获取截图文件失败: ${asset.fileName}`);
}
}
// 步骤2: 打包截图成zip(保存到本地文件系统)
console.log(`[Video Screenshot] Creating zip from ${screenshotFiles.length} screenshots...`);
const zipInfo = await createZipFromFiles(
screenshotFiles,
materialId!,
material.productModel || 'screenshots',
'任务-视频截图'
);
console.log(`[Video Screenshot] Zip created: ${zipInfo.fileName} (${zipInfo.fileSize} bytes)`);
zipFilePath = zipInfo.filePath;
// 步骤3: 生成预览图并上传到S3
const previewUrls = await generatePreviewImages(
assets.main.map((a: any) => ({ uploadId: a.uploadId, fileName: a.fileName })),
'任务-视频截图'
);
uploadedPreviewUrls.push(...previewUrls);
if (uploadedPreviewUrls.length === 0) {
throw new Error('生成预览图失败,至少需要一张预览图');
}
// 步骤4: 保存资产记录到数据库
await saveAssets(materialId!, uploadedPreviewUrls, zipInfo);
// 步骤5: 清理临时文件
await cleanupTempFiles(tempFiles, uploadIds);
console.log('[Video Screenshot] Complete success!');
return NextResponse.json({
success: true,
materialId,
});
} catch (error: any) {
console.error('[Video Screenshot Complete Error]', error);
// 清理失败时的资源
await cleanupOnError(uploadedPreviewUrls, zipFilePath, tempFiles, materialId);
return NextResponse.json(
{ error: error.message || '完成上传失败' },
{ status: 500 }
);
}
}