2022-11-06 19:48:32 +08:00
|
|
|
|
const fs = require('fs-extra');
|
|
|
|
|
const path = require('path');
|
2022-11-09 19:22:42 +08:00
|
|
|
|
const crypto = require('crypto');
|
2022-11-06 19:48:32 +08:00
|
|
|
|
|
2022-11-10 17:58:57 +08:00
|
|
|
|
const cmd = require('shelljs');
|
2022-11-09 19:22:42 +08:00
|
|
|
|
|
|
|
|
|
const cachePath = './cache.json';
|
2022-11-07 12:59:18 +08:00
|
|
|
|
const hardTypeSet = new Set(["none", "qsv", "cuda"]);
|
|
|
|
|
const supportVideoTypeSet = new Set(["mkv", "mp4"]);
|
|
|
|
|
const replaceTextArr = ['h264', 'H264', 'x264', 'X264'];
|
2022-11-09 19:22:42 +08:00
|
|
|
|
let cache = {};
|
2022-11-10 21:51:28 +08:00
|
|
|
|
let timer = setInterval(saveFile, 5000);
|
|
|
|
|
function saveFile () {
|
|
|
|
|
console.log("保存数据");
|
|
|
|
|
fs.writeFileSync(cachePath, JSON.stringify(cache));
|
|
|
|
|
}
|
2022-11-10 17:58:57 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(async () => {
|
|
|
|
|
if (fs.existsSync(cachePath)) {
|
|
|
|
|
cache = JSON.parse(fs.readFileSync(cachePath, 'utf-8'));
|
|
|
|
|
}
|
2022-11-10 21:51:28 +08:00
|
|
|
|
await deal("Z:\\userData\\视频\\电影", 2500, true, "cuda");
|
|
|
|
|
if (timer) {
|
|
|
|
|
clearInterval(timer);
|
|
|
|
|
}
|
|
|
|
|
saveFile();
|
2022-11-10 17:58:57 +08:00
|
|
|
|
})();
|
|
|
|
|
|
2022-11-07 10:53:55 +08:00
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @param {*} basePath 处理路径
|
|
|
|
|
* @param {int} maxBitRate 最大码率,默认2500
|
|
|
|
|
* @param {boolean} changeName 是否改名(true会将h264,x264变更为h265,或者在文件结尾插入h265,其他名称相同额文件也会处理)
|
|
|
|
|
* @param {*} hardType 硬件加速类型,空/none:不使用加速;qsv:使用intel核显qsv加速;cuda:使用nvdia cuda加速
|
|
|
|
|
* @returns
|
|
|
|
|
*/
|
|
|
|
|
async function deal (basePath, maxBitRate = 2500, changeName = false, hardType) {
|
|
|
|
|
let hwType = hardType == 'qsv' ? "-hwaccel qsv" : hardType == 'cuda' ? "-hwaccel cuda" : "";
|
2022-11-07 12:59:18 +08:00
|
|
|
|
let decodeType = hardType == 'qsv' ? "-c:v h264_qsv" : hardType == 'cuda' ? "-c:v h264_cuvid" : "";
|
|
|
|
|
let encodeType = hardType == 'qsv' ? "-c:v hevc_qsv" : hardType == 'cuda' ? "-c:v hevc_nvenc" : "";
|
2022-11-07 10:53:55 +08:00
|
|
|
|
|
2022-11-07 12:59:18 +08:00
|
|
|
|
if (hardType && !hardTypeSet.has(hardType)) {
|
2022-11-07 10:53:55 +08:00
|
|
|
|
throw new Error("不支持的加速方案:" + hardType + ",仅支持:空," + JSON.stringify(hardTypeSet));
|
|
|
|
|
}
|
2022-11-06 19:48:32 +08:00
|
|
|
|
let fileList = fs.readdirSync(basePath);
|
|
|
|
|
for (let i in fileList) {
|
|
|
|
|
let name = fileList[i];
|
2022-11-07 10:53:55 +08:00
|
|
|
|
let filePath = path.join(basePath, name);
|
2022-11-07 12:59:18 +08:00
|
|
|
|
if (!fs.existsSync(filePath)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2022-11-09 19:22:42 +08:00
|
|
|
|
|
2022-11-10 21:51:28 +08:00
|
|
|
|
if ((await fs.stat(filePath)).isDirectory()) {
|
2022-11-07 10:53:55 +08:00
|
|
|
|
//如果为文件夹递归处理
|
2022-11-07 12:59:18 +08:00
|
|
|
|
await deal(filePath, maxBitRate, changeName, hardType);
|
2022-11-07 10:53:55 +08:00
|
|
|
|
}
|
2022-11-09 19:22:42 +08:00
|
|
|
|
const md5Str = crypto.createHash('md5').update(filePath).digest('hex');
|
|
|
|
|
if (cache[md5Str]) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
cache[md5Str] = true;
|
2022-11-07 10:53:55 +08:00
|
|
|
|
if (!supportVideoTypeSet.has(name.substring(name.lastIndexOf(".") + 1))) {
|
2022-11-06 19:48:32 +08:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
console.log("---------开始处理:" + name);
|
2022-11-10 17:58:57 +08:00
|
|
|
|
let res = JSON.parse(cmd.exec(`ffprobe.exe "${filePath}" -show_streams -select_streams v -show_format -print_format json`, { encoding: 'utf-8' }).stdout);
|
2022-11-06 19:48:32 +08:00
|
|
|
|
if (!res.format || !res.streams || res.streams.length == 0) {
|
|
|
|
|
console.log("无法识别的格式:" + JSON.stringify(res));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
let isH264 = res.streams.filter(item => item.codec_name === 'h264').length > 0;
|
|
|
|
|
if (!isH264) {
|
|
|
|
|
console.log("非h264,不处理");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
let bitRate = res.format.bit_rate;
|
|
|
|
|
if (!bitRate) {
|
|
|
|
|
console.log("未获取到帧率,不处理", JSON.stringify(res));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2022-11-09 12:48:36 +08:00
|
|
|
|
let is10Bit = res.streams.filter(item => item.bits_per_raw_sample === '10').length > 0;
|
2022-11-06 19:48:32 +08:00
|
|
|
|
bitRate = Math.round(parseInt(bitRate) / 1000);
|
2022-11-09 12:48:36 +08:00
|
|
|
|
bitRate = bitRate > maxBitRate * 2 ? maxBitRate : bitRate / 2;
|
2022-11-07 12:59:18 +08:00
|
|
|
|
let newName = null;
|
|
|
|
|
replaceTextArr.forEach(item => {
|
|
|
|
|
if (newName == null && name.indexOf(item) > -1) {
|
|
|
|
|
newName = name.replace(item, 'h265');
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
if (newName == null) {
|
2022-11-06 19:48:32 +08:00
|
|
|
|
let index = name.lastIndexOf('.');
|
|
|
|
|
newName = name.substr(0, index) + ".h265" + name.substr(index);
|
|
|
|
|
}
|
2022-11-06 19:50:51 +08:00
|
|
|
|
let newFilePath = path.join(basePath, newName);
|
2022-11-09 12:48:36 +08:00
|
|
|
|
let cmdStr = `ffmpeg.exe ${hwType} ${is10Bit ? "" : decodeType} -i "${filePath}" ${encodeType} -maxrate ${bitRate}K -c:a copy -y "${newFilePath}"`;
|
2022-11-06 19:48:32 +08:00
|
|
|
|
console.log(cmdStr);
|
2022-11-10 17:58:57 +08:00
|
|
|
|
let cmdRes = await cmd.exec(cmdStr);
|
|
|
|
|
let index = cmdRes.stderr.indexOf("video:");
|
|
|
|
|
if (index == -1) {
|
|
|
|
|
throw Error(cmdRes.stderr);
|
|
|
|
|
} else {
|
|
|
|
|
console.log("转换倍率:" + cmdRes.stderr.substring(index - 20));
|
|
|
|
|
}
|
2022-11-10 21:51:28 +08:00
|
|
|
|
if (!fs.existsSync(newFilePath) || fs.statSync(newFilePath).size <= 10000 || cmdRes.stderr.indexOf('Error') > -1) {
|
|
|
|
|
throw new Error("未知错误,文件转换失败");
|
2022-11-06 19:50:51 +08:00
|
|
|
|
}
|
2022-11-07 10:53:55 +08:00
|
|
|
|
if (changeName) {
|
|
|
|
|
//字幕,nfo文件重命名
|
|
|
|
|
let namePart1 = name.substr(0, name.lastIndexOf('.'));
|
|
|
|
|
let newNamePart1 = newName.substr(0, newName.lastIndexOf('.'));
|
|
|
|
|
fileList.forEach(item => {
|
2022-11-07 12:59:18 +08:00
|
|
|
|
if (item.startsWith(namePart1) && item != name) {
|
2022-11-07 10:53:55 +08:00
|
|
|
|
let temp = item.replace(namePart1, newNamePart1);
|
|
|
|
|
if (item != temp) {
|
|
|
|
|
fs.renameSync(path.join(basePath, item), path.join(basePath, temp));
|
|
|
|
|
}
|
2022-11-06 19:48:32 +08:00
|
|
|
|
}
|
2022-11-07 10:53:55 +08:00
|
|
|
|
})
|
|
|
|
|
}
|
2022-11-10 21:51:28 +08:00
|
|
|
|
await fs.remove(filePath);
|
2022-11-07 10:53:55 +08:00
|
|
|
|
if (!changeName) {
|
2022-11-10 21:51:28 +08:00
|
|
|
|
await fs.move(newFilePath, filePath);
|
2022-11-07 10:53:55 +08:00
|
|
|
|
}
|
2022-11-06 19:48:32 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|