bililive-cli 3.13.1 → 3.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -11575,7 +11575,7 @@ const APP_DEFAULT_CONFIG = {
|
|
|
11575
11575
|
},
|
|
11576
11576
|
],
|
|
11577
11577
|
songRecognizeAsr: {
|
|
11578
|
-
modelId: "",
|
|
11578
|
+
modelId: "bcut",
|
|
11579
11579
|
},
|
|
11580
11580
|
songRecognizeLlm: {
|
|
11581
11581
|
modelId: "",
|
|
@@ -12136,6 +12136,7 @@ const defaultRecordConfig = {
|
|
|
12136
12136
|
sendToWebhook: false,
|
|
12137
12137
|
noGlobalFollowFields: [],
|
|
12138
12138
|
saveCover: false,
|
|
12139
|
+
convert2Mp4: false,
|
|
12139
12140
|
extra: {},
|
|
12140
12141
|
qualityRetry: 0,
|
|
12141
12142
|
formatName: "auto",
|
|
@@ -54494,7 +54495,7 @@ async function trash(paths, options) {
|
|
|
54494
54495
|
} else if (process$2.platform === 'win32') {
|
|
54495
54496
|
module = await Promise.resolve().then(function () { return require('./windows-OmnJ7a39.cjs'); });
|
|
54496
54497
|
} else {
|
|
54497
|
-
module = await Promise.resolve().then(function () { return require('./linux-
|
|
54498
|
+
module = await Promise.resolve().then(function () { return require('./linux-CH6NYi8a.cjs'); });
|
|
54498
54499
|
}
|
|
54499
54500
|
|
|
54500
54501
|
return module.default(paths);
|
|
@@ -61384,9 +61385,6 @@ const genFfmpegParams = (options) => {
|
|
|
61384
61385
|
if (options.audioCodec) {
|
|
61385
61386
|
result.push(`-c:a ${options.audioCodec}`);
|
|
61386
61387
|
}
|
|
61387
|
-
else {
|
|
61388
|
-
result.push(`-c:a copy`);
|
|
61389
|
-
}
|
|
61390
61388
|
if (options.extraOptions) {
|
|
61391
61389
|
options.extraOptions.split(" ").forEach((option) => {
|
|
61392
61390
|
result.push(option);
|
|
@@ -181078,26 +181076,31 @@ const checkMergeVideos = async (inputFiles) => {
|
|
|
181078
181076
|
const videoMetas = await Promise.all(inputFiles.map((file) => readVideoMeta(file)));
|
|
181079
181077
|
const errors = [];
|
|
181080
181078
|
const warnings = [];
|
|
181079
|
+
const videoStream0 = videoMetas[0].streams.find((stream) => stream.codec_type === "video");
|
|
181080
|
+
const audioStream0 = videoMetas[0].streams.find((stream) => stream.codec_type === "audio");
|
|
181081
181081
|
for (const meta of videoMetas) {
|
|
181082
181082
|
if (meta.format.format_name !== videoMetas[0].format.format_name) {
|
|
181083
181083
|
errors.push("输入视频容器不一致");
|
|
181084
181084
|
}
|
|
181085
181085
|
const videoStream = meta.streams.find((stream) => stream.codec_type === "video");
|
|
181086
|
-
const videoStream0 = videoMetas[0].streams.find((stream) => stream.codec_type === "video");
|
|
181087
181086
|
const audioStream = meta.streams.find((stream) => stream.codec_type === "audio");
|
|
181088
|
-
const audioStream0 = videoMetas[0].streams.find((stream) => stream.codec_type === "audio");
|
|
181089
181087
|
if (videoStream?.codec_name !== videoStream0?.codec_name) {
|
|
181090
181088
|
errors.push("输入视频编码器不一致");
|
|
181091
181089
|
}
|
|
181092
181090
|
if (audioStream?.codec_name !== audioStream0?.codec_name) {
|
|
181093
181091
|
errors.push("输入视频音频编码器不一致");
|
|
181094
181092
|
}
|
|
181095
|
-
// 分辨率不一致警告
|
|
181096
181093
|
if (videoStream?.width !== videoStream0?.width) {
|
|
181097
|
-
|
|
181094
|
+
errors.push("输入视频分辨率宽不一致");
|
|
181098
181095
|
}
|
|
181099
181096
|
if (videoStream?.height !== videoStream0?.height) {
|
|
181100
|
-
|
|
181097
|
+
errors.push("输入视频分辨率高不一致");
|
|
181098
|
+
}
|
|
181099
|
+
if (audioStream?.sample_rate !== audioStream0?.sample_rate) {
|
|
181100
|
+
errors.push("输入视频音频采样率不一致");
|
|
181101
|
+
}
|
|
181102
|
+
if (videoStream?.r_frame_rate !== videoStream0?.r_frame_rate) {
|
|
181103
|
+
warnings.push("输入视频帧率不一致");
|
|
181101
181104
|
}
|
|
181102
181105
|
}
|
|
181103
181106
|
return {
|
|
@@ -186333,6 +186336,9 @@ function createRecordExtraDataController(savePath) {
|
|
|
186333
186336
|
// 清理内存
|
|
186334
186337
|
data.pendingMessages = [];
|
|
186335
186338
|
interactedUsers.clear();
|
|
186339
|
+
danmaNum = 0;
|
|
186340
|
+
scNum = 0;
|
|
186341
|
+
guardNum = 0;
|
|
186336
186342
|
}
|
|
186337
186343
|
};
|
|
186338
186344
|
return {
|
|
@@ -186476,11 +186482,13 @@ class Segment extends EventEmitter$j {
|
|
|
186476
186482
|
outputVideoFilePath;
|
|
186477
186483
|
disableDanma;
|
|
186478
186484
|
videoExt;
|
|
186479
|
-
|
|
186485
|
+
options;
|
|
186486
|
+
constructor(getSavePath, disableDanma, videoExt, options) {
|
|
186480
186487
|
super();
|
|
186481
186488
|
this.getSavePath = getSavePath;
|
|
186482
186489
|
this.disableDanma = disableDanma;
|
|
186483
186490
|
this.videoExt = videoExt;
|
|
186491
|
+
this.options = options;
|
|
186484
186492
|
}
|
|
186485
186493
|
getVideoFileCompletedPayload() {
|
|
186486
186494
|
return {
|
|
@@ -186521,11 +186529,13 @@ class Segment extends EventEmitter$j {
|
|
|
186521
186529
|
if (!this.init) {
|
|
186522
186530
|
await this.handleSegmentEnd();
|
|
186523
186531
|
}
|
|
186532
|
+
// 首次创建使用上次的时间戳,后续创建使用当前时间戳
|
|
186533
|
+
const startTime = this.init ? (this.options?.firstStartTime ?? Date.now()) : Date.now();
|
|
186524
186534
|
this.init = false;
|
|
186525
|
-
const startTime = Date.now();
|
|
186526
186535
|
let liveInfo = { title: "", cover: "" };
|
|
186527
186536
|
if (callBack?.onUpdateLiveInfo) {
|
|
186528
186537
|
try {
|
|
186538
|
+
// TODO:这里存在bug,当调用onUpdateLiveInfo并在等待时,handleSegmentEnd被调用,那么会造成竞态导致数据错误,后续需要优化,需要保存segment状态
|
|
186529
186539
|
liveInfo = await callBack.onUpdateLiveInfo();
|
|
186530
186540
|
}
|
|
186531
186541
|
catch (err) {
|
|
@@ -186606,7 +186616,9 @@ class StreamManager extends EventEmitter$j {
|
|
|
186606
186616
|
}
|
|
186607
186617
|
this.recordSavePath = recordSavePath;
|
|
186608
186618
|
if (hasSegment) {
|
|
186609
|
-
this.segment = new Segment(getSavePath, disableDanma, this.videoExt
|
|
186619
|
+
this.segment = new Segment(getSavePath, disableDanma, this.videoExt, {
|
|
186620
|
+
firstStartTime: startTime,
|
|
186621
|
+
});
|
|
186610
186622
|
this.segment.on("DebugLog", (data) => {
|
|
186611
186623
|
this.emit("DebugLog", data);
|
|
186612
186624
|
});
|
|
@@ -187225,7 +187237,7 @@ class mesioDownloader extends EventEmitter$j {
|
|
|
187225
187237
|
});
|
|
187226
187238
|
}
|
|
187227
187239
|
createCommand() {
|
|
187228
|
-
const inputOptions = [...this.inputOptions, "--fix", "--no-proxy"];
|
|
187240
|
+
const inputOptions = [...this.inputOptions, "--fix", "--no-proxy", "--disable-log-file"];
|
|
187229
187241
|
if (this.debugLevel === "verbose") {
|
|
187230
187242
|
inputOptions.push("-v");
|
|
187231
187243
|
}
|
|
@@ -187801,6 +187813,7 @@ function defaultToJSON(provider, recorder) {
|
|
|
187801
187813
|
"segment",
|
|
187802
187814
|
"saveSCDanma",
|
|
187803
187815
|
"saveCover",
|
|
187816
|
+
"convert2Mp4",
|
|
187804
187817
|
"saveGiftDanma",
|
|
187805
187818
|
"disableProvideCommentsWhenRecording",
|
|
187806
187819
|
"liveInfo",
|
|
@@ -196084,14 +196097,14 @@ const getMatchingFiles = async (config, folderPath, startTime) => {
|
|
|
196084
196097
|
.filter((result) => {
|
|
196085
196098
|
// 排除directory,开始时间大于startTime,ctime大于当前时间五分钟,startTimeMs从旧到新排序
|
|
196086
196099
|
const filename = require$$0$7.basename(result.path);
|
|
196087
|
-
const startTimeMs = extractStartTimeFromFilename(filename, config.startTimeAutoMatch, result.value.birthtimeMs);
|
|
196100
|
+
const startTimeMs = extractStartTimeFromFilename(filename, config.startTimeAutoMatch, result.value.birthtimeMs || result.value.ctimeMs);
|
|
196088
196101
|
return (result.value.isFile() &&
|
|
196089
196102
|
startTimeMs > startTime &&
|
|
196090
196103
|
Date.now() - result.value.ctimeMs > 1 * 60 * 1000);
|
|
196091
196104
|
})
|
|
196092
196105
|
.map((result) => {
|
|
196093
196106
|
const filename = require$$0$7.basename(result.path);
|
|
196094
|
-
const startTimeMs = extractStartTimeFromFilename(filename, config.startTimeAutoMatch, result.value.birthtimeMs);
|
|
196107
|
+
const startTimeMs = extractStartTimeFromFilename(filename, config.startTimeAutoMatch, result.value.birthtimeMs || result.value.ctimeMs);
|
|
196095
196108
|
return {
|
|
196096
196109
|
path: result.path,
|
|
196097
196110
|
startTimeMs,
|
|
@@ -213077,34 +213090,47 @@ const checkLiveStatusAndRecord$2 = async function ({ getSavePath, isManualStart,
|
|
|
213077
213090
|
uid: Number(this.uid),
|
|
213078
213091
|
useServerTimestamp: this.useServerTimestamp,
|
|
213079
213092
|
});
|
|
213080
|
-
|
|
213081
|
-
|
|
213082
|
-
|
|
213083
|
-
|
|
213084
|
-
|
|
213085
|
-
|
|
213086
|
-
|
|
213087
|
-
|
|
213088
|
-
|
|
213089
|
-
|
|
213090
|
-
|
|
213091
|
-
|
|
213092
|
-
|
|
213093
|
-
|
|
213094
|
-
|
|
213095
|
-
|
|
213096
|
-
|
|
213097
|
-
|
|
213098
|
-
|
|
213099
|
-
|
|
213100
|
-
|
|
213101
|
-
|
|
213102
|
-
|
|
213103
|
-
|
|
213104
|
-
|
|
213093
|
+
// 开启了禁止提供弹幕功能,并且也没有设置标题关键词,才完全禁止连接弹幕服务器,否则都连接弹幕服务器,前者不处理弹幕消息,后者根据标题关键词来判断是否停止录制
|
|
213094
|
+
const enableDanmaListen = !this.disableProvideCommentsWhenRecording ||
|
|
213095
|
+
utils$2.shouldCheckTitleKeywords(isManualStart, this.titleKeywords);
|
|
213096
|
+
danmaClient.on("Message", (msg) => {
|
|
213097
|
+
if (this.disableProvideCommentsWhenRecording)
|
|
213098
|
+
return;
|
|
213099
|
+
const extraDataController = downloader.getExtraDataController();
|
|
213100
|
+
if (!extraDataController)
|
|
213101
|
+
return;
|
|
213102
|
+
if (msg.type === "super_chat" && this.saveSCDanma === false)
|
|
213103
|
+
return;
|
|
213104
|
+
if ((msg.type === "give_gift" || msg.type === "guard") && this.saveGiftDanma === false)
|
|
213105
|
+
return;
|
|
213106
|
+
this.emit("Message", msg);
|
|
213107
|
+
extraDataController.addMessage(msg);
|
|
213108
|
+
});
|
|
213109
|
+
danmaClient.on("onRoomInfoChange", (msg) => {
|
|
213110
|
+
if (utils$2.shouldCheckTitleKeywords(isManualStart, this.titleKeywords)) {
|
|
213111
|
+
const title = msg?.body?.title ?? "";
|
|
213112
|
+
const hasTitleKeyword = utils$2.hasBlockedTitleKeywords(title, this.titleKeywords);
|
|
213113
|
+
if (hasTitleKeyword) {
|
|
213114
|
+
this.state = "title-blocked";
|
|
213115
|
+
this.emit("DebugLog", {
|
|
213116
|
+
type: "common",
|
|
213117
|
+
text: `检测到标题包含关键词,停止录制:直播间标题 "${title}" 包含关键词 "${this.titleKeywords}"`,
|
|
213118
|
+
});
|
|
213119
|
+
// 停止录制
|
|
213120
|
+
this.recordHandle && this.recordHandle.stop("直播间标题包含关键词");
|
|
213105
213121
|
}
|
|
213106
|
-
}
|
|
213107
|
-
|
|
213122
|
+
}
|
|
213123
|
+
});
|
|
213124
|
+
if (enableDanmaListen) {
|
|
213125
|
+
try {
|
|
213126
|
+
danmaClient.start();
|
|
213127
|
+
}
|
|
213128
|
+
catch (err) {
|
|
213129
|
+
this.emit("DebugLog", {
|
|
213130
|
+
type: "error",
|
|
213131
|
+
text: `弹幕连接失败,错误信息: ${String(err)}`,
|
|
213132
|
+
});
|
|
213133
|
+
}
|
|
213108
213134
|
}
|
|
213109
213135
|
const downloaderArgs = downloader.getArguments();
|
|
213110
213136
|
downloader.run();
|
|
@@ -290881,6 +290907,7 @@ class RecorderConfig {
|
|
|
290881
290907
|
saveGiftDanma: getValue("saveGiftDanma") ?? false,
|
|
290882
290908
|
saveSCDanma: getValue("saveSCDanma") ?? true,
|
|
290883
290909
|
saveCover: getValue("saveCover") ?? false,
|
|
290910
|
+
convert2Mp4: getValue("convert2Mp4") ?? false,
|
|
290884
290911
|
segment: getValue("segment") ?? 90,
|
|
290885
290912
|
uid: uid,
|
|
290886
290913
|
qualityRetry: getValue("qualityRetry") ?? 0,
|
|
@@ -290978,6 +291005,30 @@ async function sendEndLiveNotification(appConfig, recorder, config) {
|
|
|
290978
291005
|
// 更新最后通知时间
|
|
290979
291006
|
endLiveNotificationCache.set(cacheKey, now);
|
|
290980
291007
|
}
|
|
291008
|
+
async function convert2Mp4(videoFile) {
|
|
291009
|
+
const output = replaceExtName$1(videoFile, ".mp4");
|
|
291010
|
+
if (await fs$k.pathExists(output))
|
|
291011
|
+
return output;
|
|
291012
|
+
const name = path$y.basename(output);
|
|
291013
|
+
return new Promise((resolve, reject) => {
|
|
291014
|
+
transcode(videoFile, name, {
|
|
291015
|
+
encoder: "copy",
|
|
291016
|
+
audioCodec: "copy",
|
|
291017
|
+
}, {
|
|
291018
|
+
saveType: 1,
|
|
291019
|
+
savePath: ".",
|
|
291020
|
+
override: false,
|
|
291021
|
+
removeOrigin: false,
|
|
291022
|
+
}).then((task) => {
|
|
291023
|
+
task.on("task-end", () => {
|
|
291024
|
+
resolve(output);
|
|
291025
|
+
});
|
|
291026
|
+
task.on("task-error", () => {
|
|
291027
|
+
reject();
|
|
291028
|
+
});
|
|
291029
|
+
});
|
|
291030
|
+
});
|
|
291031
|
+
}
|
|
290981
291032
|
async function createRecorderManager(appConfig) {
|
|
290982
291033
|
/**
|
|
290983
291034
|
* 更新录制器
|
|
@@ -291193,8 +291244,14 @@ async function createRecorderManager(appConfig) {
|
|
|
291193
291244
|
const config = appConfig.getAll();
|
|
291194
291245
|
try {
|
|
291195
291246
|
const xmlFile = replaceExtName$1(filename, ".xml");
|
|
291196
|
-
|
|
291197
|
-
|
|
291247
|
+
let duration = 0;
|
|
291248
|
+
try {
|
|
291249
|
+
const videoMeta = await readVideoMeta(filename);
|
|
291250
|
+
duration = videoMeta?.format?.duration ?? 0;
|
|
291251
|
+
}
|
|
291252
|
+
catch (error) {
|
|
291253
|
+
logObj.error("读取视频元信息失败", { filename, error });
|
|
291254
|
+
}
|
|
291198
291255
|
// 提取文件名(不含后缀)
|
|
291199
291256
|
const videoFilename = path$y.basename(filename, path$y.extname(filename));
|
|
291200
291257
|
// 计算文件快速哈希值
|
|
@@ -291233,6 +291290,16 @@ async function createRecorderManager(appConfig) {
|
|
|
291233
291290
|
interact_num: uniqMember,
|
|
291234
291291
|
});
|
|
291235
291292
|
}
|
|
291293
|
+
if (data?.convert2Mp4) {
|
|
291294
|
+
try {
|
|
291295
|
+
await convert2Mp4(filename);
|
|
291296
|
+
await fs$k.unlink(filename);
|
|
291297
|
+
logObj.info("转换 mp4 成功,已删除原文件", { filename });
|
|
291298
|
+
}
|
|
291299
|
+
catch (error) {
|
|
291300
|
+
logObj.error("convert2Mp4 error", error);
|
|
291301
|
+
}
|
|
291302
|
+
}
|
|
291236
291303
|
}
|
|
291237
291304
|
catch (error) {
|
|
291238
291305
|
logObj.error("Update live error", { recorder, filename, error });
|
package/lib/index.cjs
CHANGED
|
@@ -3715,7 +3715,7 @@ const {
|
|
|
3715
3715
|
Help,
|
|
3716
3716
|
} = commander;
|
|
3717
3717
|
|
|
3718
|
-
var version = "3.
|
|
3718
|
+
var version = "3.14.0";
|
|
3719
3719
|
|
|
3720
3720
|
process.on("uncaughtException", function (error) {
|
|
3721
3721
|
console.error(`${new Date().toISOString()} uncaughtException`, error);
|
|
@@ -3741,8 +3741,8 @@ program
|
|
|
3741
3741
|
throw new Error(`${c.configFolder}参数不存在,请先重新运行 config gen 命令`);
|
|
3742
3742
|
}
|
|
3743
3743
|
// 下面两行顺序不能换(
|
|
3744
|
-
const { init } = await Promise.resolve().then(function () { return require('./index-
|
|
3745
|
-
const { serverStart } = await Promise.resolve().then(function () { return require('./index-
|
|
3744
|
+
const { init } = await Promise.resolve().then(function () { return require('./index-DnN22ZAd.cjs'); }).then(function (n) { return n.index; });
|
|
3745
|
+
const { serverStart } = await Promise.resolve().then(function () { return require('./index-BxJPCbAG.cjs'); });
|
|
3746
3746
|
const globalConfig = {
|
|
3747
3747
|
ffmpegPresetPath: path$1.join(c.configFolder, "ffmpeg_presets.json"),
|
|
3748
3748
|
videoPresetPath: path$1.join(c.configFolder, "presets.json"),
|
|
@@ -4,7 +4,7 @@ var os$3 = require('node:os');
|
|
|
4
4
|
var path$5 = require('node:path');
|
|
5
5
|
var fs$3 = require('node:fs');
|
|
6
6
|
var crypto = require('node:crypto');
|
|
7
|
-
var index = require('./index-
|
|
7
|
+
var index = require('./index-DnN22ZAd.cjs');
|
|
8
8
|
var require$$0$1 = require('fs');
|
|
9
9
|
var require$$0 = require('path');
|
|
10
10
|
var require$$0$2 = require('child_process');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bililive-cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.14.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "biliLive-tools的cli程序",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
"commander": "^12.1.0",
|
|
40
40
|
"rimraf": "^6.0.1",
|
|
41
41
|
"tsx": "^4.19.2",
|
|
42
|
-
"@biliLive-tools/
|
|
43
|
-
"@biliLive-tools/
|
|
44
|
-
"@biliLive-tools/
|
|
42
|
+
"@biliLive-tools/shared": "3.14.0",
|
|
43
|
+
"@biliLive-tools/http": "3.14.0",
|
|
44
|
+
"@biliLive-tools/types": "3.14.0"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
47
|
"start": "tsx src/index.ts",
|