126 lines
3.3 KiB
JavaScript
126 lines
3.3 KiB
JavaScript
const path = require('path')
|
|
const fs = require('fs/promises')
|
|
const { PrismaClient } = require('@prisma/client')
|
|
|
|
const prisma = new PrismaClient()
|
|
|
|
const VIDEO_TYPES = ['主图视频', '安装视频', '种草视频']
|
|
|
|
function detectExtFromHeader(buffer) {
|
|
if (buffer.slice(4, 8).toString('ascii').startsWith('ftyp')) {
|
|
return '.mp4'
|
|
}
|
|
if (buffer.slice(0, 4).toString('ascii') === 'RIFF') {
|
|
return '.avi'
|
|
}
|
|
if (buffer.slice(0, 4).toString('ascii') === 'fLaC') {
|
|
return '.flac'
|
|
}
|
|
if (buffer.slice(0, 3).toString('ascii') === 'ID3') {
|
|
return '.mp3'
|
|
}
|
|
return null
|
|
}
|
|
|
|
function resolvePath(filePath) {
|
|
if (!filePath) return ''
|
|
return path.isAbsolute(filePath) ? filePath : path.join(process.cwd(), filePath)
|
|
}
|
|
|
|
function buildStoredPath(originalStoredPath, newName) {
|
|
if (!originalStoredPath) {
|
|
return `uploads/${newName}`
|
|
}
|
|
const dir = path.dirname(originalStoredPath)
|
|
return path.join(dir, newName).replace(/\\/g, '/').replace(/^\.\//, '')
|
|
}
|
|
|
|
function buildVideoSourceUrl(videoSourceUrl, newName) {
|
|
if (!videoSourceUrl) {
|
|
return `/uploads/${newName}`
|
|
}
|
|
if (videoSourceUrl.startsWith('/uploads/')) {
|
|
const segments = videoSourceUrl.split('/')
|
|
segments[segments.length - 1] = newName
|
|
return segments.join('/')
|
|
}
|
|
if (videoSourceUrl.startsWith('uploads/')) {
|
|
return `uploads/${newName}`
|
|
}
|
|
return videoSourceUrl
|
|
}
|
|
|
|
async function main() {
|
|
const records = await prisma.material.findMany({
|
|
where: {
|
|
materialType: { in: VIDEO_TYPES },
|
|
zipFileName: { endsWith: '.zip' },
|
|
deletedAt: null,
|
|
},
|
|
select: {
|
|
id: true,
|
|
zipFileName: true,
|
|
zipFilePath: true,
|
|
videoSourceUrl: true,
|
|
},
|
|
})
|
|
|
|
if (records.length === 0) {
|
|
console.log('没有需要修复的视频文件')
|
|
return
|
|
}
|
|
|
|
let fixedCount = 0
|
|
let skippedMissing = 0
|
|
|
|
for (const record of records) {
|
|
try {
|
|
const absolutePath = resolvePath(record.zipFilePath)
|
|
const fileHandle = await fs.open(absolutePath, 'r')
|
|
const buffer = Buffer.alloc(16)
|
|
await fileHandle.read(buffer, 0, 16, 0)
|
|
await fileHandle.close()
|
|
|
|
const detectedExt = detectExtFromHeader(buffer) || '.mp4'
|
|
const newName = record.zipFileName.replace(/\.zip$/i, detectedExt)
|
|
const newAbsolutePath = path.join(path.dirname(absolutePath), newName)
|
|
|
|
try {
|
|
await fs.access(newAbsolutePath)
|
|
console.warn(`目标文件已存在,跳过重命名: ${newAbsolutePath}`)
|
|
} catch {
|
|
await fs.rename(absolutePath, newAbsolutePath)
|
|
}
|
|
|
|
const storedPath = buildStoredPath(record.zipFilePath, newName)
|
|
const sourceUrl = buildVideoSourceUrl(record.videoSourceUrl, newName)
|
|
|
|
await prisma.material.update({
|
|
where: { id: record.id },
|
|
data: {
|
|
zipFileName: newName,
|
|
zipFilePath: storedPath,
|
|
videoSourceUrl: sourceUrl,
|
|
},
|
|
})
|
|
|
|
fixedCount += 1
|
|
console.log(`已修复: ${record.id} -> ${newName}`)
|
|
} catch (error) {
|
|
skippedMissing += 1
|
|
console.error(`修复失败 ${record.id}:`, error.message)
|
|
}
|
|
}
|
|
|
|
console.log(`修复完成:${fixedCount} 条成功,${skippedMissing} 条失败`)
|
|
}
|
|
|
|
main()
|
|
.catch((error) => {
|
|
console.error('修复过程出错:', error)
|
|
process.exitCode = 1
|
|
})
|
|
.finally(async () => {
|
|
await prisma.$disconnect()
|
|
})
|