bililive-cli 3.22.1 → 4.0.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.
@@ -1,10 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var path$3 = require('node:path');
4
3
  var http$4 = require('node:http');
5
- var urlLib = require('node:url');
6
4
  var https = require('node:https');
7
- var index$1 = require('./index-CmRhhp3m.cjs');
5
+ var index$1 = require('./index-CWkwaDmT.cjs');
8
6
  var require$$0$5 = require('tty');
9
7
  var require$$1$4 = require('util');
10
8
  var require$$0$b = require('assert');
@@ -20,7 +18,9 @@ var require$$1$2 = require('http');
20
18
  var require$$4$1 = require('net');
21
19
  var require$$5$1 = require('querystring');
22
20
  var require$$17 = require('async_hooks');
21
+ var urlLib = require('node:url');
23
22
  var require$$1$5 = require('string_decoder');
23
+ var path$3 = require('node:path');
24
24
  var os$1 = require('node:os');
25
25
  var require$$1$6 = require('os');
26
26
  var require$$1$1 = require('node:child_process');
@@ -38,7 +38,6 @@ var require$$0$d = require('node:stream');
38
38
  var web = require('node:stream/web');
39
39
  var shazamioCore = require('shazamio-core');
40
40
 
41
- var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
42
41
  /**
43
42
  * 百度网盘上传类
44
43
  * 使用 BaiduPCS-Go 工具进行文件上传
@@ -13491,9 +13490,9 @@ for (const method of methods$1) {
13491
13490
 
13492
13491
  Router.prototype.del = Router.prototype['delete'];
13493
13492
 
13494
- var router$i = Router;
13493
+ var router$j = Router;
13495
13494
 
13496
- var Router$1 = /*@__PURE__*/index$1.getDefaultExportFromCjs(router$i);
13495
+ var Router$1 = /*@__PURE__*/index$1.getDefaultExportFromCjs(router$j);
13497
13496
 
13498
13497
  const vary = varyExports;
13499
13498
 
@@ -30025,11 +30024,11 @@ var recorderService = {
30025
30024
  getStreamUrl,
30026
30025
  };
30027
30026
 
