@floating-live/bilibili 0.15.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 ADDED
@@ -0,0 +1,228 @@
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.initClient();
124
+ this.emit("open");
125
+ }
126
+ /** 设置登录凭据 */
127
+ async setCredentials(credentials, userToken) {
128
+ this.setTokens(await this.fetchTokens(credentials, userToken));
129
+ }
130
+ /** 请求连接tokens */
131
+ async fetchTokens(credentials = "", userToken) {
132
+ const cookie = (0, utils_1.parseCookieString)(credentials);
133
+ const buvid = userToken?.buvid ||
134
+ cookie["buvid3"] ||
135
+ (await (0, utils_1.getBuvid)(credentials)) ||
136
+ "";
137
+ const userId = userToken?.userId ||
138
+ parseInt(cookie["DedeUserID"]) ||
139
+ (await (0, utils_1.getLoginUid)(credentials));
140
+ const token = await (0, utils_1.getToken)(this.roomId, credentials);
141
+ return {
142
+ userId,
143
+ buvid,
144
+ token,
145
+ };
146
+ }
147
+ /** 设置直播连接tokens */
148
+ setTokens(tokens) {
149
+ this.tokens = tokens;
150
+ if (this.opened) {
151
+ // 如果已打开,则重连
152
+ this.reconnect();
153
+ }
154
+ }
155
+ /** 重连 */
156
+ reconnect() {
157
+ if (this.reconnecting)
158
+ return;
159
+ this.reconnecting = true;
160
+ const lastClient = this.client;
161
+ if (!lastClient) {
162
+ this.reconnecting = false;
163
+ return;
164
+ }
165
+ this.initClient();
166
+ this.client?.once("open", () => {
167
+ lastClient.removeAllListeners();
168
+ lastClient.close();
169
+ this.reconnecting = false;
170
+ });
171
+ }
172
+ emitConnention(status) {
173
+ this.connection = status;
174
+ switch (status) {
175
+ case floating_live_2.ConnectStatus.connecting:
176
+ this.emit("connecting");
177
+ break;
178
+ case floating_live_2.ConnectStatus.connected:
179
+ this.emit("connected");
180
+ break;
181
+ case floating_live_2.ConnectStatus.entered:
182
+ this.emit("enter");
183
+ break;
184
+ case floating_live_2.ConnectStatus.disconnected:
185
+ this.emit("disconnect");
186
+ break;
187
+ }
188
+ }
189
+ /** 创建直播弹幕客户端 */
190
+ initClient() {
191
+ this.emitConnention(floating_live_2.ConnectStatus.connecting);
192
+ // 与Websocket服务器连接
193
+ const client = new bilibili_live_ws_1.KeepLiveWS(this.roomId || this.id, {
194
+ uid: this.tokens.userId,
195
+ buvid: this.tokens.buvid,
196
+ key: this.tokens.token,
197
+ });
198
+ client.interval = this.connectInterval;
199
+ client.on("open", () => {
200
+ // 连接到服务器
201
+ this.opened && this.emitConnention(floating_live_2.ConnectStatus.connected);
202
+ });
203
+ client.on("close", () => {
204
+ // 断开连接
205
+ this.opened && this.emitConnention(floating_live_2.ConnectStatus.disconnected);
206
+ });
207
+ client.on("live", () => {
208
+ // 连接到直播间
209
+ this.emitConnention(floating_live_2.ConnectStatus.entered);
210
+ });
211
+ client.on("msg", (data) => {
212
+ const m = (0, parser_1.parseMessage)(data, this);
213
+ m && this.emitMessage(m);
214
+ this.emitRaw(data);
215
+ });
216
+ this.client = client;
217
+ }
218
+ /** 关闭直播间监听 */
219
+ close() {
220
+ if (!this.opened)
221
+ return;
222
+ this.opened = false;
223
+ this.client?.close();
224
+ this.emit("close");
225
+ }
226
+ }
227
+ exports.RoomBilibili = RoomBilibili;
228
+ exports.default = RoomBilibili;
package/room.ts ADDED
@@ -0,0 +1,270 @@
1
+ import { BilibiliRoomInfo, ConnectTokens } from "./types";
2
+ import { LiveRoom } from "floating-live";
3
+ import { UserInfo, RoomStatsInfo, RoomDetail, RoomInfo } from "floating-live";
4
+ import { KeepLiveWS } from "bilibili-live-ws";
5
+ import { parseInfo, parseMessage } from "./parser";
6
+ import {
7
+ getBuvid,
8
+ getInfoByRoom,
9
+ getLoginUid,
10
+ getToken,
11
+ parseCookieString,
12
+ } from "./utils";
13
+ import { ConnectStatus } from "floating-live";
14
+
15
+ /** 配置项 */
16
+ export interface RoomOptions {
17
+ /** 生成房间后是否打开 */
18
+ open?: boolean;
19
+ /** 登录凭据 */
20
+ credentials?: string;
21
+ /** 连接凭证 */
22
+ tokens?: ConnectTokens;
23
+ /** 房间信息 */
24
+ info?: BilibiliRoomInfo;
25
+ /** 断线重连间隔(默认值为100ms) */
26
+ connectInterval?: number;
27
+ }
28
+
29
+ export class RoomBilibili extends LiveRoom implements BilibiliRoomInfo {
30
+ /** 平台id */
31
+ readonly platform: string = "bilibili";
32
+ /** 直播间号 */
33
+ readonly id: number;
34
+ /** 直播间room_id */
35
+ public roomId: number = 0;
36
+ /** 直播间短房号 */
37
+ public shortId: number = 0;
38
+ /** 房间信息 */
39
+ public detail: RoomDetail = {
40
+ /** 直播标题 */
41
+ title: "",
42
+ /** 分区 */
43
+ area: [],
44
+ /** 封面url */
45
+ cover: "",
46
+ };
47
+ public stats: RoomStatsInfo = {
48
+ /** 观看数 */
49
+ view: 0,
50
+ /** 点赞数 */
51
+ like: 0,
52
+ };
53
+ /** 房间是否可用 */
54
+ public available: boolean = false;
55
+ /** 是否连接上房间 */
56
+ public connected: boolean = false;
57
+ /** 主播信息 */
58
+ public anchor: UserInfo = { name: "", id: 0 };
59
+ /** 直播间弹幕api模块 */
60
+ public client: KeepLiveWS | null = null;
61
+ /** 断线重连间隔 */
62
+ public connectInterval: number;
63
+
64
+ /** 连接凭证 */
65
+ private tokens: ConnectTokens = {
66
+ userId: 0,
67
+ buvid: "",
68
+ token: "",
69
+ };
70
+
71
+ /** 是否处于正在打开的状态 */
72
+ private opening: boolean = false;
73
+ /** 是否处于重连状态 */
74
+ private reconnecting: boolean = false;
75
+
76
+ constructor(
77
+ /** 房间id */
78
+ id: number,
79
+ options?: RoomOptions
80
+ ) {
81
+ super();
82
+ this.id = id;
83
+ this.connectInterval = options?.connectInterval || 100;
84
+ this.init(options);
85
+ }
86
+ /** 初始化 */
87
+ protected async init(options: RoomOptions = {}) {
88
+ const { info, tokens, credentials, open } = options;
89
+ if (open) this.opening = true;
90
+ if (info) {
91
+ this.updateInfo(info);
92
+ } else {
93
+ await this.getInfo();
94
+ }
95
+ if (tokens?.userId && tokens?.buvid && tokens?.token) {
96
+ this.setTokens(tokens as ConnectTokens);
97
+ } else {
98
+ await this.setCredentials(credentials || "", tokens);
99
+ }
100
+ if (open) {
101
+ this.opening = false;
102
+ this.openWS();
103
+ }
104
+ this.emit("init");
105
+ }
106
+
107
+ /** 获取房间信息 */
108
+ public async getInfo() {
109
+ await this.fetchInfo()
110
+ .then((info) => {
111
+ this.roomId = info.id as number;
112
+ this.updateInfo(info);
113
+ })
114
+ .catch((error) => {
115
+ this.emit("info_error", error);
116
+ });
117
+ }
118
+ /** 请求房间信息 */
119
+ async fetchInfo(): Promise<BilibiliRoomInfo> {
120
+ const rawInfo = await getInfoByRoom(this.id);
121
+ return parseInfo(rawInfo);
122
+ }
123
+ /** 更新直播间信息 */
124
+ updateInfo({
125
+ roomId,
126
+ shortId,
127
+ status,
128
+ timestamp,
129
+ detail,
130
+ stats,
131
+ anchor,
132
+ liveId,
133
+ available,
134
+ }: BilibiliRoomInfo) {
135
+ this.roomId = roomId;
136
+ this.shortId = shortId;
137
+ this.status = status;
138
+ this.timestamp = timestamp;
139
+ this.detail = detail;
140
+ this.stats = stats!;
141
+ this.anchor = anchor;
142
+ this.liveId = liveId;
143
+ this.available = available;
144
+ this.emit("info", this.info);
145
+ }
146
+
147
+ /** 开启直播间监听 */
148
+ public async open() {
149
+ // 如果直播间监听已打开或处于正在打开状态,则返回
150
+ if (this.opened || this.opening) return;
151
+ this.opening = true;
152
+ await this.getInfo();
153
+ this.opening = false;
154
+ this.openWS();
155
+ }
156
+ private openWS() {
157
+ // 如果直播间不可用,则返回
158
+ if (!this.available) return;
159
+ this.initClient();
160
+ this.emit("open");
161
+ }
162
+
163
+ /** 设置登录凭据 */
164
+ async setCredentials(credentials: string, userToken?: ConnectTokens) {
165
+ this.setTokens(await this.fetchTokens(credentials, userToken));
166
+ }
167
+ /** 请求连接tokens */
168
+ async fetchTokens(
169
+ credentials: string = "",
170
+ userToken?: ConnectTokens
171
+ ): Promise<ConnectTokens> {
172
+ const cookie = parseCookieString(credentials);
173
+ const buvid =
174
+ userToken?.buvid ||
175
+ cookie["buvid3"] ||
176
+ (await getBuvid(credentials)) ||
177
+ "";
178
+ const userId =
179
+ userToken?.userId ||
180
+ parseInt(cookie["DedeUserID"]) ||
181
+ (await getLoginUid(credentials));
182
+ const token = await getToken(this.roomId, credentials);
183
+ return {
184
+ userId,
185
+ buvid,
186
+ token,
187
+ };
188
+ }
189
+ /** 设置直播连接tokens */
190
+ setTokens(tokens: ConnectTokens) {
191
+ this.tokens = tokens;
192
+ if (this.opened) {
193
+ // 如果已打开,则重连
194
+ this.reconnect();
195
+ }
196
+ }
197
+ /** 重连 */
198
+ reconnect() {
199
+ if (this.reconnecting) return;
200
+ this.reconnecting = true;
201
+ const lastClient = this.client;
202
+ if (!lastClient) {
203
+ this.reconnecting = false;
204
+ return;
205
+ }
206
+ this.initClient();
207
+ this.client?.once("open", () => {
208
+ lastClient.removeAllListeners();
209
+ lastClient.close();
210
+ this.reconnecting = false;
211
+ });
212
+ }
213
+
214
+ private emitConnention(status: ConnectStatus) {
215
+ this.connection = status;
216
+ switch (status) {
217
+ case ConnectStatus.connecting:
218
+ this.emit("connecting");
219
+ break;
220
+ case ConnectStatus.connected:
221
+ this.emit("connected");
222
+ break;
223
+ case ConnectStatus.entered:
224
+ this.emit("enter");
225
+ break;
226
+ case ConnectStatus.disconnected:
227
+ this.emit("disconnect");
228
+ break;
229
+ }
230
+ }
231
+ /** 创建直播弹幕客户端 */
232
+ private initClient() {
233
+ this.emitConnention(ConnectStatus.connecting);
234
+ // 与Websocket服务器连接
235
+ const client = new KeepLiveWS(this.roomId || this.id, {
236
+ uid: this.tokens.userId,
237
+ buvid: this.tokens.buvid,
238
+ key: this.tokens.token,
239
+ });
240
+ client.interval = this.connectInterval;
241
+
242
+ client.on("open", () => {
243
+ // 连接到服务器
244
+ this.opened && this.emitConnention(ConnectStatus.connected);
245
+ });
246
+ client.on("close", () => {
247
+ // 断开连接
248
+ this.opened && this.emitConnention(ConnectStatus.disconnected);
249
+ });
250
+ client.on("live", () => {
251
+ // 连接到直播间
252
+ this.emitConnention(ConnectStatus.entered);
253
+ });
254
+ client.on("msg", (data: any) => {
255
+ const m = parseMessage(data, this);
256
+ m && this.emitMessage(m);
257
+ this.emitRaw(data);
258
+ });
259
+ this.client = client;
260
+ }
261
+ /** 关闭直播间监听 */
262
+ close() {
263
+ if (!this.opened) return;
264
+ this.opened = false;
265
+ this.client?.close();
266
+ this.emit("close");
267
+ }
268
+ }
269
+
270
+ export default RoomBilibili;
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es2020",
4
+ "lib": ["es2020", "DOM"],
5
+ "module": "Node16",
6
+ "noImplicitAny": true,
7
+ "declaration": true,
8
+ "moduleResolution": "Node16",
9
+ "esModuleInterop": true,
10
+ "strict": true,
11
+ "rootDir": "."
12
+ },
13
+ "files": ["index.ts"]
14
+ }