@bililive-tools/douyin-recorder 1.5.2 → 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 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。
@@ -94,28 +95,30 @@ export async function getRoomInfo(webRoomId, opts = {}) {
94
95
  // 所以在这里请求一次自动设置。
95
96
  cookies = await getCookie();
96
97
  }
97
- const res = await requester.get("https://live.douyin.com/webcast/room/web/enter/", {
98
- 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
- a_bogus: "1",
116
- },
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}`, {
117
119
  headers: {
118
120
  cookie: cookies,
121
+ "User-Agent": ua,
119
122
  },
120
123
  });
121
124
  // 无 cookie 时 code 为 10037
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.2",
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
+ "sm-crypto": "^0.3.13",
40
41
  "douyin-danma-listener": "0.2.0",
41
- "@bililive-tools/manager": "^1.4.1"
42
+ "@bililive-tools/manager": "^1.5.0"
42
43
  },
43
44
  "devDependencies": {
44
45
  "@types/node": "*"