30028
- const router$h = new Router$1();
30029
- router$h.get("/webhook", async (ctx) => {
30027
+ const router$i = new Router$1();
30028
+ router$i.get("/webhook", async (ctx) => {
30030
30029
  ctx.body = "webhook 服务器已启动";
30031
30030
  });
30032
- router$h.post("/webhook/bililiverecorder", async (ctx) => {
30031
+ router$i.post("/webhook/bililiverecorder", async (ctx) => {
30033
30032
  const config = appConfig.getAll();
30034
30033
  index$1.logObj.info("录播姬:", ctx.request.body);
30035
30034
  const event = ctx.request.body;
@@ -30061,7 +30060,7 @@ router$h.post("/webhook/bililiverecorder", async (ctx) => {
30061
30060
  }
30062
30061
  ctx.body = "ok";
30063
30062
  });
30064
- router$h.post("/webhook/blrec", async (ctx) => {
30063
+ router$i.post("/webhook/blrec", async (ctx) => {
30065
30064
  const webhook = appConfig.get("webhook");
30066
30065
  index$1.logObj.info("blrec webhook:", ctx.request.body);
30067
30066
  const event = ctx.request.body;
@@ -30096,7 +30095,7 @@ router$h.post("/webhook/blrec", async (ctx) => {
30096
30095
  * OneLiveRec webhook事件处理
30097
30096
  * 由于只有在结束后才能从文件的备注中获取到,只处理结束事件获取信息后模拟开始和结束事件
30098
30097
  */
30099
- router$h.post("/webhook/oneliverec", async (ctx) => {
30098
+ router$i.post("/webhook/oneliverec", async (ctx) => {
30100
30099
  const webhook = appConfig.get("webhook");
30101
30100
  index$1.logObj.info("oneliverec webhook:", ctx.request.body);
30102
30101
  const event = ctx.request.body;
@@ -30132,7 +30131,7 @@ router$h.post("/webhook/oneliverec", async (ctx) => {
30132
30131
  }
30133
30132
  ctx.body = "ok";
30134
30133
  });
30135
- router$h.post("/webhook/ddtv", async (ctx) => {
30134
+ router$i.post("/webhook/ddtv", async (ctx) => {
30136
30135
  const webhook = appConfig.get("webhook");
30137
30136
  const event = ctx.request.body;
30138
30137
  if (webhook?.open && (event.cmd === "RecordingEnd" || event.cmd === "StartRecording")) {
@@ -30190,7 +30189,7 @@ const getDDTVTrueFile = (event) => {
30190
30189
  danmuFile: danmuFileTruePath,
30191
30190
  };
30192
30191
  };
30193
- router$h.post("/webhook/custom", async (ctx) => {
30192
+ router$i.post("/webhook/custom", async (ctx) => {
30194
30193
  const webhook = appConfig.get("webhook");
30195
30194
  index$1.logObj.info("custom: webhook", ctx.request.body);
30196
30195
  const event = ctx.request.body;
@@ -30239,7 +30238,7 @@ async function checkFileInterval() {
30239
30238
  }
30240
30239
  }, 1000 * 60);
30241
30240
  }
30242
- router$h.get("/bili/stream", async (ctx) => {
30241
+ router$i.get("/bili/stream", async (ctx) => {
30243
30242
  const { id } = ctx.query;
30244
30243
  try {
30245
30244
  const m3u8Content = await recorderService.getBiliStream(id);
@@ -48104,21 +48103,21 @@ function makePromise(multer, name) {
48104
48103
  multer.diskStorage = originalMulter.diskStorage;
48105
48104
  multer.memoryStorage = originalMulter.memoryStorage;
48106
48105
 
48107
- const router$g = new Router$1({
48106
+ const router$h = new Router$1({
48108
48107
  prefix: "/config",
48109
48108
  });
48110
48109
  const upload$1 = multer({ dest: os$1.tmpdir() });
48111
- router$g.get("/", async (ctx) => {
48110
+ router$h.get("/", async (ctx) => {
48112
48111
  const config = appConfig.getAll();
48113
48112
  ctx.body = config;
48114
48113
  });
48115
- router$g.post("/", async (ctx) => {
48114
+ router$h.post("/", async (ctx) => {
48116
48115
  const data = ctx.request.body;
48117
48116
  // @ts-ignore
48118
48117
  appConfig.setAll(data);
48119
48118
  ctx.body = "success";
48120
48119
  });
48121
- router$g.get("/get", async (ctx) => {
48120
+ router$h.get("/get", async (ctx) => {
48122
48121
  const key = ctx.query.key;
48123
48122
  if (!key) {
48124
48123
  ctx.body = "key is required";
@@ -48127,7 +48126,7 @@ router$g.get("/get", async (ctx) => {
48127
48126
  const value = appConfig.get(key);
48128
48127
  ctx.body = value;
48129
48128
  });
48130
- router$g.post("/set", async (ctx) => {
48129
+ router$h.post("/set", async (ctx) => {
48131
48130
  const data = ctx.request.body;
48132
48131
  if (!data.key || !data.value) {
48133
48132
  ctx.body = "key and value is required";
@@ -48137,7 +48136,7 @@ router$g.post("/set", async (ctx) => {
48137
48136
  appConfig.set(data.key, data.value);
48138
48137
  ctx.body = "success";
48139
48138
  });
48140
- router$g.post("/verifyBiliKey", async (ctx) => {
48139
+ router$h.post("/verifyBiliKey", async (ctx) => {
48141
48140
  try {
48142
48141
  const serverKey = process.env.BILILIVE_TOOLS_BILIKEY;
48143
48142
  const configured = typeof serverKey === "string" && serverKey.trim().length > 0;
@@ -48181,7 +48180,7 @@ router$g.post("/verifyBiliKey", async (ctx) => {
48181
48180
  ctx.body = response;
48182
48181
  }
48183
48182
  });
48184
- router$g.post("/reset-bin", async (ctx) => {
48183
+ router$h.post("/reset-bin", async (ctx) => {
48185
48184
  const data = ctx.request.body;
48186
48185
  const type = data.type;
48187
48186
  if (!type) {
@@ -48238,7 +48237,7 @@ async function exportConfig(opts) {
48238
48237
  const content = zip.generateNodeStream({ type: "nodebuffer" });
48239
48238
  return content;
48240
48239
  }
48241
- router$g.get("/export", async (ctx) => {
48240
+ router$h.get("/export", async (ctx) => {
48242
48241
  try {
48243
48242
  const globalConfig = container.resolve("globalConfig");
48244
48243
  const { configPath, videoPresetPath, danmuPresetPath, ffmpegPresetPath, userDataPath } = globalConfig;
@@ -48271,7 +48270,7 @@ router$g.get("/export", async (ctx) => {
48271
48270
  ctx.body = "export error";
48272
48271
  }
48273
48272
  });
48274
- router$g.post("/import", upload$1.single("file"), async (ctx) => {
48273
+ router$h.post("/import", upload$1.single("file"), async (ctx) => {
48275
48274
  const file = ctx.request?.file?.path;
48276
48275
  if (!file) {
48277
48276
  ctx.status = 400;
@@ -48296,17 +48295,18 @@ router$g.post("/import", upload$1.single("file"), async (ctx) => {
48296
48295
  // 如果filename是 appConfig.json,那么替换掉ffmpegPath、ffprobePath、danmuFactoryPath配置
48297
48296
  if (filename === "appConfig.json") {
48298
48297
  const data = await index$1.fs.readJSON(path$3.join(userDataPath, `${filename}.backup`));
48299
- const appConfig = await index$1.fs.readJSON(configPath);
48300
- appConfig.ffmpegPath = data.ffmpegPath;
48301
- appConfig.ffprobePath = data.ffprobePath;
48302
- appConfig.danmuFactoryPath = data.danmuFactoryPath;
48303
- appConfig.mesioPath = data.mesioPath;
48304
- appConfig.bililiveRecorderPath = data.bililiveRecorderPath;
48305
- appConfig.audiowaveformPath = data.audiowaveformPath;
48306
- appConfig.webhook.recoderFolder = data.webhook.recoderFolder;
48307
- appConfig.recorder.savePath = data.recorder.savePath;
48308
- appConfig.losslessCutPath = data.losslessCutPath;
48309
- await index$1.fs.writeJSON(filePath, appConfig);
48298
+ const importedConfig = await index$1.fs.readJSON(configPath);
48299
+ importedConfig.ffmpegPath = data.ffmpegPath;
48300
+ importedConfig.ffprobePath = data.ffprobePath;
48301
+ importedConfig.danmuFactoryPath = data.danmuFactoryPath;
48302
+ importedConfig.mesioPath = data.mesioPath;
48303
+ importedConfig.bililiveRecorderPath = data.bililiveRecorderPath;
48304
+ importedConfig.audiowaveformPath = data.audiowaveformPath;
48305
+ importedConfig.webhook.recoderFolder = data.webhook.recoderFolder;
48306
+ importedConfig.recorder.savePath = data.recorder.savePath;
48307
+ importedConfig.losslessCutPath = data.losslessCutPath;
48308
+ await index$1.fs.writeJSON(filePath, importedConfig);
48309
+ appConfig.getAll();
48310
48310
  }
48311
48311
  }
48312
48312
  else if (filename === "app.db") {
@@ -48330,7 +48330,7 @@ router$g.post("/import", upload$1.single("file"), async (ctx) => {
48330
48330
  }));
48331
48331
  ctx.body = "success";
48332
48332
  });
48333
- router$g.post("/notifyTest", async (ctx) => {
48333
+ router$h.post("/notifyTest", async (ctx) => {
48334
48334
  const { title, desp, options, notifyType } = ctx.request.body;
48335
48335
  await index$1._send(title, desp, options, notifyType, {
48336
48336
  context: {
@@ -53146,15 +53146,15 @@ async function handleSrt(input, output) {
53146
53146
  return nodes;
53147
53147
  }
53148
53148
 
53149
- const router$f = new Router$1({
53149
+ const router$g = new Router$1({
53150
53150
  prefix: "/llm",
53151
53151
  });
53152
- router$f.get("/ollama/modelList", async (ctx) => {
53152
+ router$g.get("/ollama/modelList", async (ctx) => {
53153
53153
  const data = ctx.request.query;
53154
53154
  const models = (await ollama.getModelList(data.baseUrl)).models.map((item) => item.name);
53155
53155
  ctx.body = models;
53156
53156
  });
53157
- router$f.post("/translate", async (ctx) => {
53157
+ router$g.post("/translate", async (ctx) => {
53158
53158
  const data = ctx.request.body;
53159
53159
  console.log(data);
53160
53160
  const content = await addTranslateTask(data.input, data.output);
@@ -55499,10 +55499,10 @@ var semver = {
55499
55499
 
55500
55500
  var semver$1 = /*@__PURE__*/index$1.getDefaultExportFromCjs(semver);
55501
55501
 
55502
- const router$e = new Router$1({
55502
+ const router$f = new Router$1({
55503
55503
  prefix: "/common",
55504
55504
  });
55505
- router$e.get("/version", (ctx) => {
55505
+ router$f.get("/version", (ctx) => {
55506
55506
  ctx.body = config.version;
55507
55507
  });
55508
55508
  function getDriveLetters() {
@@ -55524,7 +55524,7 @@ function getDriveLetters() {
55524
55524
  function isDriveLetter(letter) {
55525
55525
  return /^[a-zA-Z]:\\$/.test(letter);
55526
55526
  }
55527
- router$e.get("/files", async (ctx) => {
55527
+ router$f.get("/files", async (ctx) => {
55528
55528
  const params = ctx.request.query;
55529
55529
  let root = params.path;
55530
55530
  const filterExts = (params.exts || "")
@@ -55580,26 +55580,26 @@ router$e.get("/files", async (ctx) => {
55580
55580
  return;
55581
55581
  }
55582
55582
  });
55583
- router$e.post("/danma/timestamp", async (ctx) => {
55583
+ router$f.post("/danma/timestamp", async (ctx) => {
55584
55584
  const { filepath } = ctx.request.body;
55585
55585
  ctx.body = await index$1.readXmlTimestamp(filepath);
55586
55586
  });
55587
- router$e.post("/parse-video-metadata", async (ctx) => {
55587
+ router$f.post("/parse-video-metadata", async (ctx) => {
55588
55588
  const files = ctx.request.body;
55589
55589
  ctx.body = await index$1.parseMeta(files);
55590
55590
  });
55591
55591
  /**
55592
55592
  * @api {get} /common/fonts 获取系统字体列表
55593
55593
  */
55594
- router$e.get("/fonts", async (ctx) => {
55594
+ router$f.get("/fonts", async (ctx) => {
55595
55595
  const { getFontsList } = await Promise.resolve().then(function () { return require('./fonts-DxTEygXH.cjs'); });
55596
55596
  ctx.body = await getFontsList();
55597
55597
  });
55598
- router$e.get("/appStartTime", async (ctx) => {
55598
+ router$f.get("/appStartTime", async (ctx) => {
55599
55599
  const data = index$1.statisticsService.query("start_time");
55600
55600
  ctx.body = data?.value;
55601
55601
  });
55602
- router$e.get("/statistics", async (ctx) => {
55602
+ router$f.get("/statistics", async (ctx) => {
55603
55603
  const startTime = index$1.statisticsService.query("start_time");
55604
55604
  const videoTotalDuaration = index$1.recordHistoryService.getTotalDuration();
55605
55605
  const recordingNum = recorderService.getRecorderNum(true);
@@ -55611,16 +55611,16 @@ router$e.get("/statistics", async (ctx) => {
55611
55611
  recorderNum,
55612
55612
  };
55613
55613
  });
55614
- router$e.get("/exportLogs", async (ctx) => {
55614
+ router$f.get("/exportLogs", async (ctx) => {
55615
55615
  const logFilePath = config.logPath;
55616
55616
  ctx.body = index$1.fs.createReadStream(logFilePath);
55617
55617
  });
55618
- router$e.get("/getLogContent", async (ctx) => {
55618
+ router$f.get("/getLogContent", async (ctx) => {
55619
55619
  const logFilePath = config.logPath;
55620
55620
  const content = await index$1.fs.readFile(logFilePath, "utf-8");
55621
55621
  ctx.body = content;
55622
55622
  });
55623
- router$e.post("/readDanma", async (ctx) => {
55623
+ router$f.post("/readDanma", async (ctx) => {
55624
55624
  const { filepath } = ctx.request.body;
55625
55625
  // 只允许读取ass或xml文件
55626
55626
  if (!filepath.endsWith(".ass") && !filepath.endsWith(".xml") && !filepath.endsWith(".srt")) {
@@ -55636,7 +55636,7 @@ router$e.post("/readDanma", async (ctx) => {
55636
55636
  const content = await index$1.fs.readFile(filepath, "utf-8");
55637
55637
  ctx.body = content;
55638
55638
  });
55639
- router$e.post("/readLLC", async (ctx) => {
55639
+ router$f.post("/readLLC", async (ctx) => {
55640
55640
  const { filepath } = ctx.request.body;
55641
55641
  if (!(await index$1.fs.pathExists(filepath))) {
55642
55642
  ctx.status = 400;
@@ -55651,7 +55651,7 @@ router$e.post("/readLLC", async (ctx) => {
55651
55651
  }
55652
55652
  ctx.body = content;
55653
55653
  });
55654
- router$e.post("/writeLLC", async (ctx) => {
55654
+ router$f.post("/writeLLC", async (ctx) => {
55655
55655
  const { filepath, content } = ctx.request.body;
55656
55656
  if (!content.includes("cutSegments")) {
55657
55657
  ctx.status = 400;
@@ -55661,13 +55661,13 @@ router$e.post("/writeLLC", async (ctx) => {
55661
55661
  await index$1.fs.writeFile(filepath, content, "utf-8");
55662
55662
  ctx.body = "success";
55663
55663
  });
55664
- router$e.post("/genTimeData", async (ctx) => {
55664
+ router$f.post("/genTimeData", async (ctx) => {
55665
55665
  const { filepath } = ctx.request.body;
55666
55666
  const data = await index$1.genTimeData(filepath);
55667
55667
  ctx.body = data;
55668
55668
  });
55669
55669
  // 申请视频ID接口
55670
- router$e.post("/applyVideoId", async (ctx) => {
55670
+ router$f.post("/applyVideoId", async (ctx) => {
55671
55671
  const { videoPath } = ctx.request.body;
55672
55672
  if (!(await index$1.fs.pathExists(videoPath))) {
55673
55673
  ctx.status = 404;
@@ -55725,7 +55725,7 @@ router$e.post("/applyVideoId", async (ctx) => {
55725
55725
  type,
55726
55726
  };
55727
55727
  });
55728
- router$e.get("/video/:videoId", async (ctx) => {
55728
+ router$f.get("/video/:videoId", async (ctx) => {
55729
55729
  const videoId = ctx.params.videoId;
55730
55730
  // 从映射表中获取视频路径
55731
55731
  const videoInfo = fileCache.get(videoId);
@@ -55800,12 +55800,12 @@ router$e.get("/video/:videoId", async (ctx) => {
55800
55800
  ctx.body = file;
55801
55801
  }
55802
55802
  });
55803
- router$e.post("/parseDanmu", async (ctx) => {
55803
+ router$f.post("/parseDanmu", async (ctx) => {
55804
55804
  const { filepath } = ctx.request.body;
55805
55805
  const data = await index$1.parseDanmu(filepath);
55806
55806
  ctx.body = data;
55807
55807
  });
55808
- router$e.post("/testWebhook", async (ctx) => {
55808
+ router$f.post("/testWebhook", async (ctx) => {
55809
55809
  const list = [];
55810
55810
  // 检查所有live数据,如果存在同一个live中,前一个part还在录制中,但是后面的part已经是其他状态,则返回相关id以及文件
55811
55811
  const liveList = handler.liveData;
@@ -55827,7 +55827,7 @@ router$e.post("/testWebhook", async (ctx) => {
55827
55827
  }
55828
55828
  ctx.body = list;
55829
55829
  });
55830
- router$e.post("/handleWebhook", async (ctx) => {
55830
+ router$f.post("/handleWebhook", async (ctx) => {
55831
55831
  const { data } = ctx.request.body;
55832
55832
  const liveList = handler.liveData;
55833
55833
  for (const live of liveList) {
@@ -55843,7 +55843,7 @@ router$e.post("/handleWebhook", async (ctx) => {
55843
55843
  }
55844
55844
  ctx.body = "success";
55845
55845
  });
55846
- router$e.post("/webhook", async (ctx) => {
55846
+ router$f.post("/webhook", async (ctx) => {
55847
55847
  const liveList = handler.liveData;
55848
55848
  ctx.body = liveList;
55849
55849
  });
@@ -55853,7 +55853,7 @@ router$e.post("/webhook", async (ctx) => {
55853
55853
  * 如果不是,检查全局webhook配置,判断是否开启全局开关,判断是否在黑名单,判断配置是否配置了上传预设,上传账号
55854
55854
  * 返回{hasError:boolean,errorInfo:string}
55855
55855
  */
55856
- router$e.get("/whyUploadFailed", async (ctx) => {
55856
+ router$f.get("/whyUploadFailed", async (ctx) => {
55857
55857
  const { roomId } = ctx.request.query;
55858
55858
  if (!roomId) {
55859
55859
  ctx.status = 400;
@@ -56010,7 +56010,7 @@ router$e.get("/whyUploadFailed", async (ctx) => {
56010
56010
  * @apiSuccess {boolean} needUpdate 是否需要更新
56011
56011
  * @apiSuccess {string} [downloadUrl] 下载地址
56012
56012
  */
56013
- router$e.get("/checkUpdate", async (ctx) => {
56013
+ router$f.get("/checkUpdate", async (ctx) => {
56014
56014
  try {
56015
56015
  const res = await fetch("https://githubraw.irenmu.com/renmu123/biliLive-tools/master/package.json");
56016
56016
  const data = await res.json();
@@ -56043,7 +56043,7 @@ router$e.get("/checkUpdate", async (ctx) => {
56043
56043
  * @apiSuccess {number} used 已用空间(GB)
56044
56044
  * @apiSuccess {number} usedPercentage 使用百分比
56045
56045
  */
56046
- router$e.get("/diskSpace", async (ctx) => {
56046
+ router$f.get("/diskSpace", async (ctx) => {
56047
56047
  try {
56048
56048
  const config = appConfig.getAll();
56049
56049
  const recoderFolder = config?.recorder?.savePath;
@@ -56077,10 +56077,10 @@ router$e.get("/diskSpace", async (ctx) => {
56077
56077
  }
56078
56078
  });
56079
56079
 
56080
- const router$d = new Router$1({
56080
+ const router$e = new Router$1({
56081
56081
  prefix: "/user",
56082
56082
  });
56083
- router$d.get("/list", async (ctx) => {
56083
+ router$e.get("/list", async (ctx) => {
56084
56084
  const list = index$1.biliApi.readUserList();
56085
56085
  ctx.body = list.map((item) => {
56086
56086
  return {
@@ -56091,22 +56091,22 @@ router$d.get("/list", async (ctx) => {
56091
56091
  };
56092
56092
  });
56093
56093
  });
56094
- router$d.post("/delete", async (ctx) => {
56094
+ router$e.post("/delete", async (ctx) => {
56095
56095
  const { uid } = ctx.request.body;
56096
56096
  await index$1.biliApi.deleteUser(uid);
56097
56097
  ctx.status = 200;
56098
56098
  });
56099
- router$d.post("/update", async (ctx) => {
56099
+ router$e.post("/update", async (ctx) => {
56100
56100
  const { uid } = ctx.request.body;
56101
56101
  await index$1.biliApi.updateUserInfo(uid);
56102
56102
  ctx.status = 200;
56103
56103
  });
56104
- router$d.post("/update_auth", async (ctx) => {
56104
+ router$e.post("/update_auth", async (ctx) => {
56105
56105
  const { uid } = ctx.request.body;
56106
56106
  await index$1.biliApi.updateAuth(uid);
56107
56107
  ctx.status = 200;
56108
56108
  });
56109
- router$d.post("/get_cookie", async (ctx) => {
56109
+ router$e.post("/get_cookie", async (ctx) => {
56110
56110
  const { uid, timestamp, signature } = ctx.request.body;
56111
56111
  const currentTimestamp = Math.floor(Date.now() / 1000);
56112
56112
  if (Math.abs(currentTimestamp - timestamp) > 10) {
@@ -56137,11 +56137,11 @@ router$d.post("/get_cookie", async (ctx) => {
56137
56137
  ctx.body = "获取失败,请重试";
56138
56138
  }
56139
56139
  });
56140
- router$d.get("/export", async (ctx) => {
56140
+ router$e.get("/export", async (ctx) => {
56141
56141
  const list = index$1.biliApi.readUserList();
56142
56142
  ctx.body = list;
56143
56143
  });
56144
- router$d.post("/export_single", async (ctx) => {
56144
+ router$e.post("/export_single", async (ctx) => {
56145
56145
  const { uid } = ctx.request.body;
56146
56146
  const user = index$1.biliApi.readUser(uid);
56147
56147
  if (!user) {
@@ -56151,7 +56151,7 @@ router$d.post("/export_single", async (ctx) => {
56151
56151
  }
56152
56152
  ctx.body = user;
56153
56153
  });
56154
- router$d.post("/import", async (ctx) => {
56154
+ router$e.post("/import", async (ctx) => {
56155
56155
  const { users } = ctx.request.body;
56156
56156
  if (!Array.isArray(users)) {
56157
56157
  ctx.status = 400;
@@ -56168,7 +56168,7 @@ router$d.post("/import", async (ctx) => {
56168
56168
  }
56169
56169
  ctx.status = 200;
56170
56170
  });
56171
- router$d.post("/import_single", async (ctx) => {
56171
+ router$e.post("/import_single", async (ctx) => {
56172
56172
  const { user } = ctx.request.body;
56173
56173
  if (!user?.mid || !user?.accessToken || !user?.refreshToken || !user?.cookie) {
56174
56174
  ctx.status = 400;
@@ -56179,42 +56179,42 @@ router$d.post("/import_single", async (ctx) => {
56179
56179
  ctx.status = 200;
56180
56180
  });
56181
56181
 
56182
- const router$c = new Router$1({
56182
+ const router$d = new Router$1({
56183
56183
  prefix: "/preset",
56184
56184
  });
56185
56185
  ////////////////// 弹幕预设 ///////////////////////////////
56186
- router$c.get("/danmu", async (ctx) => {
56186
+ router$d.get("/danmu", async (ctx) => {
56187
56187
  const danmuPreset = container.resolve("danmuPreset");
56188
56188
  ctx.body = await danmuPreset.list();
56189
56189
  });
56190
- router$c.get("/danmu/:id", async (ctx) => {
56190
+ router$d.get("/danmu/:id", async (ctx) => {
56191
56191
  const danmuPreset = container.resolve("danmuPreset");
56192
56192
  ctx.body = await danmuPreset.get(ctx.params.id);
56193
56193
  });
56194
- router$c.post("/danmu", async (ctx) => {
56194
+ router$d.post("/danmu", async (ctx) => {
56195
56195
  const danmuPreset = container.resolve("danmuPreset");
56196
56196
  const data = ctx.request.body;
56197
56197
  ctx.body = await danmuPreset.save({ ...data, id: index$1.uuid() });
56198
56198
  });
56199
- router$c.del("/danmu/:id", async (ctx) => {
56199
+ router$d.del("/danmu/:id", async (ctx) => {
56200
56200
  const danmuPreset = container.resolve("danmuPreset");
56201
56201
  ctx.body = await danmuPreset.delete(ctx.params.id);
56202
56202
  });
56203
- router$c.put("/danmu/:id", async (ctx) => {
56203
+ router$d.put("/danmu/:id", async (ctx) => {
56204
56204
  const danmuPreset = container.resolve("danmuPreset");
56205
56205
  const data = ctx.request.body;
56206
56206
  ctx.body = await danmuPreset.save({ ...data, id: ctx.params.id });
56207
56207
  });
56208
56208
  ////////////////// 视频上传预设 ///////////////////////////////
56209
- router$c.get("/video", async (ctx) => {
56209
+ router$d.get("/video", async (ctx) => {
56210
56210
  const preset = container.resolve("videoPreset");
56211
56211
  ctx.body = await preset.list();
56212
56212
  });
56213
- router$c.get("/video/:id", async (ctx) => {
56213
+ router$d.get("/video/:id", async (ctx) => {
56214
56214
  const preset = container.resolve("videoPreset");
56215
56215
  ctx.body = await preset.get(ctx.params.id);
56216
56216
  });
56217
- router$c.post("/video", async (ctx) => {
56217
+ router$d.post("/video", async (ctx) => {
56218
56218
  const preset = container.resolve("videoPreset");
56219
56219
  const data = ctx.request.body;
56220
56220
  data.config = index$1.omit(data.config, ["dtime", "staffs"]);
@@ -56224,11 +56224,11 @@ router$c.post("/video", async (ctx) => {
56224
56224
  }
56225
56225
  ctx.body = await preset.save({ ...data, id: index$1.uuid() });
56226
56226
  });
56227
- router$c.del("/video/:id", async (ctx) => {
56227
+ router$d.del("/video/:id", async (ctx) => {
56228
56228
  const preset = container.resolve("videoPreset");
56229
56229
  ctx.body = await preset.delete(ctx.params.id);
56230
56230
  });
56231
- router$c.put("/video/:id", async (ctx) => {
56231
+ router$d.put("/video/:id", async (ctx) => {
56232
56232
  const preset = container.resolve("videoPreset");
56233
56233
  const data = ctx.request.body;
56234
56234
  data.config = index$1.omit(data.config, ["dtime", "staffs"]);
@@ -56239,51 +56239,51 @@ router$c.put("/video/:id", async (ctx) => {
56239
56239
  ctx.body = await preset.save({ ...data, id: ctx.params.id });
56240
56240
  });
56241
56241
  ////////////////// ffmpeg预设 ///////////////////////////////
56242
- router$c.get("/ffmpeg", async (ctx) => {
56242
+ router$d.get("/ffmpeg", async (ctx) => {
56243
56243
  const preset = container.resolve("ffmpegPreset");
56244
56244
  ctx.body = await preset.list();
56245
56245
  });
56246
- router$c.get("/ffmpeg/options", async (ctx) => {
56246
+ router$d.get("/ffmpeg/options", async (ctx) => {
56247
56247
  const preset = container.resolve("ffmpegPreset");
56248
56248
  ctx.body = await preset.getFfmpegPresetOptions();
56249
56249
  });
56250
- router$c.get("/ffmpeg/:id", async (ctx) => {
56250
+ router$d.get("/ffmpeg/:id", async (ctx) => {
56251
56251
  const preset = container.resolve("ffmpegPreset");
56252
56252
  ctx.body = await preset.get(ctx.params.id);
56253
56253
  });
56254
- router$c.post("/ffmpeg", async (ctx) => {
56254
+ router$d.post("/ffmpeg", async (ctx) => {
56255
56255
  const preset = container.resolve("ffmpegPreset");
56256
56256
  const data = ctx.request.body;
56257
56257
  ctx.body = await preset.save({ ...data, id: index$1.uuid() });
56258
56258
  });
56259
- router$c.del("/ffmpeg/:id", async (ctx) => {
56259
+ router$d.del("/ffmpeg/:id", async (ctx) => {
56260
56260
  const preset = container.resolve("ffmpegPreset");
56261
56261
  ctx.body = await preset.delete(ctx.params.id);
56262
56262
  });
56263
- router$c.put("/ffmpeg/:id", async (ctx) => {
56263
+ router$d.put("/ffmpeg/:id", async (ctx) => {
56264
56264
  const preset = container.resolve("ffmpegPreset");
56265
56265
  const data = ctx.request.body;
56266
56266
  ctx.body = await preset.save({ ...data, id: ctx.params.id });
56267
56267
  });
56268
56268
  ////////////////// 字幕样式预设 ///////////////////////////////
56269
- router$c.get("/subtitle-style", async (ctx) => {
56269
+ router$d.get("/subtitle-style", async (ctx) => {
56270
56270
  const preset = container.resolve("subtitleStylePreset");
56271
56271
  ctx.body = await preset.list();
56272
56272
  });
56273
- router$c.get("/subtitle-style/:id", async (ctx) => {
56273
+ router$d.get("/subtitle-style/:id", async (ctx) => {
56274
56274
  const preset = container.resolve("subtitleStylePreset");
56275
56275
  ctx.body = await preset.get(ctx.params.id);
56276
56276
  });
56277
- router$c.post("/subtitle-style", async (ctx) => {
56277
+ router$d.post("/subtitle-style", async (ctx) => {
56278
56278
  const preset = container.resolve("subtitleStylePreset");
56279
56279
  const data = ctx.request.body;
56280
56280
  ctx.body = await preset.save({ ...data, id: index$1.uuid() });
56281
56281
  });
56282
- router$c.del("/subtitle-style/:id", async (ctx) => {
56282
+ router$d.del("/subtitle-style/:id", async (ctx) => {
56283
56283
  const preset = container.resolve("subtitleStylePreset");
56284
56284
  ctx.body = await preset.delete(ctx.params.id);
56285
56285
  });
56286
- router$c.put("/subtitle-style/:id", async (ctx) => {
56286
+ router$d.put("/subtitle-style/:id", async (ctx) => {
56287
56287
  const preset = container.resolve("subtitleStylePreset");
56288
56288
  const data = ctx.request.body;
56289
56289
  ctx.body = await preset.save({ ...data, id: ctx.params.id });
@@ -62606,7 +62606,7 @@ class MultipartBody {
62606
62606
  let fileFromPathWarned = false;
62607
62607
  async function fileFromPath(path, ...args) {
62608
62608
  // this import fails in environments that don't handle export maps correctly, like old versions of Jest
62609
- const { fileFromPath: _fileFromPath } = await Promise.resolve().then(function () { return require('./fileFromPath-D1rsnNRo.cjs'); });
62609
+ const { fileFromPath: _fileFromPath } = await Promise.resolve().then(function () { return require('./fileFromPath-Cl9Q2SkN.cjs'); });
62610
62610
  if (!fileFromPathWarned) {
62611
62611
  console.warn(`fileFromPath is deprecated; use fs.createReadStream(${JSON.stringify(path)}) instead`);
62612
62612
  fileFromPathWarned = true;
@@ -73576,13 +73576,13 @@ async function songRecognize(file, audioStartTime = 0) {
73576
73576
  };
73577
73577
  }
73578
73578
 
73579
- const router$b = new Router$1({
73579
+ const router$c = new Router$1({
73580
73580
  prefix: "/sse",
73581
73581
  });
73582
73582
  /**
73583
73583
  * 流式查询日志
73584
73584
  */
73585
- router$b.get("/streamLogs", sse({
73585
+ router$c.get("/streamLogs", sse({
73586
73586
  maxClients: 5000,
73587
73587
  pingInterval: 60 * 60 * 1000,
73588
73588
  }), async (ctx) => {
@@ -73621,7 +73621,7 @@ router$b.get("/streamLogs", sse({
73621
73621
  /**
73622
73622
  * 获取弹幕流
73623
73623
  */
73624
- router$b.get("/recorder/danma", sse({
73624
+ router$c.get("/recorder/danma", sse({
73625
73625
  maxClients: 5000,
73626
73626
  pingInterval: 30000,
73627
73627
  }), async (ctx) => {
@@ -73634,7 +73634,7 @@ router$b.get("/recorder/danma", sse({
73634
73634
  }
73635
73635
  });
73636
73636
  });
73637
- router$b.get("/task/runningNum", sse({
73637
+ router$c.get("/task/runningNum", sse({
73638
73638
  maxClients: 5000,
73639
73639
  pingInterval: 30000,
73640
73640
  }), async (ctx) => {
@@ -73667,7 +73667,7 @@ router$b.get("/task/runningNum", sse({
73667
73667
  /**
73668
73668
  * 波形分析进度流
73669
73669
  */
73670
- router$b.get("/analyzerWaveform", sse({
73670
+ router$c.get("/analyzerWaveform", sse({
73671
73671
  maxClients: 100,
73672
73672
  pingInterval: 30000,
73673
73673
  }), async (ctx) => {
@@ -73737,6 +73737,28 @@ const getRecordDuration = (record) => {
73737
73737
  return 0;
73738
73738
  };
73739
73739
  const toSessionKey = (record) => record.live_id || UNKNOWN_LIVE_ID;
73740
+ const parseDanmaTimeline = (value) => {
73741
+ if (!value)
73742
+ return undefined;
73743
+ try {
73744
+ const parsed = JSON.parse(value);
73745
+ const interval = parsed.danmaTimeline?.interval;
73746
+ const data = parsed.danmaTimeline?.data;
73747
+ if (typeof interval !== "number" ||
73748
+ !Number.isFinite(interval) ||
73749
+ interval <= 0 ||
73750
+ !Array.isArray(data)) {
73751
+ return undefined;
73752
+ }
73753
+ return {
73754
+ interval,
73755
+ data: data.map((item) => typeof item === "number" && Number.isFinite(item) && item >= 0 ? Math.round(item) : 0),
73756
+ };
73757
+ }
73758
+ catch {
73759
+ return undefined;
73760
+ }
73761
+ };
73740
73762
  const toSessionCard = (records) => {
73741
73763
  const sortedRecords = [...records].sort((left, right) => right.record_start_time - left.record_start_time);
73742
73764
  const latestRecord = sortedRecords[0] || null;
@@ -73771,6 +73793,7 @@ const toSessionCard = (records) => {
73771
73793
  video_duration: item.video_duration,
73772
73794
  danma_num: item.danma_num,
73773
73795
  interact_num: item.interact_num,
73796
+ danmaTimeline: parseDanmaTimeline(item.danma_stats_json),
73774
73797
  quick_hash: item.quick_hash,
73775
73798
  })),
73776
73799
  };
@@ -73842,7 +73865,7 @@ var streamerDetailService = {
73842
73865
  queryStreamerDetail,
73843
73866
  };
73844
73867
 
73845
- const router$a = new Router$1({
73868
+ const router$b = new Router$1({
73846
73869
  prefix: "/recorder",
73847
73870
  });
73848
73871
  const DANMA_TEST_WS_PATH = "/recorder/ws/danma-test";
@@ -73989,11 +74012,11 @@ const getSingleQueryValue$1 = (value) => {
73989
74012
  * @param sortDirection 排序方向 asc: 升序 desc: 降序
73990
74013
  * @returns 录制任务列表
73991
74014
  */
73992
- router$a.get("/list", async (ctx) => {
74015
+ router$b.get("/list", async (ctx) => {
73993
74016
  const query = ctx.request.query;
73994
74017
  ctx.body = { payload: await recorderService.getRecorders(query) };
73995
74018
  });
73996
- router$a.get("/detail", async (ctx) => {
74019
+ router$b.get("/detail", async (ctx) => {
73997
74020
  const recorderId = getSingleQueryValue$1(ctx.request.query.recorderId);
73998
74021
  const page = getSingleQueryValue$1(ctx.request.query.page);
73999
74022
  const pageSize = getSingleQueryValue$1(ctx.request.query.pageSize);
@@ -74030,7 +74053,7 @@ router$a.get("/detail", async (ctx) => {
74030
74053
  },
74031
74054
  };
74032
74055
  });
74033
- router$a.get("/ws/danma-test", async (ctx) => {
74056
+ router$b.get("/ws/danma-test", async (ctx) => {
74034
74057
  ctx.status = 426;
74035
74058
  ctx.body = {
74036
74059
  message: "请使用 WebSocket 连接此地址",
@@ -74045,8 +74068,8 @@ router$a.get("/ws/danma-test", async (ctx) => {
74045
74068
  },
74046
74069
  };
74047
74070
  });
74048
- router$a.post("/add", async (ctx) => {
74049
- const args = index$1.pick((ctx.request.body ?? {}), "providerId", "channelId", "remarks", "disableAutoCheck", "quality", "streamPriorities", "sourcePriorities", "extra", "noGlobalFollowFields", "line", "disableProvideCommentsWhenRecording", "saveGiftDanma", "saveSCDanma", "segment", "sendToWebhook", "uid", "saveCover", "convert2Mp4", "qualityRetry", "formatName", "useM3U8Proxy", "customHost", "codecName", "titleKeywords", "segmentOnTitleChange", "liveStartNotification", "chargeLiveNotification", "liveEndNotification", "weight", "source", "videoFormat", "recorderType", "cookie", "doubleScreen", "onlyAudio", "useServerTimestamp", "handleTime", "debugLevel", "api");
74071
+ router$b.post("/add", async (ctx) => {
74072
+ const args = index$1.pick((ctx.request.body ?? {}), "providerId", "channelId", "remarks", "disableAutoCheck", "quality", "streamPriorities", "sourcePriorities", "extra", "noGlobalFollowFields", "line", "disableProvideCommentsWhenRecording", "saveGiftDanma", "saveSCDanma", "saveDanmaStats2DB", "segment", "sendToWebhook", "uid", "saveCover", "convert2Mp4", "qualityRetry", "formatName", "useM3U8Proxy", "customHost", "codecName", "titleKeywords", "segmentOnTitleChange", "liveStartNotification", "chargeLiveNotification", "liveEndNotification", "weight", "source", "videoFormat", "recorderType", "cookie", "doubleScreen", "onlyAudio", "useServerTimestamp", "handleTime", "debugLevel", "api");
74050
74073
  const data = await recorderService.addRecorder(args);
74051
74074
  ctx.body = { payload: data };
74052
74075
  });
@@ -74056,7 +74079,7 @@ router$a.post("/add", async (ctx) => {
74056
74079
  * @param recorderId 直播间ID
74057
74080
  * @returns 录制器配置信息
74058
74081
  */
74059
- router$a.get("/:id", (ctx) => {
74082
+ router$b.get("/:id", (ctx) => {
74060
74083
  const { id } = ctx.params;
74061
74084
  ctx.body = { payload: recorderService.getRecorder({ id }) };
74062
74085
  });
@@ -74066,16 +74089,16 @@ router$a.get("/:id", (ctx) => {
74066
74089
  * @param recorderId 录制器 ID
74067
74090
  * @returns 重写分片地址后的 HLS 播放列表
74068
74091
  */
74069
- router$a.post("/:id/stream", async (ctx) => {
74092
+ router$b.post("/:id/stream", async (ctx) => {
74070
74093
  const { id } = ctx.params;
74071
74094
  const data = await recorderService.getStreamUrl(id);
74072
74095
  ctx.body = {
74073
74096
  payload: data,
74074
74097
  };
74075
74098
  });
74076
- router$a.put("/:id", (ctx) => {
74099
+ router$b.put("/:id", (ctx) => {
74077
74100
  const { id } = ctx.params;
74078
- const patch = index$1.pick(ctx.request.body, "remarks", "disableAutoCheck", "quality", "streamPriorities", "sourcePriorities", "noGlobalFollowFields", "line", "disableProvideCommentsWhenRecording", "saveGiftDanma", "saveSCDanma", "saveCover", "segment", "sendToWebhook", "uid", "qualityRetry", "formatName", "useM3U8Proxy", "customHost", "codecName", "titleKeywords", "segmentOnTitleChange", "liveStartNotification", "chargeLiveNotification", "liveEndNotification", "weight", "source", "videoFormat", "recorderType", "cookie", "doubleScreen", "onlyAudio", "useServerTimestamp", "handleTime", "debugLevel", "convert2Mp4", "api");
74101
+ const patch = index$1.pick(ctx.request.body, "remarks", "disableAutoCheck", "quality", "streamPriorities", "sourcePriorities", "noGlobalFollowFields", "line", "disableProvideCommentsWhenRecording", "saveGiftDanma", "saveSCDanma", "saveDanmaStats2DB", "saveCover", "segment", "sendToWebhook", "uid", "qualityRetry", "formatName", "useM3U8Proxy", "customHost", "codecName", "titleKeywords", "segmentOnTitleChange", "liveStartNotification", "chargeLiveNotification", "liveEndNotification", "weight", "source", "videoFormat", "recorderType", "cookie", "doubleScreen", "onlyAudio", "useServerTimestamp", "handleTime", "debugLevel", "convert2Mp4", "api");
74079
74102
  ctx.body = { payload: recorderService.updateRecorder({ id, ...patch }) };
74080
74103
  });
74081
74104
  /**
@@ -74085,7 +74108,7 @@ router$a.put("/:id", (ctx) => {
74085
74108
  * @param removeHistory 是否删除录制历史,默认false
74086
74109
  * @returns null
74087
74110
  */
74088
- router$a.delete("/:id", (ctx) => {
74111
+ router$b.delete("/:id", (ctx) => {
74089
74112
  const { id } = ctx.params;
74090
74113
  const { removeHistory } = ctx.request.query;
74091
74114
  ctx.body = {
@@ -74099,7 +74122,7 @@ router$a.delete("/:id", (ctx) => {
74099
74122
  * @param recorderId 录制ID
74100
74123
  * @returns 录制任务信息
74101
74124
  */
74102
- router$a.post("/:id/start_record", async (ctx) => {
74125
+ router$b.post("/:id/start_record", async (ctx) => {
74103
74126
  const { id } = ctx.params;
74104
74127
  ctx.body = { payload: await recorderService.startRecord({ id }) };
74105
74128
  });
@@ -74109,7 +74132,7 @@ router$a.post("/:id/start_record", async (ctx) => {
74109
74132
  * @param recorderId 录制ID
74110
74133
  * @returns 录制任务信息
74111
74134
  */
74112
- router$a.post("/:id/start", async (ctx) => {
74135
+ router$b.post("/:id/start", async (ctx) => {
74113
74136
  const { id } = ctx.params;
74114
74137
  ctx.body = { payload: await recorderService.startRecord({ id }) };
74115
74138
  });
@@ -74120,7 +74143,7 @@ router$a.post("/:id/start", async (ctx) => {
74120
74143
  * @param recorderId 直播间ID
74121
74144
  * @returns 录制任务信息
74122
74145
  */
74123
- router$a.post("/:id/stop_record", async (ctx) => {
74146
+ router$b.post("/:id/stop_record", async (ctx) => {
74124
74147
  const { id } = ctx.params;
74125
74148
  ctx.body = { payload: await recorderService.stopRecord({ id }) };
74126
74149
  });
@@ -74131,7 +74154,7 @@ router$a.post("/:id/stop_record", async (ctx) => {
74131
74154
  * @param recorderId 录制ID
74132
74155
  * @returns 录制任务信息
74133
74156
  */
74134
- router$a.post("/:id/stop", async (ctx) => {
74157
+ router$b.post("/:id/stop", async (ctx) => {
74135
74158
  const { id } = ctx.params;
74136
74159
  ctx.body = { payload: await recorderService.stopRecord({ id }) };
74137
74160
  });
@@ -74141,7 +74164,7 @@ router$a.post("/:id/stop", async (ctx) => {
74141
74164
  * @param recorderId 直播间ID
74142
74165
  * @returns 录制任务信息
74143
74166
  */
74144
- router$a.post("/:id/cut", async (ctx) => {
74167
+ router$b.post("/:id/cut", async (ctx) => {
74145
74168
  const { id } = ctx.params;
74146
74169
  ctx.body = { payload: await recorderService.cutRecord({ id }) };
74147
74170
  });
@@ -74151,7 +74174,7 @@ router$a.post("/:id/cut", async (ctx) => {
74151
74174
  * @param recorderId 直播间ID
74152
74175
  * @returns 最近录制文件目录
74153
74176
  */
74154
- router$a.get("/:id/recent-record-folder", async (ctx) => {
74177
+ router$b.get("/:id/recent-record-folder", async (ctx) => {
74155
74178
  const { id } = ctx.params;
74156
74179
  ctx.body = { payload: await recorderService.getRecentRecordFolder({ id }) };
74157
74180
  });
@@ -74161,7 +74184,7 @@ router$a.get("/:id/recent-record-folder", async (ctx) => {
74161
74184
  * @param ids 直播间ID列表
74162
74185
  * @returns 批量操作结果
74163
74186
  */
74164
- router$a.post("/manager/batch_start_record", async (ctx) => {
74187
+ router$b.post("/manager/batch_start_record", async (ctx) => {
74165
74188
  const { ids } = ctx.request.body;
74166
74189
  ctx.body = { payload: await recorderService.batchStartRecord(ids) };
74167
74190
  });
@@ -74171,7 +74194,7 @@ router$a.post("/manager/batch_start_record", async (ctx) => {
74171
74194
  * @param ids 直播间ID列表
74172
74195
  * @returns 批量操作结果
74173
74196
  */
74174
- router$a.post("/manager/batch_stop_record", async (ctx) => {
74197
+ router$b.post("/manager/batch_stop_record", async (ctx) => {
74175
74198
  const { ids } = ctx.request.body;
74176
74199
  ctx.body = { payload: await recorderService.batchStopRecord(ids) };
74177
74200
  });
@@ -74181,7 +74204,7 @@ router$a.post("/manager/batch_stop_record", async (ctx) => {
74181
74204
  * @param url 直播间地址
74182
74205
  * @returns 直播间信息
74183
74206
  */
74184
- router$a.get("/manager/resolve-channel", async (ctx) => {
74207
+ router$b.get("/manager/resolve-channel", async (ctx) => {
74185
74208
  const { url } = ctx.query;
74186
74209
  const data = await recorderService.resolveChannel(url);
74187
74210
  ctx.body = { payload: data };
@@ -74192,7 +74215,7 @@ router$a.get("/manager/resolve-channel", async (ctx) => {
74192
74215
  * @param url 直播间地址
74193
74216
  * @returns 直播间信息
74194
74217
  */
74195
- router$a.get("/manager/resolve", async (ctx) => {
74218
+ router$b.get("/manager/resolve", async (ctx) => {
74196
74219
  const { url } = ctx.query;
74197
74220
  const data = await recorderService.resolve(url);
74198
74221
  ctx.body = { payload: data };
@@ -74203,7 +74226,7 @@ router$a.get("/manager/resolve", async (ctx) => {
74203
74226
  * @param channelURLs 直播间地址数组
74204
74227
  * @returns 批量解析结果
74205
74228
  */
74206
- router$a.post("/manager/batchResolveChannel", async (ctx) => {
74229
+ router$b.post("/manager/batchResolveChannel", async (ctx) => {
74207
74230
  const { channelURLs } = ctx.request.body;
74208
74231
  const data = await recorderService.batchResolveChannel(channelURLs);
74209
74232
  ctx.body = { payload: data };
@@ -74215,7 +74238,7 @@ router$a.post("/manager/batchResolveChannel", async (ctx) => {
74215
74238
  * @param forceRequest 强制查询直播间信息,不受配置限制,默认true
74216
74239
  * @returns 直播间实时信息列表
74217
74240
  */
74218
- router$a.post("/manager/live-info", async (ctx) => {
74241
+ router$b.post("/manager/live-info", async (ctx) => {
74219
74242
  const { ids } = ctx.request.body;
74220
74243
  const forceRequest = ctx.request.body.forceRequest ?? true;
74221
74244
  let requestInfoForRecord = true;
@@ -74230,7 +74253,7 @@ router$a.post("/manager/live-info", async (ctx) => {
74230
74253
  payload: list,
74231
74254
  };
74232
74255
  });
74233
- router$a.get("/douyin/live-info", async (ctx) => {
74256
+ router$b.get("/douyin/live-info", async (ctx) => {
74234
74257
  const { roomId, platform, dev } = ctx.request.query;
74235
74258
  if (platform !== "douyin") {
74236
74259
  ctx.body = { error: "Platform not supported" };
@@ -74263,7 +74286,7 @@ router$a.get("/douyin/live-info", async (ctx) => {
74263
74286
  * @param dev 是否返回开发信息,默认为false
74264
74287
  * @returns 直播流信息
74265
74288
  */
74266
- router$a.get("/douyin/stream-info", async (ctx) => {
74289
+ router$b.get("/douyin/stream-info", async (ctx) => {
74267
74290
  const { roomId, platform, dev, quality } = ctx.request.query;
74268
74291
  if (platform !== "douyin") {
74269
74292
  ctx.body = { error: "Platform not supported" };
@@ -74312,11 +74335,11 @@ function handleRecorderUpgrade(request, socket, head) {
74312
74335
  return true;
74313
74336
  }
74314
74337
 
74315
- const router$9 = new Router$1({
74338
+ const router$a = new Router$1({
74316
74339
  prefix: "/bili",
74317
74340
  });
74318
74341
  // 验证视频上传参数
74319
- router$9.post("/validUploadParams", async (ctx) => {
74342
+ router$a.post("/validUploadParams", async (ctx) => {
74320
74343
  const params = ctx.request.body;
74321
74344
  // @ts-ignore
74322
74345
  const [status, msg] = await index$1.validateBiliupConfig(params);
@@ -74330,7 +74353,7 @@ router$9.post("/validUploadParams", async (ctx) => {
74330
74353
  /**
74331
74354
  * 投稿中心视频列表
74332
74355
  */
74333
- router$9.get("/archives", async (ctx) => {
74356
+ router$a.get("/archives", async (ctx) => {
74334
74357
  const params = ctx.request.query;
74335
74358
  const { uid } = params;
74336
74359
  const data = await index$1.biliApi.getArchives(params, uid);
@@ -74339,40 +74362,40 @@ router$9.get("/archives", async (ctx) => {
74339
74362
  /**
74340
74363
  * 用户视频详情
74341
74364
  */
74342
- router$9.get("/user/archive/:bvid", async (ctx) => {
74365
+ router$a.get("/user/archive/:bvid", async (ctx) => {
74343
74366
  const params = ctx.request.query;
74344
74367
  const { uid } = params;
74345
74368
  const { bvid } = ctx.params;
74346
74369
  const data = await index$1.biliApi.getArchiveDetail(bvid, uid);
74347
74370
  ctx.body = data;
74348
74371
  });
74349
- router$9.post("/checkTag", async (ctx) => {
74372
+ router$a.post("/checkTag", async (ctx) => {
74350
74373
  const { tag, uid, } = ctx.request.body;
74351
74374
  const data = await index$1.biliApi.checkTag(tag, uid);
74352
74375
  ctx.body = data;
74353
74376
  });
74354
- router$9.get("/searchTopic", async (ctx) => {
74377
+ router$a.get("/searchTopic", async (ctx) => {
74355
74378
  const { keyword, uid } = ctx.request.query;
74356
74379
  const data = await index$1.biliApi.searchTopic(keyword, uid);
74357
74380
  ctx.body = data;
74358
74381
  });
74359
- router$9.get("/seasons", async (ctx) => {
74382
+ router$a.get("/seasons", async (ctx) => {
74360
74383
  const { uid } = ctx.request.query;
74361
74384
  const data = await index$1.biliApi.getSeasonList(uid);
74362
74385
  ctx.body = data;
74363
74386
  });
74364
- router$9.get("/reserveList", async (ctx) => {
74387
+ router$a.get("/reserveList", async (ctx) => {
74365
74388
  const { uid } = ctx.request.query;
74366
74389
  const data = await index$1.biliApi.getReserveList(uid);
74367
74390
  ctx.body = data;
74368
74391
  });
74369
- router$9.get("/season/:aid", async (ctx) => {
74392
+ router$a.get("/season/:aid", async (ctx) => {
74370
74393
  const { uid } = ctx.request.query;
74371
74394
  const { aid } = ctx.params;
74372
74395
  const data = await index$1.biliApi.getSessionId(Number(aid), uid);
74373
74396
  ctx.body = data;
74374
74397
  });
74375
- router$9.get("/platformArchiveDetail", async (ctx) => {
74398
+ router$a.get("/platformArchiveDetail", async (ctx) => {
74376
74399
  const { aid, uid } = ctx.request.query;
74377
74400
  const data = await index$1.biliApi.getPlatformArchiveDetail(aid, uid);
74378
74401
  ctx.body = data;
@@ -74380,7 +74403,7 @@ router$9.get("/platformArchiveDetail", async (ctx) => {
74380
74403
  /**
74381
74404
  * 上传以及续传视频
74382
74405
  */
74383
- router$9.post("/upload", async (ctx) => {
74406
+ router$a.post("/upload", async (ctx) => {
74384
74407
  const data = ctx.request.body;
74385
74408
  if (!data.uid) {
74386
74409
  ctx.body = "uid required";
@@ -74425,7 +74448,7 @@ router$9.post("/upload", async (ctx) => {
74425
74448
  });
74426
74449
  // 登录相关
74427
74450
  const loginObj = {};
74428
- router$9.post("/login", async (ctx) => {
74451
+ router$a.post("/login", async (ctx) => {
74429
74452
  const tv = new index$1.TvQrcodeLogin();
74430
74453
  const id = index$1.uuid();
74431
74454
  loginObj[id] = {
@@ -74457,7 +74480,7 @@ router$9.post("/login", async (ctx) => {
74457
74480
  id,
74458
74481
  };
74459
74482
  });
74460
- router$9.post("/login/cancel", async (ctx) => {
74483
+ router$a.post("/login/cancel", async (ctx) => {
74461
74484
  const { id } = ctx.request.body;
74462
74485
  if (!id) {
74463
74486
  ctx.body = "id required";
@@ -74474,7 +74497,7 @@ router$9.post("/login/cancel", async (ctx) => {
74474
74497
  tv.interrupt();
74475
74498
  ctx.body = "success";
74476
74499
  });
74477
- router$9.get("/login/poll", async (ctx) => {
74500
+ router$a.get("/login/poll", async (ctx) => {
74478
74501
  const { id } = ctx.request.query;
74479
74502
  if (!id) {
74480
74503
  ctx.body = "id required";
@@ -74489,13 +74512,27 @@ router$9.get("/login/poll", async (ctx) => {
74489
74512
  }
74490
74513
  ctx.body = index$1.omit(loginInfo, ["client"]);
74491
74514
  });
74492
- router$9.post("/formatTitle", async (ctx) => {
74515
+ router$a.post("/formatTitle", async (ctx) => {
74493
74516
  const data = ctx.request.body;
74494
74517
  const template = (data.template || "");
74495
74518
  const title = index$1.formatTitle(data.options, template);
74496
74519
  ctx.body = title;
74497
74520
  });
74498
- router$9.post("/formatPartTitle", async (ctx) => {
74521
+ router$a.post("/formatSeasonTitle", async (ctx) => {
74522
+ const { template, mediaTitleTemplate } = ctx.request.body;
74523
+ const options = {
74524
+ title: "标题",
74525
+ username: "主播名",
74526
+ time: new Date().toISOString(),
74527
+ roomId: 123456,
74528
+ filename: "文件名",
74529
+ };
74530
+ ctx.body = index$1.formatSeasonTitle({
74531
+ ...options,
74532
+ mediaTitle: index$1.formatTitle(options, mediaTitleTemplate || options.title),
74533
+ }, template);
74534
+ });
74535
+ router$a.post("/formatPartTitle", async (ctx) => {
74499
74536
  const data = ctx.request.body;
74500
74537
  const template = (data.template || "");
74501
74538
  const title = index$1.formatPartTitle(data.options ?? {
@@ -74508,7 +74545,7 @@ router$9.post("/formatPartTitle", async (ctx) => {
74508
74545
  }, template);
74509
74546
  ctx.body = title;
74510
74547
  });
74511
- router$9.post("/formatDesc", async (ctx) => {
74548
+ router$a.post("/formatDesc", async (ctx) => {
74512
74549
  const data = ctx.request.body;
74513
74550
  const template = (data.template || "");
74514
74551
  const desc = index$1.formatDesc(data.options ?? {
@@ -74521,18 +74558,116 @@ router$9.post("/formatDesc", async (ctx) => {
74521
74558
  ctx.body = desc;
74522
74559
  });
74523
74560
  // 搜索联合投稿UP主
74524
- router$9.get("/searchStaff", async (ctx) => {
74561
+ router$a.get("/searchStaff", async (ctx) => {
74525
74562
  const { kw, uid } = ctx.request.query;
74526
74563
  const data = await index$1.biliApi.searchStaffUser(Number(uid), kw);
74527
74564
  ctx.body = data;
74528
74565
  });
74529
74566
  // 获取联合投稿剩余次数
74530
- router$9.get("/staffRemaining", async (ctx) => {
74567
+ router$a.get("/staffRemaining", async (ctx) => {
74531
74568
  const { uid } = ctx.request.query;
74532
74569
  const data = await index$1.biliApi.getStaffRemaining(Number(uid));
74533
74570
  ctx.body = data;
74534
74571
  });
74535
74572
 
74573
+ const router$9 = new Router$1({ prefix: "/douyu" });
74574
+ const sessions = new Map();
74575
+ const purgeExpiredSessions = () => {
74576
+ const now = Date.now();
74577
+ for (const [id, session] of sessions) {
74578
+ if (session.expiresAt <= now) {
74579
+ session.cancel();
74580
+ sessions.delete(id);
74581
+ }
74582
+ }
74583
+ };
74584
+ router$9.post("/login", async (ctx) => {
74585
+ purgeExpiredSessions();
74586
+ const session = await index$1.DouyuQrcodeLogin.create();
74587
+ const id = crypto$3.randomUUID();
74588
+ sessions.set(id, session);
74589
+ ctx.body = { id, url: session.url, expiresAt: session.expiresAt };
74590
+ });
74591
+ router$9.get("/login/poll", async (ctx) => {
74592
+ const id = String(ctx.query.id || "");
74593
+ if (!id) {
74594
+ ctx.status = 400;
74595
+ ctx.body = "id required";
74596
+ return;
74597
+ }
74598
+ const session = sessions.get(id);
74599
+ if (!session) {
74600
+ ctx.status = 400;
74601
+ ctx.body = "login info not found";
74602
+ return;
74603
+ }
74604
+ const result = await session.poll();
74605
+ if (result.status === "completed") {
74606
+ index$1.writeDouyuUser(result.user);
74607
+ sessions.delete(id);
74608
+ ctx.body = { status: "completed" };
74609
+ return;
74610
+ }
74611
+ if (result.status === "error")
74612
+ sessions.delete(id);
74613
+ ctx.body = result;
74614
+ });
74615
+ router$9.post("/login/cancel", async (ctx) => {
74616
+ const { id } = ctx.request.body;
74617
+ if (!id) {
74618
+ ctx.status = 400;
74619
+ ctx.body = "id required";
74620
+ return;
74621
+ }
74622
+ const session = sessions.get(id);
74623
+ if (!session) {
74624
+ ctx.status = 400;
74625
+ ctx.body = "login info not found";
74626
+ return;
74627
+ }
74628
+ session.cancel();
74629
+ sessions.delete(id);
74630
+ ctx.body = "success";
74631
+ });
74632
+ router$9.get("/user/list", (ctx) => {
74633
+ ctx.body = index$1.readDouyuUserList().map(({ uid, name, avatar, createdAt, updatedAt }) => ({
74634
+ uid,
74635
+ name,
74636
+ avatar,
74637
+ createdAt,
74638
+ updatedAt,
74639
+ }));
74640
+ });
74641
+ router$9.post("/user/update_auth", async (ctx) => {
74642
+ const uid = Number(ctx.request.body?.uid);
74643
+ if (!Number.isSafeInteger(uid) || uid <= 0) {
74644
+ ctx.status = 400;
74645
+ ctx.body = "valid uid required";
74646
+ return;
74647
+ }
74648
+ await index$1.refreshDouyuUser(uid);
74649
+ ctx.body = "success";
74650
+ });
74651
+ router$9.post("/user/validate", async (ctx) => {
74652
+ const uid = Number(ctx.request.body?.uid);
74653
+ if (!Number.isSafeInteger(uid) || uid <= 0) {
74654
+ ctx.status = 400;
74655
+ ctx.body = "valid uid required";
74656
+ return;
74657
+ }
74658
+ ctx.body = { valid: await index$1.validateDouyuUser(uid) };
74659
+ });
74660
+ router$9.post("/user/delete", (ctx) => {
74661
+ const uid = Number(ctx.request.body.uid);
74662
+ if (!Number.isSafeInteger(uid) || uid <= 0) {
74663
+ ctx.status = 400;
74664
+ ctx.body = "valid uid required";
74665
+ return;
74666
+ }
74667
+ index$1.deleteDouyuUser(uid);
74668
+ ctx.body = "success";
74669
+ });
74670
+
74536
74671
  async function flvRepair(input, output, opts) {
74537
74672
  // 如果不是flv,直接报错
74538
74673
  if (path$3.extname(input).toLowerCase() !== ".flv") {
@@ -77153,6 +77288,7 @@ class ConfigManager {
77153
77288
  const appConfigAll = this.getAppConfigAll();
77154
77289
  const roomSetting = appConfigAll.webhook?.rooms?.[roomId];
77155
77290
  const danmu = this.getRoomSetting("danmu", roomSetting) ?? false;
77291
+ const burnWithoutDanmu = this.getRoomSetting("burnWithoutDanmu", roomSetting) ?? false;
77156
77292
  const mergePart = this.getRoomSetting("autoPartMerge", roomSetting) ?? false;
77157
77293
  const minSize = this.getRoomSetting("minSize", roomSetting) ?? 10;
77158
77294
  const uploadPresetId = this.getRoomSetting("uploadPresetId", roomSetting) || "default";
@@ -77214,6 +77350,7 @@ class ConfigManager {
77214
77350
  const open = this.canRoomOpen(roomSetting, appConfigAll?.webhook?.blacklist, roomId);
77215
77351
  return {
77216
77352
  danmu,
77353
+ burnWithoutDanmu,
77217
77354
  mergePart,
77218
77355
  minSize,
77219
77356
  uploadPresetId,
@@ -78330,7 +78467,8 @@ class WebhookHandler {
78330
78467
  async processDanmuVideo(context, options, config, xmlFilePath) {
78331
78468
  try {
78332
78469
  // 验证弹幕文件
78333
- if (!(await index$1.fs.pathExists(xmlFilePath)) || (await index$1.isEmptyDanmu(xmlFilePath))) {
78470
+ const hasDanmu = (await index$1.fs.pathExists(xmlFilePath)) && !(await index$1.isEmptyDanmu(xmlFilePath));
78471
+ if (!hasDanmu && !config.burnWithoutDanmu) {
78334
78472
  context.part.recordStatus = "handled";
78335
78473
  context.part.uploadStatus = "pending";
78336
78474
  index$1.logObj.warn(`弹幕文件不存在或为空,跳过弹幕压制: ${xmlFilePath}`);
@@ -78355,6 +78493,7 @@ class WebhookHandler {
78355
78493
  danmaOptions: danmuConfig.config,
78356
78494
  ffmpegOptions: ffmpegPreset.config,
78357
78495
  hasHotProgress: config.hotProgress,
78496
+ ignoreDanmu: !hasDanmu,
78358
78497
  hotProgressOptions: {
78359
78498
  interval: config.hotProgressSample || 30,
78360
78499
  color: config.hotProgressColor || "#f9f5f3",
@@ -78367,7 +78506,7 @@ class WebhookHandler {
78367
78506
  });
78368
78507
  context.part.filePath = output;
78369
78508
  context.part.recordStatus = "handled";
78370
- return { conversionSuccessful: true, danmuConversionSuccessful: true };
78509
+ return { conversionSuccessful: true, danmuConversionSuccessful: hasDanmu };
78371
78510
  }
78372
78511
  catch (error) {
78373
78512
  index$1.logObj.error(error);
@@ -79275,7 +79414,6 @@ let handler;
79275
79414
  let appConfig;
79276
79415
  let container;
79277
79416
  const fileCache = createFileCache();
79278
- path$3.dirname(urlLib.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-B-Cevjk4.cjs', document.baseURI).href))));
79279
79417
  const authMiddleware = (passKey) => {
79280
79418
  return async (ctx, next) => {
79281
79419
  const authHeader = ctx.headers["authorization"] || ctx.request.query.auth;
@@ -79312,7 +79450,7 @@ app.use(errorMiddleware);
79312
79450
  app.use(cors$1());
79313
79451
  app.use(bodyParserWrapper());
79314
79452
  app.use(router.routes());
79315
- app.use(router$h.routes());
79453
+ app.use(router$i.routes());
79316
79454
  app.use(router$7.routes());
79317
79455
  async function serverStart(options, axContainer) {
79318
79456
  container = axContainer;
@@ -79324,11 +79462,12 @@ async function serverStart(options, axContainer) {
79324
79462
  const auth = authMiddleware(passKey);
79325
79463
  app.use(auth);
79326
79464
  }
79465
+ app.use(router$h.routes());
79327
79466
  app.use(router$g.routes());
79467
+ app.use(router$e.routes());
79328
79468
  app.use(router$f.routes());
79329
79469
  app.use(router$d.routes());
79330
- app.use(router$e.routes());
79331
- app.use(router$c.routes());
79470
+ app.use(router$b.routes());
79332
79471
  app.use(router$a.routes());
79333
79472
  app.use(router$9.routes());
79334
79473
  app.use(router$8.routes());
@@ -79338,7 +79477,7 @@ async function serverStart(options, axContainer) {
79338
79477
  app.use(router$3.routes());
79339
79478
  app.use(router$2.routes());
79340
79479
  app.use(router$1.routes());
79341
- app.use(router$b.routes());
79480
+ app.use(router$c.routes());
79342
79481
  app.use(router.allowedMethods());
79343
79482
  await createServer(options);
79344
79483
  return app;