@laplace.live/ws 7.1.8 → 7.1.10
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/dist/browser.d.ts +6 -7
- package/dist/browser.js +2155 -2126
- package/dist/index.d.ts +38 -34
- package/dist/index.js +212 -431
- package/dist/ws-DHkTB1_n.d.ts +312 -0
- package/dist/ws-DfVxzRDM.js +426 -0
- package/package.json +10 -9
- package/dist/ws-mjTot1Nb.d.ts +0 -301
package/dist/index.js
CHANGED
|
@@ -1,437 +1,218 @@
|
|
|
1
|
-
|
|
2
|
-
import { promisify } from "util";
|
|
3
|
-
import { brotliDecompress, inflate } from "zlib";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
createConnection;
|
|
30
|
-
/** `true` after {@link close} has been called; prevents further reconnects. */
|
|
31
|
-
closed;
|
|
32
|
-
/** Delay in milliseconds before attempting a reconnect. */
|
|
33
|
-
interval;
|
|
34
|
-
/** Maximum milliseconds to wait for a heartbeat before forcing a reconnect. */
|
|
35
|
-
timeout;
|
|
36
|
-
/** The current underlying connection instance. */
|
|
37
|
-
connection;
|
|
38
|
-
constructor(createConnection) {
|
|
39
|
-
super();
|
|
40
|
-
this.createConnection = createConnection;
|
|
41
|
-
this.closed = false;
|
|
42
|
-
this.interval = 100;
|
|
43
|
-
this.timeout = 45 * 1e3;
|
|
44
|
-
this.connection = this.createConnection();
|
|
45
|
-
this.connect(false);
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Wire up event forwarding and timeout handling on the current connection.
|
|
49
|
-
* When `reconnect` is `true`, the previous connection is closed and a fresh
|
|
50
|
-
* one is created via the factory.
|
|
51
|
-
*
|
|
52
|
-
* @param reconnect - Whether to tear down and recreate the connection first.
|
|
53
|
-
*/
|
|
54
|
-
connect(reconnect = true) {
|
|
55
|
-
if (reconnect) {
|
|
56
|
-
this.connection.close();
|
|
57
|
-
this.connection = this.createConnection();
|
|
58
|
-
}
|
|
59
|
-
const connection = this.connection;
|
|
60
|
-
let timeout = setTimeout(() => {
|
|
61
|
-
connection.close();
|
|
62
|
-
connection.dispatchEvent(new Event("timeout"));
|
|
63
|
-
}, this.timeout);
|
|
64
|
-
connection.addEventListener("event", (e) => {
|
|
65
|
-
const evt = e.data;
|
|
66
|
-
if (evt.type !== "error") {
|
|
67
|
-
if (evt instanceof LaplaceRawEvent) {
|
|
68
|
-
this.dispatchEvent(new LaplaceRawEvent(evt.type, evt.data));
|
|
69
|
-
} else {
|
|
70
|
-
this.dispatchEvent(new Event(evt.type));
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
connection.addEventListener("error", () => this.dispatchEvent(new Event("e")));
|
|
75
|
-
connection.addEventListener("close", () => {
|
|
76
|
-
if (!this.closed) {
|
|
77
|
-
setTimeout(() => this.connect(), this.interval);
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
connection.addEventListener("heartbeat", () => {
|
|
81
|
-
clearTimeout(timeout);
|
|
82
|
-
timeout = setTimeout(() => {
|
|
83
|
-
connection.close();
|
|
84
|
-
connection.dispatchEvent(new Event("timeout"));
|
|
85
|
-
}, this.timeout);
|
|
86
|
-
});
|
|
87
|
-
connection.addEventListener("close", () => {
|
|
88
|
-
clearTimeout(timeout);
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
/** Latest known online viewer count from the underlying connection. */
|
|
92
|
-
get online() {
|
|
93
|
-
return this.connection.online;
|
|
94
|
-
}
|
|
95
|
-
/** The live room ID. */
|
|
96
|
-
get roomid() {
|
|
97
|
-
return this.connection.roomid;
|
|
98
|
-
}
|
|
99
|
-
/** Permanently close the connection and stop reconnecting. */
|
|
100
|
-
close() {
|
|
101
|
-
this.closed = true;
|
|
102
|
-
this.connection.close();
|
|
103
|
-
}
|
|
104
|
-
/** Send a heartbeat packet to the server. */
|
|
105
|
-
heartbeat() {
|
|
106
|
-
return this.connection.heartbeat();
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Send a heartbeat and resolve with the online viewer count from the
|
|
110
|
-
* next heartbeat response.
|
|
111
|
-
* @returns A promise that resolves with the current online count.
|
|
112
|
-
*/
|
|
113
|
-
getOnline() {
|
|
114
|
-
return this.connection.getOnline();
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Send raw binary data through the underlying connection.
|
|
118
|
-
* @param data - The binary packet to send.
|
|
119
|
-
*/
|
|
120
|
-
send(data) {
|
|
121
|
-
return this.connection.send(data);
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
// src/tcp.ts
|
|
126
|
-
import net from "net";
|
|
127
|
-
|
|
128
|
-
// src/buffer.ts
|
|
129
|
-
var textEncoder = new TextEncoder();
|
|
130
|
-
var textDecoder = new TextDecoder();
|
|
131
|
-
function concatUint8Arrays(arrs) {
|
|
132
|
-
let totalLength = 0;
|
|
133
|
-
for (const arr of arrs) totalLength += arr.length;
|
|
134
|
-
const result = new Uint8Array(totalLength);
|
|
135
|
-
let offset = 0;
|
|
136
|
-
for (const arr of arrs) {
|
|
137
|
-
result.set(arr, offset);
|
|
138
|
-
offset += arr.length;
|
|
139
|
-
}
|
|
140
|
-
return result;
|
|
141
|
-
}
|
|
142
|
-
var cutBuffer = (buffer) => {
|
|
143
|
-
const bufferPacks = [];
|
|
144
|
-
const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
145
|
-
let size;
|
|
146
|
-
for (let i = 0; i < buffer.length; i += size) {
|
|
147
|
-
size = view.getInt32(i);
|
|
148
|
-
bufferPacks.push(buffer.slice(i, i + size));
|
|
149
|
-
}
|
|
150
|
-
return bufferPacks;
|
|
151
|
-
};
|
|
152
|
-
var makeDecoder = ({ inflateAsync: inflateAsync2, brotliDecompressAsync: brotliDecompressAsync2 }) => {
|
|
153
|
-
const decoder = async (buffer) => {
|
|
154
|
-
const packs = await Promise.all(
|
|
155
|
-
cutBuffer(buffer).map(async (buf) => {
|
|
156
|
-
const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
157
|
-
const body = buf.slice(16);
|
|
158
|
-
const protocol = view.getInt16(6);
|
|
159
|
-
const operation = view.getInt32(8);
|
|
160
|
-
let type = "unknow";
|
|
161
|
-
if (operation === 3) {
|
|
162
|
-
type = "heartbeat";
|
|
163
|
-
} else if (operation === 5) {
|
|
164
|
-
type = "message";
|
|
165
|
-
} else if (operation === 8) {
|
|
166
|
-
type = "welcome";
|
|
167
|
-
}
|
|
168
|
-
let data;
|
|
169
|
-
if (protocol === 0) {
|
|
170
|
-
data = JSON.parse(textDecoder.decode(body));
|
|
171
|
-
}
|
|
172
|
-
if (protocol === 1 && body.length === 4) {
|
|
173
|
-
const bodyView = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
174
|
-
data = bodyView.getUint32(0);
|
|
175
|
-
}
|
|
176
|
-
if (protocol === 2) {
|
|
177
|
-
data = await decoder(await inflateAsync2(body));
|
|
178
|
-
}
|
|
179
|
-
if (protocol === 3) {
|
|
180
|
-
data = await decoder(await brotliDecompressAsync2(body));
|
|
181
|
-
}
|
|
182
|
-
return { buf, type, protocol, data };
|
|
183
|
-
})
|
|
184
|
-
);
|
|
185
|
-
return packs.flatMap((pack) => {
|
|
186
|
-
if (pack.protocol === 2 || pack.protocol === 3) {
|
|
187
|
-
return pack.data;
|
|
188
|
-
}
|
|
189
|
-
return pack;
|
|
190
|
-
});
|
|
191
|
-
};
|
|
192
|
-
return decoder;
|
|
193
|
-
};
|
|
194
|
-
var encoder = (type, body = "") => {
|
|
195
|
-
const encoded = typeof body === "string" ? body : JSON.stringify(body);
|
|
196
|
-
const head = new Uint8Array(16);
|
|
197
|
-
const headView = new DataView(head.buffer, head.byteOffset, head.byteLength);
|
|
198
|
-
const buffer = textEncoder.encode(encoded);
|
|
199
|
-
headView.setInt32(0, buffer.length + head.length);
|
|
200
|
-
headView.setInt16(4, 16);
|
|
201
|
-
headView.setInt16(6, 1);
|
|
202
|
-
if (type === "heartbeat") {
|
|
203
|
-
headView.setInt32(8, 2);
|
|
204
|
-
}
|
|
205
|
-
if (type === "join") {
|
|
206
|
-
headView.setInt32(8, 7);
|
|
207
|
-
}
|
|
208
|
-
headView.setInt32(12, 1);
|
|
209
|
-
return concatUint8Arrays([head, buffer]);
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
// src/live.ts
|
|
213
|
-
var Live = class extends LaplaceEventTarget {
|
|
214
|
-
/** The live room ID this instance is connected to. */
|
|
215
|
-
roomid;
|
|
216
|
-
/** Latest known online viewer count, updated on each heartbeat. */
|
|
217
|
-
online;
|
|
218
|
-
/** `true` after the server acknowledges the join (`welcome` packet). */
|
|
219
|
-
live;
|
|
220
|
-
/** `true` after {@link close} has been called. */
|
|
221
|
-
closed;
|
|
222
|
-
/** @internal Handle for the heartbeat interval timer. */
|
|
223
|
-
timeout;
|
|
224
|
-
/** Send raw binary data over the underlying transport. */
|
|
225
|
-
send;
|
|
226
|
-
/** Gracefully close the connection and set {@link closed} to `true`. */
|
|
227
|
-
close;
|
|
228
|
-
constructor(inflates2, roomid, {
|
|
229
|
-
send,
|
|
230
|
-
close,
|
|
231
|
-
protover = 3,
|
|
232
|
-
key,
|
|
233
|
-
authBody,
|
|
234
|
-
uid = 0,
|
|
235
|
-
buvid
|
|
236
|
-
}) {
|
|
237
|
-
if (typeof roomid !== "number" || Number.isNaN(roomid)) {
|
|
238
|
-
throw new Error(`roomid ${roomid} must be Number not NaN`);
|
|
239
|
-
}
|
|
240
|
-
super();
|
|
241
|
-
this.roomid = roomid;
|
|
242
|
-
this.online = 0;
|
|
243
|
-
this.live = false;
|
|
244
|
-
this.closed = false;
|
|
245
|
-
this.timeout = setTimeout(() => {
|
|
246
|
-
}, 0);
|
|
247
|
-
this.send = send;
|
|
248
|
-
this.close = () => {
|
|
249
|
-
this.closed = true;
|
|
250
|
-
close();
|
|
251
|
-
};
|
|
252
|
-
const decode = makeDecoder(inflates2);
|
|
253
|
-
this.addEventListener("message", async (e) => {
|
|
254
|
-
const buffer = e.data;
|
|
255
|
-
const packs = await decode(buffer);
|
|
256
|
-
packs.forEach(({ type, data }) => {
|
|
257
|
-
if (type === "welcome") {
|
|
258
|
-
this.live = true;
|
|
259
|
-
this.dispatchEvent(new Event("live"));
|
|
260
|
-
this.send(encoder("heartbeat"));
|
|
261
|
-
}
|
|
262
|
-
if (type === "heartbeat") {
|
|
263
|
-
this.online = data;
|
|
264
|
-
clearTimeout(this.timeout);
|
|
265
|
-
this.timeout = setTimeout(() => this.heartbeat(), 1e3 * 30);
|
|
266
|
-
this.dispatchEvent(new LaplaceRawEvent("heartbeat", this.online));
|
|
267
|
-
}
|
|
268
|
-
if (type === "message") {
|
|
269
|
-
this.dispatchEvent(new LaplaceRawEvent("msg", data));
|
|
270
|
-
const cmd = data.cmd || data.msg?.cmd;
|
|
271
|
-
if (cmd) {
|
|
272
|
-
this.dispatchEvent(new LaplaceRawEvent(cmd, data));
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
});
|
|
276
|
-
});
|
|
277
|
-
this.addEventListener("open", () => {
|
|
278
|
-
if (authBody) {
|
|
279
|
-
this.send(authBody instanceof Uint8Array ? authBody : encoder("join", authBody));
|
|
280
|
-
} else {
|
|
281
|
-
const hi = { uid, roomid, protover, platform: "web", type: 2 };
|
|
282
|
-
if (key) {
|
|
283
|
-
hi.key = key;
|
|
284
|
-
}
|
|
285
|
-
if (buvid) {
|
|
286
|
-
hi.buvid = buvid;
|
|
287
|
-
}
|
|
288
|
-
const buf = encoder("join", hi);
|
|
289
|
-
this.send(buf);
|
|
290
|
-
}
|
|
291
|
-
});
|
|
292
|
-
this.addEventListener("close", () => {
|
|
293
|
-
clearTimeout(this.timeout);
|
|
294
|
-
});
|
|
295
|
-
this.addEventListener("_error", () => {
|
|
296
|
-
this.close();
|
|
297
|
-
this.dispatchEvent(new Event("error"));
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
/** Send a heartbeat packet to the server. */
|
|
301
|
-
heartbeat() {
|
|
302
|
-
this.send(encoder("heartbeat"));
|
|
303
|
-
}
|
|
304
|
-
/**
|
|
305
|
-
* Send a heartbeat and resolve with the online viewer count from the
|
|
306
|
-
* next heartbeat response.
|
|
307
|
-
* @returns A promise that resolves with the current online count.
|
|
308
|
-
*/
|
|
309
|
-
getOnline() {
|
|
310
|
-
this.heartbeat();
|
|
311
|
-
return new Promise((resolve) => this.addEventListener("heartbeat", (e) => resolve(e.data), { once: true }));
|
|
312
|
-
}
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
// src/tcp.ts
|
|
1
|
+
import { a as LaplaceRawEvent, i as LaplaceEventTarget, n as Live, r as KeepLive, t as LiveWSBase } from "./ws-DfVxzRDM.js";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { brotliDecompress, inflate } from "node:zlib";
|
|
4
|
+
import net from "node:net";
|
|
5
|
+
//#region src/inflate/node.ts
|
|
6
|
+
const _inflate = promisify(inflate);
|
|
7
|
+
const _brotli = promisify(brotliDecompress);
|
|
8
|
+
const inflateAsync = (b) => _inflate(b);
|
|
9
|
+
const brotliDecompressAsync = (b) => _brotli(b);
|
|
10
|
+
const inflates = {
|
|
11
|
+
inflateAsync,
|
|
12
|
+
brotliDecompressAsync
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/tcp.ts
|
|
16
|
+
/**
|
|
17
|
+
* TCP transport for Bilibili live room connections.
|
|
18
|
+
*
|
|
19
|
+
* Wraps a Node.js TCP {@link Socket}, handling packet reassembly from the
|
|
20
|
+
* stream and forwarding complete frames into the {@link Live} event system.
|
|
21
|
+
*
|
|
22
|
+
* Not typically instantiated directly — use {@link LiveTCP} which injects
|
|
23
|
+
* the Node.js inflate implementation.
|
|
24
|
+
*
|
|
25
|
+
* @param inflates - Platform-specific inflate/brotli decompressors.
|
|
26
|
+
* @param roomid - Numeric Bilibili live room ID.
|
|
27
|
+
* @param options - TCP host/port and authentication options.
|
|
28
|
+
*/
|
|
316
29
|
var LiveTCPBase = class extends Live {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
30
|
+
/** The underlying Node.js TCP socket. */
|
|
31
|
+
socket;
|
|
32
|
+
/** @internal Accumulator for incomplete packets from the TCP stream. */
|
|
33
|
+
buf;
|
|
34
|
+
/** @internal Counter for periodic buffer compaction. */
|
|
35
|
+
i;
|
|
36
|
+
constructor(inflates, roomid, { host = "broadcastlv.chat.bilibili.com", port = 2243, ...options } = {}) {
|
|
37
|
+
const socket = net.connect(port, host);
|
|
38
|
+
const send = (data) => {
|
|
39
|
+
socket.write(data);
|
|
40
|
+
};
|
|
41
|
+
const close = () => this.socket.end();
|
|
42
|
+
super(inflates, roomid, {
|
|
43
|
+
send,
|
|
44
|
+
close,
|
|
45
|
+
...options
|
|
46
|
+
});
|
|
47
|
+
this.i = 0;
|
|
48
|
+
this.buf = Buffer.alloc(0);
|
|
49
|
+
socket.on("ready", () => this.dispatchEvent(new Event("open")));
|
|
50
|
+
socket.on("close", () => {
|
|
51
|
+
if (!this.closed) this.dispatchEvent(new Event("close"));
|
|
52
|
+
});
|
|
53
|
+
socket.on("error", () => this.dispatchEvent(new Event("_error")));
|
|
54
|
+
socket.on("data", (buffer) => {
|
|
55
|
+
this.buf = Buffer.concat([this.buf, buffer]);
|
|
56
|
+
this.splitBuffer();
|
|
57
|
+
});
|
|
58
|
+
this.socket = socket;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Extract complete packets from the internal buffer and dispatch them
|
|
62
|
+
* as `message` events. Compacts the buffer periodically to avoid
|
|
63
|
+
* unbounded memory growth from `Buffer.concat` / `slice` chains.
|
|
64
|
+
* @internal
|
|
65
|
+
*/
|
|
66
|
+
splitBuffer() {
|
|
67
|
+
while (this.buf.length >= 4 && this.buf.readInt32BE(0) <= this.buf.length) {
|
|
68
|
+
const size = this.buf.readInt32BE(0);
|
|
69
|
+
const pack = this.buf.subarray(0, size);
|
|
70
|
+
this.buf = this.buf.subarray(size);
|
|
71
|
+
this.i++;
|
|
72
|
+
if (this.i > 5) {
|
|
73
|
+
this.i = 0;
|
|
74
|
+
this.buf = Buffer.from(this.buf);
|
|
75
|
+
}
|
|
76
|
+
this.dispatchEvent(new LaplaceRawEvent("message", pack));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/extra.ts
|
|
82
|
+
/**
|
|
83
|
+
* Fetch WebSocket connection configuration for a Bilibili live room directly
|
|
84
|
+
* from the Bilibili API (`getDanmuInfo`).
|
|
85
|
+
*
|
|
86
|
+
* **Note:** This endpoint is subject to Bilibili's risk-control system and
|
|
87
|
+
* may return error code `-352` when called without proper cookies or headers.
|
|
88
|
+
* For production use, consider proxying through an external service that
|
|
89
|
+
* handles authentication.
|
|
90
|
+
*
|
|
91
|
+
* @param roomid - Numeric Bilibili live room ID.
|
|
92
|
+
* @returns An object containing:
|
|
93
|
+
* - `key` — authentication token for the WebSocket handshake
|
|
94
|
+
* - `host` — hostname of the danmaku WebSocket server
|
|
95
|
+
* - `address` — full `wss://` URL ready to pass as `WSOptions.address`
|
|
96
|
+
* - `raw` — the complete API response for advanced use
|
|
97
|
+
* @throws {Error} If the API returns no data (e.g. invalid room or risk-control block).
|
|
98
|
+
*/
|
|
99
|
+
const getConf = async (roomid) => {
|
|
100
|
+
const json = await (await fetch(`https://api.live.bilibili.com/xlive/web-room/v1/index/getDanmuInfo?id=${roomid}`)).json();
|
|
101
|
+
if (!json.data) throw new Error(`getConf failed for room ${roomid}: ${json.message || "no data"} (code: ${json.code})`);
|
|
102
|
+
const { data: { token: key, host_list: [{ host }] } } = json;
|
|
103
|
+
return {
|
|
104
|
+
key,
|
|
105
|
+
host,
|
|
106
|
+
address: `wss://${host}/sub`,
|
|
107
|
+
json
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Resolve a short (vanity) room ID to its real numeric room ID via the
|
|
112
|
+
* Bilibili API.
|
|
113
|
+
*
|
|
114
|
+
* Short room IDs are custom aliases (e.g. `1`) that map to longer numeric
|
|
115
|
+
* IDs used internally by the live platform.
|
|
116
|
+
*
|
|
117
|
+
* @param short - The short or numeric room ID to resolve.
|
|
118
|
+
* @returns The canonical numeric room ID.
|
|
119
|
+
*/
|
|
120
|
+
const getRoomid = async (short) => {
|
|
121
|
+
const { data: { room_id } } = await fetch(`https://api.live.bilibili.com/room/v1/Room/mobileRoomInit?id=${short}`).then((w) => w.json());
|
|
122
|
+
return room_id;
|
|
123
|
+
};
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/index.ts
|
|
126
|
+
/**
|
|
127
|
+
* WebSocket client for a Bilibili live room (Node.js / Bun).
|
|
128
|
+
*
|
|
129
|
+
* Uses `node:zlib` for inflate and brotli decompression.
|
|
130
|
+
*
|
|
131
|
+
* @param roomid - Numeric Bilibili live room ID.
|
|
132
|
+
* @param opts - Optional WebSocket address and authentication options.
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```ts
|
|
136
|
+
* import { LiveWS } from '@laplace.live/ws'
|
|
137
|
+
*
|
|
138
|
+
* const live = new LiveWS(12345, { key: '...', address: 'wss://...' })
|
|
139
|
+
* live.on('DANMU_MSG', (e) => console.log(e.data))
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
408
142
|
var LiveWS = class extends LiveWSBase {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
};
|
|
143
|
+
constructor(roomid, opts) {
|
|
144
|
+
super(inflates, roomid, opts);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* TCP client for a Bilibili live room (Node.js / Bun only).
|
|
149
|
+
*
|
|
150
|
+
* Uses `node:zlib` for inflate and brotli decompression, and `node:net`
|
|
151
|
+
* for the TCP transport.
|
|
152
|
+
*
|
|
153
|
+
* @param roomid - Numeric Bilibili live room ID.
|
|
154
|
+
* @param opts - Optional TCP host/port and authentication options.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```ts
|
|
158
|
+
* import { LiveTCP } from '@laplace.live/ws'
|
|
159
|
+
*
|
|
160
|
+
* const live = new LiveTCP(12345, { key: '...' })
|
|
161
|
+
* live.on('heartbeat', (e) => console.log('online:', e.data))
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
413
164
|
var LiveTCP = class extends LiveTCPBase {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
};
|
|
165
|
+
constructor(roomid, opts) {
|
|
166
|
+
super(inflates, roomid, opts);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
/**
|
|
170
|
+
* Auto-reconnecting WebSocket client for a Bilibili live room
|
|
171
|
+
* (Node.js / Bun).
|
|
172
|
+
*
|
|
173
|
+
* Wraps {@link LiveWS} with automatic reconnection on disconnect or
|
|
174
|
+
* heartbeat timeout. All events from the underlying connection are
|
|
175
|
+
* forwarded to this instance.
|
|
176
|
+
*
|
|
177
|
+
* @param roomid - Numeric Bilibili live room ID.
|
|
178
|
+
* @param opts - Optional WebSocket address and authentication options.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```ts
|
|
182
|
+
* import { KeepLiveWS } from '@laplace.live/ws'
|
|
183
|
+
*
|
|
184
|
+
* const keep = new KeepLiveWS(12345, { key: '...' })
|
|
185
|
+
* keep.on('DANMU_MSG', (e) => console.log(e.data))
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
418
188
|
var KeepLiveWS = class extends KeepLive {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
};
|
|
189
|
+
constructor(roomid, opts) {
|
|
190
|
+
super(() => new LiveWSBase(inflates, roomid, opts));
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
/**
|
|
194
|
+
* Auto-reconnecting TCP client for a Bilibili live room
|
|
195
|
+
* (Node.js / Bun only).
|
|
196
|
+
*
|
|
197
|
+
* Wraps {@link LiveTCP} with automatic reconnection on disconnect or
|
|
198
|
+
* heartbeat timeout. All events from the underlying connection are
|
|
199
|
+
* forwarded to this instance.
|
|
200
|
+
*
|
|
201
|
+
* @param roomid - Numeric Bilibili live room ID.
|
|
202
|
+
* @param opts - Optional TCP host/port and authentication options.
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```ts
|
|
206
|
+
* import { KeepLiveTCP } from '@laplace.live/ws'
|
|
207
|
+
*
|
|
208
|
+
* const keep = new KeepLiveTCP(12345, { key: '...' })
|
|
209
|
+
* keep.on('heartbeat', (e) => console.log('online:', e.data))
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
423
212
|
var KeepLiveTCP = class extends KeepLive {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
};
|
|
428
|
-
export {
|
|
429
|
-
KeepLiveTCP,
|
|
430
|
-
KeepLiveWS,
|
|
431
|
-
LaplaceEventTarget,
|
|
432
|
-
LaplaceRawEvent,
|
|
433
|
-
LiveTCP,
|
|
434
|
-
LiveWS,
|
|
435
|
-
getConf,
|
|
436
|
-
getRoomid
|
|
213
|
+
constructor(roomid, opts) {
|
|
214
|
+
super(() => new LiveTCPBase(inflates, roomid, opts));
|
|
215
|
+
}
|
|
437
216
|
};
|
|
217
|
+
//#endregion
|
|
218
|
+
export { KeepLiveTCP, KeepLiveWS, LaplaceEventTarget, LaplaceRawEvent, LiveTCP, LiveWS, getConf, getRoomid };
|