@floating-live/bilibili 0.16.0 → 0.16.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.
@@ -1,32 +1,177 @@
1
- import { FpInfo } from "./fingerprint";
2
- import { RoomBaseInfo } from "../types";
3
- export declare function getCookie(headers: Headers): string;
4
- export declare function parseCookieString(str: string): Record<string, string>;
5
- /** 生成登录二维码 */
6
- export declare function qrcodeGenerate(options?: FetchOptions): Promise<{
7
- url: any;
8
- key: any;
9
- }>;
10
- /** 生成登录二维码 */
11
- export declare function requestFingerSpi(options?: FetchOptions): Promise<Record<"b_3" | "b_4", string>>;
12
- /** 检测登录二维码 */
13
- export declare function checkLoginQRcode(key: string, options?: FetchOptions): Promise<[number, string?]>;
14
- /** 获取并激活buvid的cookie */
15
- export declare function getActivatedBuvidCookie(fp: FpInfo | null | undefined, options: FetchOptions): Promise<Record<"buvid3" | "_uuid" | "buvid_fp" | "b_lsid", string>>;
16
- /** 获取指纹相关Cookies */
17
- export declare function getFingerprintCookie(fp: FpInfo, options: FetchOptions): {
18
- _uuid: string;
19
- buvid_fp: string;
20
- b_lsid: string;
21
- };
22
- /** 激活buvid */
23
- export declare function requestExClimbWuzhi(payload: string, options: FetchOptions): void;
24
- export declare function getRoomBaseInfo(room_id: number, options?: FetchOptions): Promise<RoomBaseInfo | undefined>;
25
- export interface FetchOptions {
26
- /** 自定义fetch函数(适用于请求中转等情况) */
27
- fetch?: (input: string, init?: RequestInit) => Promise<Response>;
28
- /** 用户代理字段 */
29
- userAgent?: string;
30
- /** cookie字段 */
31
- cookie?: string;
1
+ import { BilibiliApiClient } from "bilibili-live-danmaku";
2
+ export interface DataXpassportQrcodeGenerate {
3
+ url: string;
4
+ qrcode_key: string;
32
5
  }
6
+ export interface DataXpassportQrcodePoll {
7
+ url: string;
8
+ refresh_token: string;
9
+ /** 登录时间 */
10
+ timestamp: number;
11
+ /** 扫码状态
12
+ * > 0:扫码登录成功
13
+ * > 86038:二维码已失效
14
+ * > 86090:二维码已扫码未确认
15
+ * > 86101:未扫码
16
+ */
17
+ code: number;
18
+ /** 扫码状态信息 */
19
+ message: string;
20
+ }
21
+ export interface DataXapiNav {
22
+ isLogin: boolean;
23
+ email_verified: number;
24
+ face: string;
25
+ face_nft: number;
26
+ face_nft_type: number;
27
+ level_info: {
28
+ current_level: number;
29
+ current_min: number;
30
+ current_exp: number;
31
+ next_exp: string;
32
+ };
33
+ mid: number;
34
+ mobile_verified: number;
35
+ money: number;
36
+ moral: number;
37
+ official: {
38
+ role: number;
39
+ title: string;
40
+ desc: string;
41
+ type: number;
42
+ };
43
+ officialVerify: {
44
+ type: number;
45
+ desc: string;
46
+ };
47
+ pendant: {
48
+ pid: number;
49
+ name: string;
50
+ image: string;
51
+ expire: number;
52
+ image_enhance: string;
53
+ image_enhance_frame: string;
54
+ n_pid: number;
55
+ };
56
+ scores: number;
57
+ uname: string;
58
+ vipDueDate: number;
59
+ vipStatus: number;
60
+ vipType: number;
61
+ vip_pay_type: number;
62
+ vip_theme_type: number;
63
+ vip_label: {
64
+ path: string;
65
+ text: number;
66
+ label_theme: string;
67
+ text_color: string;
68
+ bg_style: number;
69
+ bg_color: string;
70
+ border_color: string;
71
+ use_img_label: boolean;
72
+ img_label_uri_hans: string;
73
+ img_label_uri_hant: string;
74
+ img_label_uri_hans_static: string;
75
+ img_label_uri_hant_static: string;
76
+ label_id: number;
77
+ label_goto: {
78
+ mobile: string;
79
+ pc_web: string;
80
+ };
81
+ };
82
+ vip_avatar_subscript: number;
83
+ vip_nickname_color: string;
84
+ vip: {
85
+ type: number;
86
+ status: number;
87
+ due_date: number;
88
+ vip_pay_type: number;
89
+ theme_type: number;
90
+ label: {
91
+ path: string;
92
+ text: string;
93
+ label_theme: string;
94
+ text_color: string;
95
+ bg_style: number;
96
+ bg_color: string;
97
+ border_color: string;
98
+ use_img_label: boolean;
99
+ img_label_uri_hans: string;
100
+ img_label_uri_hant: string;
101
+ img_label_uri_hans_static: string;
102
+ img_label_uri_hant_static: string;
103
+ label_id: number;
104
+ label_goto: {
105
+ mobile: string;
106
+ pc_web: string;
107
+ };
108
+ };
109
+ avatar_subscript: number;
110
+ nickname_color: string;
111
+ role: number;
112
+ avatar_subscript_url: string;
113
+ tv_vip_status: number;
114
+ tv_vip_pay_type: number;
115
+ tv_due_date: number;
116
+ avatar_icon: {
117
+ icon_type: number;
118
+ icon_resource: {};
119
+ };
120
+ };
121
+ wallet: {
122
+ mid: number;
123
+ bcoin_balance: number;
124
+ coupon_balance: number;
125
+ coupon_due_time: number;
126
+ };
127
+ has_shop: boolean;
128
+ shop_url: "";
129
+ answer_status: number;
130
+ is_senior_member: number;
131
+ wbi_img: {
132
+ img_url: string;
133
+ sub_url: string;
134
+ };
135
+ is_jury: boolean;
136
+ name_render: null;
137
+ }
138
+ export interface DataXpassportCookieInfo {
139
+ refresh: boolean;
140
+ timestamp: number;
141
+ }
142
+ export interface DataXpassportCookieRefresh {
143
+ status: number;
144
+ message: string;
145
+ refresh_token: string;
146
+ }
147
+ export declare class BilibiliApiClientEx extends BilibiliApiClient {
148
+ /** 生成登录二维码 */
149
+ xpassportQrcodeGenerate(): Promise<import("bilibili-live-danmaku").ResponseDataRoot<DataXpassportQrcodeGenerate>>;
150
+ /** 轮询登录二维码 */
151
+ xpassportQrcodePoll(params: {
152
+ qrcode_key: string;
153
+ }): Promise<import("bilibili-live-danmaku").ResponseDataRoot<DataXpassportQrcodePoll>>;
154
+ /** 获取当前登录信息 */
155
+ xapiNav(): Promise<import("bilibili-live-danmaku").ResponseDataRoot<DataXapiNav>>;
156
+ xpassportCookieInfo(): Promise<import("bilibili-live-danmaku").ResponseDataRoot<DataXpassportCookieInfo>>;
157
+ wwwCorrespond(correspondPath: string): Promise<Response>;
158
+ xpassportCookieRefresh(params: {
159
+ csrf: string;
160
+ refresh_csrf: string;
161
+ source?: string;
162
+ refresh_token: string;
163
+ }): Promise<import("bilibili-live-danmaku").ResponseDataRoot<DataXpassportCookieRefresh>>;
164
+ xpassportConfirmRefresh(params: {
165
+ csrf: string;
166
+ refresh_token: string;
167
+ }): Promise<import("bilibili-live-danmaku").ResponseDataRoot<unknown>>;
168
+ refreshCookie({ timestamp, refresh_token, }: {
169
+ timestamp: number;
170
+ refresh_token: string;
171
+ }): Promise<{
172
+ cookie: string;
173
+ refresh_token: string;
174
+ }>;
175
+ }
176
+ export declare function correspondRefreshCsrf(html: string): Promise<string>;
177
+ export declare function fetchLiveRoomData(client: BilibiliApiClient, id: number): Promise<import("..").BilibiliRoomData>;
package/utils/request.js CHANGED
@@ -1,181 +1,96 @@
1
- import { Cookies, customFetch, requestBuvidCookie, } from "bilibili-live-danmaku";
2
- import { fpGet, generateFakeFpInfo, getExClimbWuzhiPayload, } from "./fingerprint";
3
- import { generateRandomPngBase64 } from "./randomImage";
4
- import { x64hash128 } from "./fp2";
5
- import { generateUuid } from "./uuid";
6
- import { generateLsid } from "./lsid";
7
- export function getCookie(headers) {
8
- const cookies = [];
9
- headers.forEach((val, key) => {
10
- if (key == "set-cookie") {
11
- const [c] = val.split(";");
12
- cookies.push(c);
13
- }
14
- });
15
- const cookie = cookies.join("; ");
16
- return cookie;
17
- }
18
- export function parseCookieString(str) {
19
- const cookie = {};
20
- str.split(";").forEach((item) => {
21
- if (!item) {
22
- return;
23
- }
24
- const arr = item.split("=");
25
- const key = arr[0]?.trim();
26
- const val = arr[1]?.trim();
27
- cookie[key] = val;
28
- });
29
- return cookie;
30
- }
31
- /** 生成登录二维码 */
32
- export async function qrcodeGenerate(options) {
33
- const res = await customFetch(options, "https://passport.bilibili.com/x/passport-login/web/qrcode/generate").then((res) => res.json());
34
- return {
35
- url: res.data.url,
36
- key: res.data.qrcode_key,
37
- };
38
- }
39
- /** 生成登录二维码 */
40
- export async function requestFingerSpi(options) {
41
- const res = await customFetch(options, "https://api.bilibili.com/x/frontend/finger/spi").then((res) => res.json());
42
- return res.data;
43
- }
44
- /** 检测登录二维码 */
45
- export async function checkLoginQRcode(key, options) {
46
- const res = await customFetch(options, `https://passport.bilibili.com/x/passport-login/web/qrcode/poll?qrcode_key=${key}`);
47
- const { code, message } = (await res.json()).data;
48
- if (code == 86101) {
49
- // 未扫码
50
- return [1];
1
+ import { assertRequestOk, BilibiliApiClient, unwrapRequestData, } from "bilibili-live-danmaku";
2
+ import { parseGetInfoByRoom, parseRoomBaseInfo } from "../parser";
3
+ import { getCorrespondPath } from "./crypto";
4
+ export class BilibiliApiClientEx extends BilibiliApiClient {
5
+ /** 生成登录二维码 */
6
+ async xpassportQrcodeGenerate() {
7
+ const res = await this.request("https://passport.bilibili.com/x/passport-login/web/qrcode/generate");
8
+ return unwrapRequestData(res);
51
9
  }
52
- else if (code == 86090) {
53
- // 等待确认
54
- return [2];
10
+ /** 轮询登录二维码 */
11
+ async xpassportQrcodePoll(params) {
12
+ const url = new URL("https://passport.bilibili.com/x/passport-login/web/qrcode/poll");
13
+ url.searchParams.set("qrcode_key", params.qrcode_key);
14
+ const res = await this.request("https://passport.bilibili.com/x/passport-login/web/qrcode/generate");
15
+ const data = unwrapRequestData(res);
16
+ this.cookies.setFromHeaders(res.headers);
17
+ return data;
55
18
  }
56
- else if (code == 0) {
57
- const cookie = getCookie(res.headers);
58
- return [0, cookie];
19
+ /** 获取当前登录信息 */
20
+ async xapiNav() {
21
+ const res = await this.request("https://api.bilibili.com/x/web-interface/nav");
22
+ return unwrapRequestData(res);
59
23
  }
60
- else if (code == 86038) {
61
- // 二维码失效
62
- return [-1];
24
+ async xpassportCookieInfo() {
25
+ const res = await this.request("https://passport.bilibili.com/x/passport-login/web/cookie/info");
26
+ return unwrapRequestData(res);
63
27
  }
64
- else {
65
- // 其他错误
66
- console.log(`${code} ${message}`);
67
- throw message;
68
- }
69
- }
70
- function wait(t) {
71
- return new Promise((res, rej) => {
72
- const to = setTimeout(() => {
73
- res(undefined);
74
- clearTimeout(t);
75
- }, t);
76
- });
77
- }
78
- /** 获取并激活buvid的cookie */
79
- export async function getActivatedBuvidCookie(fp, options) {
80
- const cookies = new Cookies();
81
- cookies.append(await requestBuvidCookie({
82
- ...options,
83
- cookie: cookies.toString(),
84
- }));
85
- const { b_4 } = await requestFingerSpi({
86
- ...options,
87
- cookie: cookies.toString(),
88
- });
89
- cookies.set("buvid4", b_4);
90
- if (!fp) {
91
- fp = generateFakeFpInfo({
92
- canvas: generateRandomPngBase64(),
93
- webgl: generateRandomPngBase64(),
94
- userAgent: options.userAgent ||
95
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 Edg/135.0.0.0",
96
- platform: "Win32",
97
- });
28
+ async wwwCorrespond(correspondPath) {
29
+ const res = await this.request(`https://www.bilibili.com/correspond/1/${correspondPath}`);
30
+ assertRequestOk(res);
31
+ return res;
98
32
  }
99
- cookies.append(getFingerprintCookie(fp, {
100
- ...options,
101
- cookie: cookies.toString(),
102
- }));
103
- // await wait(2000);
104
- const payload = getExClimbWuzhiPayload(fp, {
105
- uuid: cookies.get("_uuid"),
106
- timestamp: Date.now().toString(),
107
- browser_resolution: fpGet(fp, "availableScreenResolution").join("x"),
108
- abtest: `{"b_ut":null,"home_version":"V8","in_new_ab":true,"ab_version":{"for_ai_home_version":"V8","enable_web_push":"DISABLE","ad_style_version":"NEW","enable_feed_channel":"ENABLE","enable_ai_floor_api":"DISABLE"},"ab_split_num":{"for_ai_home_version":54,"enable_web_push":10,"ad_style_version":54,"enable_feed_channel":54,"enable_ai_floor_api":137},"uniq_page_id":"${Math.floor(Math.random() * 2000000000000)}","is_modern":true}`,
109
- });
110
- requestExClimbWuzhi(JSON.stringify(payload), {
111
- ...options,
112
- cookie: cookies.toString(),
113
- });
114
- const cookieRecord = {};
115
- cookies.forEach((val, k) => {
116
- cookieRecord[k] = val;
117
- });
118
- return cookieRecord;
119
- }
120
- /** 获取指纹相关Cookies */
121
- export function getFingerprintCookie(fp, options) {
122
- const cookies = new Cookies(options.cookie);
123
- let _uuid = cookies.get("_uuid");
124
- let buvid_fp = cookies.get("buvid_fp");
125
- let b_lsid = cookies.get("b_lsid");
126
- if (!_uuid) {
127
- _uuid = generateUuid();
128
- cookies.set("_uuid", _uuid);
33
+ async xpassportCookieRefresh(params) {
34
+ const url = new URL("https://passport.bilibili.com/x/passport-login/web/cookie/refresh");
35
+ url.searchParams.set("csrf", params.csrf);
36
+ url.searchParams.set("refresh_csrf", params.refresh_csrf);
37
+ url.searchParams.set("source", params.source || "main_web");
38
+ url.searchParams.set("refresh_token", params.refresh_token);
39
+ const res = await this.request(url);
40
+ const data = unwrapRequestData(res);
41
+ this.cookies.setFromHeaders(res.headers);
42
+ return data;
129
43
  }
130
- if (!buvid_fp) {
131
- const fpString = fp
132
- .map((e) => {
133
- return e.value;
134
- })
135
- .join("");
136
- buvid_fp = x64hash128(fpString, 31);
137
- cookies.set("buvid_fp", buvid_fp);
44
+ async xpassportConfirmRefresh(params) {
45
+ const url = new URL("https://passport.bilibili.com/x/passport-login/web/confirm/refresh");
46
+ url.searchParams.set("csrf", params.csrf);
47
+ url.searchParams.set("refresh_token", params.refresh_token);
48
+ const res = await this.request(url);
49
+ return unwrapRequestData(res);
138
50
  }
139
- if (!b_lsid) {
140
- b_lsid = generateLsid();
141
- cookies.set("b_lsid", b_lsid);
51
+ async refreshCookie({ timestamp, refresh_token, }) {
52
+ // 生成CorrespondPath
53
+ const correspondPath = await getCorrespondPath(timestamp);
54
+ // 获取refresh_csrf
55
+ const correspondRes = await this.wwwCorrespond(correspondPath);
56
+ const correspondHtmlText = await correspondRes.text();
57
+ const refresh_csrf = await correspondRefreshCsrf(correspondHtmlText);
58
+ // 刷新cookie
59
+ const { refresh_token: new_refresh_token } = (await this.xpassportCookieRefresh({
60
+ csrf: this.cookies.get("bili_jct"),
61
+ refresh_csrf,
62
+ refresh_token,
63
+ })).data;
64
+ // 确认更新
65
+ await this.xpassportConfirmRefresh({
66
+ csrf: this.cookies.get("bili_jct"),
67
+ refresh_token,
68
+ });
69
+ return { cookie: this.cookie, refresh_token: new_refresh_token };
142
70
  }
143
- return {
144
- _uuid,
145
- buvid_fp,
146
- b_lsid,
147
- };
148
71
  }
149
- /** 激活buvid */
150
- export function requestExClimbWuzhi(payload, options) {
151
- customFetch(options, "https://api.bilibili.com/x/internal/gaia-gateway/ExClimbWuzhi", {
152
- method: "POST",
153
- body: payload,
154
- credentials: "include",
155
- headers: {
156
- Accept: "*/*",
157
- "Accept-Encoding": "gzip, deflate, br, zstd",
158
- "Accept-Language": "zh-CN,zh;q=0.9",
159
- "Content-Type": "application/json;charset=UTF-8",
160
- Origin: "https://www.bilibili.com",
161
- priority: "u=1, i",
162
- Referer: "https://www.bilibili.com/",
163
- "Sec-Ch-Ua": `"Microsoft Edge";v="135", "Not-A.Brand";v="8", "Chromium";v="135"`,
164
- "Sec-Ch-Mobile": "?0",
165
- "Sec-Ch-Platform": `"Windows"`,
166
- "Sec-Ch-Dest": "empty",
167
- "Sec-Ch-Mode": "cors",
168
- "Sec-Fetch-Site": "same-site",
169
- },
170
- }).then((res) => res.json());
72
+ const correspondRefreshCsrfRegex = /\<div id="1-name"\>([0-9a-zA-Z]+)\<\/div\>/;
73
+ export async function correspondRefreshCsrf(html) {
74
+ const csrf = html.match(correspondRefreshCsrfRegex)?.[1];
75
+ if (!csrf)
76
+ throw new Error("无法提取refresh_csrf");
77
+ return csrf;
171
78
  }
172
- export function getRoomBaseInfo(room_id, options) {
173
- return customFetch(options, `https://api.live.bilibili.com/xlive/web-room/v1/index/getRoomBaseInfo?room_ids=${room_id}&req_biz=video`)
174
- .then((res) => res.json())
175
- .then((data) => data.data)
176
- .then((data) => {
177
- for (const k in data.by_room_ids) {
178
- return data.by_room_ids[k];
79
+ export async function fetchLiveRoomData(client, id) {
80
+ try {
81
+ const res = await client.xliveGetInfoByRoom({
82
+ room_id: id,
83
+ });
84
+ return parseGetInfoByRoom(res.data);
85
+ }
86
+ catch (e) {
87
+ // fallback
88
+ const res = await client.xliveGetRoomBaseInfo({
89
+ room_ids: [id],
90
+ });
91
+ for (const r in res.data.by_room_ids) {
92
+ return parseRoomBaseInfo(res.data.by_room_ids[r]);
179
93
  }
180
- });
94
+ throw new Error("房间不存在");
95
+ }
181
96
  }
@@ -1,98 +0,0 @@
1
- interface FpInfoMap {
2
- userAgent: string;
3
- webdriver: boolean;
4
- language: string;
5
- colorDepth: number;
6
- deviceMemory: number;
7
- hardwareConcurrency: number;
8
- screenResolution: [number, number];
9
- availableScreenResolution: [number, number];
10
- timezoneOffset: number;
11
- timezone: string;
12
- sessionStorage: boolean;
13
- localStorage: boolean;
14
- indexedDb: boolean;
15
- addBehavior: boolean;
16
- openDatabase: boolean;
17
- cpuClass: string;
18
- platform: string;
19
- plugins: [string, string, [string, string][]][];
20
- canvas: [string, string];
21
- webgl: string[];
22
- webglVendorAndRenderer: string;
23
- hasLiedLanguages: boolean;
24
- hasLiedResolution: boolean;
25
- hasLiedOs: boolean;
26
- hasLiedBrowser: boolean;
27
- touchSupport: [number, boolean, boolean];
28
- fonts: string[];
29
- audio: string;
30
- }
31
- type FpItem<K extends keyof FpInfoMap> = {
32
- key: K;
33
- value: FpInfoMap[K];
34
- };
35
- export type FpInfo = FpItem<keyof FpInfoMap>[];
36
- export declare function fpGet<K extends keyof FpInfoMap>(fp: FpInfo, key: K): FpInfoMap[K];
37
- export declare function getExClimbWuzhiPayload(fp: FpInfo, info: {
38
- uuid: string;
39
- timestamp: string;
40
- browser_resolution: string;
41
- abtest: string;
42
- }): {
43
- "3064": number;
44
- "5062": string;
45
- "03bf": string;
46
- "39c8": string;
47
- "34f1": string;
48
- d402: string;
49
- "654a": string;
50
- "6e7c": string;
51
- "3c43": {
52
- "2673": number;
53
- "5766": number;
54
- "6527": number;
55
- "7003": number;
56
- "807e": number;
57
- b8ce: string;
58
- "641c": number;
59
- "07a4": string;
60
- "1c57": number;
61
- "0bd0": number;
62
- "748e": [number, number];
63
- d61f: [number, number];
64
- fc9d: number;
65
- "6aa9": string;
66
- "75b8": number;
67
- "3b21": number;
68
- "8a1c": number;
69
- d52f: string;
70
- adca: string;
71
- "80c9": [string, string, [string, string][]][];
72
- "13ab": string;
73
- bfe9: string;
74
- a3c1: string[];
75
- "6bc5": string;
76
- ed31: number;
77
- "72bd": number;
78
- "097b": number;
79
- "52cd": number[];
80
- a658: string[];
81
- d02f: string;
82
- };
83
- "54ef": string;
84
- "8b94": string;
85
- df35: string;
86
- "07a4": string;
87
- "5f45": null;
88
- db46: number;
89
- };
90
- /** 生成虚拟指纹信息 */
91
- export declare function generateFakeFpInfo(info: {
92
- canvas: string;
93
- webgl: string;
94
- userAgent: string;
95
- platform: string;
96
- }): FpInfo;
97
- export declare const mockFpInfo: FpInfo;
98
- export {};