@bililive-tools/bilibili-recorder 1.18.1 → 1.19.2

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
@@ -56,6 +56,7 @@ interface Options {
56
56
  recorderType?: "auto" | "ffmpeg" | "mesio" | "bililive"; // 底层录制器,使用mesio和bililive时videoFormat参数无效
57
57
  debugLevel?: `verbose` | "basic"; // verbose参数时,录制器会输出更加详细的log
58
58
  customHost?: string; // 自定义host来替换直播流的host
59
+ segmentOnTitleChange?: boolean; // 标题变更时分段,默认为false;bililive引擎无损切段,其他引擎中断录制
59
60
  }
60
61
  ```
61
62
 
@@ -81,15 +82,15 @@ B站录制高画质需要登录,在无法匹配到画质时,会优先使用
81
82
 
82
83
  能否录制flv下的hevc和你的ffmpeg版本有关
83
84
 
84
- | 解释 | 值 |
85
- | ----------- | --------- |
86
- | 等于flv | auto |
87
- | 优先使用flv | flv |
88
- | 优先使用hls | hls |
89
- | 只使用fmp4 | fmp4 |
90
- | 只使用flv | flv_only |
91
- | 只使用hls | hls_only |
92
- | 只使用fmp4 | fmp4_only |
85
+ | 解释 | 值 |
86
+ | ------------ | --------- |
87
+ | 等于flv | auto |
88
+ | 优先使用flv | flv |
89
+ | 优先使用hls | hls |
90
+ | 优先使用fmp4 | fmp4 |
91
+ | 只使用flv | flv_only |
92
+ | 只使用hls | hls_only |
93
+ | 只使用fmp4 | fmp4_only |
93
94
 
94
95
  ### CodecName
95
96
 
@@ -100,8 +101,10 @@ B站录制高画质需要登录,在无法匹配到画质时,会优先使用
100
101
  | 等于avc | auto |
101
102
  | 优先使用avc | avc |
102
103
  | 优先使用hevc | hevc |
104
+ | 优先使用av1 | av1 |
103
105
  | 只使用avc | avc_only |
104
106
  | 只使用hevc | hevc_only |
107
+ | 只使用av1 | av1_only |
105
108
 
106
109
  ## 直播间ID解析
107
110
 
@@ -60,8 +60,8 @@ export async function getRoomPlayInfo(roomIdOrShortId, opts = {}) {
60
60
  qn: opts.qn,
61
61
  // 0 http_stream, 1 http_hls
62
62
  protocol: "0,1",
63
- // 0 avc, 1 hevc
64
- codec: "0,1",
63
+ // 0 avc, 1 hevc,2 av1
64
+ codec: "0,1,2",
65
65
  // 0 flv, 1 ts, 2 fmp4
66
66
  format: "0,1,2",
67
67
  only_audio: opts.onlyAudio ? "1" : "0",
package/lib/index.js CHANGED
@@ -40,12 +40,16 @@ function createRecorder(opts) {
40
40
  ...info,
41
41
  };
42
42
  },
43
- async getStream() {
43
+ async getStream(opts = {}) {
44
44
  const res = await getStream({
45
45
  channelId: this.channelId,
46
46
  quality: this.quality,
47
- formatName: this.formatName,
47
+ formatName: opts.formatName || this.formatName,
48
48
  codecName: this.codecName,
49
+ cookie: this.auth,
50
+ strictQuality: false,
51
+ onlyAudio: this.onlyAudio,
52
+ customHost: this.customHost,
49
53
  });
50
54
  return res.currentStream;
51
55
  },
@@ -261,9 +265,11 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, isManualStart, b
261
265
  uid: Number(this.uid),
262
266
  useServerTimestamp: this.useServerTimestamp,
263
267
  });
264
- // 开启了禁止提供弹幕功能,并且也没有设置标题关键词,才完全禁止连接弹幕服务器,否则都连接弹幕服务器,前者不处理弹幕消息,后者根据标题关键词来判断是否停止录制
268
+ // 禁用弹幕且不需要监听标题时,才完全禁止连接弹幕服务器
265
269
  const enableDanmaListen = !this.disableProvideCommentsWhenRecording ||
266
- utils.shouldCheckTitleKeywords(isManualStart, this.titleKeywords);
270
+ utils.shouldCheckTitleKeywords(isManualStart, this.titleKeywords) ||
271
+ this.segmentOnTitleChange;
272
+ let currentTitle = title;
267
273
  danmaClient.on("Message", (msg) => {
268
274
  if (this.disableProvideCommentsWhenRecording)
269
275
  return;
@@ -277,9 +283,9 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, isManualStart, b
277
283
  this.emit("Message", msg);
278
284
  extraDataController.addMessage(msg);
279
285
  });
280
- danmaClient.on("onRoomInfoChange", (msg) => {
286
+ danmaClient.on("RoomInfoChange", (msg) => {
287
+ const title = msg?.body?.title?.trim() ?? "";
281
288
  if (utils.shouldCheckTitleKeywords(isManualStart, this.titleKeywords)) {
282
- const title = msg?.body?.title ?? "";
283
289
  const hasTitleKeyword = utils.hasBlockedTitleKeywords(title, this.titleKeywords);
284
290
  if (hasTitleKeyword) {
285
291
  this.emit("stateChange", {
@@ -292,8 +298,35 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, isManualStart, b
292
298
  });
293
299
  // 停止录制
294
300
  this.recordHandle && this.recordHandle.stop("直播间标题包含关键词");
301
+ return;
295
302
  }
296
303
  }
304
+ if (!this.segmentOnTitleChange || !title || title === currentTitle)
305
+ return;
306
+ const previousTitle = currentTitle;
307
+ currentTitle = title;
308
+ if (this.liveInfo) {
309
+ this.liveInfo.title = title;
310
+ }
311
+ if (downloader.type !== "bililive") {
312
+ this.emit("DebugLog", {
313
+ type: "common",
314
+ text: `检测到标题由 "${previousTitle}" 变更为 "${title}",中断录制以进行分段`,
315
+ });
316
+ this.appendTimeline({
317
+ text: `检测到标题由 "${previousTitle}" 变更为 "${title}",中断录制以进行分段`,
318
+ });
319
+ this.recordHandle && this.recordHandle.stop("标题变更分段");
320
+ return;
321
+ }
322
+ this.emit("DebugLog", {
323
+ type: "common",
324
+ text: `检测到标题由 "${previousTitle}" 变更为 "${title}",开始分段`,
325
+ });
326
+ this.appendTimeline({
327
+ text: `检测到标题由 "${previousTitle}" 变更为 "${title}",开始分段`,
328
+ });
329
+ downloader.cut();
297
330
  });
298
331
  danmaClient.on("open", () => {
299
332
  this.appendTimeline({
package/lib/stream.d.ts CHANGED
@@ -49,11 +49,11 @@ export declare function getStream(opts: Pick<Recorder, "channelId" | "quality">
49
49
  onlyAudio?: boolean;
50
50
  customHost?: string;
51
51
  }): Promise<{
52
- currentStream: {
53
- name: string;
54
- source: string;
55
- url: string;
56
- };
52
+ codec_name: string | "avc" | "hevc";
53
+ accept_qn: number[];
54
+ base_url: string;
55
+ current_qn: number;
56
+ url_info: Omit<SourceProfile, "name">[];
57
57
  streams: StreamProfile[];
58
58
  sources: SourceProfile[];
59
59
  name: string;
@@ -63,9 +63,9 @@ export declare function getStream(opts: Pick<Recorder, "channelId" | "quality">
63
63
  codec_name: string;
64
64
  qn: number;
65
65
  };
66
- codec_name: string | "avc" | "hevc";
67
- accept_qn: number[];
68
- base_url: string;
69
- current_qn: number;
70
- url_info: Omit<SourceProfile, "name">[];
66
+ currentStream: {
67
+ name: string;
68
+ source: string;
69
+ url: string;
70
+ };
71
71
  }>;
package/lib/stream.js CHANGED
@@ -177,6 +177,24 @@ async function getLiveInfo(roomIdOrShortId, opts) {
177
177
  codec_name: "hevc",
178
178
  sort: 4,
179
179
  },
180
+ {
181
+ protocol_name: "http_stream",
182
+ format_name: "flv",
183
+ codec_name: "av1",
184
+ sort: 3,
185
+ },
186
+ {
187
+ protocol_name: "http_hls",
188
+ format_name: "fmp4",
189
+ codec_name: "av1",
190
+ sort: 2,
191
+ },
192
+ {
193
+ protocol_name: "http_hls",
194
+ format_name: "ts",
195
+ codec_name: "av1",
196
+ sort: 1,
197
+ },
180
198
  ];
181
199
  // 处理formatName
182
200
  if (opts.formatName === "flv_only") {
@@ -221,7 +239,17 @@ async function getLiveInfo(roomIdOrShortId, opts) {
221
239
  });
222
240
  conditons = conditons.sort((a, b) => b.sort - a.sort);
223
241
  }
224
- // console.log("conditons", opts.codecName, conditons);
242
+ else if (opts.codecName === "av1") {
243
+ conditons.forEach((item) => {
244
+ if (item.codec_name === "av1") {
245
+ item.sort += 100;
246
+ }
247
+ });
248
+ conditons = conditons.sort((a, b) => b.sort - a.sort);
249
+ }
250
+ else if (opts.codecName === "av1_only") {
251
+ conditons = conditons.filter((item) => item.codec_name === "av1");
252
+ }
225
253
  let streamInfo;
226
254
  let streamOptions;
227
255
  // console.log("conditons", JSON.stringify(res.playurl_info.playurl.stream, null, 2));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bililive-tools/bilibili-recorder",
3
- "version": "1.18.1",
3
+ "version": "1.19.2",
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.15.0",
42
- "@bililive-tools/manager": "^1.17.0"
42
+ "@bililive-tools/manager": "^1.17.2"
43
43
  },
44
44
  "scripts": {
45
45
  "build": "tsc",