@ybgnb/bili-api 0.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.
Files changed (103) hide show
  1. package/LICENSE +21 -0
  2. package/dist/client/api.d.ts +23 -0
  3. package/dist/client/api.d.ts.map +1 -0
  4. package/dist/client/api.js +128 -0
  5. package/dist/client/api.js.map +1 -0
  6. package/dist/client/client.d.ts +25 -0
  7. package/dist/client/client.d.ts.map +1 -0
  8. package/dist/client/client.js +22 -0
  9. package/dist/client/client.js.map +1 -0
  10. package/dist/client/common.d.ts +6 -0
  11. package/dist/client/common.d.ts.map +1 -0
  12. package/dist/client/common.js +13 -0
  13. package/dist/client/common.js.map +1 -0
  14. package/dist/config/index.d.ts +22 -0
  15. package/dist/config/index.d.ts.map +1 -0
  16. package/dist/config/index.js +32 -0
  17. package/dist/config/index.js.map +1 -0
  18. package/dist/error/handle-error.d.ts +6 -0
  19. package/dist/error/handle-error.d.ts.map +1 -0
  20. package/dist/error/handle-error.js +32 -0
  21. package/dist/error/handle-error.js.map +1 -0
  22. package/dist/error/index.d.ts +40 -0
  23. package/dist/error/index.d.ts.map +1 -0
  24. package/dist/error/index.js +79 -0
  25. package/dist/error/index.js.map +1 -0
  26. package/dist/index.d.ts +9 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +7 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/index.mjs +704 -0
  31. package/dist/index.mjs.map +1 -0
  32. package/dist/index.umd.js +742 -0
  33. package/dist/index.umd.js.map +1 -0
  34. package/dist/service/base.d.ts +17 -0
  35. package/dist/service/base.d.ts.map +1 -0
  36. package/dist/service/base.js +34 -0
  37. package/dist/service/base.js.map +1 -0
  38. package/dist/service/user/index.d.ts +28 -0
  39. package/dist/service/user/index.d.ts.map +1 -0
  40. package/dist/service/user/index.js +148 -0
  41. package/dist/service/user/index.js.map +1 -0
  42. package/dist/service/user/qrcode-login.d.ts +13 -0
  43. package/dist/service/user/qrcode-login.d.ts.map +1 -0
  44. package/dist/service/user/qrcode-login.js +88 -0
  45. package/dist/service/user/qrcode-login.js.map +1 -0
  46. package/dist/stores/mixinkey.d.ts +8 -0
  47. package/dist/stores/mixinkey.d.ts.map +1 -0
  48. package/dist/stores/mixinkey.js +86 -0
  49. package/dist/stores/mixinkey.js.map +1 -0
  50. package/dist/test.d.ts +2 -0
  51. package/dist/test.d.ts.map +1 -0
  52. package/dist/test.js +4 -0
  53. package/dist/test.js.map +1 -0
  54. package/dist/types/business/auth.d.ts +15 -0
  55. package/dist/types/business/auth.d.ts.map +1 -0
  56. package/dist/types/business/auth.js +2 -0
  57. package/dist/types/business/auth.js.map +1 -0
  58. package/dist/types/business/level.d.ts +22 -0
  59. package/dist/types/business/level.d.ts.map +1 -0
  60. package/dist/types/business/level.js +2 -0
  61. package/dist/types/business/level.js.map +1 -0
  62. package/dist/types/business/login.d.ts +35 -0
  63. package/dist/types/business/login.d.ts.map +1 -0
  64. package/dist/types/business/login.js +2 -0
  65. package/dist/types/business/login.js.map +1 -0
  66. package/dist/types/business/misc.d.ts +9 -0
  67. package/dist/types/business/misc.d.ts.map +1 -0
  68. package/dist/types/business/misc.js +2 -0
  69. package/dist/types/business/misc.js.map +1 -0
  70. package/dist/types/business/user.d.ts +34 -0
  71. package/dist/types/business/user.d.ts.map +1 -0
  72. package/dist/types/business/user.js +10 -0
  73. package/dist/types/business/user.js.map +1 -0
  74. package/dist/types/business/vip.d.ts +36 -0
  75. package/dist/types/business/vip.d.ts.map +1 -0
  76. package/dist/types/business/vip.js +19 -0
  77. package/dist/types/business/vip.js.map +1 -0
  78. package/dist/types/core/client.d.ts +38 -0
  79. package/dist/types/core/client.d.ts.map +1 -0
  80. package/dist/types/core/client.js +2 -0
  81. package/dist/types/core/client.js.map +1 -0
  82. package/dist/types/core/request.d.ts +87 -0
  83. package/dist/types/core/request.d.ts.map +1 -0
  84. package/dist/types/core/request.js +2 -0
  85. package/dist/types/core/request.js.map +1 -0
  86. package/dist/types/core/response.d.ts +26 -0
  87. package/dist/types/core/response.d.ts.map +1 -0
  88. package/dist/types/core/response.js +2 -0
  89. package/dist/types/core/response.js.map +1 -0
  90. package/dist/utils/cookies.d.ts +3 -0
  91. package/dist/utils/cookies.d.ts.map +1 -0
  92. package/dist/utils/cookies.js +26 -0
  93. package/dist/utils/cookies.js.map +1 -0
  94. package/dist/utils/time.d.ts +3 -0
  95. package/dist/utils/time.d.ts.map +1 -0
  96. package/dist/utils/time.js +26 -0
  97. package/dist/utils/time.js.map +1 -0
  98. package/dist/utils/url.d.ts +31 -0
  99. package/dist/utils/url.d.ts.map +1 -0
  100. package/dist/utils/url.js +93 -0
  101. package/dist/utils/url.js.map +1 -0
  102. package/package.json +50 -0
  103. package/readme.md +3 -0
