前言
看文,看的是一种思路,希望笔者的文章能给诸位带来一些灵感思路☺️☺️☺️
历史效能工具文章
文本是效能工具系列文章的第九篇,前八篇分别是
- 效能工具之node在项目中的应用(一)《以表格的二次封装需要的配置化JSON为例》
- 效能工具之node在项目中的应用(二)《以开发环境多后端服务切换问题为例》
- 效能工具之node在项目中的应用(三)《以给几百个el-select统一加filterable属性可搜索为例》
- 效能工具之批处理文件.bat/.cmd在工作中的应用(四)《以多项目启动为例》
- 效能工具之前端自动化部署打包发布上传脚本(五)命令行加载、上传进度功能
- vue3中用MutationObserver采取hook方式实现视频的不可拖拽功能&&效能工具(六)一键提交git代码的bat脚本
- 效能工具(七)之在Windows系统的Startup文件夹添加bat脚本开机自启动nginx或者一些软件服务
- 效能工具(八)之vite开发或生产环境下的命令行变量传参(比如启动项目时多视图选择其一)
场景概述
实际场景描述有些冗余,特抽象出来描述如下:
公司平台上有一个视频文件列表,列表有近千条数据,对应接口返回的就是一个数组,length将近1000,如:
js
let tableData = [
{ id: 1, name: '视频1', duration: 364, url: 'https://abc.com.cn/files/1.mp4' },
{ id: 2, name: '视频2', duration: 210, url: 'https://abc.com.cn/files/2.mp4' },
...
{ id: 999, name: '视频999', duration: 540, url: 'https://abc.com.cn/files/999.mp4' },
]
其中,name是视频名字,duration是视频时长(秒),url是视频静态资源可访问地址
- 现在的问题是,因为一些无语的原因,这999条数据中,存在部分数据存储的duration字段的值不对,和实际视频的时长对不上,或多或少
- 比如第一条数据,视频1的实际时长是464秒,但duration字段值存的是364秒
- 现在需要将其修复成正确的时长,但是因为数据量太多,近千条数据,人工一条一条核对,效率十分低下,而且人工核对容易出错
因此笔者写了一个nodejs脚本,批量执行效率高,质量有保障
解决方案思路
- 首先,循环tableData得到数组的每一项
- 然后,使用get-video-duration这个包,读取每一项的url对应的视频资源
- 得到对应视频真正的时长,比如叫做trueDurationNum,和当前的duration对比一下
- 若相同,则代表时长没问题;若不同,则单独拎出来丢到notEqualArr数组里
- 最后,再把notEqualArr统一循环处理
- 或调用修改接口,批量请求修改成正确的视频时长
- 或者写个函数将其转成sql语句,直接一条命令执行解决问题
get-video-duration包介绍
get-video-duration 是github上的拥有140个Star的小众包,传给它一个视频的url,它就可以返回此视频对应的时长信息,如下
js
const { getVideoDurationInSeconds } = require('get-video-duration')
const duration = await getVideoDurationInSeconds(url)
console.log('视频时长/秒', duration)
支持mp4、mov、多种视频格式,其底层依赖FFmpeg的ffprobe套件,能够分析音视频(比如分辨率、编码格式、时长)其依赖简约如下:
json
{
"name": "get-video-duration",
"description": "Get the duration of a video file",
"version": "4.1.0",
"author": "Lluís Ulzurrun de Asanza Sàez <me@llu.is> (http://llu.is)",
"license": "MIT",
"repository": "caffco/get-video-duration",
"main": "dist/commonjs/index.js",
"module": "dist/es6/index.js",
"dependencies": {
"@ffprobe-installer/ffprobe": "^2.1.2",
"execa": "^5.0.0",
"is-stream": "^2.0.0"
},
......
}
实际上,包的作者除了有这个获取视频时长的工具包之外,还有一个获取音频时长的包:get-audio-duration
代码实现
因为要安装包,所以要npm inti -y简单创建一个node工程
而后安装对应依赖npm i get-video-duration
json
{
"name": "video-duration-check",
"version": "1.0.0",
"main": "app.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"get-video-duration": "^4.1.0"
}
}
检查是否有不相等的视频时长项
js
// app.js
const { getVideoDurationInSeconds } = require('get-video-duration')
const fs = require('fs')
// 给到一个url,返回视频时长
async function getVideoDuration(url) {
try {
const duration = await getVideoDurationInSeconds(url)
return Math.floor(duration) // 向下取整时长,精度到秒
// return duration // 精准时长,精度到毫秒
} catch (error) {
throw error
}
}
// 接口返回数据的示例
const tableData = [
{ id: 1, name: '视频1', duration: 364, url: 'https://abc.com.cn/files/1.mp4' },
{ id: 2, name: '视频2', duration: 210, url: 'https://abc.com.cn/files/2.mp4' },
{ id: 999, name: '视频999', duration: 540, url: 'https://abc.com.cn/files/999.mp4' },
]
async function main() {
// 存储不匹配的数据
const notEqualArr = []
// 遍历tableData,读取每个视频的时长
for (const item of tableData) {
try {
const trueDurationNum = await getVideoDuration(item.url) // 读取时长
if (item.duration !== trueDurationNum) { // 如果时长不匹配,则存入notEqualArr
// 把真实的时长也存到item中,方便后续处理,请求修改接口,或者生成sql语句
item.trueDurationNum = trueDurationNum
notEqualArr.push(item)
}
} catch (error) {
// 也可以在这里处理错误,比如新建一个errorArr,把错误信息存进去
console.error(error)
}
}
console.log(`共有${tableData.length}个视频,其中不匹配${notEqualArr.length}个`)
fs.writeFileSync('notEqualArr.json', JSON.stringify(notEqualArr, null, 2))
console.log('notEqualArr.json 文件已保存')
}
main()
经过这样一波操作,就能得到duration不对的notEqualArr数据项了,假设如下:
js
const notEqualArr = [
{ id: 3, name: '视频3', duration: 364, trueDurationNum: 3333, url: 'https://abc.com.cn/files/3.mp4' },
{ id: 4, name: '视频4', duration: 210, trueDurationNum: 4444, url: 'https://abc.com.cn/files/4.mp4' },
]
现在,有了不相等的数据了,可以选择两种方案
- 一种是循环notEqualArr数组,然后通过编辑接口修改duration的值为trueDurationNum的值
- 第二种是拼接成对应的sql,直接通过DBeaver或者Navicat 直接一条sql搞定
方式一:通过编辑接口修改
如下示例思路代码
js
// 编辑接口
const baseUrl = 'https://abc.com.cn/api/editVideo'
// 登录系统后,复制一份请求头的 Authorization
const Authorization = 'Bearer eyJhbGci...'
// 存储不匹配的数据
const notEqualArr = [
{ id: 3, name: '视频3', duration: 364, trueDurationNum: 3333, url: 'https://abc.com.cn/files/3.mp4' },
{ id: 4, name: '视频4', duration: 210, trueDurationNum: 4444, url: 'https://abc.com.cn/files/4.mp4' },
]
// 循环发请求修改对应duration字段的值为trueDurationNum
async function main() {
console.time('main')
for (const item of notEqualArr) {
try {
const res = await fetch(baseUrl, {
headers: {
'Authorization': Authorization,
'Content-Type': 'application/json'
},
method: 'POST',
body: JSON.stringify({
id: item.id,
duration: item.trueDurationNum
})
})
const { data } = await res.json()
console.log(`更新成功 - ID: ${item.id}, 新的时长: ${item.trueDurationNum}`)
console.log(data)
} catch (error) {
console.error(`更新失败 - ID: ${item.id}, 错误: ${error.message}`)
}
}
console.timeEnd('main')
}
main()
方式二:通过sql修改
回顾一下
- 假设,我要批量修改student表里面的
- id为2的那条数据,将其年龄改为22
- id为3的那条数据,将其年龄改为33,写法如下
sql
UPDATE student
SET age = CASE id
WHEN 2 THEN 22
WHEN 3 THEN 33
END
WHERE id IN (2, 3);
合并成为一行语法
sql
UPDATE student SET age = CASE id WHEN 2 THEN 22 WHEN 3 THEN 33 END WHERE id IN (2, 3);
对应上述修改duration的写法就是(假设表是video_table)
js
const notEqualArr = [
{ id: 3, name: '视频3', duration: 364, trueDurationNum: 3333, url: 'https://abc.com.cn/files/3.mp4' },
{ id: 4, name: '视频4', duration: 210, trueDurationNum: 4444, url: 'https://abc.com.cn/files/4.mp4' },
]
// 换行
UPDATE video_table
SET duration = CASE id
WHEN 3 THEN 3333
WHEN 4 THEN 4444
END
WHERE id IN (3, 4);
// 不换行,一行语句就是
UPDATE video_table SET duration = CASE id WHEN 3 THEN 3333 WHEN 4 THEN 4444 END WHERE id IN (3, 4);
所以,只需要写一个函数,将数组notEqualArr转成对应的单条sql语句即可
转换函数写法如下
js
const notEqualArr = [
{ id: 3, name: '视频3', duration: 364, trueDurationNum: 3333, url: 'https://abc.com.cn/files/3.mp4' },
{ id: 4, name: '视频4', duration: 210, trueDurationNum: 4444, url: 'https://abc.com.cn/files/4.mp4' },
]
/**
* 将数组转换为批量更新 SQL 语句
* @param {Array} arr - 包含 id 和 trueDurationNum 的数组
* @param {string} tableName - 表名,默认为 'video_table'
* @returns {string} 生成的 SQL 语句
*/
function generateSql(arr, tableName = 'video_table') {
// 构建 CASE WHEN 语句
const caseStatements = arr.map(item =>
`WHEN ${item.id} THEN ${item.trueDurationNum}`
).join(' ');
console.log('caseStatements---->', caseStatements); // WHEN 3 THEN 3333 WHEN 4 THEN 4444
// 构建 IN 条件
const ids = arr.map(item => item.id).join(',');
console.log('ids---->', ids); // 3,4
// 生成完整的 SQL 语句
const sql = `UPDATE ${tableName} SET duration = CASE id ${caseStatements} END WHERE id IN (${ids});`;
return sql;
}
function main() {
const sql = generateSql(notEqualArr);
return sql;
}
console.log(main());
// UPDATE video_table SET duration = CASE id WHEN 3 THEN 3333 WHEN 4 THEN 4444 END WHERE id IN (3,4);
最后,笔者采取方式二,直接sql执行的方案(先在测试环境试一下)最终快速解决了这个视频时长不对的问题
注意,这里的notEqualArr笔者是直接写到代码里面,不是将其丢到一个.json文件里面,再const notEqualArr = require('./notEqualArr.json')引入进来,这样就能避免require缓存机制,可参见这篇文章:请不要使用require引入单个文件
A good memory is better than a bad pen. Record it down...☺️☺️☺️