@bililive-tools/manager 1.17.3 → 1.18.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/lib/recorder.d.ts
CHANGED
|
@@ -29,6 +29,8 @@ export interface RecorderCreateOpts<E extends AnyObject = UnknownObject> {
|
|
|
29
29
|
convert2Mp4?: boolean;
|
|
30
30
|
/** 身份验证 */
|
|
31
31
|
auth?: string;
|
|
32
|
+
/** 抖音 Cookie 使用范围,默认为全部 */
|
|
33
|
+
cookieMode?: "all" | "stream" | "danma";
|
|
32
34
|
/** cookie所有者uid,B站弹幕录制 */
|
|
33
35
|
uid?: number | string;
|
|
34
36
|
/** 画质匹配重试次数 */
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { Message } from "./common.js";
|
|
2
|
+
export interface DanmaStatsPayload {
|
|
3
|
+
danmaTimeline: {
|
|
4
|
+
interval: number;
|
|
5
|
+
data: number[];
|
|
6
|
+
};
|
|
7
|
+
}
|
|
2
8
|
export interface XmlStreamStats {
|
|
3
9
|
danmaNum: number;
|
|
4
10
|
uniqMember: number;
|
|
5
11
|
scNum: number;
|
|
6
12
|
guardNum: number;
|
|
13
|
+
danmaStats: DanmaStatsPayload;
|
|
7
14
|
}
|
|
8
15
|
export interface XmlStreamData {
|
|
9
16
|
header: string;
|
|
@@ -7,6 +7,7 @@ import { XMLBuilder } from "fast-xml-parser";
|
|
|
7
7
|
import { pick } from "lodash-es";
|
|
8
8
|
const METADATA_PLACEHOLDER = "<!--METADATA_PLACEHOLDER-->";
|
|
9
9
|
const XML_FILE_HEADER = `<?xml version="1.0" encoding="utf-8"?>\n<?xml-stylesheet type="text/xsl" href="#s"?>\n<i>\n${METADATA_PLACEHOLDER}\n<RecorderXmlStyle><z:stylesheet version="1.0" id="s" xml:id="s" xmlns:z="http://www.w3.org/1999/XSL/Transform"><z:output method="html"/><z:template match="/"><html><meta name="viewport" content="width=device-width"/><title>弹幕文件 <z:value-of select="/i/metadata/user_name/text()"/></title><style>body{margin:0}h1,h2,p,table{margin-left:5px}table{border-spacing:0}td,th{border:1px solid grey;padding:1px 5px}th{position:sticky;top:0;background:#4098de}tr:hover{background:#d9f4ff}div{overflow:auto;max-height:80vh;max-width:100vw;width:fit-content}</style><h1>弹幕XML文件</h1><p>本文件不支持在 IE 浏览器里预览,请使用 Chrome Firefox Edge 等浏览器。</p><p>文件用法参考文档 <a href="https://rec.danmuji.org/user/danmaku/">https://rec.danmuji.org/user/danmaku/</a></p><table><tr><td>房间号</td><td><z:value-of select="/i/metadata/room_id/text()"/></td></tr><tr><td>主播名</td><td><z:value-of select="/i/metadata/user_name/text()"/></td></tr><tr><td><a href="#d">弹幕</a></td><td>共<z:value-of select="count(/i/d)"/>条记录</td></tr><tr><td><a href="#guard">上船</a></td><td>共<z:value-of select="count(/i/guard)"/>条记录</td></tr><tr><td><a href="#sc">SC</a></td><td>共<z:value-of select="count(/i/sc)"/>条记录</td></tr><tr><td><a href="#gift">礼物</a></td><td>共<z:value-of select="count(/i/gift)"/>条记录</td></tr></table><h2 id="d">弹幕</h2><div id="dm"><table><tr><th>用户名</th><th>出现时间</th><th>用户ID</th><th>弹幕</th><th>参数</th></tr><z:for-each select="/i/d"><tr><td><z:value-of select="@user"/></td><td></td><td></td><td><z:value-of select="."/></td><td><z:value-of select="@p"/></td></tr></z:for-each></table></div><script>Array.from(document.querySelectorAll('#dm tr')).slice(1).map(t=>t.querySelectorAll('td')).forEach(t=>{let p=t[4].textContent.split(','),a=p[0];t[1].textContent=\`\u0024{(Math.floor(a/60/60)+'').padStart(2,0)}:\u0024{(Math.floor(a/60%60)+'').padStart(2,0)}:\u0024{(a%60).toFixed(3).padStart(6,0)}\`;t[2].innerHTML=\`<a target=_blank rel="nofollow noreferrer" ">\u0024{p[6]}</a>\`})</script><h2 id="guard">舰长购买</h2><div><table><tr><th>用户名</th><th>用户ID</th><th>舰长等级</th><th>购买数量</th><th>出现时间</th></tr><z:for-each select="/i/guard"><tr><td><z:value-of select="@user"/></td><td><a rel="nofollow noreferrer"><z:attribute name="href"><z:text></z:text><z:value-of select="@uid" /></z:attribute><z:value-of select="@uid"/></a></td><td><z:value-of select="@level"/></td><td><z:value-of select="@count"/></td><td><z:value-of select="@ts"/></td></tr></z:for-each></table></div><h2 id="sc">SuperChat 醒目留言</h2><div><table><tr><th>用户名</th><th>用户ID</th><th>内容</th><th>显示时长</th><th>价格</th><th>出现时间</th></tr><z:for-each select="/i/sc"><tr><td><z:value-of select="@user"/></td><td><a rel="nofollow noreferrer"><z:attribute name="href"><z:text></z:text><z:value-of select="@uid" /></z:attribute><z:value-of select="@uid"/></a></td><td><z:value-of select="."/></td><td><z:value-of select="@time"/></td><td><z:value-of select="@price"/></td><td><z:value-of select="@ts"/></td></tr></z:for-each></table></div><h2 id="gift">礼物</h2><div><table><tr><th>用户名</th><th>用户ID</th><th>礼物名</th><th>礼物数量</th><th>出现时间</th></tr><z:for-each select="/i/gift"><tr><td><z:value-of select="@user"/></td><td><span rel="nofollow noreferrer"><z:attribute name="href"></z:attribute><z:value-of select="@uid"/></span></td><td><z:value-of select="@giftname"/></td><td><z:value-of select="@giftcount"/></td><td><z:value-of select="@ts"/></td></tr></z:for-each></table></div></html></z:template></z:stylesheet></RecorderXmlStyle>\n`;
|
|
10
|
+
const DANMA_STATS_INTERVAL = 10;
|
|
10
11
|
export function createRecordExtraDataController(savePath) {
|
|
11
12
|
const data = {
|
|
12
13
|
header: XML_FILE_HEADER,
|
|
@@ -20,13 +21,42 @@ export function createRecordExtraDataController(savePath) {
|
|
|
20
21
|
let danmaNum = 0;
|
|
21
22
|
let scNum = 0;
|
|
22
23
|
let guardNum = 0;
|
|
24
|
+
let maxDanmaBucketStart = -1;
|
|
23
25
|
const interactedUsers = new Set();
|
|
26
|
+
const danmaBucketCountMap = new Map();
|
|
27
|
+
const getDanmaStats = () => {
|
|
28
|
+
if (maxDanmaBucketStart < 0) {
|
|
29
|
+
return {
|
|
30
|
+
danmaTimeline: {
|
|
31
|
+
interval: DANMA_STATS_INTERVAL,
|
|
32
|
+
data: [],
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const stats = [];
|
|
37
|
+
for (let start = 0; start <= maxDanmaBucketStart; start += DANMA_STATS_INTERVAL) {
|
|
38
|
+
stats.push(danmaBucketCountMap.get(start) ?? 0);
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
danmaTimeline: {
|
|
42
|
+
interval: DANMA_STATS_INTERVAL,
|
|
43
|
+
data: stats,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
};
|
|
24
47
|
const getStats = () => ({
|
|
25
48
|
danmaNum,
|
|
26
49
|
uniqMember: interactedUsers.size,
|
|
27
50
|
scNum,
|
|
28
51
|
guardNum,
|
|
52
|
+
danmaStats: getDanmaStats(),
|
|
29
53
|
});
|
|
54
|
+
const trackDanmaBucket = (message) => {
|
|
55
|
+
const progressInSeconds = Math.max((message.timestamp - data.meta.recordStartTimestamp) / 1000, 0);
|
|
56
|
+
const bucketStart = Math.floor(progressInSeconds / DANMA_STATS_INTERVAL) * DANMA_STATS_INTERVAL;
|
|
57
|
+
danmaBucketCountMap.set(bucketStart, (danmaBucketCountMap.get(bucketStart) ?? 0) + 1);
|
|
58
|
+
maxDanmaBucketStart = Math.max(maxDanmaBucketStart, bucketStart);
|
|
59
|
+
};
|
|
30
60
|
const trackInteractedUser = (message) => {
|
|
31
61
|
const userName = message.sender?.name?.trim();
|
|
32
62
|
if (!userName)
|
|
@@ -68,6 +98,7 @@ export function createRecordExtraDataController(savePath) {
|
|
|
68
98
|
return;
|
|
69
99
|
if (message.type === "comment") {
|
|
70
100
|
danmaNum += 1;
|
|
101
|
+
trackDanmaBucket(message);
|
|
71
102
|
}
|
|
72
103
|
else if (message.type === "super_chat") {
|
|
73
104
|
scNum += 1;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bililive-tools/manager",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.0",
|
|
4
4
|
"description": "Batch scheduling recorders",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -28,7 +28,11 @@
|
|
|
28
28
|
"scheduling",
|
|
29
29
|
"recorders"
|
|
30
30
|
],
|
|
31
|
-
"repository":
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/renmu123/biliLive-tools",
|
|
34
|
+
"directory": "packages/liveManager"
|
|
35
|
+
},
|
|
32
36
|
"author": "renmu123",
|
|
33
37
|
"license": "LGPL",
|
|
34
38
|
"dependencies": {
|