gongxiangp2/scripts/diagnose-video-transcode.ts

168 lines
6.5 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 { PrismaClient } from '@prisma/client'
import { existsSync } from 'fs'
import { join } from 'path'
import { access, constants } from 'fs/promises'
const prisma = new PrismaClient()
async function diagnoseMaterial(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 || 'null'}`)
console.log(` 素材类型: ${material?.materialType || 'null'}`)
console.log(` 创建时间: ${material?.createdAt || 'null'}`)
console.log(` 转码状态: ${material?.videoProcessingStatus || 'null'}`)
console.log(` 转码尝试次数: ${material?.transcodeAttempts || 0}`)
console.log(` 最后错误: ${material?.transcodeLastError || '无'}`)
console.log(` videoSourceUrl: ${material?.videoSourceUrl || 'null'}`)
console.log(` zipFileName: ${material?.zipFileName || 'null'}`)
console.log(` zipFilePath: ${material?.zipFilePath || '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 为空')
console.log(' 可能原因:上传时未正确设置 videoSourceUrl')
return
}
console.log(`\n📁 检查视频文件:`)
const videoSourceUrl = String(material.videoSourceUrl || '')
// 检查文件是否存在
let fileExists = false
let filePath = ''
if (videoSourceUrl.startsWith('http://') || videoSourceUrl.startsWith('https://')) {
console.log(` ✅ 视频文件是 HTTP URL: ${videoSourceUrl}`)
console.log(` ⚠️ 注意:转码需要下载远程文件,可能较慢`)
fileExists = true
} else {
// 本地路径
if (videoSourceUrl.startsWith('/uploads/')) {
const uploadDir = process.env.UPLOAD_DIR || './uploads'
const absoluteUploadDir = uploadDir.startsWith('/') ? uploadDir : join(process.cwd(), uploadDir)
filePath = join(absoluteUploadDir, videoSourceUrl.replace('/uploads/', ''))
} else {
filePath = videoSourceUrl.startsWith('/') ? videoSourceUrl : join(process.cwd(), videoSourceUrl)
}
console.log(` 检查路径: ${filePath}`)
try {
await access(filePath, constants.F_OK)
fileExists = true
console.log(` ✅ 视频文件存在`)
} catch (error) {
console.log(` ❌ 视频文件不存在`)
console.log(` 错误: ${error}`)
}
}
// 检查转码状态
console.log(`\n🔄 转码状态分析:`)
const status = String(material?.videoProcessingStatus || '')
if (!status || status === 'null') {
console.log(` ❌ 问题:videoProcessingStatus 为 null`)
console.log(` 可能原因:上传时未正确设置转码状态`)
console.log(` 建议:手动触发转码`)
} else if (status === 'pending') {
console.log(` ⚠️ 状态:pending(等待转码)`)
if (!fileExists) {
console.log(` ❌ 问题:视频文件不存在,无法转码`)
} else {
console.log(` ✅ 文件存在,可以触发转码`)
console.log(` 建议:调用 /api/materials/transcode POST 接口触发转码`)
}
} else if (status === 'processing') {
console.log(` ⚠️ 状态:processing(转码中)`)
console.log(` 可能原因:`)
console.log(` 1. 转码任务正在执行`)
console.log(` 2. 上次转码中断,状态未重置`)
console.log(` 建议:检查服务器日志,或重置状态为 pending 后重试`)
} else if (status === 'done') {
console.log(` ✅ 状态:done(转码完成)`)
console.log(` 预览视频: ${material?.videoPreview540Url || '无'}`)
console.log(` 预览片段: ${material?.videoPreviewClipUrl || '无'}`)
console.log(` 封面图: ${material?.videoCoverUrl || '无'}`)
} else if (status === 'failed') {
console.log(` ❌ 状态:failed(转码失败)`)
console.log(` 错误信息: ${material?.transcodeLastError || '无'}`)
console.log(` 建议:检查错误信息,修复问题后重试`)
}
// 检查转码产物
console.log(`\n📦 转码产物:`)
console.log(` 540P预览视频: ${material?.videoPreview540Url || '无'}`)
console.log(` 10秒预览片段: ${material?.videoPreviewClipUrl || '无'}`)
console.log(` 封面图: ${material?.videoCoverUrl || '无'}`)
// 总结
console.log(`\n📊 诊断总结:`)
if (!fileExists && !videoSourceUrl.startsWith('http')) {
console.log(` ❌ 主要问题:视频文件不存在`)
console.log(` 解决方案:检查文件路径或重新上传`)
} else if (!status || status === 'null') {
console.log(` ❌ 主要问题:转码状态未设置`)
console.log(` 解决方案:手动触发转码`)
} else if (status === 'pending' && fileExists) {
console.log(` ⚠️ 问题:转码任务未触发`)
console.log(` 解决方案:调用转码 API 触发转码`)
} else if (status === 'processing') {
console.log(` ⚠️ 问题:转码状态卡在 processing`)
console.log(` 解决方案:检查服务器日志,或重置状态后重试`)
} else if (status === 'failed') {
console.log(` ❌ 问题:转码失败`)
console.log(` 解决方案:查看错误信息,修复后重试`)
} else {
console.log(` ✅ 状态正常`)
}
} catch (error) {
console.error('诊断过程出错:', error)
} finally {
await prisma.$disconnect()
}
}
// 主函数
async function main() {
const materialId = process.argv[2]
if (!materialId) {
console.error('用法: tsx scripts/diagnose-video-transcode.ts <materialId>')
console.error('示例: tsx scripts/diagnose-video-transcode.ts 0895f84d-8837-465e-be50-4db57b5eab9e')
process.exit(1)
}
await diagnoseMaterial(materialId)
}
main().catch(console.error)