7.3 KiB
7.3 KiB
视频转码问题修复说明
问题描述
视频类素材上传后,后台转码任务未触发。素材 ID: 0895f84d-8837-465e-be50-4db57b5eab9e
根本原因分析
通过诊断脚本发现以下问题:
-
类型不匹配问题
lib/video-transcode-queue.ts的process方法中,taskFn的类型定义缺少retryCount参数- 导致 TypeScript 类型检查可能失败,或运行时参数传递错误
-
错误被静默吞掉
app/api/materials/upload/route.ts中,转码任务入队失败时,错误被 catch 捕获但只打印日志- 没有更新数据库状态,导致无法追踪问题
-
动态导入可能失败
- 使用
await import('@/lib/video-transcode-task')可能因模块路径、循环依赖等问题失败 - 错误被捕获后,用户无法感知
- 使用
-
缺少文件验证
- 入队前未验证视频文件是否存在
- 如果文件不存在,转码会失败,但错误信息不够明确
-
队列处理逻辑不完善
- 队列处理时缺少详细的日志和错误处理
- 如果
process方法本身出错,可能导致队列卡死
-
恢复逻辑只在 POST 请求时触发
- 恢复逻辑只在转码 API 的 POST 方法中触发
- GET 请求(前端轮询)时不会触发恢复,导致 pending 状态的视频无法自动恢复
修复内容
1. 修复类型不匹配 (lib/video-transcode-queue.ts)
问题:process 方法的 taskFn 类型定义与 add 方法不一致
修复:
// 修复前
private async process(
taskFn: (materialId: string, videoSourceUrl: string, needsCoverImage: boolean) => Promise<void>
): Promise<void>
// 修复后
private async process(
taskFn: (materialId: string, videoSourceUrl: string, needsCoverImage: boolean, retryCount?: number) => Promise<void>
): Promise<void>
2. 改进错误处理和日志 (app/api/materials/upload/route.ts)
问题:入队失败时错误被静默吞掉
修复:
- 改用静态导入,避免动态导入失败
- 入队失败时,记录详细错误信息到数据库
- 保持
pending状态,允许手动重试 - 添加详细的日志输出
// 修复后
import { enqueueVideoTranscodeTask } from '@/lib/video-transcode-task';
if (videoSourceUrl) {
const needsCoverImage = !previewUrl || previewUrl.trim() === '';
try {
await enqueueVideoTranscodeTask(material.id, videoSourceUrl, needsCoverImage);
console.log('[Upload] ✅ Video transcode task enqueued successfully:', material.id);
} catch (e: any) {
// 记录详细错误信息到数据库
const errorMsg = e?.message || String(e) || '未知错误';
const errorStack = e?.stack ? `\n堆栈: ${e.stack.substring(0, 1000)}` : '';
const fullErrorMsg = `入队失败: ${errorMsg}${errorStack}`;
console.error('[Upload] ❌ Failed to enqueue video transcode task:', {
materialId: material.id,
videoSourceUrl,
error: errorMsg,
stack: e?.stack,
});
// 更新数据库状态,记录错误信息
await prisma.material.update({
where: { id: material.id },
data: {
videoProcessingStatus: 'pending',
transcodeLastError: fullErrorMsg.substring(0, 1000),
} as any,
});
}
}
3. 添加文件验证 (lib/video-transcode-task.ts)
问题:入队前未验证文件是否存在
修复:
- 添加
validateVideoFile函数,验证视频文件是否存在 - 入队前先验证文件,如果不存在则抛出错误
async function validateVideoFile(videoSourceUrl: string): Promise<boolean> {
// 验证逻辑...
}
export async function enqueueVideoTranscodeTask(...) {
// 验证视频文件是否存在
const fileExists = await validateVideoFile(videoSourceUrl)
if (!fileExists) {
throw new Error(`视频文件不存在: ${videoSourceUrl}`)
}
// 入队...
}
4. 改进队列处理逻辑 (lib/video-transcode-queue.ts)
问题:队列处理时缺少详细的日志和错误处理
修复:
- 添加详细的日志输出
- 改进错误处理,确保队列不会卡死
- 添加任务参数验证
- 改进
add方法,检查重复任务
async add(...) {
// 检查是否已在队列中等待
const alreadyInQueue = this.queue.some(t => t.materialId === materialId)
if (alreadyInQueue) {
console.log(`[TranscodeQueue] Material ${materialId} is already in queue, skipping`)
return
}
console.log(`[TranscodeQueue] Adding task to queue: materialId=${materialId}...`)
// ...
}
private async process(...) {
try {
// 验证任务参数
if (!task.materialId || !task.videoSourceUrl) {
console.error(`[TranscodeQueue] Invalid task parameters:`, task)
task.reject(new Error('Invalid task parameters'))
return
}
// 详细的日志输出
console.log(`[TranscodeQueue] 🚀 Starting task for material ${task.materialId}...`)
// 改进的错误处理
// ...
} catch (error) {
console.error('[TranscodeQueue] Error in process() method:', error)
// 确保队列不会卡死
setTimeout(() => {
this.process(taskFn).catch(err => {
console.error('[TranscodeQueue] Error in retry process():', err)
})
}, 1000)
}
}
5. 改进恢复逻辑 (app/api/materials/transcode/route.ts)
问题:恢复逻辑只在 POST 请求时触发
修复:
- 在 GET 请求时也触发恢复逻辑
- 确保前端轮询时能自动恢复 pending 状态的视频
// GET - 查询转码状态
export async function GET(request: NextRequest) {
// 懒加载恢复:第一次调用时自动恢复中断的任务(包括 GET 请求)
await ensureRecoveryCompleted();
// ...
}
新增工具
1. 诊断脚本 (scripts/diagnose-video-transcode.ts)
用于诊断视频转码问题,检查:
- 素材信息
- 视频文件是否存在
- 转码状态分析
- 转码产物检查
- 诊断总结和建议
使用方法:
tsx scripts/diagnose-video-transcode.ts <materialId>
2. 手动触发转码脚本 (scripts/manual-trigger-transcode.ts)
用于手动触发视频转码,修复未触发转码的素材。
使用方法:
tsx scripts/manual-trigger-transcode.ts <materialId>
验证修复
-
检查修复后的代码
- 所有类型定义已统一
- 错误处理已完善
- 日志输出已详细
-
测试新上传的视频
- 上传一个视频素材
- 检查是否自动触发转码
- 查看日志确认入队成功
-
修复已有问题素材
- 使用诊断脚本检查问题素材
- 使用手动触发脚本修复
后续建议
-
监控和告警
- 添加转码任务入队失败的监控
- 设置告警,及时发现未触发的转码任务
-
定期检查
- 定期检查 pending 状态的视频
- 自动触发未转码的视频
-
改进上传逻辑
- 确保所有视频类型都正确设置
videoSourceUrl为本地路径 - 对于旧版本的 S3 URL,转码时自动处理
- 确保所有视频类型都正确设置
相关文件
app/api/materials/upload/route.ts- 上传接口lib/video-transcode-task.ts- 转码任务封装lib/video-transcode-queue.ts- 转码队列app/api/materials/transcode/route.ts- 转码 APIscripts/diagnose-video-transcode.ts- 诊断脚本scripts/manual-trigger-transcode.ts- 手动触发脚本