Node.js脚本项目合集(一):Node.js+FFmpeg实现批量从B站导出离线缓存视频到mp4格式,mp4转mp3,实现听歌自由

这篇具有很好参考价值的文章主要介绍了Node.js脚本项目合集(一):Node.js+FFmpeg实现批量从B站导出离线缓存视频到mp4格式,mp4转mp3,实现听歌自由。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

最近被一首JISOO的FLOWER歌洗脑,但碍于版权原因,只能在B站上看mv视频,盯着尬舞听歌着实有些尴尬,突发奇想,如果能将视频中的音频和视频分开不就能只听音乐,不用看尴尬的舞蹈吗?刚好手机上有不少B站本地的学习视频想导入到电脑上看,可是B站下载的格式.m4s文件,普通播放器根本点不开,有没有什么工具能将B站中的.m4s文件转换成.mp4文件,方便观看呢。下面介绍一个十分强大的开源工具FFmpeg,一款视频解码器,配合nodejs实现听歌自由(仅个人学习开发使用,切勿商用)

一、准备工作以及介绍

1、什么是FFmpeg

FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多code都是从头开发的。详情可参考FFmpeg官网。

2、FFmpeg下载

(1)在github下载地址进入github下载页面,选择要下载的zip包(根据自己的计算机系统选择)并进行解压,我这里选择win64即可。
nodejs 调用ffmpeg,nodejs,ffmpeg,ffmpeg,音视频,缓存
(2)解压文件至本地,并进入bin目录下,查看是否有ffmpeg.exe、ffmplay.exe和ffprobe.exe文件,若有三个文件,将bin的根目录路径复制下来,并配置到环境变量中去。
nodejs 调用ffmpeg,nodejs,ffmpeg,ffmpeg,音视频,缓存
(3)验证是否成功,WIN+R输入cmd窗口下输入指令ffmpeg -version查看是否查询到ffmpeg指令生效。
nodejs 调用ffmpeg,nodejs,ffmpeg,ffmpeg,音视频,缓存

3、准备B站视频

(1)手机下载B站视频
(2)打开手机文件管理,进入Android->data->tv.danmaku.bili->download目录下,将download里面的文件全部拷贝到电脑要运行的脚本文件目录中(这里我们copy到bilibiliFile文件中。

4、创建项目文件

(1)创建目录bilibiliFile_outMP4文件,用来保存转换后的数据
(2)创建mp4File文件,用来保存原始文件(该文件下的后缀名因保持一致)
(3)创建mp3File文件,用来保存转码后的文件

5、node环境准备

(1)node版本14.0.0以上
(2)需要用到fs模块、util模块、child_process模块和path模块

二、项目代码

1.代码部分

(1) VScode通过launch.json运行:本地使用Visual Studio Code这个工具生成launch.json来进行本地debug,可以直接添加配置,一共有两个脚本,分别是BilibiliM4s_mp4.js(执行B站m4s视频转码至mp4文件格式)和mp4_mp3.js(mp4转码成mp3格式文件)。
代码如下(示例):

{
    // 使用 IntelliSense 了解相关属性。 
    // 悬停以查看现有属性的描述。
    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "BilibiliM4s_mp4",
            "skipFiles": [
                "<node_internals>/**"
            ],
            "program": "${workspaceFolder}\\BilibiliM4s_mp4.js",
            "outFiles": [
                "${workspaceFolder}/**/*.js"
            ]
        },
        {
            "type": "node",
            "request": "launch",
            "name": "mp4_mp3",
            "skipFiles": [
                "<node_internals>/**"
            ],
            "program": "${workspaceFolder}\\mp4_mp3.js",
            "outFiles": [
                "${workspaceFolder}/**/*.js"
            ]
        }
    ]
}

大致讲述一下就是name指的是运行的名字,runtimeExecutable指的是运行指令,下载了node后就可以使用npm来启动项目,runtimeArgs指的是运行的参数,里面的debug在package.json里面的script里面会有debug的key值。

(2)BilibiliM4s_mp4.js

代码如下(示例):

/**
 * 该脚本主要做了将bilibiliFile(B站)中的缓存视频M4S文件转化为普通的mp4文件(node+FFmpeg)
 * 
 * 1、FFmpeg是开源的解决视频、音频相互转码的工具
 *  (1)github地址:https://github.com/FFmpeg/FFmpeg
 *  (2)安装包:./FFmpeg安装包(该安装包是win系统)
 *  (3)运行此脚本,请先安装FFmpeg的插件(直接把(2)解压至电脑,再把这个目录配置到环境变量中去),并配置到电脑的环境变量中去
 *  (4)验证是否安装成功FFmpeg(cmd窗口输入FFmpeg version,出现有数据则安装成功)
 * 2、找到手机缓存的B站的视频,路径./Android/data/tv.danmaku.bili/download
 * 3、将B站缓存视频download文件下的所有文件放到bilibilifile文件夹下
 * 4、调用bilibiliVideoCache2Mp4Wrap方法,输入参数后方可实现
 */

