@bililive-tools/douyin-recorder 1.5.1 → 1.5.3
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/lib/douyin_api.js +31 -21
- package/lib/index.js +28 -3
- package/lib/sign.d.ts +35 -0
- package/lib/sign.js +295 -0
- package/package.json +4 -3
package/lib/douyin_api.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
2
|
import { isEmpty } from "lodash-es";
|
|
3
3
|
import { assert } from "./utils.js";
|
|
4
|
+
import { ABogus } from "./sign.js";
|
|
4
5
|
const requester = axios.create({
|
|
5
6
|
timeout: 10e3,
|
|
6
7
|
// axios 会自动读取环境变量中的 http_proxy 和 https_proxy 并应用,这会让请求发往代理的 host。
|
|
@@ -71,6 +72,13 @@ export const getCookie = async () => {
|
|
|
71
72
|
return cookie.split(";")[0];
|
|
72
73
|
})
|
|
73
74
|
.join("; ");
|
|
75
|
+
if (!cookies.includes("ttwid")) {
|
|
76
|
+
// 如果不含ttwid,且已经存在含ttwid的cookie,将缓存时间直接增加1小时,复用之前的参数
|
|
77
|
+
if (cookieCache?.cookies) {
|
|
78
|
+
cookieCache.startTimestamp += 60 * 60 * 1000; // 增加1小时
|
|
79
|
+
return cookieCache.cookies;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
74
82
|
cookieCache = {
|
|
75
83
|
startTimestamp: now,
|
|
76
84
|
cookies,
|
|
@@ -87,28 +95,30 @@ export async function getRoomInfo(webRoomId, opts = {}) {
|
|
|
87
95
|
// 所以在这里请求一次自动设置。
|
|
88
96
|
cookies = await getCookie();
|
|
89
97
|
}
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
98
|
+
const params = {
|
|
99
|
+
aid: 6383,
|
|
100
|
+
live_id: 1,
|
|
101
|
+
device_platform: "web",
|
|
102
|
+
language: "zh-CN",
|
|
103
|
+
enter_from: "web_live",
|
|
104
|
+
cookie_enabled: "true",
|
|
105
|
+
screen_width: 1920,
|
|
106
|
+
screen_height: 1080,
|
|
107
|
+
browser_language: "zh-CN",
|
|
108
|
+
browser_platform: "MacIntel",
|
|
109
|
+
browser_name: "Chrome",
|
|
110
|
+
browser_version: "108.0.0.0",
|
|
111
|
+
web_rid: webRoomId,
|
|
112
|
+
// enter_source:,
|
|
113
|
+
"Room-Enter-User-Login-Ab": 0,
|
|
114
|
+
is_need_double_stream: "false",
|
|
115
|
+
};
|
|
116
|
+
const abogus = new ABogus();
|
|
117
|
+
const [query, _, ua] = abogus.generateAbogus(new URLSearchParams(params).toString(), "");
|
|
118
|
+
const res = await requester.get(`https://live.douyin.com/webcast/room/web/enter/?${query}`, {
|
|
110
119
|
headers: {
|
|
111
120
|
cookie: cookies,
|
|
121
|
+
"User-Agent": ua,
|
|
112
122
|
},
|
|
113
123
|
});
|
|
114
124
|
// 无 cookie 时 code 为 10037
|
|
@@ -126,7 +136,7 @@ export async function getRoomInfo(webRoomId, opts = {}) {
|
|
|
126
136
|
doubleScreen: opts.doubleScreen,
|
|
127
137
|
});
|
|
128
138
|
}
|
|
129
|
-
assert(res.data.status_code === 0, `Unexpected resp, code ${res.data.status_code}, msg ${JSON.stringify(res.data.data)}, id ${webRoomId}`);
|
|
139
|
+
assert(res.data.status_code === 0, `Unexpected resp, code ${res.data.status_code}, msg ${JSON.stringify(res.data.data)}, id ${webRoomId}, cookies: ${cookies}`);
|
|
130
140
|
const data = res.data.data;
|
|
131
141
|
const room = data.data[0];
|
|
132
142
|
assert(room, `No room data, id ${webRoomId}`);
|
package/lib/index.js
CHANGED
|
@@ -253,7 +253,31 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isMan
|
|
|
253
253
|
client.on("reconnect", (attempts) => {
|
|
254
254
|
this.emit("DebugLog", {
|
|
255
255
|
type: "common",
|
|
256
|
-
text: `danma has reconnect ${attempts}`,
|
|
256
|
+
text: `douyin ${this.channelId} danma has reconnect ${attempts}`,
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
client.on("error", (err) => {
|
|
260
|
+
this.emit("DebugLog", {
|
|
261
|
+
type: "common",
|
|
262
|
+
text: `douyin ${this.channelId} danma error: ${String(err)}`,
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
client.on("init", (url) => {
|
|
266
|
+
this.emit("DebugLog", {
|
|
267
|
+
type: "common",
|
|
268
|
+
text: `douyin ${this.channelId} danma init ${url}`,
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
client.on("open", () => {
|
|
272
|
+
this.emit("DebugLog", {
|
|
273
|
+
type: "common",
|
|
274
|
+
text: `douyin ${this.channelId} danma open`,
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
client.on("close", () => {
|
|
278
|
+
this.emit("DebugLog", {
|
|
279
|
+
type: "common",
|
|
280
|
+
text: `douyin danma close`,
|
|
257
281
|
});
|
|
258
282
|
});
|
|
259
283
|
// client.on("open", () => {
|
|
@@ -287,8 +311,8 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isMan
|
|
|
287
311
|
if (!this.recordHandle)
|
|
288
312
|
return;
|
|
289
313
|
this.state = "stopping-record";
|
|
290
|
-
client.close();
|
|
291
314
|
try {
|
|
315
|
+
client.close();
|
|
292
316
|
await recorder.stop();
|
|
293
317
|
}
|
|
294
318
|
catch (err) {
|
|
@@ -323,7 +347,7 @@ export const provider = {
|
|
|
323
347
|
siteURL: "https://live.douyin.com/",
|
|
324
348
|
matchURL(channelURL) {
|
|
325
349
|
// 支持 v.douyin.com 和 live.douyin.com
|
|
326
|
-
return /https?:\/\/(live|v)\.douyin\.com\//.test(channelURL);
|
|
350
|
+
return /https?:\/\/(live|v|www)\.douyin\.com\//.test(channelURL);
|
|
327
351
|
},
|
|
328
352
|
async resolveChannelInfoFromURL(channelURL) {
|
|
329
353
|
if (!this.matchURL(channelURL))
|
|
@@ -347,6 +371,7 @@ export const provider = {
|
|
|
347
371
|
id: info.roomId,
|
|
348
372
|
title: info.title,
|
|
349
373
|
owner: info.owner,
|
|
374
|
+
avatar: info.avatar,
|
|
350
375
|
};
|
|
351
376
|
},
|
|
352
377
|
createRecorder(opts) {
|
package/lib/sign.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export declare class StringProcessor {
|
|
2
|
+
static toCharStr(bytes: Uint8Array): string;
|
|
3
|
+
static toCharArray(s: string): number[];
|
|
4
|
+
static generateRandomBytes(length: number): string;
|
|
5
|
+
}
|
|
6
|
+
export declare class CryptoUtility {
|
|
7
|
+
salt: string;
|
|
8
|
+
base64Alphabet: string[][];
|
|
9
|
+
bigArray: number[];
|
|
10
|
+
constructor(salt: string, customBase64Alphabet: string[]);
|
|
11
|
+
static sm3ToArray(input: Uint8Array | string): number[];
|
|
12
|
+
addSalt(param: string): string;
|
|
13
|
+
paramsToArray(param: string, addSalt: boolean): number[];
|
|
14
|
+
transformBytes(valuesList: number[]): number[];
|
|
15
|
+
base64Encode(bytes: Uint8Array, selectedAlphabet: number): string;
|
|
16
|
+
abogusEncode(values: number[], selectedAlphabet: number): string;
|
|
17
|
+
static rc4Encrypt(key: number[], plaintext: string): Uint8Array;
|
|
18
|
+
}
|
|
19
|
+
export declare class BrowserFingerprintGenerator {
|
|
20
|
+
static generateFingerprint(browserType: string): string;
|
|
21
|
+
private static _generateFingerprint;
|
|
22
|
+
}
|
|
23
|
+
export declare class ABogus {
|
|
24
|
+
cryptoUtility: CryptoUtility;
|
|
25
|
+
userAgent: string;
|
|
26
|
+
browserFp: string;
|
|
27
|
+
options: number[];
|
|
28
|
+
pageId: number;
|
|
29
|
+
aid: number;
|
|
30
|
+
uaKey: number[];
|
|
31
|
+
sortIndex: number[];
|
|
32
|
+
sortIndex2: number[];
|
|
33
|
+
constructor(fp?: string, userAgent?: string, options?: number[]);
|
|
34
|
+
generateAbogus(params: string, body: string): [string, string, string, string];
|
|
35
|
+
}
|
package/lib/sign.js
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
// abogus.ts
|
|
2
|
+
// 这是对 Rust 版 ABogus 的 TypeScript 实现
|
|
3
|
+
// 代码来自:https://github.com/hua0512/rust-srec/blob/6444641014ea58628af9b0fa51b099620a01d0d0/crates/platforms/src/extractor/platforms/douyin/abogus.rs
|
|
4
|
+
// 依赖 sm3、rc4、random 等库
|
|
5
|
+
import { sm3 } from "sm-crypto"; // npm install sm-crypto
|
|
6
|
+
export class StringProcessor {
|
|
7
|
+
static toCharStr(bytes) {
|
|
8
|
+
return Array.from(bytes)
|
|
9
|
+
.map((b) => String.fromCharCode(b))
|
|
10
|
+
.join("");
|
|
11
|
+
}
|
|
12
|
+
static toCharArray(s) {
|
|
13
|
+
return Array.from(s).map((c) => c.charCodeAt(0));
|
|
14
|
+
}
|
|
15
|
+
static generateRandomBytes(length) {
|
|
16
|
+
const result = [];
|
|
17
|
+
for (let i = 0; i < length; i++) {
|
|
18
|
+
const rd = Math.floor(Math.random() * 10000);
|
|
19
|
+
result.push((rd & 255 & 170) | 1);
|
|
20
|
+
result.push((rd & 255 & 85) | 2);
|
|
21
|
+
result.push(((rd >> 8) & 170) | 5);
|
|
22
|
+
result.push(((rd >> 8) & 85) | 40);
|
|
23
|
+
}
|
|
24
|
+
return this.toCharStr(Uint8Array.from(result));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export class CryptoUtility {
|
|
28
|
+
salt;
|
|
29
|
+
base64Alphabet;
|
|
30
|
+
bigArray;
|
|
31
|
+
constructor(salt, customBase64Alphabet) {
|
|
32
|
+
this.salt = salt;
|
|
33
|
+
this.base64Alphabet = customBase64Alphabet.map((s) => Array.from(s));
|
|
34
|
+
this.bigArray = [
|
|
35
|
+
121, 243, 55, 234, 103, 36, 47, 228, 30, 231, 106, 6, 115, 95, 78, 101, 250, 207, 198, 50,
|
|
36
|
+
139, 227, 220, 105, 97, 143, 34, 28, 194, 215, 18, 100, 159, 160, 43, 8, 169, 217, 180, 120,
|
|
37
|
+
247, 45, 90, 11, 27, 197, 46, 3, 84, 72, 5, 68, 62, 56, 221, 75, 144, 79, 73, 161, 178, 81,
|
|
38
|
+
64, 187, 134, 117, 186, 118, 16, 241, 130, 71, 89, 147, 122, 129, 65, 40, 88, 150, 110, 219,
|
|
39
|
+
199, 255, 181, 254, 48, 4, 195, 248, 208, 32, 116, 167, 69, 201, 17, 124, 125, 104, 96, 83,
|
|
40
|
+
80, 127, 236, 108, 154, 126, 204, 15, 20, 135, 112, 158, 13, 1, 188, 164, 210, 237, 222, 98,
|
|
41
|
+
212, 77, 253, 42, 170, 202, 26, 22, 29, 182, 251, 10, 173, 152, 58, 138, 54, 141, 185, 33,
|
|
42
|
+
157, 31, 252, 132, 233, 235, 102, 196, 191, 223, 240, 148, 39, 123, 92, 82, 128, 109, 57, 24,
|
|
43
|
+
38, 113, 209, 245, 2, 119, 153, 229, 189, 214, 230, 174, 232, 63, 52, 205, 86, 140, 66, 175,
|
|
44
|
+
111, 171, 246, 133, 238, 193, 99, 60, 74, 91, 225, 51, 76, 37, 145, 211, 166, 151, 213, 206,
|
|
45
|
+
0, 200, 244, 176, 218, 44, 184, 172, 49, 216, 93, 168, 53, 21, 183, 41, 67, 85, 224, 155, 226,
|
|
46
|
+
242, 87, 177, 146, 70, 190, 12, 162, 19, 137, 114, 25, 165, 163, 192, 23, 59, 9, 94, 179, 107,
|
|
47
|
+
35, 7, 142, 131, 239, 203, 149, 136, 61, 249, 14, 156,
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
static sm3ToArray(input) {
|
|
51
|
+
const hash = sm3(input instanceof Uint8Array ? Buffer.from(input) : input);
|
|
52
|
+
return Buffer.from(hash, "hex").toJSON().data;
|
|
53
|
+
}
|
|
54
|
+
addSalt(param) {
|
|
55
|
+
return param + this.salt;
|
|
56
|
+
}
|
|
57
|
+
paramsToArray(param, addSalt) {
|
|
58
|
+
const processed = addSalt ? this.addSalt(param) : param;
|
|
59
|
+
return CryptoUtility.sm3ToArray(processed);
|
|
60
|
+
}
|
|
61
|
+
transformBytes(valuesList) {
|
|
62
|
+
const result = [];
|
|
63
|
+
let indexB = this.bigArray[1];
|
|
64
|
+
let initialValue = 0, valueE = 0;
|
|
65
|
+
const arrayLen = this.bigArray.length;
|
|
66
|
+
for (let index = 0; index < valuesList.length; index++) {
|
|
67
|
+
let sumInitial;
|
|
68
|
+
if (index === 0) {
|
|
69
|
+
initialValue = this.bigArray[indexB];
|
|
70
|
+
sumInitial = indexB + initialValue;
|
|
71
|
+
this.bigArray[1] = initialValue;
|
|
72
|
+
this.bigArray[indexB] = indexB;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
sumInitial = initialValue + valueE;
|
|
76
|
+
}
|
|
77
|
+
const sumInitialIdx = sumInitial % arrayLen;
|
|
78
|
+
const valueF = this.bigArray[sumInitialIdx];
|
|
79
|
+
result.push(valuesList[index] ^ valueF);
|
|
80
|
+
const nextIdx = (index + 2) % arrayLen;
|
|
81
|
+
valueE = this.bigArray[nextIdx];
|
|
82
|
+
const newSumInitialIdx = (indexB + valueE) % arrayLen;
|
|
83
|
+
initialValue = this.bigArray[newSumInitialIdx];
|
|
84
|
+
[this.bigArray[newSumInitialIdx], this.bigArray[nextIdx]] = [
|
|
85
|
+
this.bigArray[nextIdx],
|
|
86
|
+
this.bigArray[newSumInitialIdx],
|
|
87
|
+
];
|
|
88
|
+
indexB = newSumInitialIdx;
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
base64Encode(bytes, selectedAlphabet) {
|
|
93
|
+
const alphabet = this.base64Alphabet[selectedAlphabet];
|
|
94
|
+
let output = "";
|
|
95
|
+
for (let i = 0; i < bytes.length; i += 3) {
|
|
96
|
+
const b1 = bytes[i];
|
|
97
|
+
const b2 = bytes[i + 1] || 0;
|
|
98
|
+
const b3 = bytes[i + 2] || 0;
|
|
99
|
+
const combined = (b1 << 16) | (b2 << 8) | b3;
|
|
100
|
+
output += alphabet[(combined >> 18) & 63];
|
|
101
|
+
output += alphabet[(combined >> 12) & 63];
|
|
102
|
+
output += i + 1 < bytes.length ? alphabet[(combined >> 6) & 63] : "";
|
|
103
|
+
output += i + 2 < bytes.length ? alphabet[combined & 63] : "";
|
|
104
|
+
}
|
|
105
|
+
while (output.length % 4 !== 0)
|
|
106
|
+
output += "=";
|
|
107
|
+
return output;
|
|
108
|
+
}
|
|
109
|
+
abogusEncode(values, selectedAlphabet) {
|
|
110
|
+
const alphabet = this.base64Alphabet[selectedAlphabet];
|
|
111
|
+
let abogus = "";
|
|
112
|
+
for (let i = 0; i < values.length; i += 3) {
|
|
113
|
+
const v1 = values[i];
|
|
114
|
+
const v2 = values[i + 1] || 0;
|
|
115
|
+
const v3 = values[i + 2] || 0;
|
|
116
|
+
const n = (v1 << 16) | (v2 << 8) | v3;
|
|
117
|
+
abogus += alphabet[(n & 0xfc0000) >> 18];
|
|
118
|
+
abogus += alphabet[(n & 0x03f000) >> 12];
|
|
119
|
+
abogus += i + 1 < values.length ? alphabet[(n & 0x0fc0) >> 6] : "";
|
|
120
|
+
abogus += i + 2 < values.length ? alphabet[n & 0x3f] : "";
|
|
121
|
+
}
|
|
122
|
+
while (abogus.length % 4 !== 0)
|
|
123
|
+
abogus += "=";
|
|
124
|
+
return abogus;
|
|
125
|
+
}
|
|
126
|
+
static rc4Encrypt(key, plaintext) {
|
|
127
|
+
// 推荐用 npm 包 rc4 或自己实现
|
|
128
|
+
const S = Array.from({ length: 256 }, (_, i) => i);
|
|
129
|
+
let j = 0;
|
|
130
|
+
for (let i = 0; i < 256; i++) {
|
|
131
|
+
j = (j + S[i] + key[i % key.length]) & 0xff;
|
|
132
|
+
[S[i], S[j]] = [S[j], S[i]];
|
|
133
|
+
}
|
|
134
|
+
let i = 0;
|
|
135
|
+
j = 0;
|
|
136
|
+
const ptBytes = StringProcessor.toCharArray(plaintext);
|
|
137
|
+
const ct = [];
|
|
138
|
+
for (const charVal of ptBytes) {
|
|
139
|
+
i = (i + 1) & 0xff;
|
|
140
|
+
j = (j + S[i]) & 0xff;
|
|
141
|
+
[S[i], S[j]] = [S[j], S[i]];
|
|
142
|
+
const k = S[(S[i] + S[j]) & 0xff];
|
|
143
|
+
ct.push(charVal ^ k);
|
|
144
|
+
}
|
|
145
|
+
return Uint8Array.from(ct);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
export class BrowserFingerprintGenerator {
|
|
149
|
+
static generateFingerprint(browserType) {
|
|
150
|
+
switch (browserType) {
|
|
151
|
+
case "Chrome":
|
|
152
|
+
case "Edge":
|
|
153
|
+
case "Firefox":
|
|
154
|
+
return this._generateFingerprint("Win32");
|
|
155
|
+
case "Safari":
|
|
156
|
+
return this._generateFingerprint("MacIntel");
|
|
157
|
+
default:
|
|
158
|
+
return this._generateFingerprint("Win32");
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
static _generateFingerprint(platform) {
|
|
162
|
+
const rand = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
|
|
163
|
+
const innerWidth = rand(1024, 1920);
|
|
164
|
+
const innerHeight = rand(768, 1080);
|
|
165
|
+
const outerWidth = innerWidth + rand(24, 32);
|
|
166
|
+
const outerHeight = innerHeight + rand(75, 90);
|
|
167
|
+
const screenX = 0;
|
|
168
|
+
const screenY = [0, 30][rand(0, 1)];
|
|
169
|
+
const sizeWidth = rand(1024, 1920);
|
|
170
|
+
const sizeHeight = rand(768, 1080);
|
|
171
|
+
const availWidth = rand(1280, 1920);
|
|
172
|
+
const availHeight = rand(800, 1080);
|
|
173
|
+
return `${innerWidth}|${innerHeight}|${outerWidth}|${outerHeight}|${screenX}|${screenY}|0|0|${sizeWidth}|${sizeHeight}|${availWidth}|${availHeight}|${innerWidth}|${innerHeight}|24|24|${platform}`;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
export class ABogus {
|
|
177
|
+
cryptoUtility;
|
|
178
|
+
userAgent;
|
|
179
|
+
browserFp;
|
|
180
|
+
options;
|
|
181
|
+
pageId;
|
|
182
|
+
aid;
|
|
183
|
+
uaKey;
|
|
184
|
+
sortIndex;
|
|
185
|
+
sortIndex2;
|
|
186
|
+
constructor(fp, userAgent, options) {
|
|
187
|
+
const salt = "cus";
|
|
188
|
+
const character = "Dkdpgh2ZmsQB80/MfvV36XI1R45-WUAlEixNLwoqYTOPuzKFjJnry79HbGcaStCe";
|
|
189
|
+
const character2 = "ckdp1h4ZKsUB80/Mfvw36XIgR25+WQAlEi7NLboqYTOPuzmFjJnryx9HVGDaStCe";
|
|
190
|
+
const characterList = [character, character2];
|
|
191
|
+
this.cryptoUtility = new CryptoUtility(salt, characterList);
|
|
192
|
+
this.userAgent =
|
|
193
|
+
userAgent && userAgent.length > 0
|
|
194
|
+
? userAgent
|
|
195
|
+
: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0";
|
|
196
|
+
this.browserFp =
|
|
197
|
+
fp && fp.length > 0 ? fp : BrowserFingerprintGenerator.generateFingerprint("Edge");
|
|
198
|
+
this.options = options || [0, 1, 14];
|
|
199
|
+
this.pageId = 0;
|
|
200
|
+
this.aid = 6383;
|
|
201
|
+
this.uaKey = [0x00, 0x01, 0x0e];
|
|
202
|
+
this.sortIndex = [
|
|
203
|
+
18, 20, 52, 26, 30, 34, 58, 38, 40, 53, 42, 21, 27, 54, 55, 31, 35, 57, 39, 41, 43, 22, 28,
|
|
204
|
+
32, 60, 36, 23, 29, 33, 37, 44, 45, 59, 46, 47, 48, 49, 50, 24, 25, 65, 66, 70, 71,
|
|
205
|
+
];
|
|
206
|
+
this.sortIndex2 = [
|
|
207
|
+
18, 20, 26, 30, 34, 38, 40, 42, 21, 27, 31, 35, 39, 41, 43, 22, 28, 32, 36, 23, 29, 33, 37,
|
|
208
|
+
44, 45, 46, 47, 48, 49, 50, 24, 25, 52, 53, 54, 55, 57, 58, 59, 60, 65, 66, 70, 71,
|
|
209
|
+
];
|
|
210
|
+
}
|
|
211
|
+
generateAbogus(params, body) {
|
|
212
|
+
const abDir = {
|
|
213
|
+
8: 3,
|
|
214
|
+
18: 44,
|
|
215
|
+
66: 0,
|
|
216
|
+
69: 0,
|
|
217
|
+
70: 0,
|
|
218
|
+
71: 0,
|
|
219
|
+
};
|
|
220
|
+
const startEncryption = Date.now();
|
|
221
|
+
// Hash(Hash(params))
|
|
222
|
+
const paramsHash1 = this.cryptoUtility.paramsToArray(params, true);
|
|
223
|
+
// @ts-ignore
|
|
224
|
+
const array1 = CryptoUtility.sm3ToArray(paramsHash1);
|
|
225
|
+
// Hash(Hash(body))
|
|
226
|
+
const bodyHash1 = this.cryptoUtility.paramsToArray(body, true);
|
|
227
|
+
// @ts-ignore
|
|
228
|
+
const array2 = CryptoUtility.sm3ToArray(bodyHash1);
|
|
229
|
+
// Hash(Base64(RC4(user_agent)))
|
|
230
|
+
const rc4Ua = CryptoUtility.rc4Encrypt(this.uaKey, this.userAgent);
|
|
231
|
+
const uaB64 = this.cryptoUtility.base64Encode(rc4Ua, 1);
|
|
232
|
+
const array3 = this.cryptoUtility.paramsToArray(uaB64, false);
|
|
233
|
+
const endEncryption = Date.now();
|
|
234
|
+
// 动态填充 abDir
|
|
235
|
+
abDir[20] = (startEncryption >> 24) & 255;
|
|
236
|
+
abDir[21] = (startEncryption >> 16) & 255;
|
|
237
|
+
abDir[22] = (startEncryption >> 8) & 255;
|
|
238
|
+
abDir[23] = startEncryption & 255;
|
|
239
|
+
abDir[24] = Math.floor(startEncryption / 0x100000000);
|
|
240
|
+
abDir[25] = Math.floor(startEncryption / 0x10000000000);
|
|
241
|
+
abDir[26] = (this.options[0] >> 24) & 255;
|
|
242
|
+
abDir[27] = (this.options[0] >> 16) & 255;
|
|
243
|
+
abDir[28] = (this.options[0] >> 8) & 255;
|
|
244
|
+
abDir[29] = this.options[0] & 255;
|
|
245
|
+
abDir[30] = Math.floor(this.options[1] / 256) & 255;
|
|
246
|
+
abDir[31] = this.options[1] % 256;
|
|
247
|
+
abDir[32] = (this.options[1] >> 24) & 255;
|
|
248
|
+
abDir[33] = (this.options[1] >> 16) & 255;
|
|
249
|
+
abDir[34] = (this.options[2] >> 24) & 255;
|
|
250
|
+
abDir[35] = (this.options[2] >> 16) & 255;
|
|
251
|
+
abDir[36] = (this.options[2] >> 8) & 255;
|
|
252
|
+
abDir[37] = this.options[2] & 255;
|
|
253
|
+
abDir[38] = array1[21];
|
|
254
|
+
abDir[39] = array1[22];
|
|
255
|
+
abDir[40] = array2[21];
|
|
256
|
+
abDir[41] = array2[22];
|
|
257
|
+
abDir[42] = array3[23];
|
|
258
|
+
abDir[43] = array3[24];
|
|
259
|
+
abDir[44] = (endEncryption >> 24) & 255;
|
|
260
|
+
abDir[45] = (endEncryption >> 16) & 255;
|
|
261
|
+
abDir[46] = (endEncryption >> 8) & 255;
|
|
262
|
+
abDir[47] = endEncryption & 255;
|
|
263
|
+
abDir[48] = abDir[8];
|
|
264
|
+
abDir[49] = Math.floor(endEncryption / 0x100000000);
|
|
265
|
+
abDir[50] = Math.floor(endEncryption / 0x10000000000);
|
|
266
|
+
abDir[51] = (this.pageId >> 24) & 255;
|
|
267
|
+
abDir[52] = (this.pageId >> 16) & 255;
|
|
268
|
+
abDir[53] = (this.pageId >> 8) & 255;
|
|
269
|
+
abDir[54] = this.pageId & 255;
|
|
270
|
+
abDir[55] = this.pageId;
|
|
271
|
+
abDir[56] = this.aid;
|
|
272
|
+
abDir[57] = this.aid & 255;
|
|
273
|
+
abDir[58] = (this.aid >> 8) & 255;
|
|
274
|
+
abDir[59] = (this.aid >> 16) & 255;
|
|
275
|
+
abDir[60] = (this.aid >> 24) & 255;
|
|
276
|
+
abDir[64] = this.browserFp.length;
|
|
277
|
+
abDir[65] = this.browserFp.length;
|
|
278
|
+
const sortedValues = this.sortIndex.map((i) => abDir[i] || 0);
|
|
279
|
+
const fpArray = StringProcessor.toCharArray(this.browserFp);
|
|
280
|
+
let abXor = 0;
|
|
281
|
+
this.sortIndex2.forEach((key, idx) => {
|
|
282
|
+
const val = abDir[key] || 0;
|
|
283
|
+
abXor = idx === 0 ? val : abXor ^ val;
|
|
284
|
+
});
|
|
285
|
+
const allValues = [...sortedValues, ...fpArray, abXor];
|
|
286
|
+
const transformedValues = this.cryptoUtility.transformBytes(allValues);
|
|
287
|
+
const randomPrefix = StringProcessor.generateRandomBytes(3)
|
|
288
|
+
.split("")
|
|
289
|
+
.map((c) => c.charCodeAt(0));
|
|
290
|
+
const finalValues = [...randomPrefix, ...transformedValues];
|
|
291
|
+
const abogus = this.cryptoUtility.abogusEncode(finalValues, 0);
|
|
292
|
+
const finalParams = `${params}&a_bogus=${abogus}`;
|
|
293
|
+
return [finalParams, abogus, this.userAgent, body];
|
|
294
|
+
}
|
|
295
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bililive-tools/douyin-recorder",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.3",
|
|
4
4
|
"description": "@bililive-tools douyin recorder implemention",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -37,8 +37,9 @@
|
|
|
37
37
|
"axios": "^1.7.8",
|
|
38
38
|
"lodash-es": "^4.17.21",
|
|
39
39
|
"mitt": "^3.0.1",
|
|
40
|
-
"
|
|
41
|
-
"douyin-danma-listener": "0.2.0"
|
|
40
|
+
"sm-crypto": "^0.3.13",
|
|
41
|
+
"douyin-danma-listener": "0.2.0",
|
|
42
|
+
"@bililive-tools/manager": "^1.5.0"
|
|
42
43
|
},
|
|
43
44
|
"devDependencies": {
|
|
44
45
|
"@types/node": "*"
|