@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 +1 -0
- package/lib/bilibili_api.d.ts +2 -0
- package/lib/bilibili_api.js +12 -0
- package/lib/danma.d.ts +1 -1
- package/lib/danma.js +57 -2
- package/lib/index.js +2 -1
- package/lib/stream.d.ts +1 -0
- package/lib/stream.js +2 -0
- package/package.json +1 -1
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
|
|
package/lib/bilibili_api.d.ts
CHANGED
|
@@ -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[];
|
package/lib/bilibili_api.js
CHANGED
|
@@ -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
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:
|
|
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];
|