@floating-live/bilibili 0.15.3 → 0.16.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/room.js CHANGED
@@ -1,230 +1,315 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RoomBilibili = void 0;
4
- const floating_live_1 = require("floating-live");
5
- const bilibili_live_ws_1 = require("bilibili-live-ws");
6
- const parser_1 = require("./parser");
7
- const utils_1 = require("./utils");
8
- const floating_live_2 = require("floating-live");
9
- class RoomBilibili extends floating_live_1.LiveRoom {
10
- constructor(
11
- /** 房间id */
12
- id, options) {
13
- super();
14
- /** 平台id */
15
- this.platform = "bilibili";
16
- /** 直播间room_id */
17
- this.roomId = 0;
18
- /** 直播间短房号 */
19
- this.shortId = 0;
20
- /** 房间信息 */
21
- this.detail = {
22
- /** 直播标题 */
23
- title: "",
24
- /** 分区 */
25
- area: [],
26
- /** 封面url */
27
- cover: "",
28
- };
29
- this.stats = {
30
- /** 观看数 */
31
- view: 0,
32
- /** 点赞数 */
33
- like: 0,
34
- };
35
- /** 房间是否可用 */
36
- this.available = false;
37
- /** 是否连接上房间 */
38
- this.connected = false;
39
- /** 主播信息 */
40
- this.anchor = { name: "", id: 0 };
41
- /** 直播间弹幕api模块 */
42
- this.client = null;
43
- /** 连接凭证 */
44
- this.tokens = {
45
- userId: 0,
46
- buvid: "",
47
- token: "",
48
- };
49
- /** 是否处于正在打开的状态 */
50
- this.opening = false;
51
- /** 是否处于重连状态 */
52
- this.reconnecting = false;
53
- this.id = id;
54
- this.connectInterval = options?.connectInterval || 100;
55
- this.init(options);
56
- }
57
- /** 初始化 */
58
- async init(options = {}) {
59
- const { info, tokens, credentials, open } = options;
60
- if (open)
61
- this.opening = true;
62
- if (info) {
63
- this.updateInfo(info);
64
- }
65
- else {
66
- await this.getInfo();
67
- }
68
- if (tokens?.userId && tokens?.buvid && tokens?.token) {
69
- this.setTokens(tokens);
70
- }
71
- else {
72
- await this.setCredentials(credentials || "", tokens);
73
- }
74
- if (open) {
75
- this.opening = false;
76
- this.openWS();
77
- }
78
- this.emit("init");
79
- }
80
- /** 获取房间信息 */
81
- async getInfo() {
82
- await this.fetchInfo()
83
- .then((info) => {
84
- this.roomId = info.id;
85
- this.updateInfo(info);
86
- })
87
- .catch((error) => {
88
- this.emit("info_error", error);
89
- });
90
- }
91
- /** 请求房间信息 */
92
- async fetchInfo() {
93
- const rawInfo = await (0, utils_1.getInfoByRoom)(this.id);
94
- return (0, parser_1.parseInfo)(rawInfo);
95
- }
96
- /** 更新直播间信息 */
97
- updateInfo({ roomId, shortId, status, timestamp, detail, stats, anchor, liveId, available, }) {
98
- this.roomId = roomId;
99
- this.shortId = shortId;
100
- this.status = status;
101
- this.timestamp = timestamp;
102
- this.detail = detail;
103
- this.stats = stats;
104
- this.anchor = anchor;
105
- this.liveId = liveId;
106
- this.available = available;
107
- this.emit("info", this.info);
108
- }
109
- /** 开启直播间监听 */
110
- async open() {
111
- // 如果直播间监听已打开或处于正在打开状态,则返回
112
- if (this.opened || this.opening)
113
- return;
114
- this.opening = true;
115
- await this.getInfo();
116
- this.opening = false;
117
- this.openWS();
118
- }
119
- openWS() {
120
- // 如果直播间不可用,则返回
121
- if (!this.available)
122
- return;
123
- this.opened = true;
124
- this.initClient();
125
- this.emit("open");
126
- }
127
- /** 设置登录凭据 */
128
- async setCredentials(credentials, userToken) {
129
- this.setTokens(await this.fetchTokens(credentials, userToken));
130
- }
131
- /** 请求连接tokens */
132
- async fetchTokens(credentials = "", userToken) {
133
- const cookie = (0, utils_1.parseCookieString)(credentials);
134
- const buvid = userToken?.buvid ||
135
- cookie["buvid3"] ||
136
- (await (0, utils_1.getBuvid)(credentials)) ||
137
- "";
138
- const userId = userToken?.userId ||
139
- parseInt(cookie["DedeUserID"]) ||
140
- (await (0, utils_1.getLoginUid)(credentials));
141
- const token = await (0, utils_1.getToken)(this.roomId, credentials);
142
- return {
143
- userId,
144
- buvid,
145
- token,
146
- };
147
- }
148
- /** 设置直播连接tokens */
149
- setTokens(tokens) {
150
- this.tokens = tokens;
151
- if (this.opened) {
152
- // 如果已打开,则重连
153
- this.reconnect();
154
- }
155
- }
156
- /** 重连 */
157
- reconnect() {
158
- if (this.reconnecting)
159
- return;
160
- this.reconnecting = true;
161
- const lastClient = this.client;
162
- if (!lastClient) {
163
- this.reconnecting = false;
164
- return;
165
- }
166
- this.initClient();
167
- this.client?.once("open", () => {
168
- lastClient.removeAllListeners();
169
- lastClient.close();
170
- this.reconnecting = false;
171
- });
172
- }
173
- emitConnention(status) {
174
- this.connection = status;
175
- switch (status) {
176
- case floating_live_2.ConnectStatus.connecting:
177
- this.emit("connecting");
178
- break;
179
- case floating_live_2.ConnectStatus.connected:
180
- this.emit("connected");
181
- break;
182
- case floating_live_2.ConnectStatus.entered:
183
- this.emit("enter");
184
- break;
185
- case floating_live_2.ConnectStatus.disconnected:
186
- this.emit("disconnect");
187
- break;
188
- }
189
- }
190
- /** 创建直播弹幕客户端 */
191
- initClient() {
192
- this.emitConnention(floating_live_2.ConnectStatus.connecting);
193
- // 与Websocket服务器连接
194
- const client = new bilibili_live_ws_1.KeepLiveWS(this.roomId || this.id, {
195
- uid: this.tokens.userId,
196
- buvid: this.tokens.buvid,
197
- key: this.tokens.token,
198
- });
199
- client.interval = this.connectInterval;
200
- client.on("open", () => {
201
- // 连接到服务器
202
- this.opened && this.emitConnention(floating_live_2.ConnectStatus.connected);
203
- });
204
- client.on("close", () => {
205
- // 断开连接
206
- this.opened && this.emitConnention(floating_live_2.ConnectStatus.disconnected);
207
- });
208
- client.on("live", () => {
209
- // 连接到直播间
210
- this.emitConnention(floating_live_2.ConnectStatus.entered);
211
- });
212
- client.on("msg", (data) => {
213
- const m = (0, parser_1.parseMessage)(data, this);
214
- m && this.emitMessage(m);
215
- this.emitRaw(data);
216
- });
217
- this.client = client;
218
- }
219
- /** 关闭直播间监听 */
220
- close() {
221
- if (!this.opened)
222
- return;
223
- this.opened = false;
224
- this.client?.close();
225
- this.emitConnention(floating_live_2.ConnectStatus.off);
226
- this.emit("close");
227
- }
228
- }
229
- exports.RoomBilibili = RoomBilibili;
230
- exports.default = RoomBilibili;
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _RoomBilibili_instances, _RoomBilibili_tokens, _RoomBilibili_cookies, _RoomBilibili_getFetchOptions;
13
+ import { LiveWS,
14
+ // requestGetInfoByRoom,
15
+ getLoginUid,
16
+ // getLiveConfig,
17
+ Cookies, customFetch, requestBuvidCookie, } from "bilibili-live-danmaku";
18
+ import { parseGetInfoByRoom, parseMessage, parseRoomBaseInfo } from "./parser";
19
+ import { getRoomBaseInfo } from "./utils";
20
+ import { LiveConnectionStatus, LiveRoom, } from "floating-live";
21
+ const symbolAbortController = Symbol("abortController");
22
+ export class RoomBilibili extends LiveRoom {
23
+ constructor(
24
+ /** 房间id */
25
+ id, options = {}) {
26
+ super();
27
+ _RoomBilibili_instances.add(this);
28
+ /** 平台id */
29
+ this.platform = "bilibili";
30
+ /** 直播间room_id */
31
+ this.roomId = 0;
32
+ /** 直播间短房号 */
33
+ this.shortId = 0;
34
+ /** 房间信息 */
35
+ this.detail = {
36
+ /** 直播标题 */
37
+ title: "",
38
+ /** 分区 */
39
+ area: [],
40
+ /** 封面url */
41
+ cover: "",
42
+ };
43
+ this.stats = {
44
+ /** 观看数 */
45
+ view: 0,
46
+ /** 点赞数 */
47
+ like: 0,
48
+ };
49
+ /** 是否连接上房间 */
50
+ this.connected = false;
51
+ /** 主播信息 */
52
+ this.anchor = { name: "", id: 0 };
53
+ /** 直播间弹幕api客户端 */
54
+ this.client = null;
55
+ /** 连接凭证 */
56
+ _RoomBilibili_tokens.set(this, void 0);
57
+ /** 用户cookie */
58
+ _RoomBilibili_cookies.set(this, new Cookies());
59
+ /** 是否处于重连状态 */
60
+ this.reconnecting = false;
61
+ /** 最后一次连接时间 */
62
+ this.lastConnectionTime = 0;
63
+ /** 自定义fetch函数 */
64
+ this.fetch = fetch;
65
+ this.id = id;
66
+ this.connectInterval = options.connectInterval ?? 500;
67
+ this.connectTimeout = options.connectTimeout ?? 45000;
68
+ this.autoReconnect = options.autoReconnect ?? false;
69
+ __classPrivateFieldSet(this, _RoomBilibili_tokens, options.tokens, "f");
70
+ __classPrivateFieldSet(this, _RoomBilibili_cookies, new Cookies(options.credentials), "f");
71
+ options.data && this.setData(options.data);
72
+ const init = options.init ?? true;
73
+ if (init) {
74
+ this.whenInit = this.init(options).then(() => this);
75
+ }
76
+ else {
77
+ this.whenInit = new Promise((res) => res(this));
78
+ }
79
+ }
80
+ /** 初始化
81
+ * 自动获取并设置 `credentials` ,自动获取房间信息和连接 `token`
82
+ */
83
+ async init(options = {}) {
84
+ const { open, data, tokens } = options;
85
+ if (open)
86
+ this.openStatus = 1;
87
+ // 设置必要 cookie
88
+ // 如果cookie中没有buvid3字段,会自动获取一个
89
+ await this.setCredentials(__classPrivateFieldGet(this, _RoomBilibili_cookies, "f").toString(), false);
90
+ // 自动更新房间信息
91
+ if (!data)
92
+ await this.update();
93
+ // 自动更新房间 tokens
94
+ if (!tokens)
95
+ await this.updateTokens();
96
+ // 自动开启
97
+ if (open) {
98
+ this.openWebsocketClient();
99
+ }
100
+ this.emit("init");
101
+ }
102
+ /** 更新房间信息 */
103
+ async update() {
104
+ const data = await this.fetchData();
105
+ this.roomId = data.id;
106
+ this.setData(data);
107
+ return data;
108
+ }
109
+ /** 获取房间信息 */
110
+ async fetchData() {
111
+ if (this.customFetchData)
112
+ return this.customFetchData(this.id);
113
+ const rawInfo = await requestGetInfoByRoom(this.id, __classPrivateFieldGet(this, _RoomBilibili_instances, "m", _RoomBilibili_getFetchOptions).call(this));
114
+ if (rawInfo) {
115
+ return parseGetInfoByRoom(rawInfo);
116
+ }
117
+ else {
118
+ // fallback
119
+ const baseInfo = await getRoomBaseInfo(this.id, __classPrivateFieldGet(this, _RoomBilibili_instances, "m", _RoomBilibili_getFetchOptions).call(this));
120
+ if (baseInfo)
121
+ return parseRoomBaseInfo(baseInfo);
122
+ }
123
+ throw new Error("获取房间信息失败");
124
+ }
125
+ /** 设置直播间数据 */
126
+ setData({ roomId, shortId, status, timestamp, detail, stats, anchor, liveId, openStatus, available, }) {
127
+ this.roomId = roomId;
128
+ this.shortId = shortId;
129
+ this.status = status;
130
+ this.timestamp = timestamp;
131
+ this.detail = detail;
132
+ this.stats = stats;
133
+ this.anchor = anchor;
134
+ this.liveId = liveId;
135
+ this.available = available;
136
+ const data = this.toData();
137
+ this.emit("update", { room: data });
138
+ return data;
139
+ }
140
+ /** 开启直播间监听 */
141
+ async open() {
142
+ // 如果直播间监听已打开或处于正在打开状态,则返回
143
+ if (this.openStatus)
144
+ return;
145
+ this.openStatus = 1;
146
+ await this.update();
147
+ this.openWebsocketClient();
148
+ }
149
+ /** 设置登录凭据 */
150
+ async setCredentials(credentials, updateTokens = true) {
151
+ const cookies = new Cookies(credentials);
152
+ if (!cookies.has("buvid3")) {
153
+ cookies.append(await requestBuvidCookie({
154
+ ...__classPrivateFieldGet(this, _RoomBilibili_instances, "m", _RoomBilibili_getFetchOptions).call(this),
155
+ cookie: cookies.toString(),
156
+ }));
157
+ }
158
+ __classPrivateFieldSet(this, _RoomBilibili_cookies, cookies, "f");
159
+ if (updateTokens)
160
+ this.updateTokens();
161
+ }
162
+ /** 更新连接tokens */
163
+ async updateTokens() {
164
+ this.setTokens(await this.fetchTokens(__classPrivateFieldGet(this, _RoomBilibili_cookies, "f").toString()));
165
+ }
166
+ /** 请求连接tokens */
167
+ async fetchTokens(credentials = "") {
168
+ if (this.customFetchTokens) {
169
+ return this.customFetchTokens(this.roomId, credentials);
170
+ }
171
+ const cookies = new Cookies(credentials);
172
+ const buvid = cookies.get("buvid3") ||
173
+ (await requestBuvidCookie(__classPrivateFieldGet(this, _RoomBilibili_instances, "m", _RoomBilibili_getFetchOptions).call(this))).buvid3 ||
174
+ "";
175
+ const uid = parseInt(cookies.get("DedeUserID")) ||
176
+ (await getLoginUid(__classPrivateFieldGet(this, _RoomBilibili_instances, "m", _RoomBilibili_getFetchOptions).call(this)));
177
+ const key = (await getLiveConfig(this.roomId, __classPrivateFieldGet(this, _RoomBilibili_instances, "m", _RoomBilibili_getFetchOptions).call(this))).key;
178
+ return {
179
+ uid,
180
+ buvid,
181
+ key,
182
+ };
183
+ }
184
+ /** 设置直播连接tokens */
185
+ setTokens(tokens) {
186
+ __classPrivateFieldSet(this, _RoomBilibili_tokens, tokens, "f");
187
+ if (this.opened) {
188
+ // 如果已打开,则重连
189
+ this.reconnect();
190
+ }
191
+ }
192
+ /** 无缝重连 */
193
+ reconnect() {
194
+ // 如果正在重连,则返回
195
+ if (this.reconnecting)
196
+ return;
197
+ this.reconnecting = true;
198
+ const lastClient = this.client;
199
+ this.initClient();
200
+ // 重连成功后,关闭上一个连接
201
+ this.client?.addEventListener("open", () => {
202
+ lastClient?.[symbolAbortController].abort();
203
+ lastClient?.close();
204
+ this.reconnecting = false;
205
+ }, { once: true });
206
+ }
207
+ emitConnention(status) {
208
+ this.connectionStatus = status;
209
+ switch (status) {
210
+ case LiveConnectionStatus.connecting:
211
+ this.emit("connecting");
212
+ break;
213
+ case LiveConnectionStatus.connected:
214
+ this.emit("connected");
215
+ break;
216
+ case LiveConnectionStatus.entered:
217
+ this.emit("enter");
218
+ break;
219
+ case LiveConnectionStatus.disconnected:
220
+ this.emit("disconnect");
221
+ break;
222
+ }
223
+ }
224
+ /** 打开直播弹幕客户端 */
225
+ openWebsocketClient() {
226
+ // 如果直播间不可用,则返回
227
+ if (!this.available) {
228
+ this.openStatus = 0;
229
+ return;
230
+ }
231
+ this.initClient();
232
+ this.openStatus = 2;
233
+ this.emit("open");
234
+ }
235
+ /** 创建直播弹幕客户端 */
236
+ initClient() {
237
+ this.emitConnention(LiveConnectionStatus.connecting);
238
+ // 与Websocket服务器连接
239
+ const client = new LiveWS(this.roomId || this.id, {
240
+ uid: __classPrivateFieldGet(this, _RoomBilibili_tokens, "f")?.uid,
241
+ buvid: __classPrivateFieldGet(this, _RoomBilibili_tokens, "f")?.buvid,
242
+ key: __classPrivateFieldGet(this, _RoomBilibili_tokens, "f")?.key,
243
+ });
244
+ const controller = new AbortController();
245
+ const signal = controller.signal;
246
+ client[symbolAbortController] = controller;
247
+ this.lastConnectionTime = Date.now();
248
+ client.addEventListener("open", () => {
249
+ // 连接到服务器
250
+ this.opened && this.emitConnention(LiveConnectionStatus.connected);
251
+ }, { signal });
252
+ client.addEventListener("close", () => {
253
+ // 断开连接
254
+ this.opened && this.emitConnention(LiveConnectionStatus.disconnected);
255
+ // 自动重连
256
+ if (this.opened && this.autoReconnect) {
257
+ const timeout = this.lastConnectionTime + this.connectInterval - Date.now();
258
+ if (timeout > 0) {
259
+ const t = setTimeout(() => {
260
+ this.reconnect();
261
+ clearTimeout(t);
262
+ }, timeout);
263
+ }
264
+ else {
265
+ this.reconnect();
266
+ }
267
+ }
268
+ }, { signal });
269
+ client.addEventListener("CONNECT_SUCCESS", () => {
270
+ // 连接到直播间
271
+ this.emitConnention(LiveConnectionStatus.entered);
272
+ }, { signal });
273
+ client.addEventListener("MESSAGE", ({ data }) => {
274
+ const m = parseMessage(data, this);
275
+ m && this.emitMessage(m);
276
+ this.emitRaw(data);
277
+ }, { signal });
278
+ this.client = client;
279
+ }
280
+ /** 关闭直播间监听 */
281
+ close() {
282
+ if (!this.opened)
283
+ return;
284
+ this.openStatus = 0;
285
+ this.client?.close();
286
+ this.emitConnention(LiveConnectionStatus.off);
287
+ this.emit("close");
288
+ }
289
+ }
290
+ _RoomBilibili_tokens = new WeakMap(), _RoomBilibili_cookies = new WeakMap(), _RoomBilibili_instances = new WeakSet(), _RoomBilibili_getFetchOptions = function _RoomBilibili_getFetchOptions() {
291
+ return {
292
+ fetch: this.fetch,
293
+ userAgent: this.userAgent,
294
+ cookie: __classPrivateFieldGet(this, _RoomBilibili_cookies, "f").toString(),
295
+ };
296
+ };
297
+ export default RoomBilibili;
298
+ const DANMAKU_SERVER_CONF_URL = "https://api.live.bilibili.com/xlive/web-room/v1/index/getDanmuInfo";
299
+ const getLiveConfig = async (roomid, options) => {
300
+ const result = (await customFetch(options, `${DANMAKU_SERVER_CONF_URL}?id=${roomid}`).then((w) => w.json()));
301
+ const { data: { token: key, host_list: [{ host }], }, } = result;
302
+ const address = `wss://${host}/sub`;
303
+ return { key, host, address, raw: result };
304
+ };
305
+ const ROOM_INIT_URL = "https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom";
306
+ /** 获取房间信息 */
307
+ export async function requestGetInfoByRoom(id, options) {
308
+ return await customFetch(options, `${ROOM_INIT_URL}?room_id=${id}`, {
309
+ method: "GET",
310
+ })
311
+ .then((response) => response.json())
312
+ .then((data) => {
313
+ return data.data;
314
+ });
315
+ }