176 lines
5.6 KiB
TypeScript
176 lines
5.6 KiB
TypeScript
/**
|
||
* 详细检查所有视频类素材的 videoSourceUrl 状态
|
||
* 逐条列出每条记录的 videoSourceUrl,便于人工核对
|
||
*/
|
||
|
||
import { PrismaClient } from '@prisma/client'
|
||
import { access } from 'fs/promises'
|
||
import { constants as fsConstants } from 'fs'
|
||
import path from 'path'
|
||
|
||
const prisma = new PrismaClient()
|
||
|
||
const S3_PUBLIC_DOMAIN = process.env.S3_PUBLIC_DOMAIN || ''
|
||
const UPLOAD_DIR = process.env.UPLOAD_DIR || './uploads'
|
||
const VIDEO_TYPES = ['主图视频', '安装视频', '种草视频']
|
||
|
||
async function fileExists(fullPath: string): Promise<boolean> {
|
||
try {
|
||
await access(fullPath, fsConstants.F_OK)
|
||
return true
|
||
} catch {
|
||
return false
|
||
}
|
||
}
|
||
|
||
async function main() {
|
||
console.log('==== 详细检查所有视频类素材的 videoSourceUrl ====\n')
|
||
|
||
// 查询所有视频类素材
|
||
const allVideos = await prisma.material.findMany({
|
||
where: {
|
||
deletedAt: null,
|
||
materialType: { in: VIDEO_TYPES } as any,
|
||
},
|
||
select: {
|
||
id: true,
|
||
materialType: true,
|
||
videoSourceUrl: true,
|
||
},
|
||
orderBy: {
|
||
createdAt: 'desc',
|
||
},
|
||
})
|
||
|
||
console.log(`共找到 ${allVideos.length} 条视频类素材\n`)
|
||
|
||
// 分类统计
|
||
const withSource = allVideos.filter((m) => !!m.videoSourceUrl)
|
||
const withoutSource = allVideos.filter((m) => !m.videoSourceUrl)
|
||
const localPaths = withSource.filter((m) => {
|
||
const url = m.videoSourceUrl as string
|
||
return url.startsWith('/uploads/video-')
|
||
})
|
||
const s3Urls = withSource.filter((m) => {
|
||
const url = m.videoSourceUrl as string
|
||
return url.startsWith('http://') || url.startsWith('https://')
|
||
})
|
||
const otherPaths = withSource.filter((m) => {
|
||
const url = m.videoSourceUrl as string
|
||
return !url.startsWith('/uploads/video-') && !url.startsWith('http://') && !url.startsWith('https://')
|
||
})
|
||
|
||
console.log('📊 统计:')
|
||
console.log(` 有 videoSourceUrl: ${withSource.length} 条`)
|
||
console.log(` 无 videoSourceUrl: ${withoutSource.length} 条`)
|
||
console.log(` 本地路径 (/uploads/video-*): ${localPaths.length} 条`)
|
||
console.log(` S3/HTTP URL: ${s3Urls.length} 条`)
|
||
console.log(` 其它格式: ${otherPaths.length} 条\n`)
|
||
|
||
// 检查 S3 URL
|
||
if (s3Urls.length > 0) {
|
||
console.log('❌ 发现指向 S3/HTTP 的 videoSourceUrl:')
|
||
s3Urls.forEach((m) => {
|
||
console.log(` - [${m.id}] ${m.materialType} => ${m.videoSourceUrl}`)
|
||
})
|
||
console.log('')
|
||
}
|
||
|
||
// 检查其它格式
|
||
if (otherPaths.length > 0) {
|
||
console.log('⚠️ 发现非标准格式的 videoSourceUrl:')
|
||
otherPaths.forEach((m) => {
|
||
console.log(` - [${m.id}] ${m.materialType} => ${m.videoSourceUrl}`)
|
||
})
|
||
console.log('')
|
||
}
|
||
|
||
// 检查无 videoSourceUrl 的记录
|
||
if (withoutSource.length > 0) {
|
||
console.log('ℹ️ 无 videoSourceUrl 的记录:')
|
||
withoutSource.forEach((m) => {
|
||
console.log(` - [${m.id}] ${m.materialType} => (空)`)
|
||
})
|
||
console.log('')
|
||
}
|
||
|
||
// 详细检查本地路径
|
||
if (localPaths.length > 0) {
|
||
console.log(`✅ 检查 ${localPaths.length} 条本地路径的 videoSourceUrl:\n`)
|
||
|
||
let okCount = 0
|
||
let missingCount = 0
|
||
let wrongFormatCount = 0
|
||
|
||
for (const m of localPaths) {
|
||
const url = m.videoSourceUrl as string
|
||
const fileName = url.replace('/uploads/', '')
|
||
const fullPath = path.join(UPLOAD_DIR, fileName)
|
||
|
||
// 检查格式是否正确(应该是 video-*.mp4,不应该是 video-source-*.mp4)
|
||
const isWrongFormat = fileName.startsWith('video-source-')
|
||
const exists = await fileExists(fullPath)
|
||
|
||
if (isWrongFormat) {
|
||
console.log(` ❌ [${m.id}] ${m.materialType}`)
|
||
console.log(` 格式错误: ${url} (应该是 video-*.mp4,不是 video-source-*.mp4)`)
|
||
wrongFormatCount++
|
||
} else if (!exists) {
|
||
console.log(` ❌ [${m.id}] ${m.materialType}`)
|
||
console.log(` 文件缺失: ${url}`)
|
||
console.log(` 本地路径: ${fullPath}`)
|
||
missingCount++
|
||
} else {
|
||
// 只显示前 10 条正常的,避免输出过多
|
||
if (okCount < 10) {
|
||
console.log(` ✅ [${m.id}] ${m.materialType} => ${url}`)
|
||
}
|
||
okCount++
|
||
}
|
||
}
|
||
|
||
if (okCount > 10) {
|
||
console.log(` ... 还有 ${okCount - 10} 条正常的记录未显示\n`)
|
||
}
|
||
|
||
console.log(`\n本地路径检查结果:`)
|
||
console.log(` ✅ 正常: ${okCount} 条`)
|
||
console.log(` ❌ 格式错误 (video-source-*): ${wrongFormatCount} 条`)
|
||
console.log(` ❌ 文件缺失: ${missingCount} 条`)
|
||
}
|
||
|
||
// 按类型分组统计
|
||
console.log('\n📋 按类型分组:')
|
||
const byType: Record<string, { total: number; withSource: number; withoutSource: number; local: number; s3: number }> = {}
|
||
for (const m of allVideos) {
|
||
if (!byType[m.materialType]) {
|
||
byType[m.materialType] = { total: 0, withSource: 0, withoutSource: 0, local: 0, s3: 0 }
|
||
}
|
||
byType[m.materialType].total++
|
||
if (m.videoSourceUrl) {
|
||
byType[m.materialType].withSource++
|
||
if ((m.videoSourceUrl as string).startsWith('/uploads/video-')) {
|
||
byType[m.materialType].local++
|
||
} else if ((m.videoSourceUrl as string).startsWith('http')) {
|
||
byType[m.materialType].s3++
|
||
}
|
||
} else {
|
||
byType[m.materialType].withoutSource++
|
||
}
|
||
}
|
||
|
||
for (const [type, stats] of Object.entries(byType)) {
|
||
console.log(` ${type}: 总计 ${stats.total} 条`)
|
||
console.log(` - 有 videoSourceUrl: ${stats.withSource} 条 (本地: ${stats.local}, S3: ${stats.s3})`)
|
||
console.log(` - 无 videoSourceUrl: ${stats.withoutSource} 条`)
|
||
}
|
||
|
||
await prisma.$disconnect()
|
||
}
|
||
|
||
main().catch((error) => {
|
||
console.error('脚本执行出错:', error)
|
||
process.exit(1)
|
||
})
|
||
|