@bililive-tools/douyin-recorder 1.5.3 → 1.6.0

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/README.md CHANGED
@@ -45,7 +45,9 @@ interface Options {
45
45
  videoFormat?: "auto"; // 视频格式: "auto", "ts", "mkv" ,auto模式下, 分段使用 "ts",不分段使用 "mp4"
46
46
  useServerTimestamp?: boolean; // 控制弹幕是否使用服务端时间戳,默认为true
47
47
  doubleScreen?: boolean; // 是否使用双屏直播流,开启后如果是双屏直播,那么就使用拼接的流,默认为true
48
- auth?: string; // 传递cookie,用于录制会员视频
48
+ recorderType?: "auto" | "ffmpeg" | "mesio"; // 底层录制器,使用mesio时videoFormat参数无效
49
+ auth?: string; // 传递cookie,
50
+ api?: "web" | "webHTML"; // 使用不同的接口
49
51
  }
50
52
  ```
51
53
 
@@ -71,7 +73,7 @@ interface Options {
71
73
  import { provider } from "@bililive-tools/douyin-recorder";
72
74
 
73
75
  const url = "https://live.douyin.com/203641303310";
74
- // 同样支持解析 https://v.douyin.com/DpfoBLAXoHM/
76
+ // 同样支持解析 https://v.douyin.com/DpfoBLAXoHM/, https://www.douyin.com/user/MS4wLjABAAAAE2ebAEBniL_0rF0vIDV4vCpdcH5RxpYBovopAURblNs
75
77
  const { id } = await provider.resolveChannelInfoFromURL(url);
76
78
  ```
77
79
 
@@ -6,9 +6,9 @@
6
6
  export declare function resolveShortURL(shortURL: string): Promise<string>;
7
7
  export declare const getCookie: () => Promise<string>;
