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