bililive-cli 3.12.1 → 3.13.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.
@@ -4,7 +4,7 @@ var path$7 = require('node:path');
4
4
  var require$$2$1 = require('node:http');
5
5
  var require$$1$2 = require('node:url');
6
6
  var a = require('node:https');
7
- var index = require('./index-BeyiZ-FP.cjs');
7
+ var index = require('./index-Bw3nOSUH.cjs');
8
8
  var require$$0$4 = require('tty');
9
9
  var require$$1$3 = require('util');
10
10
  var require$$0$b = require('assert');
@@ -30477,8 +30477,9 @@ const errorMiddleware = async (ctx, next) => {
30477
30477
  await next();
30478
30478
  }
30479
30479
  catch (error) {
30480
+ const message = error instanceof Error ? error.message : String(error);
30480
30481
  ctx.status = 500;
30481
- ctx.body = error.message;
30482
+ ctx.body = message;
30482
30483
  ctx.app.emit("error", error, ctx);
30483
30484
  index.logObj.error(error);
30484
30485
  }
@@ -49131,6 +49132,50 @@ router$f.post("/set", async (ctx) => {
49131
49132
  exports.appConfig.set(data.key, data.value);
49132
49133
  ctx.body = "success";
49133
49134
  });
49135
+ router$f.post("/verifyBiliKey", async (ctx) => {
49136
+ try {
49137
+ const serverKey = process.env.BILILIVE_TOOLS_BILIKEY;
49138
+ const configured = typeof serverKey === "string" && serverKey.trim().length > 0;
49139
+ const requestBody = ctx.request.body;
49140
+ const rawInput = typeof requestBody?.key === "string" ? requestBody.key : "";
49141
+ const inputKey = rawInput.trim();
49142
+ let response;
49143
+ if (!configured) {
49144
+ response = {
49145
+ configured: false,
49146
+ valid: false,
49147
+ matched: false,
49148
+ reason: "missing",
49149
+ };
49150
+ }
49151
+ else if (inputKey === serverKey) {
49152
+ response = {
49153
+ configured: true,
49154
+ valid: true,
49155
+ matched: true,
49156
+ reason: "ok",
49157
+ };
49158
+ }
49159
+ else {
49160
+ response = {
49161
+ configured: true,
49162
+ valid: false,
49163
+ matched: false,
49164
+ reason: "mismatch",
49165
+ };
49166
+ }
49167
+ ctx.body = response;
49168
+ }
49169
+ catch {
49170
+ const response = {
49171
+ configured: false,
49172
+ valid: false,
49173
+ matched: false,
49174
+ reason: "error",
49175
+ };
49176
+ ctx.body = response;
49177
+ }
49178
+ });
49134
49179
  router$f.post("/resetBin", async (ctx) => {
49135
49180
  const data = ctx.request.body;
49136
49181
  const type = data.type;
@@ -55005,46 +55050,54 @@ const coerce$1 = (version, options) => {
55005
55050
  };
55006
55051
  var coerce_1 = coerce$1;
55007
55052
 
55008
- class LRUCache {
55009
- constructor () {
55010
- this.max = 1000;
55011
- this.map = new Map();
55012
- }
55053
+ var lrucache;
55054
+ var hasRequiredLrucache;
55013
55055
 
55014
- get (key) {
55015
- const value = this.map.get(key);
55016
- if (value === undefined) {
55017
- return undefined
55018
- } else {
55019
- // Remove the key from the map and add it to the end
55020
- this.map.delete(key);
55021
- this.map.set(key, value);
55022
- return value
55023
- }
55024
- }
55056
+ function requireLrucache () {
55057
+ if (hasRequiredLrucache) return lrucache;
55058
+ hasRequiredLrucache = 1;
55059
+ class LRUCache {
55060
+ constructor () {
55061
+ this.max = 1000;
55062
+ this.map = new Map();
55063
+ }
55025
55064
 
55026
- delete (key) {
55027
- return this.map.delete(key)
55028
- }
55065
+ get (key) {
55066
+ const value = this.map.get(key);
55067
+ if (value === undefined) {
55068
+ return undefined
55069
+ } else {
55070
+ // Remove the key from the map and add it to the end
55071
+ this.map.delete(key);
55072
+ this.map.set(key, value);
55073
+ return value
55074
+ }
55075
+ }
55029
55076
 
55030
- set (key, value) {
55031
- const deleted = this.delete(key);
55077
+ delete (key) {
55078
+ return this.map.delete(key)
55079
+ }
55032
55080
 
55033
- if (!deleted && value !== undefined) {
55034
- // If cache is full, delete the least recently used item
55035
- if (this.map.size >= this.max) {
55036
- const firstKey = this.map.keys().next().value;
55037
- this.delete(firstKey);
55038
- }
55081
+ set (key, value) {
55082
+ const deleted = this.delete(key);
55039
55083
 
55040
- this.map.set(key, value);
55041
- }
55084
+ if (!deleted && value !== undefined) {
55085
+ // If cache is full, delete the least recently used item
55086
+ if (this.map.size >= this.max) {
55087
+ const firstKey = this.map.keys().next().value;
55088
+ this.delete(firstKey);
55089
+ }
55042
55090
 
55043
- return this
55044
- }
55045
- }
55091
+ this.map.set(key, value);
55092
+ }
55046
55093
 
55047
- var lrucache = LRUCache;
55094
+ return this
55095
+ }
55096
+ }
55097
+
55098
+ lrucache = LRUCache;
55099
+ return lrucache;
55100
+ }
55048
55101
 
55049
55102
  var range;
55050
55103
  var hasRequiredRange;
@@ -55266,7 +55319,7 @@ function requireRange () {
55266
55319
 
55267
55320
  range = Range;
55268
55321
 
55269
- const LRU = lrucache;
55322
+ const LRU = requireLrucache();
55270
55323
  const cache = new LRU();
55271
55324
 
55272
55325
  const parseOptions = parseOptions_1;
@@ -56428,7 +56481,6 @@ router$d.get("/files", async (ctx) => {
56428
56481
  const allFiles = filterExts.length === 0 || filterExts.includes(".*");
56429
56482
  if (root == "/" && process.platform === "win32") {
56430
56483
  const drives = await getDriveLetters();
56431
- root = drives[0];
56432
56484
  ctx.body = {
56433
56485
  list: drives.map((drive) => ({ type: "directory", name: drive, path: `${drive}\\` })),
56434
56486
  parent: "",
@@ -56454,6 +56506,7 @@ router$d.get("/files", async (ctx) => {
56454
56506
  type: type,
56455
56507
  name: name,
56456
56508
  path: filePath,
56509
+ size: type === "file" ? fileStat.size : undefined,
56457
56510
  });
56458
56511
  }
56459
56512
  catch (error) {
@@ -57074,6 +57127,47 @@ router$c.post("/get_cookie", async (ctx) => {
57074
57127
  ctx.body = "获取失败,请重试";
57075
57128
  }
57076
57129
  });
57130
+ router$c.get("/export", async (ctx) => {
57131
+ const list = index.biliApi.readUserList();
57132
+ ctx.body = list;
57133
+ });
57134
+ router$c.post("/export_single", async (ctx) => {
57135
+ const { uid } = ctx.request.body;
57136
+ const user = index.biliApi.readUser(uid);
57137
+ if (!user) {
57138
+ ctx.status = 404;
57139
+ ctx.body = "用户不存在";
57140
+ return;
57141
+ }
57142
+ ctx.body = user;
57143
+ });
57144
+ router$c.post("/import", async (ctx) => {
57145
+ const { users } = ctx.request.body;
57146
+ if (!Array.isArray(users)) {
57147
+ ctx.status = 400;
57148
+ ctx.body = "参数错误";
57149
+ return;
57150
+ }
57151
+ for (const item of users) {
57152
+ if (!item?.mid || !item?.accessToken || !item?.refreshToken || !item?.cookie) {
57153
+ ctx.status = 400;
57154
+ ctx.body = "账号数据不完整";
57155
+ return;
57156
+ }
57157
+ await index.biliApi.writeUser(item);
57158
+ }
57159
+ ctx.status = 200;
57160
+ });
57161
+ router$c.post("/import_single", async (ctx) => {
57162
+ const { user } = ctx.request.body;
57163
+ if (!user?.mid || !user?.accessToken || !user?.refreshToken || !user?.cookie) {
57164
+ ctx.status = 400;
57165
+ ctx.body = "账号数据不完整";
57166
+ return;
57167
+ }
57168
+ await index.biliApi.writeUser(user);
57169
+ ctx.status = 200;
57170
+ });
57077
57171
 
57078
57172
  const router$b = new Router$1({
57079
57173
  prefix: "/preset",
@@ -76579,7 +76673,7 @@ const router$a = new Router$1({
76579
76673
  */
76580
76674
  router$a.get("/streamLogs", sse({
76581
76675
  maxClients: 5000,
76582
- pingInterval: 30000,
76676
+ pingInterval: 60 * 60 * 1000,
76583
76677
  }), async (ctx) => {
76584
76678
  const logFilePath = exports.config.logPath;
76585
76679
  // 初始化logSize为文件当前大小
@@ -78181,7 +78275,7 @@ async function parseVideo({ url, }) {
78181
78275
  }
78182
78276
  }
78183
78277
  async function downloadVideo(options) {
78184
- const filepath = path$7.join(options.savePath, options.filename);
78278
+ let filepath = path$7.join(options.savePath, options.filename);
78185
78279
  if (options.platform === "douyu") {
78186
78280
  if (!options?.extra?.decodeData) {
78187
78281
  throw new Error("decodeData is required for douyu download");
@@ -78241,6 +78335,7 @@ async function downloadVideo(options) {
78241
78335
  if (!options?.extra?.bvid) {
78242
78336
  throw new Error("bvid is required for bilibili download");
78243
78337
  }
78338
+ filepath = index.replaceExtName(filepath, ".mp4");
78244
78339
  await index.biliApi.download({
78245
78340
  bvid: options.extra.bvid,
78246
78341
  cid: Number(options.id),
@@ -78709,7 +78804,7 @@ async function uploadTest(params) {
78709
78804
  const tempDir = index.getTempPath();
78710
78805
  // 在临时文件新建一个文件,内容为"biliLive-tools"
78711
78806
  const tempFilePath = path$7.join(tempDir, "biliLive-tools-upload-test.txt");
78712
- await index.fs.writeFile(tempFilePath, "biliLive-tools");
78807
+ await index.fs.writeFile(tempFilePath, `biliLive-tools-${index.uuid()}`);
78713
78808
  return new Promise(async (resolve, reject) => {
78714
78809
  const task = await addSyncTask({
78715
78810
  input: tempFilePath,
@@ -79218,9 +79313,23 @@ class FileRefManager {
79218
79313
  */
79219
79314
  class ConfigManager {
79220
79315
  appConfig;
79316
+ static APP_CONFIG_CACHE_TTL = 30 * 1000;
79317
+ appConfigCache;
79221
79318
  constructor(appConfig) {
79222
79319
  this.appConfig = appConfig;
79223
79320
  }
79321
+ getAppConfigAll() {
79322
+ const now = Date.now();
79323
+ if (this.appConfigCache && this.appConfigCache.expiresAt > now) {
79324
+ return this.appConfigCache.value;
79325
+ }
79326
+ const value = this.appConfig.getAll();
79327
+ this.appConfigCache = {
79328
+ value,
79329
+ expiresAt: now + ConfigManager.APP_CONFIG_CACHE_TTL,
79330
+ };
79331
+ return value;
79332
+ }
79224
79333
  /**
79225
79334
  * 判断房间是否开启
79226
79335
  */
@@ -79245,7 +79354,7 @@ class ConfigManager {
79245
79354
  * @returns 房间配置对象
79246
79355
  */
79247
79356
  getConfig(roomId) {
79248
- const appConfigAll = this.appConfig.getAll();
79357
+ const appConfigAll = this.getAppConfigAll();
79249
79358
  const roomSetting = appConfigAll.webhook?.rooms?.[roomId];
79250
79359
  const danmu = this.getRoomSetting("danmu", roomSetting) ?? false;
79251
79360
  const mergePart = this.getRoomSetting("autoPartMerge", roomSetting) ?? false;
@@ -79271,9 +79380,7 @@ class ConfigManager {
79271
79380
  ];
79272
79381
  const syncId = this.getRoomSetting("syncId", roomSetting);
79273
79382
  const afterConvertAction = this.getRoomSetting("afterConvertAction", roomSetting) ?? [];
79274
- // TODO: 兼容废弃选项,过渡期后删除
79275
- const removeSourceAferrConvert2Mp4Before = this.getRoomSetting("removeSourceAferrConvert2Mp4", roomSetting);
79276
- const removeSourceAferrConvert2Mp4 = afterConvertAction.includes("removeAfterConvert2Mp4") || !!removeSourceAferrConvert2Mp4Before;
79383
+ const removeSourceAferrConvert2Mp4 = afterConvertAction.includes("removeAfterConvert2Mp4");
79277
79384
  const afterConvertRemoveVideoRaw = afterConvertAction.includes("removeVideo");
79278
79385
  const afterConvertRemoveXmlRaw = afterConvertAction.includes("removeXml");
79279
79386
  const afterConvertRemoveFlvRaw = afterConvertAction.includes("removeAfterFlvRepair");
@@ -79284,6 +79391,7 @@ class ConfigManager {
79284
79391
  "23:59:59",
79285
79392
  ];
79286
79393
  const uploadNoDanmu = this.getRoomSetting("uploadNoDanmu", roomSetting) ?? false;
79394
+ const uploadToSameMedia = this.getRoomSetting("uploadToSameMedia", roomSetting) ?? false;
79287
79395
  const noDanmuVideoPreset = this.getRoomSetting("noDanmuVideoPreset", roomSetting) || "default";
79288
79396
  // 如果没有开启断播续传,那么不需要合并part
79289
79397
  if (!mergePart)
@@ -79333,6 +79441,7 @@ class ConfigManager {
79333
79441
  limitUploadTime,
79334
79442
  uploadHandleTime,
79335
79443
  uploadNoDanmu,
79444
+ uploadToSameMedia,
79336
79445
  noDanmuVideoPreset,
79337
79446
  videoHandleTime: limitVideoConvertTime ? videoHandleTime : undefined,
79338
79447
  partTitleTemplate: this.getRoomSetting("partTitleTemplate", roomSetting) || "{{filename}}",
@@ -79353,7 +79462,7 @@ class ConfigManager {
79353
79462
  * @returns 配置值
79354
79463
  */
79355
79464
  getRoomSetting(key, roomSetting) {
79356
- const appConfigAll = this.appConfig.getAll();
79465
+ const appConfigAll = this.getAppConfigAll();
79357
79466
  if (roomSetting) {
79358
79467
  if (roomSetting.noGlobal?.includes(key)) {
79359
79468
  return roomSetting[key];
@@ -79373,7 +79482,7 @@ class ConfigManager {
79373
79482
  const { syncId } = this.getConfig(roomId);
79374
79483
  if (!syncId)
79375
79484
  return null;
79376
- const appConfig = this.appConfig.getAll();
79485
+ const appConfig = this.getAppConfigAll();
79377
79486
  const syncConfig = appConfig.sync?.syncConfigs?.find((cfg) => cfg.id === syncId);
79378
79487
  if (!syncConfig)
79379
79488
  return null;
@@ -80149,6 +80258,7 @@ class EventBufferManager extends EventEmitter$3.EventEmitter {
80149
80258
  }
80150
80259
  }
80151
80260
 
80261
+ const SAME_MEDIA_UPLOAD_ORDER = ["handled", "raw"];
80152
80262
  class WebhookHandler {
80153
80263
  liveManager = new LiveManager();
80154
80264
  ffmpegPreset;
@@ -80860,11 +80970,58 @@ class WebhookHandler {
80860
80970
  * 通用上传任务处理逻辑
80861
80971
  * @private
80862
80972
  */
80863
- async handleUploadTask(task, pathArray) {
80973
+ getRuntimePart(part) {
80974
+ return part;
80975
+ }
80976
+ setPartCid(part, type, cid) {
80977
+ if (!cid)
80978
+ return;
80979
+ const runtimePart = this.getRuntimePart(part);
80980
+ if (type === "handled") {
80981
+ runtimePart.handledCid = cid;
80982
+ }
80983
+ else {
80984
+ runtimePart.rawCid = cid;
80985
+ }
80986
+ }
80987
+ getPartCid(part, type) {
80988
+ const runtimePart = this.getRuntimePart(part);
80989
+ return type === "handled" ? runtimePart.handledCid : runtimePart.rawCid;
80990
+ }
80991
+ toPathArray(items) {
80992
+ return items.map((item) => ({
80993
+ path: item.path,
80994
+ title: item.title,
80995
+ }));
80996
+ }
80997
+ createUploadItems(filePaths, type) {
80998
+ return filePaths.map((item) => ({
80999
+ ...item,
81000
+ type,
81001
+ }));
81002
+ }
81003
+ writeUploadResultToParts(uploadedItems, data) {
81004
+ const itemQueue = new Map();
81005
+ for (const item of uploadedItems) {
81006
+ const queue = itemQueue.get(item.path) ?? [];
81007
+ queue.push(item);
81008
+ itemQueue.set(item.path, queue);
81009
+ }
81010
+ for (const result of data) {
81011
+ const queue = itemQueue.get(result.filePath);
81012
+ const item = queue?.shift();
81013
+ if (!item)
81014
+ continue;
81015
+ this.setPartCid(item.part, item.type, result.cid);
81016
+ }
81017
+ }
81018
+ async handleUploadTask(task, uploadedItems) {
80864
81019
  return new Promise((resolve, reject) => {
80865
- task.on("task-end", async () => {
81020
+ task.on("task-end", async (payload) => {
81021
+ const data = payload?.data ?? [];
81022
+ this.writeUploadResultToParts(uploadedItems, data);
80866
81023
  // 释放所有文件的引用
80867
- for (const { path } of pathArray) {
81024
+ for (const { path } of uploadedItems) {
80868
81025
  await this.fileRefManager.releaseRef(path);
80869
81026
  }
80870
81027
  resolve(task.output);
@@ -80893,25 +81050,31 @@ class WebhookHandler {
80893
81050
  }
80894
81051
  };
80895
81052
  }
80896
- addUploadTask = async (uid, pathArray, options, limitedUploadTime, afterUploadDeletAction) => {
81053
+ addUploadTask = async (uid, uploadedItems, options, limitedUploadTime, afterUploadDeletAction) => {
81054
+ const pathArray = this.toPathArray(uploadedItems);
80897
81055
  const checkCallback = this.setupDeleteAfterCheckLock(pathArray, afterUploadDeletAction);
81056
+ // console.log("upload", pathArray);
80898
81057
  const task = await index.biliApi.addMedia(pathArray, options, uid, {
80899
81058
  limitedUploadTime,
80900
81059
  afterUploadDeletAction: "none",
80901
81060
  forceCheck: afterUploadDeletAction === "deleteAfterCheck",
80902
81061
  checkCallback,
80903
81062
  });
80904
- return this.handleUploadTask(task, pathArray);
81063
+ return this.handleUploadTask(task, uploadedItems);
80905
81064
  };
80906
- addEditMediaTask = async (uid, aid, pathArray, uploadPreset, limitedUploadTime, afterUploadDeletAction) => {
81065
+ addEditMediaTask = async (uid, aid, uploadedItems, uploadPreset, limitedUploadTime, afterUploadDeletAction, sortParams) => {
81066
+ const pathArray = this.toPathArray(uploadedItems);
80907
81067
  const checkCallback = this.setupDeleteAfterCheckLock(pathArray, afterUploadDeletAction);
81068
+ // console.log("sortParams111111111", sortParams, pathArray);
81069
+ // 参数sortParams: array<{filePath:string;cid?:number}>,表示按照 filePath 对历史分P和当前上传文件统一排序
80908
81070
  const task = await index.biliApi.editMedia(aid, pathArray, uploadPreset, uid, {
80909
81071
  limitedUploadTime,
80910
81072
  afterUploadDeletAction: "none",
80911
81073
  forceCheck: afterUploadDeletAction === "deleteAfterCheck",
80912
81074
  checkCallback,
81075
+ sortParams,
80913
81076
  });
80914
- return this.handleUploadTask(task, pathArray);
81077
+ return this.handleUploadTask(task, uploadedItems);
80915
81078
  };
80916
81079
  /**
80917
81080
  * 构建上传文件列表
@@ -80945,6 +81108,110 @@ class WebhookHandler {
80945
81108
  }
80946
81109
  return { filePaths, cover };
80947
81110
  }
81111
+ isSameMediaUploadEnabled(config) {
81112
+ return config.uploadNoDanmu && config.uploadToSameMedia;
81113
+ }
81114
+ updateUploadStatusForItems(filePaths, status) {
81115
+ for (const item of filePaths) {
81116
+ item.part.updateUploadStatus(status, item.type === "raw");
81117
+ }
81118
+ }
81119
+ getSameMediaUploadCandidate(part, type, config) {
81120
+ if (type === "handled") {
81121
+ return {
81122
+ type,
81123
+ path: part.filePath,
81124
+ status: part.uploadStatus,
81125
+ canUpload: part.canUpload("handled"),
81126
+ enabled: true,
81127
+ };
81128
+ }
81129
+ return {
81130
+ type,
81131
+ path: part.rawFilePath,
81132
+ status: part.rawUploadStatus,
81133
+ canUpload: part.canUpload("raw"),
81134
+ enabled: config.uploadNoDanmu,
81135
+ };
81136
+ }
81137
+ buildSameMediaSortParams(live, config) {
81138
+ const sortParams = [];
81139
+ for (const type of SAME_MEDIA_UPLOAD_ORDER) {
81140
+ for (const part of live.parts) {
81141
+ const item = this.getSameMediaUploadCandidate(part, type, config);
81142
+ if (item.enabled === false || item.status === "error") {
81143
+ continue;
81144
+ }
81145
+ const cid = this.getPartCid(part, item.type);
81146
+ sortParams.push({
81147
+ filePath: item.path,
81148
+ cid,
81149
+ });
81150
+ if (item.status !== "uploaded" && !item.canUpload) {
81151
+ return sortParams;
81152
+ }
81153
+ }
81154
+ }
81155
+ return sortParams;
81156
+ }
81157
+ getUploadedItemCountForSameMedia(live, config) {
81158
+ let uploadedCount = 0;
81159
+ for (const part of live.parts) {
81160
+ if (part.uploadStatus === "uploaded") {
81161
+ uploadedCount++;
81162
+ }
81163
+ if (config.uploadNoDanmu && part.rawUploadStatus === "uploaded") {
81164
+ uploadedCount++;
81165
+ }
81166
+ }
81167
+ return uploadedCount;
81168
+ }
81169
+ buildSameMediaUploadFileList(live, config) {
81170
+ let cover;
81171
+ const filePaths = [];
81172
+ let currentIndex = this.getUploadedItemCountForSameMedia(live, config) + 1;
81173
+ for (const type of SAME_MEDIA_UPLOAD_ORDER) {
81174
+ for (const part of live.parts) {
81175
+ const item = this.getSameMediaUploadCandidate(part, type, config);
81176
+ if (item.enabled === false)
81177
+ continue;
81178
+ if (item.status === "uploaded" || item.status === "error")
81179
+ continue;
81180
+ if (!item.canUpload)
81181
+ continue;
81182
+ const filename = path$7.parse(item.path).name;
81183
+ const baseTitle = index.formatPartTitle({
81184
+ title: part.title,
81185
+ username: live.username,
81186
+ roomId: live.roomId,
81187
+ time: part?.startTime
81188
+ ? new Date(part.startTime).toISOString()
81189
+ : new Date().toISOString(),
81190
+ filename,
81191
+ index: currentIndex,
81192
+ }, config.partTitleTemplate ?? "{{filename}}");
81193
+ filePaths.push({
81194
+ part,
81195
+ path: item.path,
81196
+ title: baseTitle,
81197
+ type: item.type,
81198
+ });
81199
+ if (!cover) {
81200
+ cover = part.cover;
81201
+ }
81202
+ currentIndex++;
81203
+ }
81204
+ }
81205
+ return { filePaths, cover };
81206
+ }
81207
+ validateCombinedUploadConfig(filePaths, config) {
81208
+ if (!config.uid) {
81209
+ this.updateUploadStatusForItems(filePaths, "error");
81210
+ index.logObj.error("无须上传,uid未配置");
81211
+ return false;
81212
+ }
81213
+ return true;
81214
+ }
80948
81215
  /**
80949
81216
  * 验证上传配置
80950
81217
  * @private
@@ -80990,6 +81257,9 @@ class WebhookHandler {
80990
81257
  }
80991
81258
  return uploadPreset;
80992
81259
  }
81260
+ async prepareSharedUploadPreset(config, live, cover) {
81261
+ return this.prepareUploadPreset("handled", config, live, cover);
81262
+ }
80993
81263
  /**
80994
81264
  * 格式化上传视频标题
80995
81265
  * @private
@@ -81031,11 +81301,9 @@ class WebhookHandler {
81031
81301
  */
81032
81302
  async performContinueUpload(live, aid, filePaths, type, config, uploadPreset, limitedUploadTime) {
81033
81303
  index.logObj.info("续传", filePaths);
81304
+ const uploadedItems = this.createUploadItems(filePaths, type);
81034
81305
  live.batchUpdateUploadStatus(filePaths.map((item) => item.part), "uploading", type);
81035
- await this.addEditMediaTask(config.uid, aid, filePaths.map((item) => ({
81036
- path: item.path,
81037
- title: item.title,
81038
- })), uploadPreset, limitedUploadTime, config.afterUploadDeletAction);
81306
+ await this.addEditMediaTask(config.uid, aid, uploadedItems, uploadPreset, limitedUploadTime, config.afterUploadDeletAction);
81039
81307
  live.batchUpdateUploadStatus(filePaths.map((item) => item.part), "uploaded", type);
81040
81308
  }
81041
81309
  /**
@@ -81044,17 +81312,61 @@ class WebhookHandler {
81044
81312
  */
81045
81313
  async performNewUpload(live, filePaths, type, config, uploadPreset, limitedUploadTime) {
81046
81314
  const aidField = type === "handled" ? "aid" : "rawAid";
81315
+ const uploadedItems = this.createUploadItems(filePaths, type);
81047
81316
  live.batchUpdateUploadStatus(filePaths.map((item) => item.part), "uploading", type);
81048
81317
  // 格式化标题
81049
81318
  const videoTitle = this.formatUploadTitle(live, filePaths[0].part, type, uploadPreset, config);
81050
81319
  uploadPreset.title = videoTitle;
81051
- const aid = (await this.addUploadTask(config.uid, filePaths.map((item) => ({
81052
- path: item.path,
81053
- title: item.title,
81054
- })), uploadPreset, limitedUploadTime, config.afterUploadDeletAction));
81320
+ const aid = (await this.addUploadTask(config.uid, uploadedItems, uploadPreset, limitedUploadTime, config.afterUploadDeletAction));
81055
81321
  live[aidField] = Number(aid);
81056
81322
  live.batchUpdateUploadStatus(filePaths.map((item) => item.part), "uploaded", type);
81057
81323
  }
81324
+ async performContinueUploadForSameMedia(live, aid, filePaths, config, uploadPreset, limitedUploadTime) {
81325
+ // log.info("同稿件续传", filePaths);
81326
+ const sortParams = this.buildSameMediaSortParams(live, config);
81327
+ live.aid = aid;
81328
+ this.updateUploadStatusForItems(filePaths, "uploading");
81329
+ await this.addEditMediaTask(config.uid, aid, filePaths, uploadPreset, limitedUploadTime, config.afterUploadDeletAction, sortParams);
81330
+ this.updateUploadStatusForItems(filePaths, "uploaded");
81331
+ }
81332
+ async performNewUploadForSameMedia(live, filePaths, config, uploadPreset, limitedUploadTime) {
81333
+ this.updateUploadStatusForItems(filePaths, "uploading");
81334
+ const videoTitle = this.formatUploadTitle(live, filePaths[0].part, "handled", uploadPreset, config);
81335
+ uploadPreset.title = videoTitle;
81336
+ const aid = (await this.addUploadTask(config.uid, filePaths, uploadPreset, limitedUploadTime, config.afterUploadDeletAction));
81337
+ live.aid = Number(aid);
81338
+ this.updateUploadStatusForItems(filePaths, "uploaded");
81339
+ }
81340
+ async uploadVideoToSameMedia(live, config) {
81341
+ if (live.hasUploadingParts("handled") || live.hasUploadingParts("raw")) {
81342
+ return;
81343
+ }
81344
+ const { filePaths, cover } = this.buildSameMediaUploadFileList(live, config);
81345
+ // console.log("同稿件上传候选列表", filePaths);
81346
+ if (filePaths.length === 0) {
81347
+ return;
81348
+ }
81349
+ if (!this.validateCombinedUploadConfig(filePaths, config)) {
81350
+ return;
81351
+ }
81352
+ const uploadPreset = await this.prepareSharedUploadPreset(config, live, cover);
81353
+ const limitedUploadTime = config.limitUploadTime
81354
+ ? config.uploadHandleTime
81355
+ : [];
81356
+ const sharedAid = live.aid ?? live.rawAid;
81357
+ try {
81358
+ if (sharedAid) {
81359
+ await this.performContinueUploadForSameMedia(live, sharedAid, filePaths, config, uploadPreset, limitedUploadTime);
81360
+ }
81361
+ else {
81362
+ await this.performNewUploadForSameMedia(live, filePaths, config, uploadPreset, limitedUploadTime);
81363
+ }
81364
+ }
81365
+ catch (error) {
81366
+ index.logObj.error(error);
81367
+ this.updateUploadStatusForItems(filePaths, "error");
81368
+ }
81369
+ }
81058
81370
  /**
81059
81371
  * 上传单个类型的视频(弹幕版或原始版)
81060
81372
  * @private
@@ -81100,9 +81412,14 @@ class WebhookHandler {
81100
81412
  /**
81101
81413
  * 处理直播上传
81102
81414
  * @param live 直播数据
81103
- * @param type 上传类型:handled-弹幕版,raw-原始版,undefined-两者都上传
81415
+ * @param type 上传类型:handled-弹幕版,raw-原始版
81104
81416
  */
81105
81417
  handleLive = async (live, type) => {
81418
+ const config = this.configManager.getConfig(live.roomId);
81419
+ if (this.isSameMediaUploadEnabled(config)) {
81420
+ await this.uploadVideoToSameMedia(live, config);
81421
+ return;
81422
+ }
81106
81423
  if (type) {
81107
81424
  if (type === "handled") {
81108
81425
  await this.uploadVideoByType(live, "handled");
@@ -81156,7 +81473,7 @@ exports.handler = void 0;
81156
81473
  exports.appConfig = void 0;
81157
81474
  exports.container = void 0;
81158
81475
  const fileCache = createFileCache();
81159
- path$7.dirname(require$$1$2.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-XgWTq_HC.cjs', document.baseURI).href))));
81476
+ path$7.dirname(require$$1$2.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-BeK_zfM-.cjs', document.baseURI).href))));
81160
81477
  const authMiddleware = (passKey) => {
81161
81478
  return async (ctx, next) => {
81162
81479
  const authHeader = ctx.headers["authorization"] || ctx.request.query.auth;