@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 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
- | 杜比 | 30000 |
67
- | 4K | 20000 |
68
- | 原画 | 10000 |
69
- | 蓝光 | 400 |
70
- | 超清 | 250 |
71
- | 高清 | 150 |
72
- | 流畅 | 80 |
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
- "15000000",
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 (!isManualStart &&
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
- let recorderType = this.recorderType === "mesio" ? "mesio" : "ffmpeg";
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({ owner, title: opts.title ?? title, startTime: opts.startTime }),
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 (!isManualStart &&
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 { BiliQualities, utils } from "@bililive-tools/manager";
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
@@ -82,7 +82,7 @@ export function createInvalidStreamChecker(count = 10) {
82
82
  };
83
83
  }
84
84
  export function hasKeyword(title, titleKeywords) {
85
- const keywords = titleKeywords
85
+ const keywords = (titleKeywords ?? "")
86
86
  .split(",")
87
87
  .map((k) => k.trim())
88
88
  .filter((k) => k);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bililive-tools/bilibili-recorder",
3
- "version": "1.7.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.6.1"
42
+ "@bililive-tools/manager": "^1.9.0"
43
43
  },
44
44
  "scripts": {
45
45
  "build": "tsc",