@@ -0,0 +1,742 @@
1
+ (function(global, factory) {
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("md5"), require("@ybgnb/utils")) : typeof define === "function" && define.amd ? define([
3
+ "exports",
4
+ "md5",
5
+ "@ybgnb/utils"
6
+ ], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["bili-api"] = {}, global.md5, global["@ybgnb/utils"]));
7
+ })(this, function(exports, md5, _ybgnb_utils) {
8
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
9
+ //#region \0rolldown/runtime.js
10
+ var __create = Object.create;
11
+ var __defProp = Object.defineProperty;
12
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
13
+ var __getOwnPropNames = Object.getOwnPropertyNames;
14
+ var __getProtoOf = Object.getPrototypeOf;
15
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
16
+ var __copyProps = (to, from, except, desc) => {
17
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
18
+ key = keys[i];
19
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
20
+ get: ((k) => from[k]).bind(null, key),
21
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
22
+ });
23
+ }
24
+ return to;
25
+ };
26
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
27
+ value: mod,
28
+ enumerable: true
29
+ }) : target, mod));
30
+ //#endregion
31
+ md5 = __toESM(md5);
32
+ //#region src/error/index.ts
33
+ /**
34
+ * bili 错误
35
+ */
36
+ var BiliError = class extends Error {
37
+ constructor(clientConfig, errorMsg, rawError) {
38
+ super(errorMsg || "操作失败");
39
+ this.name = "BiliError";
40
+ this.rawError = rawError instanceof Error ? rawError : new Error(String(rawError));
41
+ const { timeout, userAgent, context } = clientConfig;
42
+ if (context) {
43
+ const { userInfo, userCookie } = context;
44
+ this.clientConfig = {
45
+ timeout,
46
+ userAgent,
47
+ context: {
48
+ userInfo,
49
+ userCookie
50
+ }
51
+ };
52
+ } else this.clientConfig = {
53
+ timeout,
54
+ userAgent
55
+ };
56
+ }
57
+ };
58
+ /**
59
+ * bili api 错误
60
+ */
61
+ var BiliApiError = class extends BiliError {
62
+ constructor(clientConfig, requestOptions, errorMsg, rawError) {
63
+ super(clientConfig, errorMsg, rawError);
64
+ this.name = "BiliApiError";
65
+ const { url, method, query, data, skipWbiSign, autoCsrf } = requestOptions;
66
+ this.requestOptions = {
67
+ url,
68
+ method,
69
+ query,
70
+ data,
71
+ skipWbiSign,
72
+ autoCsrf
73
+ };
74
+ }
75
+ };
76
+ /**
77
+ * bili api HTTP 网络请求层面的错误(网络错误、超时、状态码超出2xx)
78
+ */
79
+ var BiliApiHttpError = class extends BiliApiError {
80
+ constructor(clientConfig, requestOptions, response, errorMsg, rawError) {
81
+ super(clientConfig, requestOptions, errorMsg || response.statusText || `HTTP ${response.status}`, rawError);
82
+ this.status = 0;
83
+ this.name = "BiliApiHttpError";
84
+ this.status = response.status;
85
+ }
86
+ };
87
+ /**
88
+ * bili api 业务出错
89
+ */
90
+ var BiliApiBusinessError = class extends BiliApiError {
91
+ constructor(clientConfig, requestOptions, apiResponse, errorMsg, rawError) {
92
+ super(clientConfig, requestOptions, errorMsg || apiResponse.message, rawError);
93
+ this.name = "BiliApiBusinessError";
94
+ this.responseCode = apiResponse.code ?? -1;
95
+ this.responseData = apiResponse.data;
96
+ }
97
+ };
98
+ /**
99
+ * 操作取消
100
+ */
101
+ var BiliAbortError = class extends BiliError {
102
+ constructor(clientConfig, errorMsg) {
103
+ super(clientConfig, errorMsg || "操作已取消");
104
+ this.name = "BiliAbortError";
105
+ }
106
+ };
107
+ //#endregion
108
+ //#region src/service/base.ts
109
+ var BaseService = class {
110
+ constructor(api) {
111
+ this.api = api;
112
+ }
113
+ get clientConfig() {
114
+ return this.api.clientConfig;
115
+ }
116
+ get logging() {
117
+ return this.api.logging;
118
+ }
119
+ /**
120
+ * 抛出封装的错误
121
+ * @param errorOrMsg 错误对象或者错误信息
122
+ * @param rawError 原始错误对象
123
+ * @protected
124
+ */
125
+ throwError(errorOrMsg, rawError) {
126
+ throw new BiliError(this.clientConfig, _ybgnb_utils.BaseUtils.getErrorMessage(errorOrMsg), rawError);
127
+ }
128
+ handleError(error, className, methodName) {
129
+ this.logging.error(`${className}.${methodName} 出现错误`, error);
130
+ if (!(error instanceof BiliError)) this.throwError(error);
131
+ }
132
+ };
133
+ //#endregion
134
+ //#region src/utils/cookies.ts
135
+ function parseUserCookie(setCookie) {
136
+ if (!setCookie) throw new Error("setCookie 为空");
137
+ const cookies = [];
138
+ let bili_jct;
139
+ let uid;
140
+ for (const cookie of setCookie) if (cookie) {
141
+ const item = cookie.slice(0, cookie.indexOf(";") + 1);
142
+ cookies.push(item);
143
+ if (item.startsWith("bili_jct=")) bili_jct = item.slice(9, -1);
144
+ if (item.startsWith("DedeUserID=")) uid = item.slice(11, -1);
145
+ }
146
+ if (!bili_jct || !uid) throw new Error("setCookie 解析错误,不存在 bili_jct/uid");
147
+ return {
148
+ cookie: cookies.join(" "),
149
+ bili_jct
150
+ };
151
+ }
152
+ //#endregion
153
+ //#region src/error/handle-error.ts
154
+ /**
155
+ * 错误处理装饰器
156
+ * @constructor
157
+ */
158
+ function CatchError() {
159
+ return function(originalMethod, context) {
160
+ const methodName = String(context.name);
161
+ return function(...args) {
162
+ const className = this.constructor.name;
163
+ try {
164
+ return originalMethod.call(this, ...args);
165
+ } catch (error) {
166
+ if (typeof this.handleError === "function") this.handleError(error, className, methodName);
167
+ else {
168
+ console.error(`${className}.${methodName} 未知错误`, error);
169
+ throw error;
170
+ }
171
+ }
172
+ };
173
+ };
174
+ }
175
+ //#endregion
176
+ //#region src/service/user/qrcode-login.ts
177
+ /**
178
+ * 登录轮询间隔时间
179
+ */
180
+ var QR_CODE_POLL_INTERVAL_MS = 1111;
181
+ /**
182
+ * 通过二维码登录
183
+ *
184
+ * @param userService - 用户服务实例
185
+ * @param callback - 状态回调接口
186
+ * @param loginOptions - 可选配置(自动刷新、更新上下文、取消信号等)
187
+ * @returns 登录成功后的请求上下文
188
+ */
189
+ function loginWithQRCode(userService, callback, loginOptions) {
190
+ const options = {
191
+ updateRequestContext: true,
192
+ autoRefresh: true,
193
+ pollIntervalMs: 1111,
194
+ ...loginOptions
195
+ };
196
+ let pollTimer = null;
197
+ let isPolling = false;
198
+ let isRefreshing = false;
199
+ let currentQrCodeKey = null;
200
+ const internalAbortController = new AbortController();
201
+ const finalSignal = options.signal ? (() => {
202
+ if (options.signal?.aborted) internalAbortController.abort();
203
+ else if (options.signal) options.signal.addEventListener("abort", () => internalAbortController.abort());
204
+ return internalAbortController.signal;
205
+ })() : internalAbortController.signal;
206
+ let resolvePromise = null;
207
+ let rejectPromise = null;
208
+ const safeStatusChange = (msg) => {
209
+ try {
210
+ callback.onStatusChange(msg);
211
+ } catch (err) {
212
+ userService.logging.error("[QRCodeLogin] onStatusChange callback error:", err);
213
+ }
214
+ };
215
+ const safeQRCodeReceived = (url) => {
216
+ try {
217
+ callback.onQRCodeReceived(url);
218
+ } catch (err) {
219
+ userService.logging.error("[QRCodeLogin] onQRCodeReceived callback error:", err);
220
+ }
221
+ };
222
+ const clearPollTimer = () => {
223
+ if (pollTimer) {
224
+ clearTimeout(pollTimer);
225
+ pollTimer = null;
226
+ }
227
+ };
228
+ const stopLoginFlow = () => {
229
+ clearPollTimer();
230
+ isPolling = false;
231
+ isRefreshing = false;
232
+ currentQrCodeKey = null;
233
+ internalAbortController.abort();
234
+ };
235
+ const handlePollError = (err) => {
236
+ if (finalSignal.aborted) return;
237
+ clearPollTimer();
238
+ const errorMsg = _ybgnb_utils.BaseUtils.getErrorMessage(err);
239
+ safeStatusChange(errorMsg);
240
+ if (rejectPromise) {
241
+ rejectPromise(new BiliError(userService.clientConfig, errorMsg, err));
242
+ rejectPromise = null;
243
+ }
244
+ stopLoginFlow();
245
+ };
246
+ const pollLoginResult = async (qrCodeKey) => {
247
+ if (finalSignal.aborted) return;
248
+ if (isPolling) return;
249
+ isPolling = true;
250
+ try {
251
+ if (finalSignal.aborted) return;
252
+ const loginResult = await userService.getQRCodeLoginResult(qrCodeKey, finalSignal);
253
+ if (!pollTimer || finalSignal.aborted) return;
254
+ safeStatusChange(loginResult.message);
255
+ if (loginResult.code === 0) {
256
+ clearPollTimer();
257
+ safeStatusChange("已登录,正在获取用户信息...");
258
+ const context = await userService.initLoginSession(loginResult.setCookie, options.updateRequestContext, finalSignal);
259
+ if (resolvePromise) {
260
+ resolvePromise(context);
261
+ resolvePromise = null;
262
+ rejectPromise = null;
263
+ }
264
+ stopLoginFlow();
265
+ return;
266
+ }
267
+ if (loginResult.code === 86038) if (options.autoRefresh) {
268
+ safeStatusChange("二维码已失效,正在刷新...");
269
+ clearPollTimer();
270
+ await refreshQRCode();
271
+ return;
272
+ } else throw new BiliError(userService.clientConfig, "二维码已失效");
273
+ if (!finalSignal.aborted && pollTimer === null) pollTimer = setTimeout(() => {
274
+ pollTimer = null;
275
+ pollLoginResult(qrCodeKey).catch(handlePollError);
276
+ }, options.pollIntervalMs || QR_CODE_POLL_INTERVAL_MS);
277
+ } catch (err) {
278
+ handlePollError(err);
279
+ } finally {
280
+ isPolling = false;
281
+ }
282
+ };
283
+ const refreshQRCode = async () => {
284
+ if (isRefreshing || finalSignal.aborted) return;
285
+ isRefreshing = true;
286
+ clearPollTimer();
287
+ isPolling = false;
288
+ currentQrCodeKey = null;
289
+ try {
290
+ safeStatusChange("获取二维码中...");
291
+ const qrCode = await userService.getLoginQRCode(finalSignal);
292
+ if (finalSignal.aborted) return;
293
+ currentQrCodeKey = qrCode.qrcode_key;
294
+ safeQRCodeReceived(qrCode.url);
295
+ safeStatusChange("请使用手机App扫码登录...");
296
+ if (!finalSignal.aborted) {
297
+ clearPollTimer();
298
+ pollLoginResult(currentQrCodeKey).catch(handlePollError);
299
+ }
300
+ } catch (err) {
301
+ if (!finalSignal.aborted) {
302
+ const errorMsg = _ybgnb_utils.BaseUtils.getErrorMessage(err);
303
+ safeStatusChange(errorMsg);
304
+ if (rejectPromise) {
305
+ rejectPromise(new BiliError(userService.clientConfig, errorMsg, err));
306
+ rejectPromise = null;
307
+ }
308
+ stopLoginFlow();
309
+ }
310
+ } finally {
311
+ isRefreshing = false;
312
+ }
313
+ };
314
+ const onExternalAbort = () => {
315
+ stopLoginFlow();
316
+ if (rejectPromise) {
317
+ rejectPromise("取消登录");
318
+ rejectPromise = null;
319
+ }
320
+ };
321
+ options.signal?.addEventListener("abort", onExternalAbort);
322
+ return new Promise((resolve, reject) => {
323
+ resolvePromise = resolve;
324
+ rejectPromise = reject;
325
+ if (options.signal?.aborted) {
326
+ reject("取消登录");
327
+ return;
328
+ }
329
+ refreshQRCode().catch((err) => {
330
+ if (rejectPromise) {
331
+ rejectPromise(err);
332
+ rejectPromise = null;
333
+ }
334
+ });
335
+ }).finally(() => {
336
+ options.signal?.removeEventListener("abort", onExternalAbort);
337
+ stopLoginFlow();
338
+ resolvePromise = null;
339
+ rejectPromise = null;
340
+ });
341
+ }
342
+ //#endregion
343
+ //#region \0@oxc-project+runtime@0.122.0/helpers/decorate.js
344
+ function __decorate(decorators, target, key, desc) {
345
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
346
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
347
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
348
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
349
+ }
350
+ //#endregion
351
+ //#region src/service/user/index.ts
352
+ var UserService = class extends BaseService {
353
+ /**
354
+ * 获取登录二维码
355
+ */
356
+ getLoginQRCode(signal) {
357
+ return this.api.request(`https://passport.bilibili.com/x/passport-login/web/qrcode/generate`, { signal });
358
+ }
359
+ /**
360
+ * 获取二维码登录结果
361
+ */
362
+ async getQRCodeLoginResult(qrcode_key, signal) {
363
+ const response = await this.api.request("https://passport.bilibili.com/x/passport-login/web/qrcode/poll", {
364
+ query: { qrcode_key },
365
+ skipWbiSign: true,
366
+ returnFormat: "RawResponse",
367
+ signal
368
+ });
369
+ return {
370
+ ...(await response.json()).data,
371
+ setCookie: response.headers.getSetCookie()
372
+ };
373
+ }
374
+ async loginWithQRCode(callback, loginOptions) {
375
+ return loginWithQRCode(this, callback, loginOptions);
376
+ }
377
+ getBuvids(signal) {
378
+ return this.api.request("https://api.bilibili.com/x/frontend/finger/spi", { signal });
379
+ }
380
+ getUserInfo(cookie, signal) {
381
+ return this.api.request("https://api.bilibili.com/x/space/myinfo", {
382
+ cookie,
383
+ signal
384
+ });
385
+ }
386
+ /**
387
+ * 登录后初始化(cookie / 用户信息等)
388
+ */
389
+ async initLoginSession(setCookie, updateRequestContext = true, signal) {
390
+ this.logging.info(`初始化用户数据...`);
391
+ const userCookie = parseUserCookie(setCookie);
392
+ const { b_3, b_4 } = await this.getBuvids(signal);
393
+ userCookie.cookie = `${userCookie.cookie} buvid3=${b_3}; buvid4=${b_4}; `;
394
+ const userInfo = await this.getUserInfo(userCookie, signal);
395
+ const context = {
396
+ userCookie,
397
+ userInfo
398
+ };
399
+ if (updateRequestContext) this.clientConfig.context = context;
400
+ this.logging.info(`已完成用户登录:${userInfo.name} ${userInfo.mid}`);
401
+ return context;
402
+ }
403
+ };
404
+ __decorate([CatchError()], UserService.prototype, "getLoginQRCode", null);
405
+ __decorate([CatchError()], UserService.prototype, "getQRCodeLoginResult", null);
406
+ __decorate([CatchError()], UserService.prototype, "loginWithQRCode", null);
407
+ __decorate([CatchError()], UserService.prototype, "getBuvids", null);
408
+ __decorate([CatchError()], UserService.prototype, "getUserInfo", null);
409
+ __decorate([CatchError()], UserService.prototype, "initLoginSession", null);
410
+ //#endregion
411
+ //#region src/config/index.ts
412
+ /**
413
+ * 默认的客户端配置
414
+ */
415
+ var defaultClientConfig = {
416
+ timeout: 5e3,
417
+ userAgent: "\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Google Chrome\";v=\"146\"",
418
+ referer: "https://space.bilibili.com/10000",
419
+ logLevel: "info",
420
+ logging: {
421
+ debug: (...data) => {
422
+ console.error(`[${(/* @__PURE__ */ new Date()).toLocaleString()}]`, ...data);
423
+ },
424
+ info: (...data) => {
425
+ console.info(`[${(/* @__PURE__ */ new Date()).toLocaleString()}]`, ...data);
426
+ },
427
+ error: (...data) => {
428
+ console.error(`[${(/* @__PURE__ */ new Date()).toLocaleString()}]`, ...data);
429
+ }
430
+ }
431
+ };
432
+ /**
433
+ * 默认的请求选项
434
+ */
435
+ var defaultRequestOptions = {
436
+ method: "GET",
437
+ returnFormat: "ApiResponseData",
438
+ ignoreBusinessError: false,
439
+ skipWbiSign: false,
440
+ autoCsrf: true
441
+ };
442
+ //#endregion
443
+ //#region src/client/client.ts
444
+ /**
445
+ * bili 客户端
446
+ */
447
+ var BiliClient = class {
448
+ get logging() {
449
+ return this.config.logging;
450
+ }
451
+ constructor(config) {
452
+ this.config = {
453
+ ...defaultClientConfig,
454
+ ...config ?? {}
455
+ };
456
+ this.api = new BiliApi(this);
457
+ this.user = new UserService(this.api);
458
+ }
459
+ updateRequestContext(requestContext) {
460
+ this.config.context = requestContext;
461
+ }
462
+ };
463
+ /**
464
+ * 公用的Bili客户端
465
+ */
466
+ var commonBiliClient = new BiliClient();
467
+ //#endregion
468
+ //#region src/stores/mixinkey.ts
469
+ var mixinKey;
470
+ var initPromise = null;
471
+ var wbiKeyLastUpdatedAt = 0;
472
+ var mixinKeyUpdateTimer = void 0;
473
+ var MILLIS_PER_DAY = 1440 * 60 * 1e3;
474
+ /**
475
+ * mixinKey(wbi签名需要的参数)
476
+ */
477
+ var mixinKeyStore = { get: async () => {
478
+ if (mixinKey) return mixinKey;
479
+ if (!initPromise) initPromise = initMixinKey().catch(() => {
480
+ initPromise = null;
481
+ });
482
+ await initPromise;
483
+ return mixinKey;
484
+ } };
485
+ function isNotSameDay(timestamp1, timestamp2) {
486
+ return Math.floor(timestamp1 / MILLIS_PER_DAY) !== Math.floor(timestamp2 / MILLIS_PER_DAY);
487
+ }
488
+ var mixinKeyEncTab = [
489
+ 46,
490
+ 47,
491
+ 18,
492
+ 2,
493
+ 53,
494
+ 8,
495
+ 23,
496
+ 32,
497
+ 15,
498
+ 50,
499
+ 10,
500
+ 31,
501
+ 58,
502
+ 3,
503
+ 45,
504
+ 35,
505
+ 27,
506
+ 43,
507
+ 5,
508
+ 49,
509
+ 33,
510
+ 9,
511
+ 42,
512
+ 19,
513
+ 29,
514
+ 28,
515
+ 14,
516
+ 39,
517
+ 12,
518
+ 38,
519
+ 41,
520
+ 13,
521
+ 37,
522
+ 48,
523
+ 7,
524
+ 16,
525
+ 24,
526
+ 55,
527
+ 40,
528
+ 61,
529
+ 26,
530
+ 17,
531
+ 0,
532
+ 1,
533
+ 60,
534
+ 51,
535
+ 30,
536
+ 4,
537
+ 22,
538
+ 25,
539
+ 54,
540
+ 21,
541
+ 56,
542
+ 59,
543
+ 6,
544
+ 63,
545
+ 57,
546
+ 62,
547
+ 11,
548
+ 36,
549
+ 20,
550
+ 34,
551
+ 44,
552
+ 52
553
+ ];
554
+ var generateMixinKey = (imgKey, subKey) => {
555
+ const raw_wbi_key = imgKey + subKey;
556
+ return mixinKeyEncTab.map((n) => raw_wbi_key[n]).join("").slice(0, 32);
557
+ };
558
+ var initMixinKey = async () => {
559
+ await updateMixinKey();
560
+ if (!mixinKeyUpdateTimer) clearInterval(mixinKeyUpdateTimer);
561
+ mixinKeyUpdateTimer = setInterval(() => {
562
+ if (isNotSameDay(wbiKeyLastUpdatedAt, Date.now())) updateMixinKey();
563
+ }, 3e5);
564
+ };
565
+ var updateMixinKey = async () => {
566
+ try {
567
+ const now = Date.now();
568
+ const nav = (await (await fetch("https://api.bilibili.com/x/web-interface/nav")).json()).data;
569
+ const getKey = (url) => {
570
+ return new URL(url).pathname.split("/").pop().replace(/\.[^.]+$/, "");
571
+ };
572
+ mixinKey = generateMixinKey(getKey(nav.wbi_img.img_url), getKey(nav.wbi_img.sub_url));
573
+ commonBiliClient.logging.info(`初始化 MixinKey:${mixinKey}`);
574
+ wbiKeyLastUpdatedAt = now;
575
+ } catch (err) {
576
+ commonBiliClient.logging.error("初始化 MixinKey 失败", err);
577
+ throw err;
578
+ }
579
+ };
580
+ //#endregion
581
+ //#region src/utils/url.ts
582
+ var invalidCharRegex = /[!'()*]/g;
583
+ /**
584
+ * 解析 url
585
+ * @param urlStr
586
+ */
587
+ function parseURL(urlStr) {
588
+ try {
589
+ const url = new URL(urlStr);
590
+ return {
591
+ baseUrl: url.origin + url.pathname,
592
+ searchParams: url.searchParams
593
+ };
594
+ } catch (_) {
595
+ throw new Error("URL 无效");
596
+ }
597
+ }
598
+ /**
599
+ * 编码 URL 参数
600
+ * @param params 查询参数
601
+ */
602
+ function encodeURLParams(params) {
603
+ return Object.keys(params).sort().map((key) => {
604
+ const raw = params[key];
605
+ if (raw === void 0 || raw === null) return "";
606
+ const value = String(raw).replace(invalidCharRegex, "");
607
+ return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
608
+ }).join("&");
609
+ }
610
+ /**
611
+ * wbi 签名
612
+ * @param params
613
+ */
614
+ function wbiSign(params) {
615
+ const query = encodeURLParams(params);
616
+ return query + "&w_rid=" + (0, md5.default)(query + mixinKeyStore.get());
617
+ }
618
+ /**
619
+ * 处理 url 查询参数
620
+ * @param urlSearchParams
621
+ * @param params
622
+ * @param skipWbiSign
623
+ * @return 查询字符串
624
+ */
625
+ function handleUrlParams(urlSearchParams, params, skipWbiSign) {
626
+ try {
627
+ let finalParams = {};
628
+ if (urlSearchParams.size > 0) urlSearchParams.forEach((value, key) => {
629
+ finalParams[key] = value;
630
+ });
631
+ if (params) finalParams = {
632
+ ...finalParams,
633
+ ...params
634
+ };
635
+ if (!finalParams.wts) finalParams.wts = Math.floor(Date.now() / 1e3);
636
+ if (skipWbiSign || Object.keys(finalParams).length === 1) return `${encodeURLParams(finalParams)}`;
637
+ else return `${wbiSign(finalParams)}`;
638
+ } catch (_) {
639
+ throw new Error("查询参数无效");
640
+ }
641
+ }
642
+ //#endregion
643
+ //#region src/client/api.ts
644
+ /**
645
+ * bili 接口
646
+ */
647
+ var BiliApi = class {
648
+ constructor(client) {
649
+ this.client = client;
650
+ }
651
+ get clientConfig() {
652
+ return this.client.config;
653
+ }
654
+ get logging() {
655
+ return this.clientConfig.logging;
656
+ }
657
+ get isDebug() {
658
+ return this.clientConfig.logLevel === "debug";
659
+ }
660
+ /**
661
+ * 记录接口成功的日志
662
+ */
663
+ logApiSuccess(prefix, returnFormat, response) {
664
+ if (returnFormat === "RawResponse") this.logging.info(`${prefix} 请求成功 [RawResponse]`);
665
+ else if (returnFormat === "ApiResponse") this.logging.info(`${prefix} 请求成功 [ApiResponse]`, this.isDebug ? JSON.stringify(response) : "");
666
+ else this.logging.info(`${prefix} 请求成功`, this.isDebug ? JSON.stringify(response) : "");
667
+ }
668
+ /**
669
+ * 记录接口错误的日志
670
+ */
671
+ logApiError(prefix, error) {
672
+ if (error instanceof BiliAbortError) this.logging.info(`${prefix} [${error.message}]`);
673
+ else if (error instanceof BiliApiHttpError) this.logging.info(`${prefix} [http error]:${error.message}(${error.status})`);
674
+ else if (error instanceof BiliError) this.logging.info(`${prefix} ${error.message}`);
675
+ else this.logging.info(`${prefix} 出现未知错误`, error);
676
+ }
677
+ async request(url, options) {
678
+ const { context, userAgent, timeout: timeoutMS, referer } = this.clientConfig;
679
+ const finalOptions = {
680
+ ...defaultRequestOptions,
681
+ url,
682
+ ...options
683
+ };
684
+ const prefix = `[bili API]${context?.userInfo.mid ? ` [${context?.userInfo.mid}]` : ""} ${finalOptions.method} ${new URL(url).pathname}`;
685
+ try {
686
+ const { baseUrl, searchParams } = parseURL(url);
687
+ const searchStr = handleUrlParams(searchParams, finalOptions.query, finalOptions.skipWbiSign);
688
+ const finalUrl = `${baseUrl}?${searchStr}`;
689
+ finalOptions.url = finalUrl;
690
+ this.logging.info(`${prefix} 请求中 ${searchStr}`);
691
+ if (finalOptions.method === "POST" && finalOptions.data && finalOptions.autoCsrf) {
692
+ if (finalOptions.data instanceof FormData) finalOptions.data.set("csrf", context?.userCookie.bili_jct ?? "");
693
+ }
694
+ const fetchInit = {
695
+ signal: options?.signal ? AbortSignal.any([options?.signal, AbortSignal.timeout(timeoutMS)]) : AbortSignal.timeout(timeoutMS),
696
+ headers: {
697
+ "User-Agent": userAgent,
698
+ cookie: finalOptions.cookie?.cookie || context?.userCookie.cookie || "",
699
+ Referer: typeof referer === "function" ? referer(finalUrl) : referer
700
+ },
701
+ method: finalOptions.method,
702
+ body: finalOptions.data,
703
+ ...options?.init ?? {}
704
+ };
705
+ const httpResponse = await fetch(finalUrl, fetchInit);
706
+ if (!httpResponse.ok) throw new BiliApiHttpError(this.clientConfig, finalOptions, httpResponse);
707
+ if (finalOptions.returnFormat === "RawResponse") {
708
+ this.logApiSuccess(prefix, "RawResponse", httpResponse);
709
+ return httpResponse;
710
+ }
711
+ let biliResponse;
712
+ try {
713
+ biliResponse = await httpResponse.json();
714
+ } catch (e) {
715
+ throw new BiliApiHttpError(this.clientConfig, finalOptions, httpResponse, "解析响应数据错误", e);
716
+ }
717
+ if (!finalOptions.ignoreBusinessError && biliResponse.code !== 0) throw new BiliApiBusinessError(this.clientConfig, finalOptions, biliResponse);
718
+ if (finalOptions.returnFormat === "ApiResponse") {
719
+ this.logApiSuccess(prefix, "ApiResponse", biliResponse);
720
+ return biliResponse;
721
+ }
722
+ this.logApiSuccess(prefix, "ApiResponseData", biliResponse.data);
723
+ return biliResponse.data;
724
+ } catch (error) {
725
+ this.logApiError("", error);
726
+ throw error;
727
+ }
728
+ }
729
+ };
730
+ //#endregion
731
+ exports.BaseService = BaseService;
732
+ exports.BiliAbortError = BiliAbortError;
733
+ exports.BiliApi = BiliApi;
734
+ exports.BiliApiBusinessError = BiliApiBusinessError;
735
+ exports.BiliApiError = BiliApiError;
736
+ exports.BiliApiHttpError = BiliApiHttpError;
737
+ exports.BiliClient = BiliClient;
738
+ exports.BiliError = BiliError;
739
+ exports.UserService = UserService;
740
+ });
741
+
742
+ //# sourceMappingURL=index.umd.js.map