bililive-cli 3.12.1 → 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.
|
@@ -11363,12 +11363,12 @@ const APP_DEFAULT_CONFIG = {
|
|
|
11363
11363
|
hotProgressColor: "#f9f5f3",
|
|
11364
11364
|
hotProgressFillColor: "#333333",
|
|
11365
11365
|
convert2Mp4: false,
|
|
11366
|
-
removeSourceAferrConvert2Mp4: true,
|
|
11367
11366
|
flvRepair: false,
|
|
11368
11367
|
syncId: undefined,
|
|
11369
11368
|
uploadHandleTime: ["00:00:00", "23:59:59"],
|
|
11370
11369
|
limitUploadTime: false,
|
|
11371
11370
|
uploadNoDanmu: false,
|
|
11371
|
+
uploadToSameMedia: false,
|
|
11372
11372
|
noDanmuVideoPreset: undefined,
|
|
11373
11373
|
limitVideoConvertTime: false,
|
|
11374
11374
|
videoHandleTime: ["00:00:00", "23:59:59"],
|
|
@@ -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);
|
|
@@ -65707,8 +65707,9 @@ class Platform extends BaseRequest {
|
|
|
65707
65707
|
*/
|
|
65708
65708
|
async editMediaWebApi(videos, options, mode) {
|
|
65709
65709
|
this.auth.authLogin();
|
|
65710
|
+
const { sortByCid, ...mediaOptions } = options;
|
|
65710
65711
|
const archive = await this.getArchive({
|
|
65711
|
-
aid:
|
|
65712
|
+
aid: mediaOptions.aid,
|
|
65712
65713
|
});
|
|
65713
65714
|
const archiveData = archive.archive;
|
|
65714
65715
|
for (const key of [
|
|
@@ -65725,9 +65726,9 @@ class Platform extends BaseRequest {
|
|
|
65725
65726
|
videos: [],
|
|
65726
65727
|
...archiveData,
|
|
65727
65728
|
csrf: csrf,
|
|
65728
|
-
...
|
|
65729
|
+
...mediaOptions,
|
|
65729
65730
|
watermark: {
|
|
65730
|
-
state:
|
|
65731
|
+
state: mediaOptions.watermark?.state ?? archive.watermark.state,
|
|
65731
65732
|
},
|
|
65732
65733
|
};
|
|
65733
65734
|
this.checkOptions(data);
|
|
@@ -65740,10 +65741,10 @@ class Platform extends BaseRequest {
|
|
|
65740
65741
|
data.desc = this.convertDescV2ToDesc(data.desc_v2);
|
|
65741
65742
|
}
|
|
65742
65743
|
if (mode === "append") {
|
|
65743
|
-
data.videos = [...archive.videos, ...videos];
|
|
65744
|
+
data.videos = this.sortVideosByCid([...archive.videos, ...videos], sortByCid);
|
|
65744
65745
|
}
|
|
65745
65746
|
else if (mode === "replace") {
|
|
65746
|
-
data.videos = videos;
|
|
65747
|
+
data.videos = this.sortVideosByCid(videos, sortByCid);
|
|
65747
65748
|
}
|
|
65748
65749
|
else {
|
|
65749
65750
|
throw new Error("mode can only be append or replace");
|
|
@@ -65872,6 +65873,29 @@ class Platform extends BaseRequest {
|
|
|
65872
65873
|
// }
|
|
65873
65874
|
return true;
|
|
65874
65875
|
}
|
|
65876
|
+
sortVideosByCid(videos, cidOrder) {
|
|
65877
|
+
if (!cidOrder || cidOrder.length === 0) {
|
|
65878
|
+
return videos;
|
|
65879
|
+
}
|
|
65880
|
+
const orderMap = new Map();
|
|
65881
|
+
cidOrder.forEach((cid, index) => {
|
|
65882
|
+
if (!orderMap.has(cid)) {
|
|
65883
|
+
orderMap.set(cid, index);
|
|
65884
|
+
}
|
|
65885
|
+
});
|
|
65886
|
+
const matchedVideos = [];
|
|
65887
|
+
const unmatchedVideos = [];
|
|
65888
|
+
videos.forEach(video => {
|
|
65889
|
+
const order = orderMap.get(video.cid);
|
|
65890
|
+
if (order === undefined) {
|
|
65891
|
+
unmatchedVideos.push(video);
|
|
65892
|
+
return;
|
|
65893
|
+
}
|
|
65894
|
+
matchedVideos.push({ video, order });
|
|
65895
|
+
});
|
|
65896
|
+
matchedVideos.sort((left, right) => left.order - right.order);
|
|
65897
|
+
return [...matchedVideos.map(item => item.video), ...unmatchedVideos];
|
|
65898
|
+
}
|
|
65875
65899
|
/**
|
|
65876
65900
|
* 获取推荐标签
|
|
65877
65901
|
* subtype,title,description三个参数对结果影响较大
|
|
@@ -66923,7 +66947,10 @@ class Video extends BaseRequest {
|
|
|
66923
66947
|
const duration = detail.View?.duration;
|
|
66924
66948
|
if (!duration)
|
|
66925
66949
|
throw new Error("视频时长获取失败");
|
|
66926
|
-
const
|
|
66950
|
+
const part = detail.View?.pages?.find((p) => p.cid === params.cid);
|
|
66951
|
+
if (!part)
|
|
66952
|
+
throw new Error("未找到对应cid的视频分P");
|
|
66953
|
+
const totalSegment = Math.ceil(part.duration / 360);
|
|
66927
66954
|
const dmList = [];
|
|
66928
66955
|
// 从1开始
|
|
66929
66956
|
for (let i = 0; i < totalSegment; i++) {
|
|
@@ -179736,31 +179763,119 @@ async function drawSmoothLineChart(data, width, height) {
|
|
|
179736
179763
|
return canvas$1;
|
|
179737
179764
|
}
|
|
179738
179765
|
|
|
179766
|
+
const createEmptyXmlBuckets = () => ({
|
|
179767
|
+
danmu: [],
|
|
179768
|
+
sc: [],
|
|
179769
|
+
guard: [],
|
|
179770
|
+
gift: [],
|
|
179771
|
+
});
|
|
179772
|
+
const isXmlDanmuItemType = (value) => {
|
|
179773
|
+
return ["danmu", "sc", "guard", "gift"].includes(String(value));
|
|
179774
|
+
};
|
|
179775
|
+
const isValidXmlItemForType = (type, item) => {
|
|
179776
|
+
if (type === "danmu") {
|
|
179777
|
+
return typeof item["@_p"] === "string" && item["@_p"].length > 0;
|
|
179778
|
+
}
|
|
179779
|
+
return ((typeof item["@_ts"] === "string" && item["@_ts"].length > 0) ||
|
|
179780
|
+
typeof item["@_ts"] === "number");
|
|
179781
|
+
};
|
|
179782
|
+
const logInvalidTransformedItem = (reason, sourceType, item, targetType) => {
|
|
179783
|
+
logObj.error("filterFunction transform item dropped", {
|
|
179784
|
+
reason,
|
|
179785
|
+
sourceType,
|
|
179786
|
+
targetType,
|
|
179787
|
+
item,
|
|
179788
|
+
});
|
|
179789
|
+
};
|
|
179790
|
+
const createFilterFunction = (filterFunction) => {
|
|
179791
|
+
if (!filterFunction.includes("filter")) {
|
|
179792
|
+
return;
|
|
179793
|
+
}
|
|
179794
|
+
return new Function("type", "danmu", "logger", `
|
|
179795
|
+
${filterFunction}
|
|
179796
|
+
return filter(type, danmu, logger);`);
|
|
179797
|
+
};
|
|
179798
|
+
const createTransformFunction = (filterFunction) => {
|
|
179799
|
+
if (!filterFunction.includes("transform")) {
|
|
179800
|
+
return;
|
|
179801
|
+
}
|
|
179802
|
+
return new Function("type", "danmu", "logger", `
|
|
179803
|
+
${filterFunction}
|
|
179804
|
+
return transform(type, danmu, logger);`);
|
|
179805
|
+
};
|
|
179806
|
+
const transformXmlItem = (sourceType, item, transformFunc) => {
|
|
179807
|
+
const transformed = transformFunc(sourceType, item, logObj);
|
|
179808
|
+
if (transformed == null || transformed === false) {
|
|
179809
|
+
return null;
|
|
179810
|
+
}
|
|
179811
|
+
const candidate = transformed === true || transformed === undefined ? item : transformed;
|
|
179812
|
+
if (typeof candidate !== "object" || candidate === null || Array.isArray(candidate)) {
|
|
179813
|
+
logInvalidTransformedItem("transform must return an object", sourceType, candidate);
|
|
179814
|
+
return null;
|
|
179815
|
+
}
|
|
179816
|
+
const nextType = "type" in candidate ? candidate.type : sourceType;
|
|
179817
|
+
if (!isXmlDanmuItemType(nextType)) {
|
|
179818
|
+
logInvalidTransformedItem("unsupported target type", sourceType, candidate, nextType);
|
|
179819
|
+
return null;
|
|
179820
|
+
}
|
|
179821
|
+
const normalizedItem = { ...candidate };
|
|
179822
|
+
delete normalizedItem.type;
|
|
179823
|
+
if (!isValidXmlItemForType(nextType, normalizedItem)) {
|
|
179824
|
+
logInvalidTransformedItem("missing required fields for target type", sourceType, candidate, nextType);
|
|
179825
|
+
return null;
|
|
179826
|
+
}
|
|
179827
|
+
return {
|
|
179828
|
+
type: nextType,
|
|
179829
|
+
item: normalizedItem,
|
|
179830
|
+
};
|
|
179831
|
+
};
|
|
179832
|
+
const processXmlItems = (events, filterFunction) => {
|
|
179833
|
+
const buckets = createEmptyXmlBuckets();
|
|
179834
|
+
const filterFunc = createFilterFunction(filterFunction);
|
|
179835
|
+
const transformFunc = createTransformFunction(filterFunction);
|
|
179836
|
+
for (const event of events) {
|
|
179837
|
+
if (filterFunc && !filterFunc(event.sourceType, event.item, logObj)) {
|
|
179838
|
+
continue;
|
|
179839
|
+
}
|
|
179840
|
+
if (!transformFunc) {
|
|
179841
|
+
buckets[event.sourceType].push(event.item);
|
|
179842
|
+
continue;
|
|
179843
|
+
}
|
|
179844
|
+
const transformedItem = transformXmlItem(event.sourceType, event.item, transformFunc);
|
|
179845
|
+
if (!transformedItem) {
|
|
179846
|
+
continue;
|
|
179847
|
+
}
|
|
179848
|
+
buckets[transformedItem.type].push(transformedItem.item);
|
|
179849
|
+
}
|
|
179850
|
+
return buckets;
|
|
179851
|
+
};
|
|
179852
|
+
const createXmlEvents = (items, sourceType) => {
|
|
179853
|
+
const result = [];
|
|
179854
|
+
for (const item of items) {
|
|
179855
|
+
result.push({
|
|
179856
|
+
sourceType,
|
|
179857
|
+
item,
|
|
179858
|
+
});
|
|
179859
|
+
}
|
|
179860
|
+
return result;
|
|
179861
|
+
};
|
|
179739
179862
|
/**
|
|
179740
|
-
*
|
|
179863
|
+
* 生成经过自定义处理后的xml文件
|
|
179741
179864
|
* @param input
|
|
179742
179865
|
* @param output
|
|
179743
|
-
* @param
|
|
179866
|
+
* @param options
|
|
179744
179867
|
* @returns
|
|
179745
179868
|
*/
|
|
179746
|
-
const
|
|
179747
|
-
const filterFunc = new Function("type", "danmu", "logger", `
|
|
179748
|
-
${filterFunction}
|
|
179749
|
-
return filter(type, danmu, logger);`);
|
|
179869
|
+
const genProcessedXml = async (input, output, filterFunction) => {
|
|
179750
179870
|
const { jObj, danmuku, sc, guard, gift } = await parseXmlFile(input, true);
|
|
179751
|
-
const
|
|
179752
|
-
|
|
179753
|
-
|
|
179754
|
-
|
|
179755
|
-
|
|
179756
|
-
|
|
179757
|
-
const
|
|
179758
|
-
|
|
179759
|
-
});
|
|
179760
|
-
const filteredGift = gift.filter((item) => {
|
|
179761
|
-
return filterFunc("gift", item, logObj);
|
|
179762
|
-
});
|
|
179763
|
-
const xmlData = generateMergedXmlContent(filteredDanmuku, filteredGift, filteredSc, filteredGuard, jObj.i?.metadata || {});
|
|
179871
|
+
const events = [
|
|
179872
|
+
...createXmlEvents(danmuku, "danmu"),
|
|
179873
|
+
...createXmlEvents(sc, "sc"),
|
|
179874
|
+
...createXmlEvents(guard, "guard"),
|
|
179875
|
+
...createXmlEvents(gift, "gift"),
|
|
179876
|
+
];
|
|
179877
|
+
const processedBuckets = processXmlItems(events, filterFunction);
|
|
179878
|
+
const xmlData = generateMergedXmlContent(processedBuckets.danmu, processedBuckets.gift, processedBuckets.sc, processedBuckets.guard, jObj.i?.metadata || {});
|
|
179764
179879
|
await fs$k.writeFile(output, xmlData);
|
|
179765
179880
|
return output;
|
|
179766
179881
|
};
|
|
@@ -179785,10 +179900,13 @@ const addConvertDanmu2AssTask = async (originInput, output, danmuOptions, option
|
|
|
179785
179900
|
opts = await customChangeFunc(originInput, opts);
|
|
179786
179901
|
}
|
|
179787
179902
|
let filteredOutput;
|
|
179788
|
-
|
|
179789
|
-
|
|
179903
|
+
const hasFilterFunction = Boolean((opts.filterFunction ?? "").trim()) && (opts.filterFunction ?? "").includes("filter");
|
|
179904
|
+
const hasTransformFunction = Boolean((opts.filterFunction ?? "").trim()) &&
|
|
179905
|
+
(opts.filterFunction ?? "").includes("transform");
|
|
179906
|
+
if (hasTransformFunction || hasFilterFunction) {
|
|
179907
|
+
// 如果存在自定义数据处理函数,则需要把处理后的xml保存到临时文件夹中
|
|
179790
179908
|
filteredOutput = path$y.join(tempDir, `${uuid$5()}.xml`);
|
|
179791
|
-
await
|
|
179909
|
+
await genProcessedXml(originInput, filteredOutput, opts.filterFunction);
|
|
179792
179910
|
}
|
|
179793
179911
|
if (opts.blacklist) {
|
|
179794
179912
|
const fileTxtPath = path$y.join(tempDir, `${uuid$5()}.txt`);
|
|
@@ -181468,8 +181586,10 @@ async function addMediaApi(uid, video, options) {
|
|
|
181468
181586
|
* 编辑视频接口
|
|
181469
181587
|
*/
|
|
181470
181588
|
async function editMediaApi(uid, aid, video, options) {
|
|
181471
|
-
const mediaOptions = {
|
|
181472
|
-
|
|
181589
|
+
const mediaOptions = {
|
|
181590
|
+
sortByCid: options.sortByCid,
|
|
181591
|
+
};
|
|
181592
|
+
// console.log("编辑视频", options);
|
|
181473
181593
|
// const globalConfig = container.resolve("globalConfig");
|
|
181474
181594
|
// const mediaOptions = formatOptions(options, path.join(globalConfig.userDataPath, "cover"));
|
|
181475
181595
|
const client = createClient(uid);
|
|
@@ -181808,6 +181928,7 @@ async function editMedia(aid, filePath, options, uid, extraOptions) {
|
|
|
181808
181928
|
uid,
|
|
181809
181929
|
mediaOptions: formattedOptions,
|
|
181810
181930
|
aid,
|
|
181931
|
+
sortParams: extraOptions?.sortParams,
|
|
181811
181932
|
}, {
|
|
181812
181933
|
onEnd: async () => {
|
|
181813
181934
|
// 审核检查
|
|
@@ -182159,6 +182280,8 @@ const biliApi = {
|
|
|
182159
182280
|
editVideoPartName,
|
|
182160
182281
|
queryVideoStatus,
|
|
182161
182282
|
getPlayUrl,
|
|
182283
|
+
readUser,
|
|
182284
|
+
writeUser,
|
|
182162
182285
|
};
|
|
182163
182286
|
|
|
182164
182287
|
/**
|
|
@@ -184110,11 +184233,14 @@ class BiliPartVideoTask extends AbstractTask {
|
|
|
184110
184233
|
logObj.error(`task ${this.taskId} error: ${error}`);
|
|
184111
184234
|
}
|
|
184112
184235
|
}
|
|
184113
|
-
this.completedPart =
|
|
184236
|
+
this.completedPart = {
|
|
184237
|
+
...data,
|
|
184238
|
+
filePath: this.command.filePath,
|
|
184239
|
+
};
|
|
184114
184240
|
this.endTime = Date.now();
|
|
184115
184241
|
// 重置进度追踪
|
|
184116
184242
|
this.speedCalculator.reset();
|
|
184117
|
-
callback.onEnd && callback.onEnd(
|
|
184243
|
+
callback.onEnd && callback.onEnd(this.completedPart);
|
|
184118
184244
|
this.emitter.emit("task-end", { taskId: this.taskId });
|
|
184119
184245
|
});
|
|
184120
184246
|
command.emitter.on("error", (err) => {
|
|
@@ -184161,6 +184287,7 @@ class BiliPartVideoTask extends AbstractTask {
|
|
|
184161
184287
|
cid: part.cid,
|
|
184162
184288
|
filename: part.filename,
|
|
184163
184289
|
title: this.command.title,
|
|
184290
|
+
filePath: this.command.filePath,
|
|
184164
184291
|
};
|
|
184165
184292
|
this.endTime = Date.now();
|
|
184166
184293
|
// 重置进度追踪
|
|
@@ -184365,7 +184492,7 @@ class BiliAddVideoTask extends BiliVideoTask {
|
|
|
184365
184492
|
this.progress = 100;
|
|
184366
184493
|
this.callback.onEnd && this.callback.onEnd(data);
|
|
184367
184494
|
this.output = String(data.aid);
|
|
184368
|
-
this.emitter.emit("task-end", { taskId: this.taskId });
|
|
184495
|
+
this.emitter.emit("task-end", { taskId: this.taskId, data: parts });
|
|
184369
184496
|
uploadPartService.removeByCids(parts.map((part) => part.cid));
|
|
184370
184497
|
exports.statisticsService.addOrUpdate({
|
|
184371
184498
|
where: { stat_key: this.lastUpdateTimeKey },
|
|
@@ -184391,10 +184518,12 @@ class BiliAddVideoTask extends BiliVideoTask {
|
|
|
184391
184518
|
class BiliEditVideoTask extends BiliVideoTask {
|
|
184392
184519
|
aid;
|
|
184393
184520
|
mediaOptions;
|
|
184521
|
+
sortParams;
|
|
184394
184522
|
constructor(options, callback) {
|
|
184395
184523
|
super(options, callback);
|
|
184396
184524
|
this.aid = options.aid;
|
|
184397
184525
|
this.mediaOptions = options.mediaOptions;
|
|
184526
|
+
this.sortParams = options.sortParams;
|
|
184398
184527
|
this.on("completed", () => {
|
|
184399
184528
|
this.submit();
|
|
184400
184529
|
});
|
|
@@ -184439,12 +184568,26 @@ class BiliEditVideoTask extends BiliVideoTask {
|
|
|
184439
184568
|
return;
|
|
184440
184569
|
}
|
|
184441
184570
|
try {
|
|
184442
|
-
const
|
|
184571
|
+
const sortByCid = [];
|
|
184572
|
+
if (this.sortParams && Array.isArray(this.sortParams)) {
|
|
184573
|
+
for (const param of this.sortParams) {
|
|
184574
|
+
if (param.cid) {
|
|
184575
|
+
sortByCid.push(param.cid);
|
|
184576
|
+
}
|
|
184577
|
+
else {
|
|
184578
|
+
const part = parts.find((p) => p.filePath === param.filePath);
|
|
184579
|
+
if (part) {
|
|
184580
|
+
sortByCid.push(part.cid);
|
|
184581
|
+
}
|
|
184582
|
+
}
|
|
184583
|
+
}
|
|
184584
|
+
}
|
|
184585
|
+
const data = await retryWithAxiosError(() => editMediaApi(this.uid, this.aid, parts, { ...this.mediaOptions, sortByCid: sortByCid }), 5);
|
|
184443
184586
|
this.status = "completed";
|
|
184444
184587
|
this.progress = 100;
|
|
184445
184588
|
this.callback.onEnd && this.callback.onEnd(data);
|
|
184446
184589
|
this.output = String(data.aid);
|
|
184447
|
-
this.emitter.emit("task-end", { taskId: this.taskId });
|
|
184590
|
+
this.emitter.emit("task-end", { taskId: this.taskId, data: parts });
|
|
184448
184591
|
uploadPartService.removeByCids(parts.map((part) => part.cid));
|
|
184449
184592
|
exports.statisticsService.addOrUpdate({
|
|
184450
184593
|
where: { stat_key: this.lastUpdateTimeKey },
|
|
@@ -185517,7 +185660,7 @@ function getValuesFromArrayLikeFlexSpaceBetween(array, columnCount) {
|
|
|
185517
185660
|
});
|
|
185518
185661
|
return columnValues;
|
|
185519
185662
|
}
|
|
185520
|
-
function ensureFolderExist
|
|
185663
|
+
function ensureFolderExist(fileOrFolderPath) {
|
|
185521
185664
|
const folder = path$y.dirname(fileOrFolderPath);
|
|
185522
185665
|
if (!fs$D.existsSync(folder)) {
|
|
185523
185666
|
fs$D.mkdirSync(folder, { recursive: true });
|
|
@@ -185926,7 +186069,7 @@ var utils$2 = {
|
|
|
185926
186069
|
replaceExtName,
|
|
185927
186070
|
singleton: singleton$1,
|
|
185928
186071
|
getValuesFromArrayLikeFlexSpaceBetween,
|
|
185929
|
-
ensureFolderExist
|
|
186072
|
+
ensureFolderExist,
|
|
185930
186073
|
assert: assert$m,
|
|
185931
186074
|
assertStringType,
|
|
185932
186075
|
assertNumberType,
|
|
@@ -186091,71 +186234,80 @@ async function getBiliStatusInfoByRoomIds(RoomIds) {
|
|
|
186091
186234
|
|
|
186092
186235
|
/**
|
|
186093
186236
|
* XML流式写入控制器,用于实时写入弹幕、礼物等信息到XML文件
|
|
186094
|
-
* 相比原有的json方案,这个实现每隔
|
|
186237
|
+
* 相比原有的json方案,这个实现每隔10秒就会写入数据,减少内存占用和数据丢失风险
|
|
186095
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`;
|
|
186096
186241
|
function createRecordExtraDataController(savePath) {
|
|
186097
186242
|
const data = {
|
|
186243
|
+
header: XML_FILE_HEADER,
|
|
186098
186244
|
meta: {
|
|
186099
186245
|
recordStartTimestamp: Date.now(),
|
|
186100
186246
|
},
|
|
186101
186247
|
pendingMessages: [],
|
|
186102
186248
|
};
|
|
186103
186249
|
let hasCompleted = false;
|
|
186104
|
-
let
|
|
186105
|
-
let
|
|
186106
|
-
|
|
186107
|
-
|
|
186108
|
-
|
|
186109
|
-
|
|
186110
|
-
|
|
186111
|
-
|
|
186112
|
-
|
|
186113
|
-
|
|
186114
|
-
await fs$D.promises.writeFile(savePath, header);
|
|
186115
|
-
}
|
|
186116
|
-
catch (error) {
|
|
186117
|
-
console.error("初始化XML文件失败:", error);
|
|
186118
|
-
isInitialized = false;
|
|
186119
|
-
throw error;
|
|
186120
|
-
}
|
|
186121
|
-
};
|
|
186122
|
-
// 每10秒写入一次数据
|
|
186123
|
-
const scheduleWrite = asyncThrottle(() => writeToFile(), 10e3, {
|
|
186124
|
-
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,
|
|
186125
186260
|
});
|
|
186126
|
-
const
|
|
186127
|
-
|
|
186261
|
+
const trackInteractedUser = (message) => {
|
|
186262
|
+
const userName = message.sender?.name?.trim();
|
|
186263
|
+
if (!userName)
|
|
186128
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();
|
|
186129
186276
|
}
|
|
186130
|
-
|
|
186131
|
-
|
|
186132
|
-
isWriting = true;
|
|
186277
|
+
const messagesToWrite = [...data.pendingMessages];
|
|
186278
|
+
data.pendingMessages = [];
|
|
186133
186279
|
try {
|
|
186134
|
-
// 获取待写入的消息
|
|
186135
|
-
const messagesToWrite = [...data.pendingMessages];
|
|
186136
|
-
data.pendingMessages = [];
|
|
186137
|
-
// 生成XML内容
|
|
186138
186280
|
const xmlContent = generateXmlContent(data.meta, messagesToWrite);
|
|
186139
|
-
|
|
186140
|
-
|
|
186281
|
+
if (!hasPersistedHeader) {
|
|
186282
|
+
await initializeFile(xmlContent);
|
|
186283
|
+
}
|
|
186284
|
+
else if (xmlContent) {
|
|
186285
|
+
await appendToXmlFile(savePath, xmlContent);
|
|
186286
|
+
}
|
|
186141
186287
|
}
|
|
186142
186288
|
catch (error) {
|
|
186143
186289
|
console.error("写入XML文件失败:", error);
|
|
186144
|
-
|
|
186145
|
-
data.pendingMessages = [...data.pendingMessages];
|
|
186146
|
-
}
|
|
186147
|
-
finally {
|
|
186148
|
-
isWriting = false;
|
|
186290
|
+
data.pendingMessages = [...messagesToWrite, ...data.pendingMessages];
|
|
186149
186291
|
}
|
|
186150
186292
|
};
|
|
186293
|
+
// 每10秒写入一次数据
|
|
186294
|
+
const writeTimer = setInterval(() => {
|
|
186295
|
+
writeToFile();
|
|
186296
|
+
}, 10e3);
|
|
186151
186297
|
const addMessage = (message) => {
|
|
186152
186298
|
if (hasCompleted)
|
|
186153
186299
|
return;
|
|
186154
|
-
|
|
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);
|
|
186155
186310
|
data.pendingMessages.push(message);
|
|
186156
|
-
// 确保文件已初始化
|
|
186157
|
-
initializeFile().catch(console.error);
|
|
186158
|
-
scheduleWrite();
|
|
186159
186311
|
};
|
|
186160
186312
|
const setMeta = async (meta) => {
|
|
186161
186313
|
if (hasCompleted)
|
|
@@ -186164,30 +186316,31 @@ function createRecordExtraDataController(savePath) {
|
|
|
186164
186316
|
...data.meta,
|
|
186165
186317
|
...meta,
|
|
186166
186318
|
};
|
|
186167
|
-
// 确保文件已初始化,然后立即更新文件中的metadata
|
|
186168
|
-
await initializeFile().catch(console.error);
|
|
186169
|
-
await updateMetadataInFile(savePath, data.meta).catch(console.error);
|
|
186170
186319
|
};
|
|
186171
186320
|
const flush = async () => {
|
|
186172
186321
|
if (hasCompleted)
|
|
186173
186322
|
return;
|
|
186174
186323
|
hasCompleted = true;
|
|
186175
|
-
|
|
186176
|
-
|
|
186177
|
-
|
|
186178
|
-
|
|
186179
|
-
|
|
186180
|
-
|
|
186181
|
-
|
|
186182
|
-
|
|
186183
|
-
|
|
186184
|
-
|
|
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
|
+
}
|
|
186185
186337
|
};
|
|
186186
186338
|
return {
|
|
186187
186339
|
data,
|
|
186188
186340
|
addMessage,
|
|
186189
186341
|
setMeta,
|
|
186190
186342
|
flush,
|
|
186343
|
+
getStats,
|
|
186191
186344
|
};
|
|
186192
186345
|
}
|
|
186193
186346
|
/**
|
|
@@ -186203,7 +186356,7 @@ function generateXmlContent(metadata, messages) {
|
|
|
186203
186356
|
.filter((item) => item.type === "comment")
|
|
186204
186357
|
.map((ele) => {
|
|
186205
186358
|
const progress = Math.max((ele.timestamp - metadata.recordStartTimestamp) / 1000, 0);
|
|
186206
|
-
const
|
|
186359
|
+
const attrs = {
|
|
186207
186360
|
"@@p": "",
|
|
186208
186361
|
"@@progress": progress,
|
|
186209
186362
|
"@@mode": String(ele.mode ?? 1),
|
|
@@ -186218,18 +186371,18 @@ function generateXmlContent(metadata, messages) {
|
|
|
186218
186371
|
"@@uid": String(ele?.sender?.uid),
|
|
186219
186372
|
"@@timestamp": String(ele.timestamp),
|
|
186220
186373
|
};
|
|
186221
|
-
|
|
186222
|
-
|
|
186223
|
-
|
|
186224
|
-
|
|
186225
|
-
|
|
186226
|
-
|
|
186227
|
-
|
|
186228
|
-
|
|
186229
|
-
|
|
186230
|
-
|
|
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"],
|
|
186231
186384
|
].join(",");
|
|
186232
|
-
return pick$1(
|
|
186385
|
+
return pick$1(attrs, ["@@p", "#text", "@@user", "@@uid", "@@timestamp"]);
|
|
186233
186386
|
});
|
|
186234
186387
|
const gifts = messages
|
|
186235
186388
|
.filter((item) => item.type === "give_gift")
|
|
@@ -186295,55 +186448,22 @@ async function appendToXmlFile(filePath, content) {
|
|
|
186295
186448
|
throw error;
|
|
186296
186449
|
}
|
|
186297
186450
|
}
|
|
186298
|
-
|
|
186299
|
-
|
|
186300
|
-
|
|
186301
|
-
|
|
186302
|
-
|
|
186303
|
-
|
|
186304
|
-
|
|
186305
|
-
|
|
186306
|
-
|
|
186307
|
-
|
|
186308
|
-
|
|
186309
|
-
|
|
186310
|
-
|
|
186311
|
-
|
|
186312
|
-
|
|
186313
|
-
|
|
186314
|
-
room_title: metadata.title,
|
|
186315
|
-
user_name: metadata.user_name,
|
|
186316
|
-
room_id: metadata.room_id,
|
|
186317
|
-
},
|
|
186318
|
-
});
|
|
186319
|
-
// 读取文件内容
|
|
186320
|
-
const content = await fs$D.promises.readFile(filePath, "utf-8");
|
|
186321
|
-
// 替换占位符为实际的metadata
|
|
186322
|
-
const updatedContent = content.replace("<!--METADATA_PLACEHOLDER-->", metadataXml);
|
|
186323
|
-
// 写回文件
|
|
186324
|
-
await fs$D.promises.writeFile(filePath, updatedContent);
|
|
186325
|
-
}
|
|
186326
|
-
catch (error) {
|
|
186327
|
-
console.error(`更新XML文件metadata失败: ${filePath}`, error);
|
|
186328
|
-
throw error;
|
|
186329
|
-
}
|
|
186330
|
-
}
|
|
186331
|
-
/**
|
|
186332
|
-
* 完成XML文件写入
|
|
186333
|
-
*/
|
|
186334
|
-
async function finalizeXmlFile(filePath) {
|
|
186335
|
-
try {
|
|
186336
|
-
// 读取文件内容
|
|
186337
|
-
const content = await fs$D.promises.readFile(filePath, "utf-8");
|
|
186338
|
-
// 添加结束标签
|
|
186339
|
-
const finalContent = content + "</i>";
|
|
186340
|
-
// 写回文件
|
|
186341
|
-
await fs$D.promises.writeFile(filePath, finalContent);
|
|
186342
|
-
}
|
|
186343
|
-
catch (error) {
|
|
186344
|
-
console.error(`完成XML文件写入失败: ${filePath}`, error);
|
|
186345
|
-
throw error;
|
|
186346
|
-
}
|
|
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
|
+
});
|
|
186347
186467
|
}
|
|
186348
186468
|
|
|
186349
186469
|
class Segment extends EventEmitter$j {
|
|
@@ -186362,6 +186482,12 @@ class Segment extends EventEmitter$j {
|
|
|
186362
186482
|
this.disableDanma = disableDanma;
|
|
186363
186483
|
this.videoExt = videoExt;
|
|
186364
186484
|
}
|
|
186485
|
+
getVideoFileCompletedPayload() {
|
|
186486
|
+
return {
|
|
186487
|
+
filename: this.outputFilePath,
|
|
186488
|
+
stats: this.extraDataController?.getStats(),
|
|
186489
|
+
};
|
|
186490
|
+
}
|
|
186365
186491
|
async handleSegmentEnd() {
|
|
186366
186492
|
if (!this.outputVideoFilePath) {
|
|
186367
186493
|
this.emit("DebugLog", {
|
|
@@ -186370,6 +186496,7 @@ class Segment extends EventEmitter$j {
|
|
|
186370
186496
|
});
|
|
186371
186497
|
return;
|
|
186372
186498
|
}
|
|
186499
|
+
const data = this.getVideoFileCompletedPayload();
|
|
186373
186500
|
try {
|
|
186374
186501
|
this.emit("DebugLog", {
|
|
186375
186502
|
type: "info",
|
|
@@ -186379,7 +186506,7 @@ class Segment extends EventEmitter$j {
|
|
|
186379
186506
|
retry$1(() => fs$E.rename(this.rawRecordingVideoPath, this.outputFilePath), 20, 1000),
|
|
186380
186507
|
this.extraDataController?.flush(),
|
|
186381
186508
|
]);
|
|
186382
|
-
this.emit("videoFileCompleted",
|
|
186509
|
+
this.emit("videoFileCompleted", data);
|
|
186383
186510
|
}
|
|
186384
186511
|
catch (err) {
|
|
186385
186512
|
this.emit("DebugLog", {
|
|
@@ -186387,7 +186514,7 @@ class Segment extends EventEmitter$j {
|
|
|
186387
186514
|
text: "videoFileCompleted error " + String(err),
|
|
186388
186515
|
});
|
|
186389
186516
|
// 虽然重命名失败了,但是也当作完成处理,避免卡住录制流程
|
|
186390
|
-
this.emit("videoFileCompleted",
|
|
186517
|
+
this.emit("videoFileCompleted", data);
|
|
186391
186518
|
}
|
|
186392
186519
|
}
|
|
186393
186520
|
async onSegmentStart(stderrLine, callBack) {
|
|
@@ -186408,11 +186535,20 @@ class Segment extends EventEmitter$j {
|
|
|
186408
186535
|
});
|
|
186409
186536
|
}
|
|
186410
186537
|
}
|
|
186411
|
-
|
|
186538
|
+
let recordSavePath = this.getSavePath({
|
|
186412
186539
|
startTime: startTime,
|
|
186413
|
-
title: liveInfo?.title,
|
|
186540
|
+
title: liveInfo?.title ? liveInfo.title : undefined,
|
|
186414
186541
|
});
|
|
186415
|
-
|
|
186542
|
+
// 文件重复判断
|
|
186543
|
+
if (require$$0$6.existsSync(recordSavePath + "." + this.videoExt)) {
|
|
186544
|
+
recordSavePath = this.getSavePath({
|
|
186545
|
+
startTime: startTime,
|
|
186546
|
+
title: liveInfo?.title,
|
|
186547
|
+
extraMs: true,
|
|
186548
|
+
});
|
|
186549
|
+
}
|
|
186550
|
+
this.outputVideoFilePath = recordSavePath;
|
|
186551
|
+
ensureFolderExist(this.outputVideoFilePath);
|
|
186416
186552
|
if (!this.disableDanma) {
|
|
186417
186553
|
this.extraDataController = createRecordExtraDataController(`${this.outputVideoFilePath}.xml`);
|
|
186418
186554
|
}
|
|
@@ -186456,12 +186592,19 @@ class StreamManager extends EventEmitter$j {
|
|
|
186456
186592
|
callBack;
|
|
186457
186593
|
constructor(getSavePath, hasSegment, disableDanma, recorderType, videoFormat, callBack) {
|
|
186458
186594
|
super();
|
|
186459
|
-
const
|
|
186460
|
-
|
|
186595
|
+
const startTime = Date.now();
|
|
186596
|
+
let recordSavePath = getSavePath({ startTime });
|
|
186461
186597
|
this.videoFormat = videoFormat;
|
|
186462
186598
|
this.recorderType = recorderType;
|
|
186463
186599
|
this.hasSegment = hasSegment;
|
|
186464
186600
|
this.callBack = callBack;
|
|
186601
|
+
console.log("Initial recordSavePath:", recordSavePath);
|
|
186602
|
+
// 文件重复判断
|
|
186603
|
+
if (require$$0$6.existsSync(recordSavePath + "." + videoFormat)) {
|
|
186604
|
+
console.log("File already exists, generating new save path with extraMs");
|
|
186605
|
+
recordSavePath = getSavePath({ startTime, extraMs: true });
|
|
186606
|
+
}
|
|
186607
|
+
this.recordSavePath = recordSavePath;
|
|
186465
186608
|
if (hasSegment) {
|
|
186466
186609
|
this.segment = new Segment(getSavePath, disableDanma, this.videoExt);
|
|
186467
186610
|
this.segment.on("DebugLog", (data) => {
|
|
@@ -186475,6 +186618,7 @@ class StreamManager extends EventEmitter$j {
|
|
|
186475
186618
|
});
|
|
186476
186619
|
}
|
|
186477
186620
|
else {
|
|
186621
|
+
ensureFolderExist(recordSavePath);
|
|
186478
186622
|
const extraDataSavePath = `${recordSavePath}.xml`;
|
|
186479
186623
|
if (!disableDanma) {
|
|
186480
186624
|
this.extraDataController = createRecordExtraDataController(extraDataSavePath);
|
|
@@ -186520,8 +186664,13 @@ class StreamManager extends EventEmitter$j {
|
|
|
186520
186664
|
}
|
|
186521
186665
|
else {
|
|
186522
186666
|
if (this.recordStartTime) {
|
|
186523
|
-
|
|
186524
|
-
|
|
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
|
+
});
|
|
186525
186674
|
}
|
|
186526
186675
|
}
|
|
186527
186676
|
}
|
|
@@ -186685,7 +186834,7 @@ function createRecorderManager$1(opts) {
|
|
|
186685
186834
|
}
|
|
186686
186835
|
this.emit("videoFileCreated", { recorder: recorder.toJSON(), filename, rawFilename });
|
|
186687
186836
|
});
|
|
186688
|
-
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 }));
|
|
186689
186838
|
recorder.on("Message", (message) => this.emit("Message", { recorder: recorder.toJSON(), message }));
|
|
186690
186839
|
recorder.on("Updated", (keys) => this.emit("RecorderUpdated", { recorder: recorder.toJSON(), keys }));
|
|
186691
186840
|
recorder.on("DebugLog", (log) => this.emit("RecorderDebugLog", { recorder: recorder, ...log }));
|
|
@@ -186830,18 +186979,9 @@ function createRecorderManager$1(opts) {
|
|
|
186830
186979
|
}
|
|
186831
186980
|
}
|
|
186832
186981
|
else {
|
|
186833
|
-
//
|
|
186834
|
-
const
|
|
186835
|
-
|
|
186836
|
-
// 继续循环
|
|
186837
|
-
const timer = setTimeout(checkLoop, providerConfig.autoCheckInterval);
|
|
186838
|
-
checkLoopTimers.set(providerId, timer);
|
|
186839
|
-
}
|
|
186840
|
-
else {
|
|
186841
|
-
// 没有 recorder 了,停止该 provider 的检查循环
|
|
186842
|
-
// TODO: 也许不需要删除定时器
|
|
186843
|
-
checkLoopTimers.delete(providerId);
|
|
186844
|
-
}
|
|
186982
|
+
// 即使当前 provider 暂时没有 recorder,也保留轮询,避免后续新增 recorder 时漏掉自动检查。
|
|
186983
|
+
const timer = setTimeout(checkLoop, providerConfig.autoCheckInterval);
|
|
186984
|
+
checkLoopTimers.set(providerId, timer);
|
|
186845
186985
|
}
|
|
186846
186986
|
}
|
|
186847
186987
|
};
|
|
@@ -186932,6 +187072,9 @@ function genSavePathFromRule(manager, recorder, extData) {
|
|
|
186932
187072
|
channelId,
|
|
186933
187073
|
};
|
|
186934
187074
|
let savePathRule = manager.savePathRule;
|
|
187075
|
+
if (extData?.extraMs) {
|
|
187076
|
+
savePathRule += "_{ms}";
|
|
187077
|
+
}
|
|
186935
187078
|
try {
|
|
186936
187079
|
savePathRule = ejs.render(savePathRule, params);
|
|
186937
187080
|
}
|
|
@@ -187074,8 +187217,8 @@ class mesioDownloader extends EventEmitter$j {
|
|
|
187074
187217
|
this.streamManager.on("videoFileCreated", ({ filename, cover, rawFilename, title }) => {
|
|
187075
187218
|
this.emit("videoFileCreated", { filename, cover, rawFilename, title });
|
|
187076
187219
|
});
|
|
187077
|
-
this.streamManager.on("videoFileCompleted", (
|
|
187078
|
-
this.emit("videoFileCompleted",
|
|
187220
|
+
this.streamManager.on("videoFileCompleted", (data) => {
|
|
187221
|
+
this.emit("videoFileCompleted", data);
|
|
187079
187222
|
});
|
|
187080
187223
|
this.streamManager.on("DebugLog", (data) => {
|
|
187081
187224
|
this.emit("DebugLog", data);
|
|
@@ -187277,8 +187420,8 @@ class BililiveDownloader extends EventEmitter$j {
|
|
|
187277
187420
|
this.streamManager.on("videoFileCreated", ({ filename, cover, rawFilename, title }) => {
|
|
187278
187421
|
this.emit("videoFileCreated", { filename, cover, rawFilename, title });
|
|
187279
187422
|
});
|
|
187280
|
-
this.streamManager.on("videoFileCompleted", (
|
|
187281
|
-
this.emit("videoFileCompleted",
|
|
187423
|
+
this.streamManager.on("videoFileCompleted", (data) => {
|
|
187424
|
+
this.emit("videoFileCompleted", data);
|
|
187282
187425
|
});
|
|
187283
187426
|
this.streamManager.on("DebugLog", (data) => {
|
|
187284
187427
|
this.emit("DebugLog", data);
|
|
@@ -187331,6 +187474,11 @@ class BililiveDownloader extends EventEmitter$j {
|
|
|
187331
187474
|
if (timeMatch) {
|
|
187332
187475
|
time = timeMatch[1];
|
|
187333
187476
|
}
|
|
187477
|
+
const spaceMath = line.match(/下载进度:\s*([\d.]+\s*MB)\s*/);
|
|
187478
|
+
if (spaceMath) {
|
|
187479
|
+
const space = spaceMath[1];
|
|
187480
|
+
time = time ? `${time} ${space}` : space;
|
|
187481
|
+
}
|
|
187334
187482
|
return {
|
|
187335
187483
|
time,
|
|
187336
187484
|
};
|
|
@@ -187503,8 +187651,8 @@ class FFmpegDownloader extends EventEmitter$j {
|
|
|
187503
187651
|
this.streamManager.on("videoFileCreated", ({ filename, cover, rawFilename, title }) => {
|
|
187504
187652
|
this.emit("videoFileCreated", { filename, cover, rawFilename, title });
|
|
187505
187653
|
});
|
|
187506
|
-
this.streamManager.on("videoFileCompleted", (
|
|
187507
|
-
this.emit("videoFileCompleted",
|
|
187654
|
+
this.streamManager.on("videoFileCompleted", (data) => {
|
|
187655
|
+
this.emit("videoFileCompleted", data);
|
|
187508
187656
|
});
|
|
187509
187657
|
this.streamManager.on("DebugLog", (data) => {
|
|
187510
187658
|
this.emit("DebugLog", data);
|
|
@@ -187590,7 +187738,7 @@ class FFmpegDownloader extends EventEmitter$j {
|
|
|
187590
187738
|
let time = null;
|
|
187591
187739
|
const timeMatch = line.match(/time=([0-9:.]+)/);
|
|
187592
187740
|
if (timeMatch) {
|
|
187593
|
-
time = timeMatch[1];
|
|
187741
|
+
time = timeMatch[1].split(".")[0];
|
|
187594
187742
|
}
|
|
187595
187743
|
return {
|
|
187596
187744
|
time,
|
|
@@ -187694,12 +187842,6 @@ function getBililivePath() {
|
|
|
187694
187842
|
return bililivePath;
|
|
187695
187843
|
}
|
|
187696
187844
|
|
|
187697
|
-
function ensureFolderExist$4(fileOrFolderPath) {
|
|
187698
|
-
const folder = path$y.dirname(fileOrFolderPath);
|
|
187699
|
-
if (!fs$D.existsSync(folder)) {
|
|
187700
|
-
fs$D.mkdirSync(folder, { recursive: true });
|
|
187701
|
-
}
|
|
187702
|
-
}
|
|
187703
187845
|
const uuid$1 = () => {
|
|
187704
187846
|
return crypto$c.randomUUID();
|
|
187705
187847
|
};
|
|
@@ -195043,6 +195185,7 @@ const checkLiveStatusAndRecord$4 = async function ({ getSavePath, banLiveId, isM
|
|
|
195043
195185
|
startTime: opts.startTime,
|
|
195044
195186
|
liveStartTime,
|
|
195045
195187
|
recordStartTime,
|
|
195188
|
+
extraMs: opts.extraMs,
|
|
195046
195189
|
}),
|
|
195047
195190
|
disableDanma: this.disableProvideCommentsWhenRecording,
|
|
195048
195191
|
videoFormat: this.videoFormat ?? "auto",
|
|
@@ -195052,20 +195195,6 @@ const checkLiveStatusAndRecord$4 = async function ({ getSavePath, banLiveId, isM
|
|
|
195052
195195
|
const info = await getInfo$4(this.channelId);
|
|
195053
195196
|
return info;
|
|
195054
195197
|
});
|
|
195055
|
-
const savePath = getSavePath({
|
|
195056
|
-
owner,
|
|
195057
|
-
title,
|
|
195058
|
-
startTime: Date.now(),
|
|
195059
|
-
liveStartTime,
|
|
195060
|
-
recordStartTime,
|
|
195061
|
-
});
|
|
195062
|
-
try {
|
|
195063
|
-
ensureFolderExist$4(savePath);
|
|
195064
|
-
}
|
|
195065
|
-
catch (err) {
|
|
195066
|
-
this.state = "idle";
|
|
195067
|
-
throw err;
|
|
195068
|
-
}
|
|
195069
195198
|
const handleVideoCreated = async ({ filename, title, cover, rawFilename }) => {
|
|
195070
195199
|
this.emit("videoFileCreated", { filename, cover, rawFilename });
|
|
195071
195200
|
if (title && this?.liveInfo) {
|
|
@@ -195085,8 +195214,8 @@ const checkLiveStatusAndRecord$4 = async function ({ getSavePath, banLiveId, isM
|
|
|
195085
195214
|
});
|
|
195086
195215
|
};
|
|
195087
195216
|
downloader.on("videoFileCreated", handleVideoCreated);
|
|
195088
|
-
downloader.on("videoFileCompleted", (
|
|
195089
|
-
this.emit("videoFileCompleted",
|
|
195217
|
+
downloader.on("videoFileCompleted", (data) => {
|
|
195218
|
+
this.emit("videoFileCompleted", data);
|
|
195090
195219
|
});
|
|
195091
195220
|
downloader.on("DebugLog", (data) => {
|
|
195092
195221
|
this.emit("DebugLog", data);
|
|
@@ -195277,7 +195406,7 @@ const checkLiveStatusAndRecord$4 = async function ({ getSavePath, banLiveId, isM
|
|
|
195277
195406
|
recorderType: downloader.type,
|
|
195278
195407
|
url: stream.url,
|
|
195279
195408
|
downloaderArgs,
|
|
195280
|
-
savePath:
|
|
195409
|
+
savePath: downloader.videoFilePath,
|
|
195281
195410
|
stop,
|
|
195282
195411
|
cut,
|
|
195283
195412
|
};
|
|
@@ -196180,12 +196309,6 @@ const requester$2 = axios.create({
|
|
|
196180
196309
|
proxy: false,
|
|
196181
196310
|
});
|
|
196182
196311
|
|
|
196183
|
-
function ensureFolderExist$3(fileOrFolderPath) {
|
|
196184
|
-
const folder = path$y.dirname(fileOrFolderPath);
|
|
196185
|
-
if (!fs$D.existsSync(folder)) {
|
|
196186
|
-
fs$D.mkdirSync(folder, { recursive: true });
|
|
196187
|
-
}
|
|
196188
|
-
}
|
|
196189
196312
|
function assert$l(assertion, msg) {
|
|
196190
196313
|
if (!assertion) {
|
|
196191
196314
|
throw new Error(msg);
|
|
@@ -211223,6 +211346,7 @@ const checkLiveStatusAndRecord$3 = async function ({ getSavePath, banLiveId, isM
|
|
|
211223
211346
|
startTime: opts.startTime,
|
|
211224
211347
|
liveStartTime,
|
|
211225
211348
|
recordStartTime,
|
|
211349
|
+
extraMs: opts.extraMs,
|
|
211226
211350
|
}),
|
|
211227
211351
|
disableDanma: this.disableProvideCommentsWhenRecording,
|
|
211228
211352
|
videoFormat: this.videoFormat ?? "auto",
|
|
@@ -211234,20 +211358,6 @@ const checkLiveStatusAndRecord$3 = async function ({ getSavePath, banLiveId, isM
|
|
|
211234
211358
|
const info = await getInfo$3(this.channelId);
|
|
211235
211359
|
return info;
|
|
211236
211360
|
});
|
|
211237
|
-
const savePath = getSavePath({
|
|
211238
|
-
owner,
|
|
211239
|
-
title,
|
|
211240
|
-
startTime: Date.now(),
|
|
211241
|
-
liveStartTime,
|
|
211242
|
-
recordStartTime,
|
|
211243
|
-
});
|
|
211244
|
-
try {
|
|
211245
|
-
ensureFolderExist$3(savePath);
|
|
211246
|
-
}
|
|
211247
|
-
catch (err) {
|
|
211248
|
-
this.state = "idle";
|
|
211249
|
-
throw err;
|
|
211250
|
-
}
|
|
211251
211361
|
const handleVideoCreated = async ({ filename, title, cover, rawFilename }) => {
|
|
211252
211362
|
this.emit("videoFileCreated", { filename, cover, rawFilename });
|
|
211253
211363
|
if (title && this?.liveInfo) {
|
|
@@ -211267,8 +211377,8 @@ const checkLiveStatusAndRecord$3 = async function ({ getSavePath, banLiveId, isM
|
|
|
211267
211377
|
});
|
|
211268
211378
|
};
|
|
211269
211379
|
downloader.on("videoFileCreated", handleVideoCreated);
|
|
211270
|
-
downloader.on("videoFileCompleted", (
|
|
211271
|
-
this.emit("videoFileCompleted",
|
|
211380
|
+
downloader.on("videoFileCompleted", (data) => {
|
|
211381
|
+
this.emit("videoFileCompleted", data);
|
|
211272
211382
|
});
|
|
211273
211383
|
downloader.on("DebugLog", (data) => {
|
|
211274
211384
|
this.emit("DebugLog", data);
|
|
@@ -211376,7 +211486,7 @@ const checkLiveStatusAndRecord$3 = async function ({ getSavePath, banLiveId, isM
|
|
|
211376
211486
|
recorderType: downloader.type,
|
|
211377
211487
|
url: stream.url,
|
|
211378
211488
|
downloaderArgs,
|
|
211379
|
-
savePath:
|
|
211489
|
+
savePath: downloader.videoFilePath,
|
|
211380
211490
|
stop,
|
|
211381
211491
|
cut,
|
|
211382
211492
|
};
|
|
@@ -211413,12 +211523,6 @@ const provider$3 = {
|
|
|
211413
211523
|
},
|
|
211414
211524
|
};
|
|
211415
211525
|
|
|
211416
|
-
function ensureFolderExist$2(fileOrFolderPath) {
|
|
211417
|
-
const folder = path$y.dirname(fileOrFolderPath);
|
|
211418
|
-
if (!fs$D.existsSync(folder)) {
|
|
211419
|
-
fs$D.mkdirSync(folder, { recursive: true });
|
|
211420
|
-
}
|
|
211421
|
-
}
|
|
211422
211526
|
function assert$j(assertion, msg) {
|
|
211423
211527
|
if (!assertion) {
|
|
211424
211528
|
throw new Error(msg);
|
|
@@ -212924,6 +213028,7 @@ const checkLiveStatusAndRecord$2 = async function ({ getSavePath, isManualStart,
|
|
|
212924
213028
|
startTime: opts.startTime,
|
|
212925
213029
|
liveStartTime: liveStartTime,
|
|
212926
213030
|
recordStartTime,
|
|
213031
|
+
extraMs: opts.extraMs,
|
|
212927
213032
|
}),
|
|
212928
213033
|
formatName: streamOptions.format_name,
|
|
212929
213034
|
disableDanma: this.disableProvideCommentsWhenRecording,
|
|
@@ -212936,20 +213041,6 @@ const checkLiveStatusAndRecord$2 = async function ({ getSavePath, isManualStart,
|
|
|
212936
213041
|
const info = await getInfo$2(this.channelId);
|
|
212937
213042
|
return info;
|
|
212938
213043
|
});
|
|
212939
|
-
const savePath = getSavePath({
|
|
212940
|
-
owner,
|
|
212941
|
-
title,
|
|
212942
|
-
startTime: Date.now(),
|
|
212943
|
-
liveStartTime: liveStartTime,
|
|
212944
|
-
recordStartTime,
|
|
212945
|
-
});
|
|
212946
|
-
try {
|
|
212947
|
-
ensureFolderExist$2(savePath);
|
|
212948
|
-
}
|
|
212949
|
-
catch (err) {
|
|
212950
|
-
this.state = "idle";
|
|
212951
|
-
throw err;
|
|
212952
|
-
}
|
|
212953
213044
|
const handleVideoCreated = async ({ filename, title, cover, rawFilename }) => {
|
|
212954
213045
|
this.emit("videoFileCreated", { filename, cover, rawFilename });
|
|
212955
213046
|
if (title && this?.liveInfo) {
|
|
@@ -212969,8 +213060,8 @@ const checkLiveStatusAndRecord$2 = async function ({ getSavePath, isManualStart,
|
|
|
212969
213060
|
});
|
|
212970
213061
|
};
|
|
212971
213062
|
downloader.on("videoFileCreated", handleVideoCreated);
|
|
212972
|
-
downloader.on("videoFileCompleted", (
|
|
212973
|
-
this.emit("videoFileCompleted",
|
|
213063
|
+
downloader.on("videoFileCompleted", (data) => {
|
|
213064
|
+
this.emit("videoFileCompleted", data);
|
|
212974
213065
|
});
|
|
212975
213066
|
downloader.on("DebugLog", (data) => {
|
|
212976
213067
|
this.emit("DebugLog", data);
|
|
@@ -213052,7 +213143,7 @@ const checkLiveStatusAndRecord$2 = async function ({ getSavePath, isManualStart,
|
|
|
213052
213143
|
recorderType: downloader.type,
|
|
213053
213144
|
url: stream.url,
|
|
213054
213145
|
downloaderArgs,
|
|
213055
|
-
savePath:
|
|
213146
|
+
savePath: downloader.videoFilePath,
|
|
213056
213147
|
stop,
|
|
213057
213148
|
cut,
|
|
213058
213149
|
};
|
|
@@ -213109,12 +213200,6 @@ function singleton(fn) {
|
|
|
213109
213200
|
return promise;
|
|
213110
213201
|
};
|
|
213111
213202
|
}
|
|
213112
|
-
function ensureFolderExist$1(fileOrFolderPath) {
|
|
213113
|
-
const folder = path$y.dirname(fileOrFolderPath);
|
|
213114
|
-
if (!fs$D.existsSync(folder)) {
|
|
213115
|
-
fs$D.mkdirSync(folder, { recursive: true });
|
|
213116
|
-
}
|
|
213117
|
-
}
|
|
213118
213203
|
function assert$i(assertion, msg) {
|
|
213119
213204
|
if (!assertion) {
|
|
213120
213205
|
throw new Error(msg);
|
|
@@ -216585,6 +216670,9 @@ async function getRoomInfoByUserWeb(secUserId, opts = {}) {
|
|
|
216585
216670
|
if (res.data.includes("验证码")) {
|
|
216586
216671
|
throw new Error("需要验证码,请在浏览器中打开链接获取" + url);
|
|
216587
216672
|
}
|
|
216673
|
+
if (!res.data.includes("抖音号")) {
|
|
216674
|
+
throw new Error("userHTML页面没有正常加载" + String(res.data));
|
|
216675
|
+
}
|
|
216588
216676
|
if (!res.data.includes("直播中")) {
|
|
216589
216677
|
return {
|
|
216590
216678
|
living: false,
|
|
@@ -216776,6 +216864,7 @@ async function getRoomInfoByWeb(webRoomId, opts = {}) {
|
|
|
216776
216864
|
}
|
|
216777
216865
|
async function getRoomInfoByMobile(secUserId, opts = {}) {
|
|
216778
216866
|
if (!secUserId) {
|
|
216867
|
+
console.error(opts);
|
|
216779
216868
|
throw new Error("Mobile API need secUserId, please set uid field");
|
|
216780
216869
|
}
|
|
216781
216870
|
if (typeof secUserId === "number") {
|
|
@@ -216792,7 +216881,7 @@ async function getRoomInfoByMobile(secUserId, opts = {}) {
|
|
|
216792
216881
|
const res = await requester.get(`https://webcast.amemv.com/webcast/room/reflow/info/`, {
|
|
216793
216882
|
params,
|
|
216794
216883
|
headers: {
|
|
216795
|
-
|
|
216884
|
+
// cookie: opts.auth,
|
|
216796
216885
|
},
|
|
216797
216886
|
});
|
|
216798
216887
|
// @ts-ignore
|
|
@@ -249016,6 +249105,7 @@ const checkLiveStatusAndRecord$1 = async function ({ getSavePath, banLiveId, isM
|
|
|
249016
249105
|
startTime: opts.startTime,
|
|
249017
249106
|
liveStartTime: liveStartTime,
|
|
249018
249107
|
recordStartTime,
|
|
249108
|
+
extraMs: opts.extraMs,
|
|
249019
249109
|
}),
|
|
249020
249110
|
disableDanma: this.disableProvideCommentsWhenRecording,
|
|
249021
249111
|
videoFormat: this.videoFormat ?? "auto",
|
|
@@ -249030,20 +249120,6 @@ const checkLiveStatusAndRecord$1 = async function ({ getSavePath, banLiveId, isM
|
|
|
249030
249120
|
});
|
|
249031
249121
|
return info;
|
|
249032
249122
|
});
|
|
249033
|
-
const savePath = getSavePath({
|
|
249034
|
-
owner,
|
|
249035
|
-
title,
|
|
249036
|
-
startTime: Date.now(),
|
|
249037
|
-
liveStartTime,
|
|
249038
|
-
recordStartTime,
|
|
249039
|
-
});
|
|
249040
|
-
try {
|
|
249041
|
-
ensureFolderExist$1(savePath);
|
|
249042
|
-
}
|
|
249043
|
-
catch (err) {
|
|
249044
|
-
this.state = "idle";
|
|
249045
|
-
throw err;
|
|
249046
|
-
}
|
|
249047
249123
|
const handleVideoCreated = async ({ filename, title, cover, rawFilename }) => {
|
|
249048
249124
|
this.emit("videoFileCreated", { filename, cover, rawFilename });
|
|
249049
249125
|
if (title && this?.liveInfo) {
|
|
@@ -249063,8 +249139,8 @@ const checkLiveStatusAndRecord$1 = async function ({ getSavePath, banLiveId, isM
|
|
|
249063
249139
|
});
|
|
249064
249140
|
};
|
|
249065
249141
|
downloader.on("videoFileCreated", handleVideoCreated);
|
|
249066
|
-
downloader.on("videoFileCompleted", (
|
|
249067
|
-
this.emit("videoFileCompleted",
|
|
249142
|
+
downloader.on("videoFileCompleted", (data) => {
|
|
249143
|
+
this.emit("videoFileCompleted", data);
|
|
249068
249144
|
});
|
|
249069
249145
|
downloader.on("DebugLog", (data) => {
|
|
249070
249146
|
this.emit("DebugLog", data);
|
|
@@ -249280,7 +249356,7 @@ const checkLiveStatusAndRecord$1 = async function ({ getSavePath, banLiveId, isM
|
|
|
249280
249356
|
recorderType: downloader.type,
|
|
249281
249357
|
url: stream.url,
|
|
249282
249358
|
downloaderArgs,
|
|
249283
|
-
savePath:
|
|
249359
|
+
savePath: downloader.videoFilePath,
|
|
249284
249360
|
stop,
|
|
249285
249361
|
cut,
|
|
249286
249362
|
};
|
|
@@ -290180,13 +290256,6 @@ async function getStream(opts) {
|
|
|
290180
290256
|
};
|
|
290181
290257
|
}
|
|
290182
290258
|
|
|
290183
|
-
function ensureFolderExist(fileOrFolderPath) {
|
|
290184
|
-
const folder = path$y.dirname(fileOrFolderPath);
|
|
290185
|
-
if (!fs$D.existsSync(folder)) {
|
|
290186
|
-
fs$D.mkdirSync(folder, { recursive: true });
|
|
290187
|
-
}
|
|
290188
|
-
}
|
|
290189
|
-
|
|
290190
290259
|
function createRecorder(opts) {
|
|
290191
290260
|
// 内部实现时,应该只有 proxy 包裹的那一层会使用这个 recorder 标识符,不应该有直接通过
|
|
290192
290261
|
// 此标志来操作这个对象的地方,不然会跳过 proxy 的拦截。
|
|
@@ -290357,6 +290426,7 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isMan
|
|
|
290357
290426
|
startTime: opts.startTime,
|
|
290358
290427
|
liveStartTime,
|
|
290359
290428
|
recordStartTime,
|
|
290429
|
+
extraMs: opts.extraMs,
|
|
290360
290430
|
}),
|
|
290361
290431
|
disableDanma: this.disableProvideCommentsWhenRecording,
|
|
290362
290432
|
videoFormat: this.videoFormat ?? "auto",
|
|
@@ -290365,20 +290435,6 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isMan
|
|
|
290365
290435
|
const info = await getInfo(roomId);
|
|
290366
290436
|
return info;
|
|
290367
290437
|
});
|
|
290368
|
-
const savePath = getSavePath({
|
|
290369
|
-
owner,
|
|
290370
|
-
title,
|
|
290371
|
-
startTime: Date.now(),
|
|
290372
|
-
liveStartTime,
|
|
290373
|
-
recordStartTime,
|
|
290374
|
-
});
|
|
290375
|
-
try {
|
|
290376
|
-
ensureFolderExist(savePath);
|
|
290377
|
-
}
|
|
290378
|
-
catch (err) {
|
|
290379
|
-
this.state = "idle";
|
|
290380
|
-
throw err;
|
|
290381
|
-
}
|
|
290382
290438
|
const handleVideoCreated = async ({ filename, title, cover, rawFilename }) => {
|
|
290383
290439
|
this.emit("videoFileCreated", { filename, cover, rawFilename });
|
|
290384
290440
|
if (title && this?.liveInfo) {
|
|
@@ -290398,8 +290454,8 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isMan
|
|
|
290398
290454
|
});
|
|
290399
290455
|
};
|
|
290400
290456
|
downloader.on("videoFileCreated", handleVideoCreated);
|
|
290401
|
-
downloader.on("videoFileCompleted", (
|
|
290402
|
-
this.emit("videoFileCompleted",
|
|
290457
|
+
downloader.on("videoFileCompleted", (data) => {
|
|
290458
|
+
this.emit("videoFileCompleted", data);
|
|
290403
290459
|
});
|
|
290404
290460
|
downloader.on("DebugLog", (data) => {
|
|
290405
290461
|
this.emit("DebugLog", data);
|
|
@@ -290445,7 +290501,7 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isMan
|
|
|
290445
290501
|
recorderType: downloader.type,
|
|
290446
290502
|
url: stream.url,
|
|
290447
290503
|
downloaderArgs,
|
|
290448
|
-
savePath:
|
|
290504
|
+
savePath: downloader.videoFilePath,
|
|
290449
290505
|
stop,
|
|
290450
290506
|
cut,
|
|
290451
290507
|
};
|
|
@@ -291047,7 +291103,7 @@ async function createRecorderManager(appConfig) {
|
|
|
291047
291103
|
return;
|
|
291048
291104
|
if (recorder.recordHandle) {
|
|
291049
291105
|
const logFilePath = utils$2.replaceExtName(`${recorder.recordHandle.savePath}_${recorder.id}`, ".recorder.log");
|
|
291050
|
-
fs$k.
|
|
291106
|
+
fs$k.appendFile(logFilePath, log.text + "\n").catch(() => { });
|
|
291051
291107
|
return;
|
|
291052
291108
|
}
|
|
291053
291109
|
else {
|
|
@@ -291126,8 +291182,8 @@ async function createRecorderManager(appConfig) {
|
|
|
291126
291182
|
platform: recorder.providerId,
|
|
291127
291183
|
});
|
|
291128
291184
|
});
|
|
291129
|
-
manager.on("videoFileCompleted", async ({ recorder, filename }) => {
|
|
291130
|
-
logObj.info("Manager videoFileCompleted", { recorder, filename });
|
|
291185
|
+
manager.on("videoFileCompleted", async ({ recorder, filename, stats }) => {
|
|
291186
|
+
logObj.info("Manager videoFileCompleted", { recorder, filename, stats });
|
|
291131
291187
|
const endTime = new Date();
|
|
291132
291188
|
const data = recorderConfig.get(recorder.id);
|
|
291133
291189
|
const title = recorder?.liveInfo?.title;
|
|
@@ -291158,7 +291214,16 @@ async function createRecorderManager(appConfig) {
|
|
|
291158
291214
|
video_filename: videoFilename,
|
|
291159
291215
|
quick_hash: quickHash,
|
|
291160
291216
|
});
|
|
291161
|
-
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))) {
|
|
291162
291227
|
const { uniqMember, danmaNum } = await danmaReport(xmlFile);
|
|
291163
291228
|
recordHistory.upadteLive({
|
|
291164
291229
|
video_file: filename,
|