douyin-danma-listener 0.4.0 → 0.4.2
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/api.js +18 -0
- package/lib/index.js +58 -43
- package/package.json +7 -2
package/lib/api.js
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
+
let cookieCache;
|
|
1
2
|
export const getCookie = async () => {
|
|
3
|
+
const now = new Date().getTime();
|
|
4
|
+
// 缓存1小时:重连时优先复用,避免每次连接都依赖实时网络抓取,
|
|
5
|
+
// 瞬时网络抖动时不会导致 connect() 失败、重试链断裂
|
|
6
|
+
if (cookieCache?.startTimestamp && now - cookieCache.startTimestamp < 60 * 60 * 1000) {
|
|
7
|
+
return cookieCache.cookies;
|
|
8
|
+
}
|
|
2
9
|
const res = await fetch("https://live.douyin.com/", {
|
|
3
10
|
method: "GET",
|
|
4
11
|
});
|
|
@@ -11,5 +18,16 @@ export const getCookie = async () => {
|
|
|
11
18
|
return cookie.split(";")[0];
|
|
12
19
|
})
|
|
13
20
|
.join("; ");
|
|
21
|
+
if (!cookies.includes("ttwid")) {
|
|
22
|
+
// 新cookie不含ttwid,且缓存中有含ttwid的旧cookie时,延长旧缓存继续复用
|
|
23
|
+
if (cookieCache?.cookies) {
|
|
24
|
+
cookieCache.startTimestamp += 60 * 60 * 1000;
|
|
25
|
+
return cookieCache.cookies;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
cookieCache = {
|
|
29
|
+
startTimestamp: now,
|
|
30
|
+
cookies,
|
|
31
|
+
};
|
|
14
32
|
return cookies;
|
|
15
33
|
};
|
package/lib/index.js
CHANGED
|
@@ -1,22 +1,8 @@
|
|
|
1
|
-
import { parse, format } from "node:url";
|
|
2
1
|
import WebSocket from "ws";
|
|
3
2
|
import { TypedEmitter } from "tiny-typed-emitter";
|
|
4
3
|
import { decompressGzip, getXMsStub, getSignature, getUserUniqueId } from "./utils.js";
|
|
5
4
|
import protobuf from "./proto.js";
|
|
6
5
|
import { getCookie } from "./api.js";
|
|
7
|
-
function buildRequestUrl(url) {
|
|
8
|
-
const parsedUrl = parse(url, true);
|
|
9
|
-
const existingParams = parsedUrl.query;
|
|
10
|
-
existingParams["aid"] = "6383";
|
|
11
|
-
existingParams["device_platform"] = "web";
|
|
12
|
-
existingParams["browser_language"] = "zh-CN";
|
|
13
|
-
existingParams["browser_platform"] = "Win32";
|
|
14
|
-
existingParams["browser_name"] = "Mozilla";
|
|
15
|
-
existingParams["browser_version"] = "92.0.4515.159";
|
|
16
|
-
parsedUrl.search = null;
|
|
17
|
-
parsedUrl.query = existingParams;
|
|
18
|
-
return format(parsedUrl);
|
|
19
|
-
}
|
|
20
6
|
class DouYinDanmaClient extends TypedEmitter {
|
|
21
7
|
ws;
|
|
22
8
|
roomId;
|
|
@@ -51,36 +37,46 @@ class DouYinDanmaClient extends TypedEmitter {
|
|
|
51
37
|
}
|
|
52
38
|
}
|
|
53
39
|
async connect() {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
40
|
+
try {
|
|
41
|
+
const url = await this.getWsInfo(this.roomId);
|
|
42
|
+
if (!url) {
|
|
43
|
+
throw new Error("获取抖音弹幕签名失败");
|
|
44
|
+
}
|
|
45
|
+
this.emit("init", url);
|
|
46
|
+
const cookies = this.cookie || (await getCookie());
|
|
47
|
+
this.ws = new WebSocket(url, {
|
|
48
|
+
headers: {
|
|
49
|
+
Cookie: cookies,
|
|
50
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
|
|
51
|
+
Origin: "https://live.douyin.com",
|
|
52
|
+
Referer: "https://live.douyin.com/",
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
this.ws.on("open", () => {
|
|
56
|
+
this.emit("open");
|
|
57
|
+
this.startHeartbeat();
|
|
58
|
+
this.startTimeoutCheck();
|
|
59
|
+
});
|
|
60
|
+
this.ws.on("message", (data) => {
|
|
61
|
+
this.lastMessageTime = Date.now();
|
|
62
|
+
this.decode(data);
|
|
63
|
+
});
|
|
64
|
+
this.ws.on("close", () => {
|
|
65
|
+
this.emit("close");
|
|
66
|
+
this.reconnect();
|
|
67
|
+
});
|
|
68
|
+
this.ws.on("error", (error) => {
|
|
69
|
+
this.emit("error", error);
|
|
70
|
+
this.reconnect();
|
|
71
|
+
});
|
|
58
72
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
},
|
|
65
|
-
});
|
|
66
|
-
this.ws.on("open", () => {
|
|
67
|
-
this.reconnectAttempts = 0;
|
|
68
|
-
this.emit("open");
|
|
69
|
-
this.startHeartbeat();
|
|
70
|
-
this.startTimeoutCheck();
|
|
71
|
-
});
|
|
72
|
-
this.ws.on("message", (data) => {
|
|
73
|
-
this.lastMessageTime = Date.now();
|
|
74
|
-
this.decode(data);
|
|
75
|
-
});
|
|
76
|
-
this.ws.on("close", () => {
|
|
77
|
-
this.emit("close");
|
|
78
|
-
this.reconnect();
|
|
79
|
-
});
|
|
80
|
-
this.ws.on("error", (error) => {
|
|
81
|
-
this.emit("error", error);
|
|
73
|
+
catch (err) {
|
|
74
|
+
// getWsInfo/getCookie 等前置步骤失败时,必须让错误可见并继续重试:
|
|
75
|
+
// 否则 WebSocket 不会创建,没有任何事件能触发下一次 reconnect,
|
|
76
|
+
// 弹幕客户端将静默死亡直到录制结束
|
|
77
|
+
this.emit("error", err);
|
|
82
78
|
this.reconnect();
|
|
83
|
-
}
|
|
79
|
+
}
|
|
84
80
|
}
|
|
85
81
|
send(data) {
|
|
86
82
|
if (!this.ws) {
|
|
@@ -373,18 +369,37 @@ class DouYinDanmaClient extends TypedEmitter {
|
|
|
373
369
|
return;
|
|
374
370
|
}
|
|
375
371
|
const webcast5Params = {
|
|
372
|
+
app_name: "douyin_web",
|
|
376
373
|
room_id: roomId,
|
|
377
374
|
compress: "gzip",
|
|
378
375
|
version_code: String(versionCode),
|
|
379
376
|
webcast_sdk_version: webcastSdkVersion,
|
|
377
|
+
update_version_code: webcastSdkVersion,
|
|
380
378
|
live_id: "1",
|
|
381
379
|
did_rule: "3",
|
|
382
380
|
user_unique_id: userUniqueId,
|
|
383
381
|
identity: "audience",
|
|
384
382
|
signature: signature.toString(),
|
|
383
|
+
device_platform: "web",
|
|
384
|
+
cookie_enabled: "true",
|
|
385
|
+
screen_width: "1920",
|
|
386
|
+
screen_height: "1080",
|
|
387
|
+
browser_language: "zh-CN",
|
|
388
|
+
browser_platform: "Win32",
|
|
389
|
+
browser_name: "Mozilla",
|
|
390
|
+
browser_version: "5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
|
|
391
|
+
browser_online: "true",
|
|
392
|
+
tz_name: "Etc/GMT-8",
|
|
393
|
+
host: "https://live.douyin.com",
|
|
394
|
+
aid: "6383",
|
|
395
|
+
endpoint: "live_pc",
|
|
396
|
+
support_wrds: "1",
|
|
397
|
+
im_path: "/webcast/im/fetch/",
|
|
398
|
+
need_persist_msg_count: "15",
|
|
399
|
+
heartbeatDuration: "0",
|
|
385
400
|
};
|
|
386
401
|
const wssUrl = `wss://${this.host}/webcast/im/push/v2/?${new URLSearchParams(webcast5Params).toString()}`;
|
|
387
|
-
return
|
|
402
|
+
return wssUrl;
|
|
388
403
|
}
|
|
389
404
|
}
|
|
390
405
|
export default DouYinDanmaClient;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "douyin-danma-listener",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "douyin danma listener",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"types": "./types/index.d.ts",
|
|
@@ -28,7 +28,11 @@
|
|
|
28
28
|
"弹幕",
|
|
29
29
|
"抖音"
|
|
30
30
|
],
|
|
31
|
-
"repository":
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/renmu123/biliLive-tools",
|
|
34
|
+
"directory": "packages/DouYinDanma"
|
|
35
|
+
},
|
|
32
36
|
"author": "renmu123",
|
|
33
37
|
"license": "GPLV3",
|
|
34
38
|
"dependencies": {
|
|
@@ -38,6 +42,7 @@
|
|
|
38
42
|
},
|
|
39
43
|
"devDependencies": {
|
|
40
44
|
"@types/node": "*",
|
|
45
|
+
"@types/ws": "^8.5.13",
|
|
41
46
|
"protobufjs-cli": "^1.1.3"
|
|
42
47
|
},
|
|
43
48
|
"scripts": {
|