脚本-把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 分钟前
el-table 多选改成单选
javascript·vue.js·elementui
快乐小土豆~~1 分钟前
el-input绑定点击回车事件意外触发页面刷新
javascript·vue.js·elementui
建群新人小猿1 小时前
会员等级经验问题
android·开发语言·前端·javascript·php
djk88881 小时前
Layui Table 行号
前端·javascript·layui
今天啥也没干1 小时前
使用 Sparkle 实现 macOS 应用自定义更新弹窗
前端·javascript·swift
痴憨道人1 小时前
openharmony sdk描述
javascript
loey_ln2 小时前
FIber + webWorker
javascript·react.js
lv程序媛2 小时前
el-table表头前几列固定,后面几列根据接口返回的值不同展示不同
javascript·vue.js·elementui
ZwaterZ2 小时前
el-table-column自动生成序号&&在序号前插入图标
前端·javascript·c#·vue
蒟蒻的贤3 小时前
vue学习11.21
javascript·vue.js·学习