8
8
  export declare function getRoomInfo(webRoomId: string, opts?: {
9
- retryOnSpecialCode?: boolean;
10
9
  auth?: string;
11
10
  doubleScreen?: boolean;
11
+ api?: "web" | "webHTML";
12
12
  }): Promise<{
13
13
  living: boolean;
14
14
  roomId: string;
@@ -20,6 +20,11 @@ export declare function getRoomInfo(webRoomId: string, opts?: {
20
20
  cover: string;
21
21
  liveId: string;
22
22
  }>;
23
+ /**
24
+ * 解析抖音号
25
+ * @param url
26
+ */
27
+ export declare function parseUser(url: string): Promise<any>;
23
28
  export interface StreamProfile {
24
29
  desc: string;
25
30
  key: string;
package/lib/douyin_api.js CHANGED
@@ -1,6 +1,7 @@
1
+ import { URL, URLSearchParams } from "url";
1
2
  import axios from "axios";
2
3
  import { isEmpty } from "lodash-es";
3
- import { assert } from "./utils.js";
4
+ import { assert, get__ac_signature } from "./utils.js";
4
5
  import { ABogus } from "./sign.js";
5
6
  const requester = axios.create({
6
7
  timeout: 10e3,
@@ -19,6 +20,14 @@ const requester = axios.create({
19
20
  export async function resolveShortURL(shortURL) {
20
21
  // 获取跳转后的页面内容
21
22
  const response = await requester.get(shortURL);
23
+ const redirectedURL = response.request.res.responseUrl;
24
+ if (redirectedURL.includes("/user/")) {
25
+ const secUid = new URL(redirectedURL).searchParams.get("sec_uid");
26
+ if (!secUid) {
27
+ throw new Error("无法从短链接解析出直播间ID");
28
+ }
29
+ return parseUser(`https://www.douyin.com/user/${secUid}`);
30
+ }
22
31
  // 尝试从页面内容中提取webRid
23
32
  const webRidMatch = response.data.match(/"webRid\\":\\"(\d+)\\"/);
24
33
  if (webRidMatch) {
@@ -85,7 +94,77 @@ export const getCookie = async () => {
85
94
  };
86
95
  return cookies;
87
96
  };
88
- export async function getRoomInfo(webRoomId, opts = {}) {
97
+ function generateNonce() {
98
+ // 21味随机字母数字组合
99
+ const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
100
+ let nonce = "";
101
+ for (let i = 0; i < 21; i++) {
102
+ nonce += chars.charAt(Math.floor(Math.random() * chars.length));
103
+ }
104
+ return nonce;
105
+ }
106
+ /**
107
+ * 通过解析html页面来获取房间数据
108
+ * @param webRoomId
109
+ * @param opts
110
+ */
111
+ async function getRoomInfoByHtml(webRoomId, opts = {}) {
112
+ const url = `https://live.douyin.com/${webRoomId}`;
113
+ const ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0";
114
+ const nonce = generateNonce();
115
+ let cookies = undefined;
116
+ if (opts.auth) {
117
+ cookies = opts.auth;
118
+ }
119
+ else {
120
+ const timestamp = Math.floor(Date.now() / 1000);
121
+ const signed = get__ac_signature(timestamp, url, nonce, ua);
122
+ cookies = `__ac_nonce=${nonce}; __ac_signature=${signed}; __ac_referer=__ac_blank`;
123
+ }
124
+ const res = await axios.get(url, {
125
+ headers: {
126
+ "User-Agent": ua,
127
+ cookie: cookies,
128
+ },
129
+ });
130
+ const regex = /(\{\\"state\\":.*?)\]\\n"\]\)/;
131
+ const match = res.data.match(regex);
132
+ if (!match) {
133
+ throw new Error("No match found in HTML");
134
+ }
135
+ let jsonStr = match[1];
136
+ jsonStr = jsonStr.replace(/\\"/g, '"');
137
+ jsonStr = jsonStr.replace(/\\"/g, '"');
138
+ try {
139
+ const data = JSON.parse(jsonStr);
140
+ const roomInfo = data.state.roomStore.roomInfo;
141
+ const streamData = data.state.streamStore.streamData;
142
+ return {
143
+ living: roomInfo.room.status === 2,
144
+ nickname: roomInfo.anchor.nickname,
145
+ avatar: roomInfo.anchor?.avatar_thumb?.url_list?.[0],
146
+ room: {
147
+ title: roomInfo.room.title,
148
+ cover: roomInfo.room.cover?.url_list?.[0],
149
+ id_str: roomInfo.room.id_str,
150
+ stream_url: {
151
+ pull_datas: roomInfo.room?.stream_url?.pull_datas,
152
+ live_core_sdk_data: {
153
+ pull_data: {
154
+ options: { qualities: streamData.H264_streamData?.options?.qualities ?? [] },
155
+ stream_data: streamData.H264_streamData?.stream ?? {},
156
+ },
157
+ },
158
+ },
159
+ },
160
+ };
161
+ }
162
+ catch (e) {
163
+ console.error("Failed to parse JSON:", e);
164
+ throw e;
165
+ }
166
+ }
167
+ async function getRoomInfoByWeb(webRoomId, opts = {}) {
89
168
  let cookies = undefined;
90
169
  if (opts.auth) {
91
170
  cookies = opts.auth;
@@ -109,7 +188,6 @@ export async function getRoomInfo(webRoomId, opts = {}) {
109
188
  browser_name: "Chrome",
110
189
  browser_version: "108.0.0.0",
111
190
  web_rid: webRoomId,
112
- // enter_source:,
113
191
  "Room-Enter-User-Login-Ab": 0,
114
192
  is_need_double_stream: "false",
115
193
  };
@@ -121,35 +199,42 @@ export async function getRoomInfo(webRoomId, opts = {}) {
121
199
  "User-Agent": ua,
122
200
  },
123
201
  });
124
- // 无 cookie 时 code 为 10037
125
- if (res.data.status_code === 10037 && opts.retryOnSpecialCode) {
126
- // resp 自动设置 cookie
127
- // const cookieRes = await requester.get("https://live.douyin.com/favicon.ico");
128
- // const cookies = cookieRes.headers["set-cookie"]
129
- // .map((cookie) => {
130
- // return cookie.split(";")[0];
131
- // })
132
- // .join("; ");
133
- // console.log("cookies", cookies);
134
- return getRoomInfo(webRoomId, {
135
- retryOnSpecialCode: false,
136
- doubleScreen: opts.doubleScreen,
137
- });
138
- }
139
202
  assert(res.data.status_code === 0, `Unexpected resp, code ${res.data.status_code}, msg ${JSON.stringify(res.data.data)}, id ${webRoomId}, cookies: ${cookies}`);
140
203
  const data = res.data.data;
141
204
  const room = data.data[0];
142
205
  assert(room, `No room data, id ${webRoomId}`);
206
+ return {
207
+ living: data.room_status === 0,
208
+ nickname: data.user.nickname,
209
+ avatar: data?.user?.avatar_thumb?.url_list?.[0],
210
+ room: {
211
+ title: room.title,
212
+ cover: room.cover?.url_list?.[0],
213
+ id_str: room.id_str,
214
+ stream_url: room.stream_url,
215
+ },
216
+ };
217
+ }
218
+ export async function getRoomInfo(webRoomId, opts = {}) {
219
+ let data;
220
+ if (opts.api === "webHTML") {
221
+ data = await getRoomInfoByHtml(webRoomId, opts);
222
+ }
223
+ else {
224
+ data = await getRoomInfoByWeb(webRoomId, opts);
225
+ }
226
+ const room = data.room;
227
+ assert(room, `No room data, id ${webRoomId}`);
143
228
  if (room?.stream_url == null) {
144
229
  return {
145
230
  living: false,
146
231
  roomId: webRoomId,
147
- owner: data.user.nickname,
148
- title: room?.title ?? data.user.nickname,
232
+ owner: data.nickname,
233
+ title: room?.title ?? data.nickname,
149
234
  streams: [],
150
235
  sources: [],
151
- avatar: data.user?.avatar_thumb?.url_list?.[0],
152
- cover: room.cover?.url_list?.[0],
236
+ avatar: data.avatar,
237
+ cover: room.cover,
153
238
  liveId: room.id_str,
154
239
  };
155
240
  }
@@ -162,14 +247,16 @@ export async function getRoomInfo(webRoomId, opts = {}) {
162
247
  },
163
248
  stream_data: "",
164
249
  };
250
+ // @ts-ignore
165
251
  qualities = pull_data.options.qualities;
252
+ // @ts-ignore
166
253
  stream_data = pull_data.stream_data;
167
254
  }
168
255
  if (!stream_data) {
169
256
  qualities = room.stream_url.live_core_sdk_data.pull_data.options.qualities;
170
257
  stream_data = room.stream_url.live_core_sdk_data.pull_data.stream_data;
171
258
  }
172
- const streamData = JSON.parse(stream_data).data;
259
+ const streamData = typeof stream_data === "string" ? JSON.parse(stream_data).data : stream_data;
173
260
  const streams = qualities.map((info) => ({
174
261
  desc: info.name,
175
262
  key: info.sdk_key,
@@ -218,14 +305,55 @@ export async function getRoomInfo(webRoomId, opts = {}) {
218
305
  ];
219
306
  // console.log(JSON.stringify(sources, null, 2), qualities);
220
307
  return {
221
- living: data.room_status === 0,
308
+ living: data.living,
222
309
  roomId: webRoomId,
223
- owner: data.user.nickname,
310
+ owner: data.nickname,
224
311
  title: room.title,
225
312
  streams,
226
313
  sources,
227
- avatar: data.user.avatar_thumb.url_list[0],
228
- cover: room.cover?.url_list?.[0],
314
+ avatar: data.avatar,
315
+ cover: room.cover,
229
316
  liveId: room.id_str,
230
317
  };
231
318
  }
319
+ /**
320
+ * 获取nonce
321
+ */
322
+ async function getNonce(url) {
323
+ const res = await requester.get(url);
324
+ if (!res.headers["set-cookie"]) {
325
+ throw new Error("No cookie in response");
326
+ }
327
+ const cookies = {};
328
+ (res.headers["set-cookie"] ?? []).forEach((cookie) => {
329
+ const [key, _] = cookie.split(";");
330
+ const [keyPart, valuePart] = key.split("=");
331
+ if (!keyPart || !valuePart)
332
+ return;
333
+ cookies[keyPart.trim()] = valuePart.trim();
334
+ });
335
+ return cookies["__ac_nonce"];
336
+ }
337
+ /**
338
+ * 解析抖音号
339
+ * @param url
340
+ */
341
+ export async function parseUser(url) {
342
+ const timestamp = Math.floor(Date.now() / 1000);
343
+ const ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0";
344
+ const nonce = (await getNonce(url)) ?? generateNonce();
345
+ const signed = get__ac_signature(timestamp, url, nonce, ua);
346
+ const res = await requester.get(url, {
347
+ headers: {
348
+ "User-Agent": ua,
349
+ cookie: `__ac_nonce=${nonce}; __ac_signature=${signed}`,
350
+ },
351
+ });
352
+ const text = res.data;
353
+ const regex = /\\"uniqueId\\":\\"(.*?)\\"/;
354
+ const match = text.match(regex);
355
+ if (match && match[1]) {
356
+ return match[1];
357
+ }
358
+ return null;
359
+ }
package/lib/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import path from "node:path";
2
2
  import mitt from "mitt";
3
- import { defaultFromJSON, defaultToJSON, genRecorderUUID, genRecordUUID, FFMPEGRecorder, } from "@bililive-tools/manager";
3
+ import { defaultFromJSON, defaultToJSON, genRecorderUUID, genRecordUUID, createBaseRecorder, } from "@bililive-tools/manager";
4
4
  import { getInfo, getStream } from "./stream.js";
5
5
  import { ensureFolderExist, singleton } from "./utils.js";
6
- import { resolveShortURL } from "./douyin_api.js";
6
+ import { resolveShortURL, parseUser } from "./douyin_api.js";
7
7
  import DouYinDanmaClient from "douyin-danma-listener";
8
8
  function createRecorder(opts) {
9
9
  // 内部实现时,应该只有 proxy 包裹的那一层会使用这个 recorder 标识符,不应该有直接通过
@@ -29,7 +29,10 @@ function createRecorder(opts) {
29
29
  },
30
30
  async getLiveInfo() {
31
31
  const channelId = this.channelId;
32
- const info = await getInfo(channelId);
32
+ const info = await getInfo(channelId, {
33
+ cookie: this.auth,
34
+ api: this.api,
35
+ });
33
36
  return {
34
37
  channelId,
35
38
  ...info,
@@ -77,7 +80,10 @@ const ffmpegInputOptions = [
77
80
  const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isManualStart, }) {
78
81
  if (this.recordHandle != null)
79
82
  return this.recordHandle;
80
- const liveInfo = await getInfo(this.channelId);
83
+ const liveInfo = await getInfo(this.channelId, {
84
+ cookie: this.auth,
85
+ api: this.api,
86
+ });
81
87
  const { living, owner, title } = liveInfo;
82
88
  this.liveInfo = liveInfo;
83
89
  if (liveInfo.liveId === banLiveId) {
@@ -111,9 +117,12 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isMan
111
117
  cookie: this.auth,
112
118
  formatPriorities: this.formatPriorities,
113
119
  doubleScreen: this.doubleScreen,
120
+ api: this.api,
114
121
  });
115
122
  }
116
123
  catch (err) {
124
+ if (this.qualityRetry > 0)
125
+ this.qualityRetry -= 1;
117
126
  this.state = "idle";
118
127
  throw err;
119
128
  }
@@ -123,7 +132,6 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isMan
123
132
  this.availableSources = availableSources.map((s) => s.name);
124
133
  this.usedStream = stream.name;
125
134
  this.usedSource = stream.source;
126
- // TODO: emit update event
127
135
  let isEnded = false;
128
136
  let isCutting = false;
129
137
  const onEnd = (...args) => {
@@ -136,12 +144,14 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isMan
136
144
  isEnded = true;
137
145
  this.emit("DebugLog", {
138
146
  type: "common",
139
- text: `ffmpeg end, reason: ${JSON.stringify(args, (_, v) => (v instanceof Error ? v.stack : v))}`,
147
+ text: `record end, reason: ${JSON.stringify(args, (_, v) => (v instanceof Error ? v.stack : v))}`,
140
148
  });
141
149
  const reason = args[0] instanceof Error ? args[0].message : String(args[0]);
142
150
  this.recordHandle?.stop(reason);
143
151
  };
144
- const recorder = new FFMPEGRecorder({
152
+ let recorderType = this.recorderType === "mesio" ? "mesio" : "ffmpeg";
153
+ // TODO:测试只录制音频,hls以及fmp4
154
+ const recorder = createBaseRecorder(recorderType, {
145
155
  url: stream.url,
146
156
  outputOptions: ffmpegOutputOptions,
147
157
  inputOptions: ffmpegInputOptions,
@@ -235,7 +245,7 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isMan
235
245
  type: "give_gift",
236
246
  timestamp: this.useServerTimestamp ? serverTimestamp : Date.now(),
237
247
  name: msg.gift.name,
238
- price: 1,
248
+ price: msg.gift.diamondCount / 10 || 0,
239
249
  count: Number(msg.totalCount ?? 1),
240
250
  color: "#ffffff",
241
251
  sender: {
@@ -362,6 +372,13 @@ export const provider = {
362
372
  throw new Error(`解析抖音短链接失败: ${err?.message}`);
363
373
  }
364
374
  }
375
+ else if (channelURL.includes("/user/")) {
376
+ // 解析用户主页
377
+ id = await parseUser(channelURL);
378
+ if (!id) {
379
+ throw new Error(`解析抖音用户主页失败`);
380
+ }
381
+ }
365
382
  else {
366
383
  // 处理常规直播链接
367
384
  id = path.basename(new URL(channelURL).pathname);
package/lib/stream.d.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  import type { Recorder } from "@bililive-tools/manager";
2
- export declare function getInfo(channelId: string): Promise<{
2
+ export declare function getInfo(channelId: string, opts?: {
3
+ cookie?: string;
4
+ api?: "web" | "webHTML";
5
+ }): Promise<{
3
6
  living: boolean;
4
7
  owner: string;
5
8
  title: string;
@@ -15,6 +18,7 @@ export declare function getStream(opts: Pick<Recorder, "channelId" | "quality" |
15
18
  cookie?: string;
16
19
  formatPriorities?: Array<"flv" | "hls">;
17
20
  doubleScreen?: boolean;
21
+ api?: "web" | "webHTML";
18
22
  }): Promise<{
19
23
  currentStream: {
20
24
  name: string;
package/lib/stream.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { getRoomInfo } from "./douyin_api.js";
2
- export async function getInfo(channelId) {
3
- const info = await getRoomInfo(channelId);
2
+ export async function getInfo(channelId, opts) {
3
+ const info = await getRoomInfo(channelId, opts ?? {});
4
4
  return {
5
5
  living: info.living,
6
6
  owner: info.owner,
@@ -14,9 +14,9 @@ export async function getInfo(channelId) {
14
14
  }
15
15
  export async function getStream(opts) {
16
16
  const info = await getRoomInfo(opts.channelId, {
17
- retryOnSpecialCode: true,
18
17
  doubleScreen: opts.doubleScreen ?? true,
19
18
  auth: opts.cookie,
19
+ api: opts.api ?? "web",
20
20
  });
21
21
  if (!info.living) {
22
22
  throw new Error("It must be called getStream when living");
package/lib/utils.d.ts CHANGED
@@ -9,3 +9,4 @@ export declare function assertStringType(data: unknown, msg?: string): asserts d
9
9
  export declare function assertNumberType(data: unknown, msg?: string): asserts data is number;
10
10
  export declare function assertObjectType(data: unknown, msg?: string): asserts data is object;
11
11
  export declare function replaceExtName(filePath: string, newExtName: string): string;
12
+ export declare function get__ac_signature(one_time_stamp: any, one_site: any, one_nonce: any, ua_n: any): string;
package/lib/utils.js CHANGED
@@ -42,3 +42,70 @@ export function assertObjectType(data, msg) {
42
42
  export function replaceExtName(filePath, newExtName) {
43
43
  return path.join(path.dirname(filePath), path.basename(filePath, path.extname(filePath)) + newExtName);
44
44
  }
45
+ export function get__ac_signature(one_time_stamp, one_site, one_nonce, ua_n) {
46
+ function cal_one_str(one_str, orgi_iv) {
47
+ var k = orgi_iv;
48
+ for (var i = 0; i < one_str.length; i++) {
49
+ var a = one_str.charCodeAt(i);
50
+ k = ((k ^ a) * 65599) >>> 0;
51
+ }
52
+ return k;
53
+ }
54
+ function cal_one_str_3(one_str, orgi_iv) {
55
+ // 用于计算后两位
56
+ var k = orgi_iv;
57
+ for (var i = 0; i < one_str.length; i++) {
58
+ k = (k * 65599 + one_str.charCodeAt(i)) >>> 0;
59
+ }
60
+ return k;
61
+ }
62
+ function get_one_chr(enc_chr_code) {
63
+ if (enc_chr_code < 26) {
64
+ return String.fromCharCode(enc_chr_code + 65);
65
+ }
66
+ else if (enc_chr_code < 52) {
67
+ return String.fromCharCode(enc_chr_code + 71);
68
+ }
69
+ else if (enc_chr_code < 62) {
70
+ return String.fromCharCode(enc_chr_code - 4);
71
+ }
72
+ else {
73
+ return String.fromCharCode(enc_chr_code - 17);
74
+ }
75
+ }
76
+ function enc_num_to_str(one_orgi_enc) {
77
+ var s = "";
78
+ for (var i = 24; i >= 0; i -= 6) {
79
+ s += get_one_chr((one_orgi_enc >> i) & 63);
80
+ }
81
+ return s;
82
+ }
83
+ // var time_stamp = 1740635781
84
+ // var site = 'www.douyin.com/'; 提取自window.location.href
85
+ // var data_url = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAQCAYAAABQrvyxAAAAAXNSR0IArs4c6QAAAV5JREFUSEvFlU1OAkEQhT9WLlyx5BqujTfhAMTbsNIYj+EpXLjyDCzhAgbSZMoUb6q6GwWGhMxM/8y8ej/VM05/e3kuj7NhrMzZfbBskqF9BMiAKmAt7trFRGR6Qsv9SQHZBt103DiRGiNSlcWIdW8h75NrK6Ce9M74nYsK0I1ZBnyxme0ytWpZy8IUqT7KQE0BA6MqeDCl2FZhmf1qttR3VhU4h3HPsKoQFaIkeAeEDA9IM7umITYmLcARy1GLrYHuAat5a+VsVIC3QM0KNvcCfAGvclb0WLHVQLxV08woK1GAvBo6/wk8A+Ua2cnWRx7uKcCAZ+dV82RtBesHuAPK1fs7A/eXblWz0b8zUCy0crJkjGXZUJtkbb1LgZ4u4tfsgHnWtC883p0BDbHvQjZXQG8b7e3C+PPXRdIsgE2yZQmsgUfg+2YoKx+KCngCPoD3IZz3wMPwfxPPT17DAcLKggYInDZeAAAAAElFTkSuQmCC'
86
+ // var ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0";
87
+ // var nonce = '067bffadf00143f576ddf';
88
+ // var result = '_02B4Z6wo00f01HBw-fgAAIDA-rdLmXfuMxxwUP1AAHurb5'
89
+ var sign_head = "_02B4Z6wo00f01", time_stamp_s = one_time_stamp + "";
90
+ var a = cal_one_str(one_site, cal_one_str(time_stamp_s, 0)) % 65521;
91
+ var b = parseInt("10000000110000" +
92
+ // @ts-ignore
93
+ parseInt((one_time_stamp ^ (a * 65521)) >>> 0)
94
+ .toString(2)
95
+ .padStart(32, "0"), 2), b_s = b + "";
96
+ var c = cal_one_str(b_s, 0);
97
+ var d = enc_num_to_str(b >> 2);
98
+ var e = (b / 4294967296) >>> 0;
99
+ var f = enc_num_to_str((b << 28) | (e >>> 4));
100
+ var g = 582085784 ^ b;
101
+ var h = enc_num_to_str((e << 26) | (g >>> 6));
102
+ var i = get_one_chr(g & 63);
103
+ var j = (cal_one_str(ua_n, c) % 65521 << 16) | cal_one_str(one_nonce, c) % 65521;
104
+ var k = enc_num_to_str(j >> 2);
105
+ var l = enc_num_to_str((j << 28) | ((524576 ^ b) >>> 4));
106
+ var m = enc_num_to_str(a);
107
+ var n = sign_head + d + f + h + i + k + l + m;
108
+ var o = parseInt(cal_one_str_3(n, 0)).toString(16).slice(-2);
109
+ var signature = n + o;
110
+ return signature;
111
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bililive-tools/douyin-recorder",
3
- "version": "1.5.3",
3
+ "version": "1.6.0",
4
4
  "description": "@bililive-tools douyin recorder implemention",
5
5
  "main": "./lib/index.js",
6
6
  "type": "module",
@@ -39,7 +39,7 @@
39
39
  "mitt": "^3.0.1",
40
40
  "sm-crypto": "^0.3.13",
41
41
  "douyin-danma-listener": "0.2.0",
42
- "@bililive-tools/manager": "^1.5.0"
42
+ "@bililive-tools/manager": "^1.6.0"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@types/node": "*"