效能工具(九)之编写nodejs脚本使用get-video-duration批量读取视频时长,并生成sql语句修复数据库表字段值

前言

看文,看的是一种思路,希望笔者的文章能给诸位带来一些灵感思路☺️☺️☺️

历史效能工具文章

文本是效能工具系列文章的第九篇,前八篇分别是

场景概述

实际场景描述有些冗余,特抽象出来描述如下:

公司平台上有一个视频文件列表,列表有近千条数据,对应接口返回的就是一个数组,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...☺️☺️☺️

相关推荐
0***h94243 分钟前
Windows 11 如何配置node.js
windows·node.js
yaoxin5211232 小时前
为什么 IRIS SQL 会比 Spring JDBC 更快?
数据库·sql·spring
M***Z2102 小时前
SQL中如何添加数据
数据库·sql
n***26563 小时前
Python连接SQL SEVER数据库全流程
数据库·python·sql
r***l7663 小时前
sql中COALESCE函数详解
数据库·sql
1***35775 小时前
SQL之CASE WHEN用法详解
数据库·python·sql
Dr_哈哈5 小时前
LangChain Tools —— 让 AI 拥有「双手」
langchain·node.js·ai编程
Dr_哈哈5 小时前
LangChain Chain & Pipe 知识点详解
langchain·node.js·ai编程
j***29486 小时前
如何在Windows系统上安装和配置Node.js及Node版本管理器(nvm)
windows·node.js
进击的野人6 小时前
Node.js文件系统(fs模块)深度解析与实践应用
后端·正则表达式·node.js