131 lines
4.0 KiB
TypeScript
131 lines
4.0 KiB
TypeScript
/**
|
||
* 手动触发视频转码
|
||
* 用于修复未触发转码的素材
|
||
*/
|
||
|
||
import { PrismaClient } from '@prisma/client'
|
||
import { enqueueVideoTranscodeTask } from '../lib/video-transcode-task'
|
||
|
||
const prisma = new PrismaClient()
|
||
|
||
async function triggerTranscode(materialId: string) {
|
||
console.log(`\n==== 手动触发转码: ${materialId} ====\n`)
|
||
|
||
try {
|
||
// 查询素材信息
|
||
const material = await prisma.material.findUnique({
|
||
where: { id: materialId },
|
||
}) as any
|
||
|
||
if (!material) {
|
||
console.error('❌ 素材不存在')
|
||
return
|
||
}
|
||
|
||
console.log('📋 素材信息:')
|
||
console.log(` 素材ID: ${material.id}`)
|
||
console.log(` 素材类型: ${material.materialType}`)
|
||
console.log(` 转码状态: ${material.videoProcessingStatus || 'null'}`)
|
||
console.log(` videoSourceUrl: ${material.videoSourceUrl || 'null'}`)
|
||
|
||
// 检查是否为视频类型
|
||
const videoTypes = ['主图视频', '安装视频', '种草视频', '视频', 'VIDEO']
|
||
const materialType = String(material?.materialType || '')
|
||
if (!videoTypes.includes(materialType)) {
|
||
console.log('\n⚠️ 此素材不是视频类型,不需要转码')
|
||
return
|
||
}
|
||
|
||
// 检查 videoSourceUrl
|
||
if (!material?.videoSourceUrl) {
|
||
console.log('\n❌ 错误:videoSourceUrl 为空,无法转码')
|
||
return
|
||
}
|
||
|
||
// 判断是否需要生成封面图
|
||
const needsCoverImage = !material?.videoCoverUrl || String(material.videoCoverUrl || '').trim() === ''
|
||
const hasPreviewUrl = material?.previewUrl && String(material.previewUrl || '').trim() !== ''
|
||
|
||
console.log(`\n🔄 转码配置:`)
|
||
console.log(` 需要生成封面: ${needsCoverImage}`)
|
||
console.log(` 已有预览图: ${hasPreviewUrl}`)
|
||
|
||
// 如果状态是 processing,先重置为 pending
|
||
const currentStatus = String(material?.videoProcessingStatus || '')
|
||
if (currentStatus === 'processing') {
|
||
console.log('\n⚠️ 当前状态是 processing,重置为 pending...')
|
||
await prisma.material.update({
|
||
where: { id: materialId },
|
||
data: {
|
||
videoProcessingStatus: 'pending',
|
||
transcodeLastError: null,
|
||
} as any,
|
||
})
|
||
console.log('✅ 状态已重置')
|
||
}
|
||
|
||
// 触发转码
|
||
console.log(`\n🚀 触发转码任务...`)
|
||
try {
|
||
await enqueueVideoTranscodeTask(
|
||
material.id,
|
||
String(material.videoSourceUrl || ''),
|
||
needsCoverImage
|
||
)
|
||
console.log('✅ 转码任务已入队')
|
||
|
||
// 更新状态为 pending(如果之前是 null)
|
||
if (!currentStatus || currentStatus === 'null') {
|
||
await prisma.material.update({
|
||
where: { id: materialId },
|
||
data: {
|
||
videoProcessingStatus: 'pending',
|
||
} as any,
|
||
})
|
||
console.log('✅ 状态已更新为 pending')
|
||
}
|
||
} catch (error: any) {
|
||
console.error('❌ 入队失败:', error)
|
||
console.error('错误详情:', error?.message || String(error))
|
||
console.error('堆栈:', error?.stack)
|
||
|
||
// 记录错误到数据库
|
||
await prisma.material.update({
|
||
where: { id: materialId },
|
||
data: {
|
||
videoProcessingStatus: 'pending',
|
||
transcodeLastError: `手动触发失败: ${error?.message || String(error)}`,
|
||
} as any,
|
||
})
|
||
throw error
|
||
}
|
||
|
||
console.log(`\n✅ 转码任务已成功触发`)
|
||
console.log(` 素材ID: ${materialId}`)
|
||
console.log(` 视频源: ${material.videoSourceUrl}`)
|
||
console.log(` 状态: pending -> 将转为 processing`)
|
||
|
||
} catch (error) {
|
||
console.error('\n❌ 操作失败:', error)
|
||
throw error
|
||
} finally {
|
||
await prisma.$disconnect()
|
||
}
|
||
}
|
||
|
||
// 主函数
|
||
async function main() {
|
||
const materialId = process.argv[2]
|
||
|
||
if (!materialId) {
|
||
console.error('用法: tsx scripts/manual-trigger-transcode.ts <materialId>')
|
||
console.error('示例: tsx scripts/manual-trigger-transcode.ts 0895f84d-8837-465e-be50-4db57b5eab9e')
|
||
process.exit(1)
|
||
}
|
||
|
||
await triggerTranscode(materialId)
|
||
}
|
||
|
||
main().catch(console.error)
|
||
|