@bililive-tools/bilibili-recorder 1.7.1 → 1.9.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 +12 -10
- package/lib/index.js +29 -31
- package/lib/stream.js +2 -1
- package/lib/utils.d.ts +1 -1
- package/lib/utils.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -53,7 +53,8 @@ interface Options {
|
|
|
53
53
|
m3u8ProxyUrl?: string; // 代理链接,文档待补充
|
|
54
54
|
videoFormat?: "auto"; // 视频格式: "auto", "ts", "mkv" ,auto模式下, 分段使用 "ts",不分段使用 "mp4"
|
|
55
55
|
onlyAudio?: boolean; // 只录制音频,默认为否
|
|
56
|
-
recorderType?: "auto" | "ffmpeg" | "mesio"; // 底层录制器,使用mesio时videoFormat参数无效
|
|
56
|
+
recorderType?: "auto" | "ffmpeg" | "mesio" | "bililive"; // 底层录制器,使用mesio和bililive时videoFormat参数无效
|
|
57
|
+
debugLevel?: `verbose` | "basic"; // verbose参数时,录制器会输出更加详细的log
|
|
57
58
|
}
|
|
58
59
|
```
|
|
59
60
|
|
|
@@ -61,15 +62,16 @@ interface Options {
|
|
|
61
62
|
|
|
62
63
|
B站录制高画质需要登录,在无法匹配到画质时,会优先使用高画质
|
|
63
64
|
|
|
64
|
-
| 画质
|
|
65
|
-
|
|
|
66
|
-
| 杜比
|
|
67
|
-
| 4K
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
65
|
+
| 画质 | 值 |
|
|
66
|
+
| -------- | ----- |
|
|
67
|
+
| 杜比 | 30000 |
|
|
68
|
+
| 4K | 20000 |
|
|
69
|
+
| 原画真彩 | 25000 |
|
|
70
|
+
| 原画 | 10000 |
|
|
71
|
+
| 蓝光 | 400 |
|
|
72
|
+
| 超清 | 250 |
|
|
73
|
+
| 高清 | 150 |
|
|
74
|
+
| 流畅 | 80 |
|
|
73
75
|
|
|
74
76
|
### formatName
|
|
75
77
|
|
package/lib/index.js
CHANGED
|
@@ -63,23 +63,12 @@ function createRecorder(opts) {
|
|
|
63
63
|
});
|
|
64
64
|
return recorderWithSupportUpdatedEvent;
|
|
65
65
|
}
|
|
66
|
-
const ffmpegOutputOptions = [
|
|
67
|
-
"-c",
|
|
68
|
-
"copy",
|
|
69
|
-
"-movflags",
|
|
70
|
-
"faststart+frag_keyframe+empty_moov",
|
|
71
|
-
"-min_frag_duration",
|
|
72
|
-
"10000000",
|
|
73
|
-
];
|
|
66
|
+
const ffmpegOutputOptions = [];
|
|
74
67
|
const ffmpegInputOptions = [
|
|
75
|
-
"-reconnect",
|
|
76
|
-
"1",
|
|
77
|
-
"-reconnect_streamed",
|
|
78
|
-
"1",
|
|
79
|
-
"-reconnect_delay_max",
|
|
80
|
-
"10",
|
|
81
68
|
"-rw_timeout",
|
|
82
|
-
"
|
|
69
|
+
"10000000",
|
|
70
|
+
"-timeout",
|
|
71
|
+
"10000000",
|
|
83
72
|
"-headers",
|
|
84
73
|
"Referer:https://live.bilibili.com/",
|
|
85
74
|
];
|
|
@@ -95,6 +84,7 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, isManualStart, b
|
|
|
95
84
|
avatar: "",
|
|
96
85
|
cover: "",
|
|
97
86
|
liveId: liveId,
|
|
87
|
+
startTime: new Date(),
|
|
98
88
|
};
|
|
99
89
|
this.state = "idle";
|
|
100
90
|
}
|
|
@@ -114,12 +104,10 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, isManualStart, b
|
|
|
114
104
|
return null;
|
|
115
105
|
// 检查标题是否包含关键词,如果包含则不自动录制
|
|
116
106
|
// 手动开始录制时不检查标题关键词
|
|
117
|
-
if (
|
|
118
|
-
this.titleKeywords &&
|
|
119
|
-
typeof this.titleKeywords === "string" &&
|
|
120
|
-
this.titleKeywords.trim()) {
|
|
107
|
+
if (utils.shouldCheckTitleKeywords(isManualStart, this.titleKeywords)) {
|
|
121
108
|
const hasTitleKeyword = hasKeyword(this.liveInfo.title, this.titleKeywords);
|
|
122
109
|
if (hasTitleKeyword) {
|
|
110
|
+
this.state = "title-blocked";
|
|
123
111
|
this.emit("DebugLog", {
|
|
124
112
|
type: "common",
|
|
125
113
|
text: `跳过录制:直播间标题 "${this.liveInfo.title}" 包含关键词 "${this.titleKeywords}"`,
|
|
@@ -128,10 +116,9 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, isManualStart, b
|
|
|
128
116
|
}
|
|
129
117
|
}
|
|
130
118
|
const liveInfo = await getInfo(this.channelId);
|
|
131
|
-
const { owner, title, roomId } = liveInfo;
|
|
119
|
+
const { owner, title, roomId, startTime } = liveInfo;
|
|
132
120
|
this.liveInfo = liveInfo;
|
|
133
121
|
let res;
|
|
134
|
-
// TODO: 先不做什么错误处理,就简单包一下预期上会有错误的地方
|
|
135
122
|
try {
|
|
136
123
|
let strictQuality = false;
|
|
137
124
|
if (this.qualityRetry > 0) {
|
|
@@ -207,17 +194,26 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, isManualStart, b
|
|
|
207
194
|
const reason = args[0] instanceof Error ? args[0].message : String(args[0]);
|
|
208
195
|
this.recordHandle?.stop(reason);
|
|
209
196
|
};
|
|
210
|
-
|
|
211
|
-
const recorder = createBaseRecorder(recorderType, {
|
|
197
|
+
const recordStartTime = new Date();
|
|
198
|
+
const recorder = createBaseRecorder(this.recorderType, {
|
|
212
199
|
url: url,
|
|
213
200
|
outputOptions: ffmpegOutputOptions,
|
|
214
201
|
inputOptions: ffmpegInputOptions,
|
|
215
|
-
mesioOptions: ["-H", "Referer:https://live.bilibili.com/"],
|
|
216
202
|
segment: this.segment ?? 0,
|
|
217
|
-
getSavePath: (opts) => getSavePath({
|
|
203
|
+
getSavePath: (opts) => getSavePath({
|
|
204
|
+
owner,
|
|
205
|
+
title: opts.title ?? title,
|
|
206
|
+
startTime: opts.startTime,
|
|
207
|
+
liveStartTime: startTime,
|
|
208
|
+
recordStartTime,
|
|
209
|
+
}),
|
|
218
210
|
formatName: streamOptions.format_name,
|
|
219
211
|
disableDanma: this.disableProvideCommentsWhenRecording,
|
|
220
212
|
videoFormat: this.videoFormat,
|
|
213
|
+
debugLevel: this.debugLevel ?? "none",
|
|
214
|
+
headers: {
|
|
215
|
+
Referer: "https://live.bilibili.com/",
|
|
216
|
+
},
|
|
221
217
|
}, onEnd, async () => {
|
|
222
218
|
const info = await getInfo(this.channelId);
|
|
223
219
|
return info;
|
|
@@ -225,6 +221,9 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, isManualStart, b
|
|
|
225
221
|
const savePath = getSavePath({
|
|
226
222
|
owner,
|
|
227
223
|
title,
|
|
224
|
+
startTime: Date.now(),
|
|
225
|
+
liveStartTime: startTime,
|
|
226
|
+
recordStartTime,
|
|
228
227
|
});
|
|
229
228
|
try {
|
|
230
229
|
ensureFolderExist(savePath);
|
|
@@ -233,8 +232,8 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, isManualStart, b
|
|
|
233
232
|
this.state = "idle";
|
|
234
233
|
throw err;
|
|
235
234
|
}
|
|
236
|
-
const handleVideoCreated = async ({ filename, title, cover }) => {
|
|
237
|
-
this.emit("videoFileCreated", { filename, cover });
|
|
235
|
+
const handleVideoCreated = async ({ filename, title, cover, rawFilename }) => {
|
|
236
|
+
this.emit("videoFileCreated", { filename, cover, rawFilename });
|
|
238
237
|
if (title && this?.liveInfo) {
|
|
239
238
|
this.liveInfo.title = title;
|
|
240
239
|
}
|
|
@@ -282,13 +281,11 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, isManualStart, b
|
|
|
282
281
|
extraDataController.addMessage(msg);
|
|
283
282
|
});
|
|
284
283
|
danmaClient.on("onRoomInfoChange", (msg) => {
|
|
285
|
-
if (
|
|
286
|
-
this.titleKeywords &&
|
|
287
|
-
typeof this.titleKeywords === "string" &&
|
|
288
|
-
this.titleKeywords.trim()) {
|
|
284
|
+
if (utils.shouldCheckTitleKeywords(isManualStart, this.titleKeywords)) {
|
|
289
285
|
const title = msg?.body?.title ?? "";
|
|
290
286
|
const hasTitleKeyword = hasKeyword(title, this.titleKeywords);
|
|
291
287
|
if (hasTitleKeyword) {
|
|
288
|
+
this.state = "title-blocked";
|
|
292
289
|
this.emit("DebugLog", {
|
|
293
290
|
type: "common",
|
|
294
291
|
text: `检测到标题包含关键词,停止录制:直播间标题 "${title}" 包含关键词 "${this.titleKeywords}"`,
|
|
@@ -339,6 +336,7 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, isManualStart, b
|
|
|
339
336
|
id: genRecordUUID(),
|
|
340
337
|
stream: stream.name,
|
|
341
338
|
source: stream.source,
|
|
339
|
+
recorderType: recorder.type,
|
|
342
340
|
url: stream.url,
|
|
343
341
|
ffmpegArgs,
|
|
344
342
|
savePath: savePath,
|
package/lib/stream.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { utils } from "@bililive-tools/manager";
|
|
2
2
|
import { getRoomInit, getRoomPlayInfo, getStatusInfoByUIDs, getRoomBaseInfo, } from "./bilibili_api.js";
|
|
3
3
|
import { assert } from "./utils.js";
|
|
4
|
+
const BiliQualities = [30000, 20000, 25000, 10000, 400, 250, 150, 80];
|
|
4
5
|
export async function getStrictStream(roomId, options) {
|
|
5
6
|
const res = await getRoomPlayInfo(roomId, options);
|
|
6
7
|
const streamInfo = res.playurl_info.playurl.stream
|
package/lib/utils.d.ts
CHANGED
|
@@ -21,4 +21,4 @@ export declare function assertStringType(data: unknown, msg?: string): asserts d
|
|
|
21
21
|
export declare function assertNumberType(data: unknown, msg?: string): asserts data is number;
|
|
22
22
|
export declare function assertObjectType(data: unknown, msg?: string): asserts data is object;
|
|
23
23
|
export declare function createInvalidStreamChecker(count?: number): (ffmpegLogLine: string) => boolean;
|
|
24
|
-
export declare function hasKeyword(title: string, titleKeywords: string): boolean;
|
|
24
|
+
export declare function hasKeyword(title: string, titleKeywords: string | undefined): boolean;
|
package/lib/utils.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bililive-tools/bilibili-recorder",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "bililive-tools bilibili recorder implemention",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"tiny-bilibili-ws": "^1.0.2",
|
|
40
40
|
"lodash-es": "^4.17.21",
|
|
41
41
|
"axios": "^1.7.8",
|
|
42
|
-
"@bililive-tools/manager": "^1.
|
|
42
|
+
"@bililive-tools/manager": "^1.9.0"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "tsc",
|