@bililive-tools/bilibili-recorder 1.3.0 → 1.4.1

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
@@ -51,6 +51,7 @@ interface Options {
51
51
  useM3U8Proxy?: boolean; // 是否使用m3u8代理,由于hls及fmp4存在一个小时超时时间,需自行实现代理避免
52
52
  m3u8ProxyUrl?: string; // 代理链接,文档待补充
53
53
  videoFormat?: "auto"; // 视频格式: "auto", "ts", "mkv" ,auto模式下, 分段使用 "ts",不分段使用 "mp4"
54
+ onlyAudio?: boolean; // 只录制音频,默认为否
54
55
  }
55
56
  ```
56
57
 
@@ -71,6 +71,7 @@ export declare function getPlayURL(roomId: number, opts?: {
71
71
  export declare function getRoomPlayInfo(roomIdOrShortId: number, opts?: {
72
72
  qn?: number;
73
73
  cookie?: string;
74
+ onlyAudio?: boolean;
74
75
  }): Promise<{
75
76
  uid: number;
76
77
  room_id: number;
@@ -85,6 +86,7 @@ export declare function getRoomPlayInfo(roomIdOrShortId: number, opts?: {
85
86
  };
86
87
  };
87
88
  }>;
89
+ export declare function getBuvidConf(): Promise<any>;
88
90
  export interface ProtocolInfo {
89
91
  protocol_name: "http_stream" | "http_hls";
90
92
  format: FormatInfo[];
@@ -61,6 +61,7 @@ export async function getRoomPlayInfo(roomIdOrShortId, opts = {}) {
61
61
  codec: "0,1",
62
62
  // 0 flv, 1 ts, 2 fmp4
63
63
  format: "0,1,2",
64
+ only_audio: opts.onlyAudio ? "1" : "0",
64
65
  },
65
66
  headers: {
66
67
  Cookie: opts.cookie,
@@ -69,3 +70,14 @@ export async function getRoomPlayInfo(roomIdOrShortId, opts = {}) {
69
70
  assert(res.data.code === 0, `Unexpected resp, code ${res.data.code}, msg ${res.data.message}`);
70
71
  return res.data.data;
71
72
  }
73
+ export async function getBuvidConf() {
74
+ const res = await fetch("https://api.bilibili.com/x/frontend/finger/spi", {
75
+ headers: {
76
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
77
+ },
78
+ });
79
+ if (!res.ok)
80
+ throw new Error(`Failed to get buvid conf: ${res.statusText}`);
81
+ const data = await res.json();
82
+ return data;
83
+ }
package/lib/danma.d.ts CHANGED
@@ -6,7 +6,7 @@ declare class DanmaClient extends EventEmitter {
6
6
  private uid;
7
7
  private retryCount;
8
8
  constructor(roomId: number, auth: string | undefined, uid: number | undefined);
9
- start(): void;
9
+ start(): Promise<void>;
10
10
  stop(): void;
11
11
  }
12
12
  export default DanmaClient;
package/lib/danma.js CHANGED
@@ -1,5 +1,46 @@
1
1
  import { EventEmitter } from "node:events";
2
2
  import { startListen } from "./blive-message-listener/index.js";
3
+ import { getBuvidConf } from "./bilibili_api.js";
4
+ // 全局缓存,一天过期时间 (24 * 60 * 60 * 1000 ms)
5
+ const CACHE_DURATION = 24 * 60 * 60 * 1000;
6
+ let buvidCache = null;
7
+ // 获取带缓存的 buvid 配置
8
+ async function getCachedBuvidConf() {
9
+ const now = Date.now();
10
+ // 检查缓存是否有效
11
+ if (buvidCache && now - buvidCache.timestamp < CACHE_DURATION) {
12
+ return buvidCache.data;
13
+ }
14
+ // 缓存失效或不存在,重新获取(带重试)
15
+ const info = await getBuvidConfWithRetry();
16
+ buvidCache = {
17
+ data: info,
18
+ timestamp: now,
19
+ };
20
+ return info;
21
+ }
22
+ // 带重试功能的 getBuvidConf
23
+ async function getBuvidConfWithRetry(maxRetries = 3, retryDelay = 1000) {
24
+ let lastError;
25
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
26
+ try {
27
+ const result = await getBuvidConf();
28
+ return result;
29
+ }
30
+ catch (error) {
31
+ lastError = error;
32
+ // 如果是最后一次尝试,直接抛出错误
33
+ if (attempt === maxRetries) {
34
+ throw error;
35
+ }
36
+ // 等待指定时间后重试,使用指数退避策略
37
+ const delay = retryDelay * Math.pow(2, attempt - 1);
38
+ await new Promise((resolve) => setTimeout(resolve, delay));
39
+ }
40
+ }
41
+ // 这里不应该到达,但为了类型安全
42
+ throw lastError;
43
+ }
3
44
  class DanmaClient extends EventEmitter {
4
45
  client = null;
5
46
  roomId;
@@ -12,7 +53,9 @@ class DanmaClient extends EventEmitter {
12
53
  this.auth = auth;
13
54
  this.uid = uid;
14
55
  }
15
- start() {
56
+ async start() {
57
+ const info = await getCachedBuvidConf();
58
+ const buvid3 = info.data.b_3;
16
59
  const handler = {
17
60
  onIncomeDanmu: (msg) => {
18
61
  let content = msg.body.content;
@@ -102,10 +145,22 @@ class DanmaClient extends EventEmitter {
102
145
  this.emit("RoomInfoChange", msg);
103
146
  },
104
147
  };
148
+ let lastAuth = "";
149
+ if (this.auth?.includes("buvid3")) {
150
+ lastAuth = this.auth;
151
+ }
152
+ else {
153
+ if (this.auth) {
154
+ lastAuth = `${this.auth}; buvid3=${buvid3}`;
155
+ }
156
+ else {
157
+ lastAuth = `buvid3=${buvid3}`;
158
+ }
159
+ }
105
160
  this.client = startListen(this.roomId, handler, {
106
161
  ws: {
107
162
  headers: {
108
- Cookie: this.auth ?? "",
163
+ Cookie: lastAuth,
109
164
  },
110
165
  uid: this.uid ?? 0,
111
166
  },
package/lib/index.js CHANGED
@@ -141,6 +141,7 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, isManualStart, b
141
141
  strictQuality: strictQuality,
142
142
  formatName: this.formatName,
143
143
  codecName: this.codecName,
144
+ onlyAudio: this.onlyAudio,
144
145
  });
145
146
  }
146
147
  catch (err) {
@@ -233,7 +234,7 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, isManualStart, b
233
234
  room_id: String(roomId),
234
235
  platform: provider?.id,
235
236
  liveStartTimestamp: liveInfo.startTime?.getTime(),
236
- recordStopTimestamp: Date.now(),
237
+ // recordStopTimestamp: Date.now(),
237
238
  title: title,
238
239
  user_name: owner,
239
240
  });
package/lib/stream.d.ts CHANGED
@@ -30,6 +30,7 @@ export declare function getStream(opts: Pick<Recorder, "channelId" | "quality">
30
30
  strictQuality?: boolean;
31
31
  formatName: RecorderCreateOpts["formatName"];
32
32
  codecName: RecorderCreateOpts["codecName"];
33
+ onlyAudio?: boolean;
33
34
  }): Promise<{
34
35
  currentStream: {
35
36
  name: string;
package/lib/stream.js CHANGED
@@ -211,6 +211,7 @@ export async function getStream(opts) {
211
211
  cookie: opts.cookie,
212
212
  formatName: opts.formatName,
213
213
  codecName: opts.codecName,
214
+ onlyAudio: opts.onlyAudio,
214
215
  });
215
216
  // console.log(JSON.stringify(liveInfo, null, 2));
216
217
  if (liveInfo.current_qn !== qn && opts.strictQuality) {
@@ -224,6 +225,7 @@ export async function getStream(opts) {
224
225
  cookie: opts.cookie,
225
226
  formatName: opts.formatName,
226
227
  codecName: opts.codecName,
228
+ onlyAudio: opts.onlyAudio,
227
229
  });
228
230
  }
229
231
  let expectSource = liveInfo.sources[0];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bililive-tools/bilibili-recorder",
3
- "version": "1.3.0",
3
+ "version": "1.4.1",
4
4
  "description": "bililive-tools bilibili recorder implemention",
5
5
  "main": "./lib/index.js",
6
6
  "type": "module",