gongxiangp2/scripts/check-video-source-url.ts

176 lines
5.6 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.

/**
* 详细检查所有视频类素材的 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)
})