@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.
- package/LICENSE +21 -0
- package/dist/client/api.d.ts +23 -0
- package/dist/client/api.d.ts.map +1 -0
- package/dist/client/api.js +128 -0
- package/dist/client/api.js.map +1 -0
- package/dist/client/client.d.ts +25 -0
- package/dist/client/client.d.ts.map +1 -0
- package/dist/client/client.js +22 -0
- package/dist/client/client.js.map +1 -0
- package/dist/client/common.d.ts +6 -0
- package/dist/client/common.d.ts.map +1 -0
- package/dist/client/common.js +13 -0
- package/dist/client/common.js.map +1 -0
- package/dist/config/index.d.ts +22 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +32 -0
- package/dist/config/index.js.map +1 -0
- package/dist/error/handle-error.d.ts +6 -0
- package/dist/error/handle-error.d.ts.map +1 -0
- package/dist/error/handle-error.js +32 -0
- package/dist/error/handle-error.js.map +1 -0
- package/dist/error/index.d.ts +40 -0
- package/dist/error/index.d.ts.map +1 -0
- package/dist/error/index.js +79 -0
- package/dist/error/index.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +704 -0
- package/dist/index.mjs.map +1 -0
- package/dist/index.umd.js +742 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/service/base.d.ts +17 -0
- package/dist/service/base.d.ts.map +1 -0
- package/dist/service/base.js +34 -0
- package/dist/service/base.js.map +1 -0
- package/dist/service/user/index.d.ts +28 -0
- package/dist/service/user/index.d.ts.map +1 -0
- package/dist/service/user/index.js +148 -0
- package/dist/service/user/index.js.map +1 -0
- package/dist/service/user/qrcode-login.d.ts +13 -0
- package/dist/service/user/qrcode-login.d.ts.map +1 -0
- package/dist/service/user/qrcode-login.js +88 -0
- package/dist/service/user/qrcode-login.js.map +1 -0
- package/dist/stores/mixinkey.d.ts +8 -0
- package/dist/stores/mixinkey.d.ts.map +1 -0
- package/dist/stores/mixinkey.js +86 -0
- package/dist/stores/mixinkey.js.map +1 -0
- package/dist/test.d.ts +2 -0
- package/dist/test.d.ts.map +1 -0
- package/dist/test.js +4 -0
- package/dist/test.js.map +1 -0
- package/dist/types/business/auth.d.ts +15 -0
- package/dist/types/business/auth.d.ts.map +1 -0
- package/dist/types/business/auth.js +2 -0
- package/dist/types/business/auth.js.map +1 -0
- package/dist/types/business/level.d.ts +22 -0
- package/dist/types/business/level.d.ts.map +1 -0
- package/dist/types/business/level.js +2 -0
- package/dist/types/business/level.js.map +1 -0
- package/dist/types/business/login.d.ts +35 -0
- package/dist/types/business/login.d.ts.map +1 -0
- package/dist/types/business/login.js +2 -0
- package/dist/types/business/login.js.map +1 -0
- package/dist/types/business/misc.d.ts +9 -0
- package/dist/types/business/misc.d.ts.map +1 -0
- package/dist/types/business/misc.js +2 -0
- package/dist/types/business/misc.js.map +1 -0
- package/dist/types/business/user.d.ts +34 -0
- package/dist/types/business/user.d.ts.map +1 -0
- package/dist/types/business/user.js +10 -0
- package/dist/types/business/user.js.map +1 -0
- package/dist/types/business/vip.d.ts +36 -0
- package/dist/types/business/vip.d.ts.map +1 -0
- package/dist/types/business/vip.js +19 -0
- package/dist/types/business/vip.js.map +1 -0
- package/dist/types/core/client.d.ts +38 -0
- package/dist/types/core/client.d.ts.map +1 -0
- package/dist/types/core/client.js +2 -0
- package/dist/types/core/client.js.map +1 -0
- package/dist/types/core/request.d.ts +87 -0
- package/dist/types/core/request.d.ts.map +1 -0
- package/dist/types/core/request.js +2 -0
- package/dist/types/core/request.js.map +1 -0
- package/dist/types/core/response.d.ts +26 -0
- package/dist/types/core/response.d.ts.map +1 -0
- package/dist/types/core/response.js +2 -0
- package/dist/types/core/response.js.map +1 -0
- package/dist/utils/cookies.d.ts +3 -0
- package/dist/utils/cookies.d.ts.map +1 -0
- package/dist/utils/cookies.js +26 -0
- package/dist/utils/cookies.js.map +1 -0
- package/dist/utils/time.d.ts +3 -0
- package/dist/utils/time.d.ts.map +1 -0
- package/dist/utils/time.js +26 -0
- package/dist/utils/time.js.map +1 -0
- package/dist/utils/url.d.ts +31 -0
- package/dist/utils/url.d.ts.map +1 -0
- package/dist/utils/url.js +93 -0
- package/dist/utils/url.js.map +1 -0
- package/package.json +50 -0
- 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
|