gongxiangp2/scripts/retranscode-once.js

108 lines
3.5 KiB
JavaScript
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.

/**
* 一次性全量视频重转码脚本(容器内执行)
* - 直接复用 /api/materials/transcode 队列接口,无需登录
* - 自动兼容缺失 zipFilePath / deletedAt 列的旧库
* - 逐个排队并输出进度,避免误以为卡住
*
* 使用方式(宿主机运行):
* docker cp scripts/retranscode-once.js youyidian-3d-material:/app/
* docker exec -it youyidian-3d-material node /app/retranscode-once.js
*/
const { PrismaClient } = require('/app/node_modules/@prisma/client')
const prisma = new PrismaClient()
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
async function hasColumn(column) {
try {
const rows = await prisma.$queryRaw`
SELECT column_name FROM information_schema.columns
WHERE table_name = 'materials' AND column_name = ${column}
`
return rows.length > 0
} catch (e) {
console.warn('[retranscode] column check failed:', column, e?.message || e)
return false
}
}
async function main() {
console.log('========== 全量重转码开始 ==========')
const hasZipFilePath = await hasColumn('zipFilePath')
const hasDeletedAt = await hasColumn('deletedAt')
console.log('列检测 => zipFilePath:', hasZipFilePath, 'deletedAt:', hasDeletedAt)
// 使用最兼容的条件:仅基于 videoSourceUrl,避免 Prisma 客户端缺少字段类型时报错
const andConditions = [
{ materialType: { in: ['主图视频', '安装视频', '种草视频', '视频', 'VIDEO'] } },
{ videoSourceUrl: { not: null } },
]
if (hasDeletedAt) {
andConditions.push({ deletedAt: null })
}
console.log('[retranscode] 查询候选素材...')
const candidates = await prisma.material.findMany({
where: { AND: andConditions },
select: { id: true, productModel: true, createdAt: true },
orderBy: { createdAt: 'asc' },
})
console.log(`[retranscode] 匹配到 ${candidates.length} 条素材,开始逐个入队...`)
let baseUrl = process.env.NEXTAUTH_URL || process.env.APP_PUBLIC_URL || 'http://127.0.0.1:3000'
// 避免 localhost 解析到 IPv6(::1) 导致连接失败
baseUrl = baseUrl.replace('localhost', '127.0.0.1').replace(/\/$/, '')
console.log('[retranscode] Base URL =', baseUrl)
let success = 0
let failed = 0
for (let i = 0; i < candidates.length; i++) {
const m = candidates[i]
const idx = i + 1
const prefix = `[${idx}/${candidates.length}] ${m.id} (${m.productModel || '无型号'})`
try {
process.stdout.write(`${prefix} -> 入队中... `)
const res = await fetch(`${baseUrl}/api/materials/transcode`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ materialId: m.id, forceRetry: true }),
})
if (!res.ok) {
const text = await res.text().catch(() => '')
console.log(`失败 ${res.status} ${text}`)
failed++
} else {
console.log('成功')
success++
}
} catch (e) {
console.log(`异常 ${(e && e.message) || e}`)
if (e && e.stack) {
console.log(String(e.stack).split('\n')[0])
}
failed++
}
// 节流,避免请求过快
if (idx % 10 === 0) {
await sleep(100)
}
}
console.log('========== 全量重转码完成 ==========')
console.log(`总计: ${candidates.length},入队成功: ${success},失败: ${failed}`)
await prisma.$disconnect()
}
main().catch(async (e) => {
console.error('运行出错:', e)
await prisma.$disconnect()
process.exit(1)
})