// bilibili中m4s缓存文件转换成mp4文件
// bilibili手机中保存的地址:./Android/data/tv.danmaku.bili/download

const fs = require('fs');
const path = require('path');
const util = require('util');
const exec = util.promisify(require('child_process').exec);

/**
 * 创建要转换mp4的文件夹
 * @param {*} targetPath 
 * @returns 
 */
const mkdirSync = async (targetPath) => {
  try {
    if (!fs.existsSync(targetPath)) {
      fs.mkdirSync(targetPath)
    } 
  } catch (error) {
    console.log(error);
  }
}

/**
 * 利用FFmpeg通过创建子进程去操作,将audio.m4s和video.m4s文件转化为mp4文件
 * @param {*} output 输出的文件夹路径
 * @param {*} curDirPath 当前递归目录位置(一层一层找entry.json的数据) 
 * @param {*} isTarget 是否找到了需要转换成mp4的目标文件夹
 * @param {*} outputFileName 转换成mp4的视频名(通过entry.json拿到原来视频的名字)
 * @param {*} dir 整个视频合集文件夹的名字
 */
const bilibiliVideoCache2Mp4 = async ({output, curDirPath, isTarget, outputFileName, dir}) => {
  if (isTarget) {
    /* 
    使用replace去除空格不然ffmpeg会报错(eg:Unable to find a suitable output format for 'XXX')
    这里需要注意targetPath和outputFileName不应该有特殊的字符比如【 】,不然ffmpeg也会报错(eg:Unable to find a suitable output format for 'XXX')
    */
    let dirName = dir.replace(new RegExp(/[\\\\/:*?\"<>|\s]/,'g'), '');
    let targetPath = `${output}\\${dirName}`
    await mkdirSync(targetPath)
    outputFileName = outputFileName?.replace(new RegExp(/[\\\\/:*?\"<>|\s]/,'g'), '') // 有可能不存在,因为else调用bilibiliVideoCache2Mp4方法的时候没有传outputFileName,所以要outputFileName?判断是否存在
    try {
      console.log(`---开始将名为《${outputFileName}》的音频、视频文件合并输出成mp4文件---`)
      await exec(`ffmpeg -i ${curDirPath}\\video.m4s -i ${curDirPath}\\audio.m4s -codec copy ${targetPath}/${outputFileName}.mp4`)
      console.log(`------已完成对《${outputFileName}》文件的合成------`)
    } catch (error) {
      console.error(`***《${outputFileName}》文件转换失败***`)
      console.log(error.stderr);
    }
    return
  }
  let files = fs.readdirSync(curDirPath);
  files.forEach(function (file) {
    let stat = fs.statSync(curDirPath + '/' + file)
    // stat.isDirectory()判断路径是否存在
    if (stat.isDirectory()) {
      // 文件夹名字如果是80的话,说明已经到了目标文件夹audio.m4s和video.m4s就在里面
      // 判断是否能读到文件entry.json的文件
      let videoInfojson = {};
      try {
        videoInfojson = JSON.parse(fs.readFileSync(path.join(curDirPath, 'entry.json'), 'utf-8'));
      } catch (error) {
        console.log(`${curDirPath}目录下未找到entry.json文件,将继续递归继续找子文件`);
      }
      if (Object.keys(videoInfojson).length > 0) {
        // json.page_data.part是这组视频的名字,json.title是单个视频的名字
        bilibiliVideoCache2Mp4({ 
          output, 
          curDirPath: path.join(curDirPath, file), 
          isTarget: true, 
          outputFileName: videoInfojson.page_data?.part ? videoInfojson.page_data.part : videoInfojson.time_create_stamp.toString(), // 没有文件名字就去那下载视频的时间戳当名字
          dir: videoInfojson.title 
        })
      }
      else bilibiliVideoCache2Mp4({ 
        output, 
        curDirPath: path.join(curDirPath, file) 
      })
    }
  });
}

/**
 * 执行转换的管道
 * @param {*} entry 当前递归目录位置(这里是bilibiliFile文件夹)
 * @param {*} output 创建新的文件夹
 */
async function bilibiliVideoCache2Mp4Wrap(entry, output) {
  await mkdirSync(output);
  bilibiliVideoCache2Mp4({
    curDirPath: entry,
    output,
  })
}

bilibiliVideoCache2Mp4Wrap(path.join(__dirname, 'bilibiliFile'), path.join(__dirname, 'bilibiliFile_outMP4'))



(3)mp4_mp3.js: 这里面输入的初始参数文件格式可以定义为别的模式(mov,mp4,m4a,3gp,3g2,mj2等都可以)。这里简单介绍一下FFmpeg的指令:
-i 传入的mp4路径
-vn保留原视频,不对原始平做处理
-ar采样率(22050, 441000, 48000)
-ac声道数
-b:a比特率(也可以用ab来代替。mp3有96k, 128k, 192k, 256k, 320k)
-f转成的格式(默认也会是mp3这种自动
代码如下(示例):

/**
 * 该脚本主要将视频软件切换成无损画质文件(主要看传参),本代码实例依靠FFmpeg+node批量将mp4格式转换成mp3格式
 * 1、先将视频文件导入到mp4File文件中
 * 2、新建mp3File文件
 * 3、将原始文件的路径,原始文件的风格(即文件后缀名,支持mov,mp4,m4a,3gp,3g2,mj2等视频格式),目标保存路径,目标格式四个参数写入到mp4Tomp3方法中去即可
 * 4、运行脚本等待输出结果
 */

const fs = require('fs');
const path = require('path');
const util = require('util');
const exec = util.promisify(require('child_process').exec);


/**
 * 
 * @param {*} param 
 * @param {string} param.originDir 原始文件路径
 * @param {string} param.originFormat 原始文件风格(可选mp4等等)
 * @param {string} param.targetDir 目标文件路径
 * @param {string} param.targetFormat 目标文件风格(可选mp3等等)
 */
const mp4Tomp3 = async ({originDir, originFormat, targetDir, targetFormat}) => {
  // 1、新建歌曲异常和歌曲列表清单
  const SongListDir = `${targetDir}\\SongListDir.txt`;
  const SongListDirError = `${targetDir}\\SongListDirError.txt`
  let files = fs.readdirSync(originDir);
  let errorNum = 0;
  let errorItemName = ''
  for (const file of files) {
    // 截取后缀名
    const fileLastName = file.substring(file.lastIndexOf('.') + 1);
    const filefistName = file.substring(0, file.lastIndexOf('.'))
    if (fileLastName === originFormat) {
      // 简单解释一下:
      // -i 传入的mp4路径    -vn保留原视频,不对原始平做处理    -ar采样率(22050, 441000, 48000)    -ac声道数    -b:a比特率(也可以用ab来代替。mp3有96k, 128k, 192k, 256k, 320k)    -f转成的格式(默认也会是mp3这种自动
      // ffmpeg.exe -i xxx.mp4 -vn -ar 44100 -ac 2 -b:a 192k -f mp3 xxx.mp3
      try {
        console.log(`---开始将名为《${filefistName}》的${fileLastName}文件类型转换为${targetFormat}类型---`);
        await exec(`ffmpeg -i ${originDir}\\${filefistName}.${originFormat} -vn -ar 44100 -ac 2 -b:a 192k -f mp3 ${targetDir}\\${filefistName}.${targetFormat}`)
        console.log(`------已完成对《${filefistName}》的${fileLastName}文件类型进行转换------`);
        fs.writeFileSync(SongListDir, `\n${file}`, { flag: 'a' });
      } catch (error) {
        errorNum++;
        errorItemName = `${errorItemName}${file}`
        console.error(`***《${filefistName}》转换失败***`)
        console.log(error)
        fs.writeFileSync(SongListDir, `\n${file}`, { flag: 'a' });
        fs.writeFileSync(SongListDirError, `\n报错提示如下:${error}`, { flag: 'a' });
      }
    }
  }
  console.log(`!!!共有${errorNum}个异常,异常的名字为${errorItemName}!!!`)
  fs.writeFileSync(SongListDir, `\n\n\n!!!共有${errorNum}个异常,异常的名字为${errorItemName}!!!`, { flag: 'a' });
}

mp4Tomp3({
  originDir: path.join(__dirname, 'mp4File'),
  originFormat: 'mp4',
  targetDir: path.join(__dirname, 'mp3File'),
  targetFormat: 'mp3'
})

2.完整项目地址

https://github.com/lp970703/node_job/tree/master/ffmpeg_node

总结

以上就是通过FFmpeg+nodejs实现批量视频转音频的功能,FFmpeg的功能远不止如此,更多的学习可以去FFmpeg官网学习,本人还开放了其他关于nodejs实用脚本,感兴趣可以去本人github地址:node_job中学习文章来源地址https://www.toymoban.com/news/detail-724426.html

到了这里,关于Node.js脚本项目合集(一):Node.js+FFmpeg实现批量从B站导出离线缓存视频到mp4格式,mp4转mp3,实现听歌自由的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Node.js模块的导出

    在Node.js中, module.exports 和 exports 是两种导出模块的方式,它们的作用是使得模块中的内容可以被其他模块引用和使用。 module.exports : module.exports 是一个指向当前模块所导出内容的对象的引用。你可以通过给 module.exports 赋值来导出一个对象、函数、类或者任何其他的JavaScrip

    2024年04月14日
    浏览(47)
  • 基于Node.js的后台管理系统的数据表格导出下载

    今天在工作的时候接触到一个需求,就是现在有一个简单的后台管理系统是基于node.js来实现的,现在需要将其中的一个表格数据下载下来。乍一听还以为这个是一个简单的需求,以为只要简单的一个小时就能完成,没有想到直接花了我将近两个半小时并且还是在他人的帮助下

    2024年02月13日
    浏览(57)
  • Node.js Shell 脚本开发指南(下)

    原文: exploringjs.com/nodejs-shell-scripting/ch_creating-shell-scripts.html 译者:飞龙 协议:CC BY-NC-SA 4.0 14.1 所需的知识 14.1.1 本章的下一步是什么 14.2 Node.js ESM 模块作为 Unix 上独立的 shell 脚本 14.2.1 Unix 上的 Node.js shell 脚本 14.2.2 Unix 上的 Hashbangs 14.2.3 在 Unix 上使文件可执行 14.2.4 直接运行

    2024年01月23日
    浏览(48)
  • Node.js Shell 脚本开发指南(中)

    原文: exploringjs.com/nodejs-shell-scripting/ch_nodejs-streams.html 译者:飞龙 协议:CC BY-NC-SA 4.0 9.1 总结:异步迭代和异步生成器 9.2 流 9.2.1 管道 9.2.2 文本编码 9.2.3 辅助函数: readableToString() 9.2.4 一些初步说明 9.3 可读流 9.3.1 创建可读流 9.3.2 通过 for-await-of 从可读流中读取

    2024年01月24日
    浏览(62)
  • 在服务器部署Next.js、Node.js项目,并实现自动部署(伪CI\CD)超详细

    目录 一、引言 二、配置服务器 1. 远程连接服务器 · 用服务商提供的远程连接 · 用MotaXterm来远程连接 ​编辑​编辑​编辑  2. 登录实例 · 如何获取账号和密码 · 服务商远程连接方式:  · MotaXterm连接方式 三、项目准备与测试 1. 安装nodejs · 进入安装目录 · 下载 · 解压 ·

    2024年02月05日
    浏览(96)
  • Puppeteer+RabbitMQ:Node.js 批量加工pdf服务架构设计与落地

    智慧作业最近上线「 个性化手册 」(简称个册)功能,一份完整的个性化手册分为 三部分 : 学情分析 :根据学生阶段性的学习和考试情况进行学情分析、归纳、总结,汇总学情数据; 精准推荐 :推荐算法基于学情数据结合知识图谱进行精准练习题推荐; 错题回顾 :错题

    2023年04月19日
    浏览(43)
  • 在Node.js中,什么是模块(module)?如何导入和导出模块?

    聚沙成塔·每天进步一点点 前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发者,这里都将为你提供一个系统而

    2024年02月05日
    浏览(47)
  • JS游戏项目合集【附源码】

    【迷宫游戏】是一款基于 HTML5 技术开发的游戏,玩法简单。玩家需要在一个迷宫中找到出口并成功逃脱,本项目还有自动寻路(Track)的功能 运行效果: 源码: 【俄罗斯方块】是一款基于 HTML5 技术开发的益智类游戏。游戏开始时,会出现一个空的游戏区域,以及一个正在下

    2024年02月20日
    浏览(38)
  • Node.js @zurmokeeper/exceljs 如何快速导出多表头的excel文件

    Node.js 如何快速导出嵌套列(多表头)的excel文件。效果图如下: 1:使用 @zurmokeeper/exceljs, V4.4.1以上 安装: npm i @zurmokeeper/exceljs 2: 有一个 worksheet.makeColumns 方法,API文档: 代码示例:

    2024年02月10日
    浏览(49)
  • node笔记_连接mysql编写js脚本的crud

    2024年02月04日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包