'use strict'; const node_net = require('node:net'); const https = require('node:https'); const node_buffer = require('node:buffer'); const WebSocket = require('ws'); const types = require('./shared/tiny-bilibili-ws.78ac57c4.cjs'); const node_zlib = require('node:zlib'); require('eventemitter3'); function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; } const https__default = /*#__PURE__*/_interopDefaultCompat(https); const WebSocket__default = /*#__PURE__*/_interopDefaultCompat(WebSocket); function handler(resolve, reject) { return (error, result) => { if (error) reject(error); else resolve(result); }; } function brotliDecompress(buf, options) { return new Promise((resolve, reject) => { node_zlib.brotliDecompress(buf, options ?? {}, handler(resolve, reject)); }); } function inflate(buf, options) { return new Promise((resolve, reject) => { node_zlib.inflate(buf, options ?? {}, handler(resolve, reject)); }); } const inflateAsync = inflate; const brotliDecompressAsync = brotliDecompress; const inflates = { inflateAsync, brotliDecompressAsync }; var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; function getLongRoomId(room, headers) { return new Promise((resolve, reject) => { https__default.get(`https://api.live.bilibili.com/room/v1/Room/mobileRoomInit?id=${room}`, { headers }, (res) => { let data = node_buffer.Buffer.alloc(0); res.on("data", (chunk) => { data += chunk; }); res.once("end", () => { resolve(JSON.parse(node_buffer.Buffer.from(data).toString())); }); }).on("error", (err) => reject(err)); }); } function getDanmuConf(room, headers) { return new Promise((resolve, reject) => { https__default.get(`https://api.live.bilibili.com/xlive/web-room/v1/index/getDanmuInfo?id=${room}&type=0`, { headers }, (res) => { let data = node_buffer.Buffer.alloc(0); res.on("data", (chunk) => { data += chunk; }); res.once("end", () => { resolve(JSON.parse(node_buffer.Buffer.from(data).toString())); }); }).on("error", (err) => reject(err)); }); } function getBuvidConf(headers) { return new Promise((resolve, reject) => { https__default.get("https://api.bilibili.com/x/frontend/finger/spi", { headers }, (res) => { let data = node_buffer.Buffer.alloc(0); res.on("data", (chunk) => { data += chunk; }); res.once("end", () => { resolve(JSON.parse(node_buffer.Buffer.from(data).toString())); }); }).on("error", (err) => reject(err)); }); } const cachedRoomInfo = /* @__PURE__ */ new Map(); const cachedDanmuConf = /* @__PURE__ */ new Map(); let fingerprint; async function getCachedInfo(room, options) { let roomInfo; let danmuInfo; if (cachedRoomInfo.has(room)) { roomInfo = cachedRoomInfo.get(room); } else { const info = await types.retry(() => getLongRoomId(room, options.headers), 2, 200); cachedRoomInfo.set(room, info); roomInfo = info; } if (cachedDanmuConf.has(roomInfo.data.room_id)) { danmuInfo = cachedDanmuConf.get(roomInfo.data.room_id); } else { const info = await types.retry(() => getDanmuConf(roomInfo.data.room_id, options.headers), 2, 200); cachedDanmuConf.set(roomInfo.data.room_id, info); danmuInfo = info; } if (!fingerprint) { const info = await types.retry(() => getBuvidConf(options.headers), 2, 200); fingerprint = info.data.b_3; } return [roomInfo, danmuInfo, fingerprint]; } class KeepLiveTCP extends types.LiveClient { constructor(roomId, options = types.DEFAULT_WS_OPTIONS) { const resolvedOptions = Object.assign({}, types.DEFAULT_WS_OPTIONS, options); const liveOptions = { ...resolvedOptions, socket: { type: "tcp", write: (data) => { this.tcpSocket.write(data); }, reconnect: types.NOOP, end: () => { this.tcpSocket.end(); } }, zlib: inflates, room: roomId }; super(liveOptions); __publicField(this, "buffer", node_buffer.Buffer.alloc(0)); __publicField(this, "i", 0); __publicField(this, "tcpSocket"); this.init(liveOptions); } async getSocketUrl(roomId) { let host; try { const [_, danmu, fingerprint2] = await getCachedInfo(roomId, this.options); if (!this.options.key) this.options.key = danmu.data.token; if (!this.options.buvid) { this.options.buvid = fingerprint2; } host = types.randomElement(danmu.data.host_list); } catch (error) { console.error(error); } return { host: host?.host, port: host?.port }; } async init(options) { const roomId = types.parseRoomId(options.room); const url = options.url ? options.url : await this.getSocketUrl(roomId); const room = cachedRoomInfo.get(roomId)?.data?.room_id || roomId; this.options.room = room; this.roomId = room; const socket = typeof url === "string" ? node_net.connect(url) : node_net.connect(url.port ?? types.NODE_SOCKET_PORT, url.host ?? types.SOCKET_HOST); this.options.socket.reconnect = () => { this.tcpSocket?.end(); this.tcpSocket = null; const socket2 = typeof url === "string" ? node_net.connect(url) : node_net.connect(url.port ?? types.NODE_SOCKET_PORT, url.host ?? types.SOCKET_HOST); this.tcpSocket = socket2; this._bindEvent(socket2); }; this.tcpSocket = socket; this._bindEvent(socket); } _bindEvent(socket) { socket.on("ready", () => this.emit(types.OPEN_EVENT)); socket.on("close", (hadError) => this.emit(types.CLOSE_EVENT, hadError)); socket.on("error", (e) => this.emit(types.ERROR_EVENT, e)); socket.on("data", (buffer) => { this.buffer = node_buffer.Buffer.concat([this.buffer, buffer]); this.splitBuffer(); }); } splitBuffer() { while (this.buffer.length >= 4 && this.buffer.readInt32BE(0) <= this.buffer.length) { const size = this.buffer.readInt32BE(0); const pack = this.buffer.subarray(0, size); this.buffer = this.buffer.subarray(size); this.i++; if (this.i > 5) { this.i = 0; this.buffer = node_buffer.Buffer.from(this.buffer); } this.emit(types.MESSAGE_EVENT, pack); } } } class KeepLiveWS extends types.LiveClient { constructor(roomId, options = types.DEFAULT_WS_OPTIONS) { const resolvedOptions = Object.assign({}, types.DEFAULT_WS_OPTIONS, options); const liveOptions = { ...resolvedOptions, socket: { type: "websocket", send: (data) => { this.ws.send(data, (err) => { if (err) this.emit(types.ERROR_EVENT, err); }); }, close: () => { this.ws.close(); }, reconnect: types.NOOP }, zlib: inflates, room: roomId }; super(liveOptions); __publicField(this, "ws"); this.init(liveOptions); } async getWebSocketUrl(ssl, roomId) { let host; try { const [_, danmu, fingerprint2] = await getCachedInfo(roomId, this.options); if (!this.options.key) this.options.key = danmu.data.token; if (!this.options.buvid) { this.options.buvid = fingerprint2; } host = types.randomElement(danmu.data.host_list); } catch (error) { console.error(error); } return ssl ? types.WEBSOCKET_SSL_URL( this.options?.host ?? host?.host, this.options?.port ?? types.WEBSOCKET_SSL_PORT, this.options.path ) : types.WEBSOCKET_URL( this.options?.host ?? host?.host, this.options?.port ?? types.WEBSOCKET_PORT, this.options.path ); } async init(options) { const roomId = types.parseRoomId(options.room); const ssl = !!options.ssl; const url = await this.getWebSocketUrl(ssl, roomId); const room = cachedRoomInfo.get(roomId)?.data?.room_id || roomId; this.options.room = room; this.roomId = room; this.options.url = url; options.socket.reconnect = (reconnectUrl) => { this.ws?.close(); this.ws = null; let socket2; const _url = reconnectUrl ?? url; if (options.customWebSocket) { const ws = options.customWebSocket(_url); if (ws instanceof Promise) { ws.then((socket3) => { socket3.binaryType = "arraybuffer"; this.ws = socket3; this._bindEvent(socket3); }); } else { socket2 = ws; ws.binaryType = "arraybuffer"; } } else { socket2 = new WebSocket__default(_url, { headers: options.headers }); socket2.binaryType = "arraybuffer"; } if (socket2) { this.ws = socket2; this._bindEvent(socket2); } }; let socket; if (options.customWebSocket) { const ws = options.customWebSocket(url); if (ws instanceof Promise) { ws.then((socket2) => { socket2.binaryType = "arraybuffer"; this.ws = socket2; this._bindEvent(socket2); }); } else { socket = ws; ws.binaryType = "arraybuffer"; } } else { socket = new WebSocket__default(url, { headers: options.headers }); socket.binaryType = "arraybuffer"; } if (socket) { this.ws = socket; this._bindEvent(socket); } } _bindEvent(socket) { socket.addEventListener("open", () => this.emit(types.OPEN_EVENT)); socket.addEventListener("message", (e) => this.emit(types.MESSAGE_EVENT, node_buffer.Buffer.from(e.data))); socket.addEventListener("error", (e) => this.emit(types.ERROR_EVENT, e)); socket.addEventListener("close", (e) => this.emit(types.CLOSE_EVENT, e)); } } function deserialize(buffer) { return types.parser(buffer, inflates); } exports.EventEmitter = types.EventEmitter; exports.SOCKET_HOSTS = types.SOCKET_HOSTS; exports.WEBSOCKET_SSL_URL = types.WEBSOCKET_SSL_URL; exports.WEBSOCKET_URL = types.WEBSOCKET_URL; exports.WS_BODY_PROTOCOL_VERSION = types.WS_BODY_PROTOCOL_VERSION; exports.WS_OP = types.WS_OP; exports.fromEvent = types.fromEvent; exports.serialize = types.serialize; exports.toMessageData = types.toMessageData; exports.KeepLiveTCP = KeepLiveTCP; exports.KeepLiveWS = KeepLiveWS; exports.deserialize = deserialize; exports.getLongRoomId = getLongRoomId;