bililive-cli 3.13.0 → 3.13.1
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.
|
@@ -54494,7 +54494,7 @@ async function trash(paths, options) {
|
|
|
54494
54494
|
} else if (process$2.platform === 'win32') {
|
|
54495
54495
|
module = await Promise.resolve().then(function () { return require('./windows-OmnJ7a39.cjs'); });
|
|
54496
54496
|
} else {
|
|
54497
|
-
module = await Promise.resolve().then(function () { return require('./linux-
|
|
54497
|
+
module = await Promise.resolve().then(function () { return require('./linux-B6245EX2.cjs'); });
|
|
54498
54498
|
}
|
|
54499
54499
|
|
|
54500
54500
|
return module.default(paths);
|
|
@@ -186234,71 +186234,80 @@ async function getBiliStatusInfoByRoomIds(RoomIds) {
|
|
|
186234
186234
|
|
|
186235
186235
|
/**
|
|
186236
186236
|
* XML流式写入控制器,用于实时写入弹幕、礼物等信息到XML文件
|
|
186237
|
-
* 相比原有的json方案,这个实现每隔
|
|
186237
|
+
* 相比原有的json方案,这个实现每隔10秒就会写入数据,减少内存占用和数据丢失风险
|
|
186238
186238
|
*/
|
|
186239
|
+
const METADATA_PLACEHOLDER = "<!--METADATA_PLACEHOLDER-->";
|
|
186240
|
+
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`;
|
|
186239
186241
|
function createRecordExtraDataController(savePath) {
|
|
186240
186242
|
const data = {
|
|
186243
|
+
header: XML_FILE_HEADER,
|
|
186241
186244
|
meta: {
|
|
186242
186245
|
recordStartTimestamp: Date.now(),
|
|
186243
186246
|
},
|
|
186244
186247
|
pendingMessages: [],
|
|
186245
186248
|
};
|
|
186246
186249
|
let hasCompleted = false;
|
|
186247
|
-
let
|
|
186248
|
-
let
|
|
186249
|
-
|
|
186250
|
-
|
|
186251
|
-
|
|
186252
|
-
|
|
186253
|
-
|
|
186254
|
-
|
|
186255
|
-
|
|
186256
|
-
|
|
186257
|
-
await fs$D.promises.writeFile(savePath, header);
|
|
186258
|
-
}
|
|
186259
|
-
catch (error) {
|
|
186260
|
-
console.error("初始化XML文件失败:", error);
|
|
186261
|
-
isInitialized = false;
|
|
186262
|
-
throw error;
|
|
186263
|
-
}
|
|
186264
|
-
};
|
|
186265
|
-
// 每10秒写入一次数据
|
|
186266
|
-
const scheduleWrite = asyncThrottle(() => writeToFile(), 10e3, {
|
|
186267
|
-
immediateRunWhenEndOfDefer: true,
|
|
186250
|
+
let hasPersistedHeader = false;
|
|
186251
|
+
let danmaNum = 0;
|
|
186252
|
+
let scNum = 0;
|
|
186253
|
+
let guardNum = 0;
|
|
186254
|
+
const interactedUsers = new Set();
|
|
186255
|
+
const getStats = () => ({
|
|
186256
|
+
danmaNum,
|
|
186257
|
+
uniqMember: interactedUsers.size,
|
|
186258
|
+
scNum,
|
|
186259
|
+
guardNum,
|
|
186268
186260
|
});
|
|
186269
|
-
const
|
|
186270
|
-
|
|
186261
|
+
const trackInteractedUser = (message) => {
|
|
186262
|
+
const userName = message.sender?.name?.trim();
|
|
186263
|
+
if (!userName)
|
|
186271
186264
|
return;
|
|
186265
|
+
interactedUsers.add(userName);
|
|
186266
|
+
};
|
|
186267
|
+
const initializeFile = async (content) => {
|
|
186268
|
+
// 这里有个假设,那就是第一次保存必然存在metatdata信息
|
|
186269
|
+
const initialContent = data.header.replace(METADATA_PLACEHOLDER, generateMetadataXml(data.meta));
|
|
186270
|
+
await fs$D.promises.writeFile(savePath, initialContent + content);
|
|
186271
|
+
hasPersistedHeader = true;
|
|
186272
|
+
};
|
|
186273
|
+
const writeToFile = async (force = false) => {
|
|
186274
|
+
if (!force && data.pendingMessages.length === 0) {
|
|
186275
|
+
return Promise.resolve();
|
|
186272
186276
|
}
|
|
186273
|
-
|
|
186274
|
-
|
|
186275
|
-
isWriting = true;
|
|
186277
|
+
const messagesToWrite = [...data.pendingMessages];
|
|
186278
|
+
data.pendingMessages = [];
|
|
186276
186279
|
try {
|
|
186277
|
-
// 获取待写入的消息
|
|
186278
|
-
const messagesToWrite = [...data.pendingMessages];
|
|
186279
|
-
data.pendingMessages = [];
|
|
186280
|
-
// 生成XML内容
|
|
186281
186280
|
const xmlContent = generateXmlContent(data.meta, messagesToWrite);
|
|
186282
|
-
|
|
186283
|
-
|
|
186281
|
+
if (!hasPersistedHeader) {
|
|
186282
|
+
await initializeFile(xmlContent);
|
|
186283
|
+
}
|
|
186284
|
+
else if (xmlContent) {
|
|
186285
|
+
await appendToXmlFile(savePath, xmlContent);
|
|
186286
|
+
}
|
|
186284
186287
|
}
|
|
186285
186288
|
catch (error) {
|
|
186286
186289
|
console.error("写入XML文件失败:", error);
|
|
186287
|
-
|
|
186288
|
-
data.pendingMessages = [...data.pendingMessages];
|
|
186289
|
-
}
|
|
186290
|
-
finally {
|
|
186291
|
-
isWriting = false;
|
|
186290
|
+
data.pendingMessages = [...messagesToWrite, ...data.pendingMessages];
|
|
186292
186291
|
}
|
|
186293
186292
|
};
|
|
186293
|
+
// 每10秒写入一次数据
|
|
186294
|
+
const writeTimer = setInterval(() => {
|
|
186295
|
+
writeToFile();
|
|
186296
|
+
}, 10e3);
|
|
186294
186297
|
const addMessage = (message) => {
|
|
186295
186298
|
if (hasCompleted)
|
|
186296
186299
|
return;
|
|
186297
|
-
|
|
186300
|
+
if (message.type === "comment") {
|
|
186301
|
+
danmaNum += 1;
|
|
186302
|
+
}
|
|
186303
|
+
else if (message.type === "super_chat") {
|
|
186304
|
+
scNum += 1;
|
|
186305
|
+
}
|
|
186306
|
+
else if (message.type === "guard") {
|
|
186307
|
+
guardNum += 1;
|
|
186308
|
+
}
|
|
186309
|
+
trackInteractedUser(message);
|
|
186298
186310
|
data.pendingMessages.push(message);
|
|
186299
|
-
// 确保文件已初始化
|
|
186300
|
-
initializeFile().catch(console.error);
|
|
186301
|
-
scheduleWrite();
|
|
186302
186311
|
};
|
|
186303
186312
|
const setMeta = async (meta) => {
|
|
186304
186313
|
if (hasCompleted)
|
|
@@ -186307,30 +186316,31 @@ function createRecordExtraDataController(savePath) {
|
|
|
186307
186316
|
...data.meta,
|
|
186308
186317
|
...meta,
|
|
186309
186318
|
};
|
|
186310
|
-
// 确保文件已初始化,然后立即更新文件中的metadata
|
|
186311
|
-
await initializeFile().catch(console.error);
|
|
186312
|
-
await updateMetadataInFile(savePath, data.meta).catch(console.error);
|
|
186313
186319
|
};
|
|
186314
186320
|
const flush = async () => {
|
|
186315
186321
|
if (hasCompleted)
|
|
186316
186322
|
return;
|
|
186317
186323
|
hasCompleted = true;
|
|
186318
|
-
|
|
186319
|
-
|
|
186320
|
-
|
|
186321
|
-
|
|
186322
|
-
|
|
186323
|
-
|
|
186324
|
-
|
|
186325
|
-
|
|
186326
|
-
|
|
186327
|
-
|
|
186324
|
+
writeTimer && clearInterval(writeTimer);
|
|
186325
|
+
try {
|
|
186326
|
+
await writeToFile(true);
|
|
186327
|
+
await appendToXmlFile(savePath, "</i>");
|
|
186328
|
+
}
|
|
186329
|
+
catch (error) {
|
|
186330
|
+
console.error("完成XML文件写入失败:", error);
|
|
186331
|
+
}
|
|
186332
|
+
finally {
|
|
186333
|
+
// 清理内存
|
|
186334
|
+
data.pendingMessages = [];
|
|
186335
|
+
interactedUsers.clear();
|
|
186336
|
+
}
|
|
186328
186337
|
};
|
|
186329
186338
|
return {
|
|
186330
186339
|
data,
|
|
186331
186340
|
addMessage,
|
|
186332
186341
|
setMeta,
|
|
186333
186342
|
flush,
|
|
186343
|
+
getStats,
|
|
186334
186344
|
};
|
|
186335
186345
|
}
|
|
186336
186346
|
/**
|
|
@@ -186346,7 +186356,7 @@ function generateXmlContent(metadata, messages) {
|
|
|
186346
186356
|
.filter((item) => item.type === "comment")
|
|
186347
186357
|
.map((ele) => {
|
|
186348
186358
|
const progress = Math.max((ele.timestamp - metadata.recordStartTimestamp) / 1000, 0);
|
|
186349
|
-
const
|
|
186359
|
+
const attrs = {
|
|
186350
186360
|
"@@p": "",
|
|
186351
186361
|
"@@progress": progress,
|
|
186352
186362
|
"@@mode": String(ele.mode ?? 1),
|
|
@@ -186361,18 +186371,18 @@ function generateXmlContent(metadata, messages) {
|
|
|
186361
186371
|
"@@uid": String(ele?.sender?.uid),
|
|
186362
186372
|
"@@timestamp": String(ele.timestamp),
|
|
186363
186373
|
};
|
|
186364
|
-
|
|
186365
|
-
|
|
186366
|
-
|
|
186367
|
-
|
|
186368
|
-
|
|
186369
|
-
|
|
186370
|
-
|
|
186371
|
-
|
|
186372
|
-
|
|
186373
|
-
|
|
186374
|
+
attrs["@@p"] = [
|
|
186375
|
+
attrs["@@progress"],
|
|
186376
|
+
attrs["@@mode"],
|
|
186377
|
+
attrs["@@fontsize"],
|
|
186378
|
+
attrs["@@color"],
|
|
186379
|
+
attrs["@@ctime"],
|
|
186380
|
+
attrs["@@pool"],
|
|
186381
|
+
attrs["@@midHash"],
|
|
186382
|
+
attrs["@@uid"],
|
|
186383
|
+
attrs["@@weight"],
|
|
186374
186384
|
].join(",");
|
|
186375
|
-
return pick$1(
|
|
186385
|
+
return pick$1(attrs, ["@@p", "#text", "@@user", "@@uid", "@@timestamp"]);
|
|
186376
186386
|
});
|
|
186377
186387
|
const gifts = messages
|
|
186378
186388
|
.filter((item) => item.type === "give_gift")
|
|
@@ -186438,55 +186448,22 @@ async function appendToXmlFile(filePath, content) {
|
|
|
186438
186448
|
throw error;
|
|
186439
186449
|
}
|
|
186440
186450
|
}
|
|
186441
|
-
|
|
186442
|
-
|
|
186443
|
-
|
|
186444
|
-
|
|
186445
|
-
|
|
186446
|
-
|
|
186447
|
-
|
|
186448
|
-
|
|
186449
|
-
|
|
186450
|
-
|
|
186451
|
-
|
|
186452
|
-
|
|
186453
|
-
|
|
186454
|
-
|
|
186455
|
-
|
|
186456
|
-
|
|
186457
|
-
room_title: metadata.title,
|
|
186458
|
-
user_name: metadata.user_name,
|
|
186459
|
-
room_id: metadata.room_id,
|
|
186460
|
-
},
|
|
186461
|
-
});
|
|
186462
|
-
// 读取文件内容
|
|
186463
|
-
const content = await fs$D.promises.readFile(filePath, "utf-8");
|
|
186464
|
-
// 替换占位符为实际的metadata
|
|
186465
|
-
const updatedContent = content.replace("<!--METADATA_PLACEHOLDER-->", metadataXml);
|
|
186466
|
-
// 写回文件
|
|
186467
|
-
await fs$D.promises.writeFile(filePath, updatedContent);
|
|
186468
|
-
}
|
|
186469
|
-
catch (error) {
|
|
186470
|
-
console.error(`更新XML文件metadata失败: ${filePath}`, error);
|
|
186471
|
-
throw error;
|
|
186472
|
-
}
|
|
186473
|
-
}
|
|
186474
|
-
/**
|
|
186475
|
-
* 完成XML文件写入
|
|
186476
|
-
*/
|
|
186477
|
-
async function finalizeXmlFile(filePath) {
|
|
186478
|
-
try {
|
|
186479
|
-
// 读取文件内容
|
|
186480
|
-
const content = await fs$D.promises.readFile(filePath, "utf-8");
|
|
186481
|
-
// 添加结束标签
|
|
186482
|
-
const finalContent = content + "</i>";
|
|
186483
|
-
// 写回文件
|
|
186484
|
-
await fs$D.promises.writeFile(filePath, finalContent);
|
|
186485
|
-
}
|
|
186486
|
-
catch (error) {
|
|
186487
|
-
console.error(`完成XML文件写入失败: ${filePath}`, error);
|
|
186488
|
-
throw error;
|
|
186489
|
-
}
|
|
186451
|
+
function generateMetadataXml(metadata) {
|
|
186452
|
+
const builder = new fxp.XMLBuilder({
|
|
186453
|
+
ignoreAttributes: false,
|
|
186454
|
+
attributeNamePrefix: "@@",
|
|
186455
|
+
format: true,
|
|
186456
|
+
});
|
|
186457
|
+
return builder.build({
|
|
186458
|
+
metadata: {
|
|
186459
|
+
platform: metadata.platform,
|
|
186460
|
+
video_start_time: metadata.recordStartTimestamp,
|
|
186461
|
+
live_start_time: metadata.liveStartTimestamp,
|
|
186462
|
+
room_title: metadata.title,
|
|
186463
|
+
user_name: metadata.user_name,
|
|
186464
|
+
room_id: metadata.room_id,
|
|
186465
|
+
},
|
|
186466
|
+
});
|
|
186490
186467
|
}
|
|
186491
186468
|
|
|
186492
186469
|
class Segment extends EventEmitter$j {
|
|
@@ -186505,6 +186482,12 @@ class Segment extends EventEmitter$j {
|
|
|
186505
186482
|
this.disableDanma = disableDanma;
|
|
186506
186483
|
this.videoExt = videoExt;
|
|
186507
186484
|
}
|
|
186485
|
+
getVideoFileCompletedPayload() {
|
|
186486
|
+
return {
|
|
186487
|
+
filename: this.outputFilePath,
|
|
186488
|
+
stats: this.extraDataController?.getStats(),
|
|
186489
|
+
};
|
|
186490
|
+
}
|
|
186508
186491
|
async handleSegmentEnd() {
|
|
186509
186492
|
if (!this.outputVideoFilePath) {
|
|
186510
186493
|
this.emit("DebugLog", {
|
|
@@ -186513,6 +186496,7 @@ class Segment extends EventEmitter$j {
|
|
|
186513
186496
|
});
|
|
186514
186497
|
return;
|
|
186515
186498
|
}
|
|
186499
|
+
const data = this.getVideoFileCompletedPayload();
|
|
186516
186500
|
try {
|
|
186517
186501
|
this.emit("DebugLog", {
|
|
186518
186502
|
type: "info",
|
|
@@ -186522,7 +186506,7 @@ class Segment extends EventEmitter$j {
|
|
|
186522
186506
|
retry$1(() => fs$E.rename(this.rawRecordingVideoPath, this.outputFilePath), 20, 1000),
|
|
186523
186507
|
this.extraDataController?.flush(),
|
|
186524
186508
|
]);
|
|
186525
|
-
this.emit("videoFileCompleted",
|
|
186509
|
+
this.emit("videoFileCompleted", data);
|
|
186526
186510
|
}
|
|
186527
186511
|
catch (err) {
|
|
186528
186512
|
this.emit("DebugLog", {
|
|
@@ -186530,7 +186514,7 @@ class Segment extends EventEmitter$j {
|
|
|
186530
186514
|
text: "videoFileCompleted error " + String(err),
|
|
186531
186515
|
});
|
|
186532
186516
|
// 虽然重命名失败了,但是也当作完成处理,避免卡住录制流程
|
|
186533
|
-
this.emit("videoFileCompleted",
|
|
186517
|
+
this.emit("videoFileCompleted", data);
|
|
186534
186518
|
}
|
|
186535
186519
|
}
|
|
186536
186520
|
async onSegmentStart(stderrLine, callBack) {
|
|
@@ -186680,8 +186664,13 @@ class StreamManager extends EventEmitter$j {
|
|
|
186680
186664
|
}
|
|
186681
186665
|
else {
|
|
186682
186666
|
if (this.recordStartTime) {
|
|
186683
|
-
|
|
186684
|
-
|
|
186667
|
+
const stats = this.extraDataController?.getStats();
|
|
186668
|
+
const extraDataController = this.getExtraDataController();
|
|
186669
|
+
await extraDataController?.flush();
|
|
186670
|
+
this.emit("videoFileCompleted", {
|
|
186671
|
+
filename: this.videoFilePath,
|
|
186672
|
+
stats: stats,
|
|
186673
|
+
});
|
|
186685
186674
|
}
|
|
186686
186675
|
}
|
|
186687
186676
|
}
|
|
@@ -186845,7 +186834,7 @@ function createRecorderManager$1(opts) {
|
|
|
186845
186834
|
}
|
|
186846
186835
|
this.emit("videoFileCreated", { recorder: recorder.toJSON(), filename, rawFilename });
|
|
186847
186836
|
});
|
|
186848
|
-
recorder.on("videoFileCompleted", ({ filename }) => this.emit("videoFileCompleted", { recorder: recorder.toJSON(), filename }));
|
|
186837
|
+
recorder.on("videoFileCompleted", ({ filename, stats }) => this.emit("videoFileCompleted", { recorder: recorder.toJSON(), filename, stats }));
|
|
186849
186838
|
recorder.on("Message", (message) => this.emit("Message", { recorder: recorder.toJSON(), message }));
|
|
186850
186839
|
recorder.on("Updated", (keys) => this.emit("RecorderUpdated", { recorder: recorder.toJSON(), keys }));
|
|
186851
186840
|
recorder.on("DebugLog", (log) => this.emit("RecorderDebugLog", { recorder: recorder, ...log }));
|
|
@@ -186990,18 +186979,9 @@ function createRecorderManager$1(opts) {
|
|
|
186990
186979
|
}
|
|
186991
186980
|
}
|
|
186992
186981
|
else {
|
|
186993
|
-
//
|
|
186994
|
-
const
|
|
186995
|
-
|
|
186996
|
-
// 继续循环
|
|
186997
|
-
const timer = setTimeout(checkLoop, providerConfig.autoCheckInterval);
|
|
186998
|
-
checkLoopTimers.set(providerId, timer);
|
|
186999
|
-
}
|
|
187000
|
-
else {
|
|
187001
|
-
// 没有 recorder 了,停止该 provider 的检查循环
|
|
187002
|
-
// TODO: 也许不需要删除定时器
|
|
187003
|
-
checkLoopTimers.delete(providerId);
|
|
187004
|
-
}
|
|
186982
|
+
// 即使当前 provider 暂时没有 recorder,也保留轮询,避免后续新增 recorder 时漏掉自动检查。
|
|
186983
|
+
const timer = setTimeout(checkLoop, providerConfig.autoCheckInterval);
|
|
186984
|
+
checkLoopTimers.set(providerId, timer);
|
|
187005
186985
|
}
|
|
187006
186986
|
}
|
|
187007
186987
|
};
|
|
@@ -187237,8 +187217,8 @@ class mesioDownloader extends EventEmitter$j {
|
|
|
187237
187217
|
this.streamManager.on("videoFileCreated", ({ filename, cover, rawFilename, title }) => {
|
|
187238
187218
|
this.emit("videoFileCreated", { filename, cover, rawFilename, title });
|
|
187239
187219
|
});
|
|
187240
|
-
this.streamManager.on("videoFileCompleted", (
|
|
187241
|
-
this.emit("videoFileCompleted",
|
|
187220
|
+
this.streamManager.on("videoFileCompleted", (data) => {
|
|
187221
|
+
this.emit("videoFileCompleted", data);
|
|
187242
187222
|
});
|
|
187243
187223
|
this.streamManager.on("DebugLog", (data) => {
|
|
187244
187224
|
this.emit("DebugLog", data);
|
|
@@ -187440,8 +187420,8 @@ class BililiveDownloader extends EventEmitter$j {
|
|
|
187440
187420
|
this.streamManager.on("videoFileCreated", ({ filename, cover, rawFilename, title }) => {
|
|
187441
187421
|
this.emit("videoFileCreated", { filename, cover, rawFilename, title });
|
|
187442
187422
|
});
|
|
187443
|
-
this.streamManager.on("videoFileCompleted", (
|
|
187444
|
-
this.emit("videoFileCompleted",
|
|
187423
|
+
this.streamManager.on("videoFileCompleted", (data) => {
|
|
187424
|
+
this.emit("videoFileCompleted", data);
|
|
187445
187425
|
});
|
|
187446
187426
|
this.streamManager.on("DebugLog", (data) => {
|
|
187447
187427
|
this.emit("DebugLog", data);
|
|
@@ -187671,8 +187651,8 @@ class FFmpegDownloader extends EventEmitter$j {
|
|
|
187671
187651
|
this.streamManager.on("videoFileCreated", ({ filename, cover, rawFilename, title }) => {
|
|
187672
187652
|
this.emit("videoFileCreated", { filename, cover, rawFilename, title });
|
|
187673
187653
|
});
|
|
187674
|
-
this.streamManager.on("videoFileCompleted", (
|
|
187675
|
-
this.emit("videoFileCompleted",
|
|
187654
|
+
this.streamManager.on("videoFileCompleted", (data) => {
|
|
187655
|
+
this.emit("videoFileCompleted", data);
|
|
187676
187656
|
});
|
|
187677
187657
|
this.streamManager.on("DebugLog", (data) => {
|
|
187678
187658
|
this.emit("DebugLog", data);
|
|
@@ -195234,8 +195214,8 @@ const checkLiveStatusAndRecord$4 = async function ({ getSavePath, banLiveId, isM
|
|
|
195234
195214
|
});
|
|
195235
195215
|
};
|
|
195236
195216
|
downloader.on("videoFileCreated", handleVideoCreated);
|
|
195237
|
-
downloader.on("videoFileCompleted", (
|
|
195238
|
-
this.emit("videoFileCompleted",
|
|
195217
|
+
downloader.on("videoFileCompleted", (data) => {
|
|
195218
|
+
this.emit("videoFileCompleted", data);
|
|
195239
195219
|
});
|
|
195240
195220
|
downloader.on("DebugLog", (data) => {
|
|
195241
195221
|
this.emit("DebugLog", data);
|
|
@@ -211397,8 +211377,8 @@ const checkLiveStatusAndRecord$3 = async function ({ getSavePath, banLiveId, isM
|
|
|
211397
211377
|
});
|
|
211398
211378
|
};
|
|
211399
211379
|
downloader.on("videoFileCreated", handleVideoCreated);
|
|
211400
|
-
downloader.on("videoFileCompleted", (
|
|
211401
|
-
this.emit("videoFileCompleted",
|
|
211380
|
+
downloader.on("videoFileCompleted", (data) => {
|
|
211381
|
+
this.emit("videoFileCompleted", data);
|
|
211402
211382
|
});
|
|
211403
211383
|
downloader.on("DebugLog", (data) => {
|
|
211404
211384
|
this.emit("DebugLog", data);
|
|
@@ -213080,8 +213060,8 @@ const checkLiveStatusAndRecord$2 = async function ({ getSavePath, isManualStart,
|
|
|
213080
213060
|
});
|
|
213081
213061
|
};
|
|
213082
213062
|
downloader.on("videoFileCreated", handleVideoCreated);
|
|
213083
|
-
downloader.on("videoFileCompleted", (
|
|
213084
|
-
this.emit("videoFileCompleted",
|
|
213063
|
+
downloader.on("videoFileCompleted", (data) => {
|
|
213064
|
+
this.emit("videoFileCompleted", data);
|
|
213085
213065
|
});
|
|
213086
213066
|
downloader.on("DebugLog", (data) => {
|
|
213087
213067
|
this.emit("DebugLog", data);
|
|
@@ -216690,6 +216670,9 @@ async function getRoomInfoByUserWeb(secUserId, opts = {}) {
|
|
|
216690
216670
|
if (res.data.includes("验证码")) {
|
|
216691
216671
|
throw new Error("需要验证码,请在浏览器中打开链接获取" + url);
|
|
216692
216672
|
}
|
|
216673
|
+
if (!res.data.includes("抖音号")) {
|
|
216674
|
+
throw new Error("userHTML页面没有正常加载" + String(res.data));
|
|
216675
|
+
}
|
|
216693
216676
|
if (!res.data.includes("直播中")) {
|
|
216694
216677
|
return {
|
|
216695
216678
|
living: false,
|
|
@@ -216881,6 +216864,7 @@ async function getRoomInfoByWeb(webRoomId, opts = {}) {
|
|
|
216881
216864
|
}
|
|
216882
216865
|
async function getRoomInfoByMobile(secUserId, opts = {}) {
|
|
216883
216866
|
if (!secUserId) {
|
|
216867
|
+
console.error(opts);
|
|
216884
216868
|
throw new Error("Mobile API need secUserId, please set uid field");
|
|
216885
216869
|
}
|
|
216886
216870
|
if (typeof secUserId === "number") {
|
|
@@ -216897,7 +216881,7 @@ async function getRoomInfoByMobile(secUserId, opts = {}) {
|
|
|
216897
216881
|
const res = await requester.get(`https://webcast.amemv.com/webcast/room/reflow/info/`, {
|
|
216898
216882
|
params,
|
|
216899
216883
|
headers: {
|
|
216900
|
-
|
|
216884
|
+
// cookie: opts.auth,
|
|
216901
216885
|
},
|
|
216902
216886
|
});
|
|
216903
216887
|
// @ts-ignore
|
|
@@ -249155,8 +249139,8 @@ const checkLiveStatusAndRecord$1 = async function ({ getSavePath, banLiveId, isM
|
|
|
249155
249139
|
});
|
|
249156
249140
|
};
|
|
249157
249141
|
downloader.on("videoFileCreated", handleVideoCreated);
|
|
249158
|
-
downloader.on("videoFileCompleted", (
|
|
249159
|
-
this.emit("videoFileCompleted",
|
|
249142
|
+
downloader.on("videoFileCompleted", (data) => {
|
|
249143
|
+
this.emit("videoFileCompleted", data);
|
|
249160
249144
|
});
|
|
249161
249145
|
downloader.on("DebugLog", (data) => {
|
|
249162
249146
|
this.emit("DebugLog", data);
|
|
@@ -290470,8 +290454,8 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isMan
|
|
|
290470
290454
|
});
|
|
290471
290455
|
};
|
|
290472
290456
|
downloader.on("videoFileCreated", handleVideoCreated);
|
|
290473
|
-
downloader.on("videoFileCompleted", (
|
|
290474
|
-
this.emit("videoFileCompleted",
|
|
290457
|
+
downloader.on("videoFileCompleted", (data) => {
|
|
290458
|
+
this.emit("videoFileCompleted", data);
|
|
290475
290459
|
});
|
|
290476
290460
|
downloader.on("DebugLog", (data) => {
|
|
290477
290461
|
this.emit("DebugLog", data);
|
|
@@ -291119,7 +291103,7 @@ async function createRecorderManager(appConfig) {
|
|
|
291119
291103
|
return;
|
|
291120
291104
|
if (recorder.recordHandle) {
|
|
291121
291105
|
const logFilePath = utils$2.replaceExtName(`${recorder.recordHandle.savePath}_${recorder.id}`, ".recorder.log");
|
|
291122
|
-
fs$k.
|
|
291106
|
+
fs$k.appendFile(logFilePath, log.text + "\n").catch(() => { });
|
|
291123
291107
|
return;
|
|
291124
291108
|
}
|
|
291125
291109
|
else {
|
|
@@ -291198,8 +291182,8 @@ async function createRecorderManager(appConfig) {
|
|
|
291198
291182
|
platform: recorder.providerId,
|
|
291199
291183
|
});
|
|
291200
291184
|
});
|
|
291201
|
-
manager.on("videoFileCompleted", async ({ recorder, filename }) => {
|
|
291202
|
-
logObj.info("Manager videoFileCompleted", { recorder, filename });
|
|
291185
|
+
manager.on("videoFileCompleted", async ({ recorder, filename, stats }) => {
|
|
291186
|
+
logObj.info("Manager videoFileCompleted", { recorder, filename, stats });
|
|
291203
291187
|
const endTime = new Date();
|
|
291204
291188
|
const data = recorderConfig.get(recorder.id);
|
|
291205
291189
|
const title = recorder?.liveInfo?.title;
|
|
@@ -291230,7 +291214,16 @@ async function createRecorderManager(appConfig) {
|
|
|
291230
291214
|
video_filename: videoFilename,
|
|
291231
291215
|
quick_hash: quickHash,
|
|
291232
291216
|
});
|
|
291233
|
-
if (
|
|
291217
|
+
if (stats) {
|
|
291218
|
+
recordHistory.upadteLive({
|
|
291219
|
+
video_file: filename,
|
|
291220
|
+
live_id: liveId,
|
|
291221
|
+
}, {
|
|
291222
|
+
danma_num: stats.danmaNum,
|
|
291223
|
+
interact_num: stats.uniqMember,
|
|
291224
|
+
});
|
|
291225
|
+
}
|
|
291226
|
+
else if (xmlFile && (await fs$k.pathExists(xmlFile))) {
|
|
291234
291227
|
const { uniqMember, danmaNum } = await danmaReport(xmlFile);
|
|
291235
291228
|
recordHistory.upadteLive({
|
|
291236
291229
|
video_file: filename,
|
|
@@ -4,7 +4,7 @@ var path$7 = require('node:path');
|
|
|
4
4
|
var require$$2$1 = require('node:http');
|
|
5
5
|
var require$$1$2 = require('node:url');
|
|
6
6
|
var a = require('node:https');
|
|
7
|
-
var index = require('./index-
|
|
7
|
+
var index = require('./index-CgpPN0zA.cjs');
|
|
8
8
|
var require$$0$4 = require('tty');
|
|
9
9
|
var require$$1$3 = require('util');
|
|
10
10
|
var require$$0$b = require('assert');
|
|
@@ -55050,55 +55050,47 @@ const coerce$1 = (version, options) => {
|
|
|
55050
55050
|
};
|
|
55051
55051
|
var coerce_1 = coerce$1;
|
|
55052
55052
|
|
|
55053
|
-
|
|
55054
|
-
|
|
55055
|
-
|
|
55056
|
-
|
|
55057
|
-
|
|
55058
|
-
hasRequiredLrucache = 1;
|
|
55059
|
-
class LRUCache {
|
|
55060
|
-
constructor () {
|
|
55061
|
-
this.max = 1000;
|
|
55062
|
-
this.map = new Map();
|
|
55063
|
-
}
|
|
55064
|
-
|
|
55065
|
-
get (key) {
|
|
55066
|
-
const value = this.map.get(key);
|
|
55067
|
-
if (value === undefined) {
|
|
55068
|
-
return undefined
|
|
55069
|
-
} else {
|
|
55070
|
-
// Remove the key from the map and add it to the end
|
|
55071
|
-
this.map.delete(key);
|
|
55072
|
-
this.map.set(key, value);
|
|
55073
|
-
return value
|
|
55074
|
-
}
|
|
55075
|
-
}
|
|
55053
|
+
class LRUCache {
|
|
55054
|
+
constructor () {
|
|
55055
|
+
this.max = 1000;
|
|
55056
|
+
this.map = new Map();
|
|
55057
|
+
}
|
|
55076
55058
|
|
|
55077
|
-
|
|
55078
|
-
|
|
55079
|
-
|
|
55059
|
+
get (key) {
|
|
55060
|
+
const value = this.map.get(key);
|
|
55061
|
+
if (value === undefined) {
|
|
55062
|
+
return undefined
|
|
55063
|
+
} else {
|
|
55064
|
+
// Remove the key from the map and add it to the end
|
|
55065
|
+
this.map.delete(key);
|
|
55066
|
+
this.map.set(key, value);
|
|
55067
|
+
return value
|
|
55068
|
+
}
|
|
55069
|
+
}
|
|
55080
55070
|
|
|
55081
|
-
|
|
55082
|
-
|
|
55071
|
+
delete (key) {
|
|
55072
|
+
return this.map.delete(key)
|
|
55073
|
+
}
|
|
55083
55074
|
|
|
55084
|
-
|
|
55085
|
-
|
|
55086
|
-
if (this.map.size >= this.max) {
|
|
55087
|
-
const firstKey = this.map.keys().next().value;
|
|
55088
|
-
this.delete(firstKey);
|
|
55089
|
-
}
|
|
55075
|
+
set (key, value) {
|
|
55076
|
+
const deleted = this.delete(key);
|
|
55090
55077
|
|
|
55091
|
-
|
|
55092
|
-
|
|
55078
|
+
if (!deleted && value !== undefined) {
|
|
55079
|
+
// If cache is full, delete the least recently used item
|
|
55080
|
+
if (this.map.size >= this.max) {
|
|
55081
|
+
const firstKey = this.map.keys().next().value;
|
|
55082
|
+
this.delete(firstKey);
|
|
55083
|
+
}
|
|
55093
55084
|
|
|
55094
|
-
|
|
55095
|
-
|
|
55096
|
-
}
|
|
55085
|
+
this.map.set(key, value);
|
|
55086
|
+
}
|
|
55097
55087
|
|
|
55098
|
-
|
|
55099
|
-
|
|
55088
|
+
return this
|
|
55089
|
+
}
|
|
55100
55090
|
}
|
|
55101
55091
|
|
|
55092
|
+
var lrucache = LRUCache;
|
|
55093
|
+
|
|
55102
55094
|
var range;
|
|
55103
55095
|
var hasRequiredRange;
|
|
55104
55096
|
|
|
@@ -55319,7 +55311,7 @@ function requireRange () {
|
|
|
55319
55311
|
|
|
55320
55312
|
range = Range;
|
|
55321
55313
|
|
|
55322
|
-
const LRU =
|
|
55314
|
+
const LRU = lrucache;
|
|
55323
55315
|
const cache = new LRU();
|
|
55324
55316
|
|
|
55325
55317
|
const parseOptions = parseOptions_1;
|
|
@@ -81473,7 +81465,7 @@ exports.handler = void 0;
|
|
|
81473
81465
|
exports.appConfig = void 0;
|
|
81474
81466
|
exports.container = void 0;
|
|
81475
81467
|
const fileCache = createFileCache();
|
|
81476
|
-
path$7.dirname(require$$1$2.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-
|
|
81468
|
+
path$7.dirname(require$$1$2.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-DG03bxlI.cjs', document.baseURI).href))));
|
|
81477
81469
|
const authMiddleware = (passKey) => {
|
|
81478
81470
|
return async (ctx, next) => {
|
|
81479
81471
|
const authHeader = ctx.headers["authorization"] || ctx.request.query.auth;
|
package/lib/index.cjs
CHANGED
|
@@ -3715,7 +3715,7 @@ const {
|
|
|
3715
3715
|
Help,
|
|
3716
3716
|
} = commander;
|
|
3717
3717
|
|
|
3718
|
-
var version = "3.13.
|
|
3718
|
+
var version = "3.13.1";
|
|
3719
3719
|
|
|
3720
3720
|
process.on("uncaughtException", function (error) {
|
|
3721
3721
|
console.error(`${new Date().toISOString()} uncaughtException`, error);
|
|
@@ -3741,8 +3741,8 @@ program
|
|
|
3741
3741
|
throw new Error(`${c.configFolder}参数不存在,请先重新运行 config gen 命令`);
|
|
3742
3742
|
}
|
|
3743
3743
|
// 下面两行顺序不能换(
|
|
3744
|
-
const { init } = await Promise.resolve().then(function () { return require('./index-
|
|
3745
|
-
const { serverStart } = await Promise.resolve().then(function () { return require('./index-
|
|
3744
|
+
const { init } = await Promise.resolve().then(function () { return require('./index-CgpPN0zA.cjs'); }).then(function (n) { return n.index; });
|
|
3745
|
+
const { serverStart } = await Promise.resolve().then(function () { return require('./index-DG03bxlI.cjs'); });
|
|
3746
3746
|
const globalConfig = {
|
|
3747
3747
|
ffmpegPresetPath: path$1.join(c.configFolder, "ffmpeg_presets.json"),
|
|
3748
3748
|
videoPresetPath: path$1.join(c.configFolder, "presets.json"),
|
|
@@ -4,7 +4,7 @@ var os$3 = require('node:os');
|
|
|
4
4
|
var path$5 = require('node:path');
|
|
5
5
|
var fs$3 = require('node:fs');
|
|
6
6
|
var crypto = require('node:crypto');
|
|
7
|
-
var index = require('./index-
|
|
7
|
+
var index = require('./index-CgpPN0zA.cjs');
|
|
8
8
|
var require$$0$1 = require('fs');
|
|
9
9
|
var require$$0 = require('path');
|
|
10
10
|
var require$$0$2 = require('child_process');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bililive-cli",
|
|
3
|
-
"version": "3.13.
|
|
3
|
+
"version": "3.13.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "biliLive-tools的cli程序",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
"commander": "^12.1.0",
|
|
40
40
|
"rimraf": "^6.0.1",
|
|
41
41
|
"tsx": "^4.19.2",
|
|
42
|
-
"@biliLive-tools/
|
|
43
|
-
"@biliLive-tools/types": "3.13.
|
|
44
|
-
"@biliLive-tools/
|
|
42
|
+
"@biliLive-tools/http": "3.13.1",
|
|
43
|
+
"@biliLive-tools/types": "3.13.1",
|
|
44
|
+
"@biliLive-tools/shared": "3.13.1"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
47
|
"start": "tsx src/index.ts",
|