@bililive-tools/huya-recorder 1.7.0 → 1.8.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.
- package/README.md +2 -0
- package/lib/index.js +53 -16
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -37,6 +37,7 @@ interface Options {
|
|
|
37
37
|
quality: number; // 见画质参数
|
|
38
38
|
qualityRetry?: number; // 画质匹配重试次数, -1为强制匹配画质,0为自动配置,正整数为最大匹配次数
|
|
39
39
|
streamPriorities: []; // 废弃
|
|
40
|
+
titleKeywords?: string; // 禁止录制的标题关键字,英文逗号分开多个
|
|
40
41
|
sourcePriorities: []; // 按提供的源优先级去给CDN列表排序,并过滤掉不在优先级配置中的源,在未匹配到的情况下会优先使用TX的CDN,具体参数见 CDN 参数
|
|
41
42
|
formatPriorities?: string[]; // 支持,`flv`和`hls` 参数,默认为['flv','hls']
|
|
42
43
|
disableAutoCheck?: boolean; // 为 true 时 manager 将跳过自动检查
|
|
@@ -47,6 +48,7 @@ interface Options {
|
|
|
47
48
|
api?: "auto" | "mp" | "web"; // 默认为auto,在星秀区使用mp接口,其他使用web接口,你也可以强制指定
|
|
48
49
|
videoFormat?: "auto"; // 视频格式: "auto", "ts", "mkv" ,auto模式下, 分段使用 "ts",不分段使用 "mp4"
|
|
49
50
|
recorderType?: "auto" | "ffmpeg" | "mesio"; // 底层录制器,使用mesio时videoFormat参数无效
|
|
51
|
+
debugLevel?: `verbose` | "basic"; // verbose参数时,录制器会输出更加详细的log
|
|
50
52
|
}
|
|
51
53
|
```
|
|
52
54
|
|
package/lib/index.js
CHANGED
|
@@ -64,22 +64,47 @@ const ffmpegOutputOptions = [
|
|
|
64
64
|
"-min_frag_duration",
|
|
65
65
|
"10000000",
|
|
66
66
|
];
|
|
67
|
-
const ffmpegInputOptions = [
|
|
68
|
-
"-reconnect",
|
|
69
|
-
"1",
|
|
70
|
-
"-reconnect_streamed",
|
|
71
|
-
"1",
|
|
72
|
-
"-reconnect_delay_max",
|
|
73
|
-
"10",
|
|
74
|
-
"-rw_timeout",
|
|
75
|
-
"15000000",
|
|
76
|
-
];
|
|
67
|
+
const ffmpegInputOptions = ["-rw_timeout", "10000000", "-timeout", "10000000"];
|
|
77
68
|
const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isManualStart, }) {
|
|
78
|
-
|
|
69
|
+
// 如果已经在录制中,只在需要检查标题关键词时才获取最新信息
|
|
70
|
+
if (this.recordHandle != null) {
|
|
71
|
+
// 只有当设置了标题关键词时,并且不是手动启动的录制,才获取最新的直播间信息
|
|
72
|
+
if (utils.shouldCheckTitleKeywords(isManualStart, this.titleKeywords)) {
|
|
73
|
+
const now = Date.now();
|
|
74
|
+
// 每5分钟检查一次标题变化
|
|
75
|
+
const titleCheckInterval = 5 * 60 * 1000; // 5分钟
|
|
76
|
+
// 获取上次检查时间
|
|
77
|
+
const lastCheckTime = typeof this.extra.lastTitleCheckTime === "number" ? this.extra.lastTitleCheckTime : 0;
|
|
78
|
+
// 如果距离上次检查时间不足指定间隔,则跳过检查
|
|
79
|
+
if (now - lastCheckTime < titleCheckInterval) {
|
|
80
|
+
return this.recordHandle;
|
|
81
|
+
}
|
|
82
|
+
// 更新检查时间
|
|
83
|
+
this.extra.lastTitleCheckTime = now;
|
|
84
|
+
// 获取直播间信息
|
|
85
|
+
const liveInfo = await getInfo(this.channelId);
|
|
86
|
+
const { title } = liveInfo;
|
|
87
|
+
// 检查标题是否包含关键词
|
|
88
|
+
if (utils.hasBlockedTitleKeywords(title, this.titleKeywords)) {
|
|
89
|
+
this.state = "title-blocked";
|
|
90
|
+
this.emit("DebugLog", {
|
|
91
|
+
type: "common",
|
|
92
|
+
text: `检测到标题包含关键词,停止录制:直播间标题 "${title}" 包含关键词 "${this.titleKeywords}"`,
|
|
93
|
+
});
|
|
94
|
+
// 停止录制
|
|
95
|
+
await this.recordHandle.stop("直播间标题包含关键词");
|
|
96
|
+
// 返回 null,停止录制
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// 已经在录制中,直接返回
|
|
79
101
|
return this.recordHandle;
|
|
102
|
+
}
|
|
103
|
+
// 获取直播间信息
|
|
80
104
|
try {
|
|
81
105
|
const liveInfo = await getInfo(this.channelId);
|
|
82
106
|
this.liveInfo = liveInfo;
|
|
107
|
+
this.state = "idle";
|
|
83
108
|
}
|
|
84
109
|
catch (error) {
|
|
85
110
|
this.state = "check-error";
|
|
@@ -96,6 +121,18 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isMan
|
|
|
96
121
|
return null;
|
|
97
122
|
if (!living)
|
|
98
123
|
return null;
|
|
124
|
+
// 检查标题是否包含关键词,如果包含则不自动录制
|
|
125
|
+
// 手动开始录制时不检查标题关键词
|
|
126
|
+
if (utils.shouldCheckTitleKeywords(isManualStart, this.titleKeywords)) {
|
|
127
|
+
if (utils.hasBlockedTitleKeywords(title, this.titleKeywords)) {
|
|
128
|
+
this.state = "title-blocked";
|
|
129
|
+
this.emit("DebugLog", {
|
|
130
|
+
type: "common",
|
|
131
|
+
text: `跳过录制:直播间标题 "${title}" 包含关键词 "${this.titleKeywords}"`,
|
|
132
|
+
});
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
99
136
|
let res;
|
|
100
137
|
// TODO: 先不做什么错误处理,就简单包一下预期上会有错误的地方
|
|
101
138
|
try {
|
|
@@ -148,8 +185,7 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isMan
|
|
|
148
185
|
const reason = args[0] instanceof Error ? args[0].message : String(args[0]);
|
|
149
186
|
this.recordHandle?.stop(reason);
|
|
150
187
|
};
|
|
151
|
-
|
|
152
|
-
const recorder = createBaseRecorder(recorderType, {
|
|
188
|
+
const recorder = createBaseRecorder(this.recorderType, {
|
|
153
189
|
url: stream.url,
|
|
154
190
|
outputOptions: ffmpegOutputOptions,
|
|
155
191
|
inputOptions: ffmpegInputOptions,
|
|
@@ -157,6 +193,7 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isMan
|
|
|
157
193
|
getSavePath: (opts) => getSavePath({ owner, title: opts.title ?? title, startTime: opts.startTime }),
|
|
158
194
|
disableDanma: this.disableProvideCommentsWhenRecording,
|
|
159
195
|
videoFormat: this.videoFormat ?? "auto",
|
|
196
|
+
debugLevel: this.debugLevel ?? "none",
|
|
160
197
|
}, onEnd, async () => {
|
|
161
198
|
const info = await getInfo(this.channelId);
|
|
162
199
|
return info;
|
|
@@ -172,8 +209,8 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isMan
|
|
|
172
209
|
this.state = "idle";
|
|
173
210
|
throw err;
|
|
174
211
|
}
|
|
175
|
-
const handleVideoCreated = async ({ filename, title, cover }) => {
|
|
176
|
-
this.emit("videoFileCreated", { filename, cover });
|
|
212
|
+
const handleVideoCreated = async ({ filename, title, cover, rawFilename }) => {
|
|
213
|
+
this.emit("videoFileCreated", { filename, cover, rawFilename });
|
|
177
214
|
if (title && this?.liveInfo) {
|
|
178
215
|
this.liveInfo.title = title;
|
|
179
216
|
}
|
|
@@ -254,7 +291,7 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isMan
|
|
|
254
291
|
client.on("retry", (e) => {
|
|
255
292
|
this.emit("DebugLog", {
|
|
256
293
|
type: "common",
|
|
257
|
-
text:
|
|
294
|
+
text: `${this?.liveInfo?.owner}:${this.channelId} huya danmu retry: ${e.count}/${e.max}`,
|
|
258
295
|
});
|
|
259
296
|
});
|
|
260
297
|
client.start();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bililive-tools/huya-recorder",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "bililive-tools huya recorder implemention",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"mitt": "^3.0.1",
|
|
38
38
|
"lodash-es": "^4.17.21",
|
|
39
39
|
"axios": "^1.7.8",
|
|
40
|
-
"
|
|
41
|
-
"
|
|
40
|
+
"@bililive-tools/manager": "^1.8.0",
|
|
41
|
+
"huya-danma-listener": "0.1.3"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {},
|
|
44
44
|
"scripts": {
|