脚本-把B站缓存m4s文件转换成mp4格式

js脚本,自动处理视频

  • [1. 需求简介](#1. 需求简介)
    • [1.1 pc安装b站客户端](#1.1 pc安装b站客户端)
    • [1.2 设置视频缓存目录](#1.2 设置视频缓存目录)
    • [1.3 找个视频缓存](#1.3 找个视频缓存)
    • [1.4 打开缓存文件夹![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/0eb346a84d5f42a7908f1d39bf410c3b.png)](#1.4 打开缓存文件夹)
    • [1.5 用notepad++编辑后缀m4s文件,删除文件内开头9个0,把文件小的保存为mp3格式,文件大的保存为mp4格式](#1.5 用notepad++编辑后缀m4s文件,删除文件内开头9个0,把文件小的保存为mp3格式,文件大的保存为mp4格式)
    • [1.6 用视频软件或blender把视频和音频合并](#1.6 用视频软件或blender把视频和音频合并)
    • [1.7 根据需要视频转换分辨率](#1.7 根据需要视频转换分辨率)
  • [2. 脚本使用](#2. 脚本使用)

背景:需要从b站下载视频,有方法,需要手动一步步操作,繁琐,作为程序员,自然选择自动化。

1. 需求简介

自动化就是把人做的事情用工具或代码自动实现,所以需要先了解需求。

1.1 pc安装b站客户端

1.2 设置视频缓存目录

1.3 找个视频缓存

1.4 打开缓存文件夹

1.5 用notepad++编辑后缀m4s文件,删除文件内开头9个0,把文件小的保存为mp3格式,文件大的保存为mp4格式

1.6 用视频软件或blender把视频和音频合并

1.7 根据需要视频转换分辨率

2. 脚本使用

2.1 需要安装nodejs https://nodejs.org/zh-cn

2.2 需要下载ffmpeg.exe

2.3 运行脚本

js 复制代码
node .\main.js E:\bilibili "C:\Program Files (x86)\FormatFactory\ffmpeg.exe"
  • 参数1表示b站视频缓存目录,一般这里会有很多文件夹,每一个文件夹就是一个缓存视频
  • 参数2表示ffmpeg.exe目录,注意路径有空格的需要用引号包起来
    main.js内容
js 复制代码
// main.js

const fs = require('fs');
const path = require('path');
const { exec } = require('child_process');

let myfiledataall = [];


// 获取文件大小,返回 Promise
function getFileSize(filePath) {
    return new Promise((resolve, reject) => {
        fs.stat(filePath, (err, stats) => {
            if (err) {
                reject(`获取文件大小失败: ${err}`);
            } else if (stats.isFile()) {
                resolve(stats.size);
            } else {
                resolve(0);
            }
        });
    });
}

// 遍历目录
async function traverseDirectory(directory) {
    try {
        const files = await fs.promises.readdir(directory);
        let myfiledata = [];
        for (const file of files) {
            const filePath = path.join(directory, file);
            const stats = await fs.promises.stat(filePath);
            if (stats.isDirectory()) {
                console.log(`文件夹: ${filePath}`);
                await traverseDirectory(filePath);
            } else if (filePath.endsWith('.m4s')) {
                const filesize = await getFileSize(filePath);
                myfiledata.push({ directory: directory, filename: filePath, filesize });
            }
        }
        myfiledataall.push(myfiledata);
    } catch (err) {
        console.error(`无法读取目录: ${err}`);
    }
}

// 读取文件
async function readFile(filePath) {
    try {
        return await fs.promises.readFile(filePath);
    } catch (err) {
        console.error(`读取文件失败: ${err}`);
        return '';
    }
}

// 写入文件
async function writeFile(filePath, content) {
    try {
        await fs.promises.writeFile(filePath, content);
        console.log('文件写入成功!');
    } catch (err) {
        console.error(`写入文件失败: ${err}`);
    }
}

// 获取命令行参数
const args = process.argv.slice(2);

let folderPath = args[0];
let ffmpegPath = args[1];
args.forEach(async (folderPath, index) => {
    console.log(`参数 ${index + 1}: ${folderPath}`);

});


async function handle() {
    await traverseDirectory(folderPath);
    // console.log("myfiledata:", myfiledataall);

    for (let myfiledata of myfiledataall) {
        console.log(myfiledata);
        if (myfiledata.length === 2) {
            const [first, second] = myfiledata;
            const largerFile = first.filesize > second.filesize ? first : second;
            const smallerFile = first === largerFile ? second : first;

            const videoFilePath = largerFile.filename.replace(".m4s", ".mp4");
            const audioFilePath = smallerFile.filename.replace(".m4s", ".mp3");

            await writeFile(videoFilePath, (await readFile(largerFile.filename)).slice(9));
            await writeFile(audioFilePath, (await readFile(smallerFile.filename)).slice(9));

            const outputFilePath = path.join(largerFile.directory, "output.mp4");
            console.log(outputFilePath);
            const outputFilePath2 = path.join(largerFile.directory, "output2.mp4");
            console.log(outputFilePath2);

            const command = `"${ffmpegPath}" -i "${videoFilePath}" -i "${audioFilePath}" -c:v copy -c:a aac -strict experimental "${outputFilePath}"`;
            console.log(command);
            exec(command, (error, stdout, stderr) => {
                if (error) {
                    console.error(`执行错误: ${error}`);
                    return;
                }
                console.log(`stdout: ${stdout}`);
                console.error(`stderr: ${stderr}`);
                console.log('合成完成!');

                const scaleCommand = `"${ffmpegPath}" -i "${outputFilePath}" -vf scale=960:544 "${outputFilePath2}"`;
                console.log(scaleCommand);
                exec(scaleCommand, (error, stdout, stderr) => {
                    if (error) {
                        console.error(`执行错误: ${error}`);
                        return;
                    }
                    console.log(`stdout: ${stdout}`);
                    console.error(`stderr: ${stderr}`);
                    console.log('分辨率转换完成!');
                });
            });
        }
    }

    console.log('处理完成!');
}

handle();

脚本最后执行2次ffmpeg,一次把mp4和mp3合成一个视频,一次把视频转换分辨率为960x544。

2.4 结果

  • output.mp4是第一次合成的原视频
  • output2.mp4 是分辨率为960x544的视频

如果视频很大,处理比较慢

相关推荐
李长渊哦1 小时前
深入理解 JavaScript 中的全局对象与 JSON 序列化
开发语言·javascript·json
Senar3 小时前
如何判断浏览器是否开启硬件加速
前端·javascript·数据可视化
codingandsleeping3 小时前
一个简易版无缝轮播图的实现思路
前端·javascript·css
拉不动的猪5 小时前
简单回顾下插槽透传
前端·javascript·面试
爱吃鱼的锅包肉5 小时前
Flutter路由模块化管理方案
前端·javascript·flutter
风清扬雨5 小时前
Vue3具名插槽用法全解——从零到一的详细指南
前端·javascript·vue.js
海盗强6 小时前
Vue 3 常见的通信方式
javascript·vue.js·ecmascript
XY.散人6 小时前
初识Redis · C++客户端list和hash
数据库·redis·缓存
oscar9997 小时前
JavaScript与TypeScript
开发语言·javascript·typescript
橘子味的冰淇淋~7 小时前
【解决】Vue + Vite + TS 配置路径别名成功仍爆红
前端·javascript·vue.js