karin-plugin-kkk 2.20.0 → 2.20.2

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,6 +1,6 @@
1
1
  import { n as __esmMin, o as __toESM, r as __export } from "./rolldown-runtime-BMXAG3ag.js";
2
- import { E as zhCN, T as init_locale, _n as init_zod, a as init_lib, cn as require_express, dn as init_source, et as init_date_fns, fn as AxiosError$1, gn as init_esm, hn as Xhshow, i as require_qr_code_styling, it as differenceInSeconds, ln as require_protobufjs, mn as axios_default, n as require_dist, nt as formatDistanceToNow, o as Window, on as require_png, pn as init_axios, r as require_lib, rt as format, sn as require_jsQR, t as require_heic_convert, tt as fromUnixTime, un as Chalk, vn as zod_default } from "./vendor-CZcAPGm1.js";
3
- import { n as init_client, r as reactServerRender } from "./template-BMkKJo_s.js";
2
+ import { E as zhCN, T as init_locale, _n as zod_default, a as init_lib, cn as require_protobufjs, dn as AxiosError$1, et as init_date_fns, fn as init_axios, gn as init_zod, hn as init_esm, i as require_qr_code_styling, it as differenceInSeconds, ln as Chalk, mn as Xhshow, n as require_dist, nt as formatDistanceToNow, o as Window, on as require_jsQR, pn as axios_default, r as require_lib, rt as format, sn as require_express, t as require_heic_convert, tt as fromUnixTime, un as init_source } from "./vendor-B3KhGHI_.js";
3
+ import { n as init_client, r as reactServerRender } from "./template-CQ-u6h09.js";
4
4
  import { createRequire } from "node:module";
5
5
  import karin$1, { BOT_CONNECT, app, authMiddleware, checkPkgUpdate, checkPort, common, components, config, copyConfigSync, createBadRequestResponse, createNotFoundResponse, createServerErrorResponse, createSuccessResponse, db, defineConfig, ffmpeg, ffprobe, filesByExt, getBot, hooks, karin, karinPathHtml, karinPathTemp, logger, logs, mkdirSync, range, render, requireFileSync, restart, segment, updatePkg, watch } from "node-karin";
6
6
  import fs from "node:fs";
@@ -13,6 +13,7 @@ import { karinPathBase, karinPathTemp as karinPathTemp$1 } from "node-karin/root
13
13
  import sqlite3 from "node-karin/sqlite3";
14
14
  import YAML from "node-karin/yaml";
15
15
  import util from "node:util";
16
+ import { Transformer } from "@napi-rs/image";
16
17
  import axios, { AxiosError } from "node-karin/axios";
17
18
  import { pipeline } from "node:stream/promises";
18
19
  import { Transform } from "node:stream";
@@ -6148,7 +6149,7 @@ var init_Config = __esmMin(() => {
6148
6149
  return getConfigInstance()[prop];
6149
6150
  } });
6150
6151
  });
6151
- var Base, statBotId$1, Count, uploadFile, downloadVideo, downloadFile, processFilename;
6152
+ var Base, Count, uploadFile, downloadVideo, downloadFile, processFilename;
6152
6153
  var init_Base = __esmMin(() => {
6153
6154
  init_utils$1();
6154
6155
  init_Config();
@@ -6162,50 +6163,6 @@ var init_Base = __esmMin(() => {
6162
6163
  this.headers = BASE_HEADERS;
6163
6164
  }
6164
6165
  };
6165
- statBotId$1 = (pushList) => {
6166
- const platformBotCount = {
6167
- douyin: /* @__PURE__ */ new Map(),
6168
- bilibili: /* @__PURE__ */ new Map()
6169
- };
6170
- pushList.douyin.forEach((item) => {
6171
- item.group_id.forEach((gid) => {
6172
- const botId = gid.split(":")[1];
6173
- platformBotCount.douyin.set(botId, (platformBotCount.douyin.get(botId) ?? 0) + 1);
6174
- });
6175
- });
6176
- pushList.bilibili.forEach((item) => {
6177
- item.group_id.forEach((gid) => {
6178
- const botId = gid.split(":")[1];
6179
- platformBotCount.bilibili.set(botId, (platformBotCount.bilibili.get(botId) ?? 0) + 1);
6180
- });
6181
- });
6182
- let douyinMaxCount = 0;
6183
- let douyinMostFrequentBot = "";
6184
- platformBotCount.douyin.forEach((count, botId) => {
6185
- if (count > douyinMaxCount) {
6186
- douyinMaxCount = count;
6187
- douyinMostFrequentBot = botId;
6188
- }
6189
- });
6190
- let biliMaxCount = 0;
6191
- let biliMostFrequentBot = "";
6192
- platformBotCount.bilibili.forEach((count, botId) => {
6193
- if (count > biliMaxCount) {
6194
- biliMaxCount = count;
6195
- biliMostFrequentBot = botId;
6196
- }
6197
- });
6198
- return {
6199
- douyin: {
6200
- botId: douyinMostFrequentBot,
6201
- count: douyinMaxCount
6202
- },
6203
- bilibili: {
6204
- botId: biliMostFrequentBot,
6205
- count: biliMaxCount
6206
- }
6207
- };
6208
- };
6209
6166
  Count = (count) => {
6210
6167
  if (count >= 1e8) return (count / 1e8).toFixed(1) + "亿";
6211
6168
  else if (count >= 1e4) return (count / 1e4).toFixed(1) + "万";
@@ -6386,16 +6343,15 @@ var init_build_metadata = __esmMin(() => {
6386
6343
  return `${date.getFullYear()}年${String(date.getMonth() + 1).padStart(2, "0")}月${String(date.getDate()).padStart(2, "0")}日 ${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
6387
6344
  };
6388
6345
  });
6389
- var import_jsQR$1, import_png$1, QRCodeScanner;
6346
+ var import_jsQR, QRCodeScanner;
6390
6347
  var init_QRCodeScanner = __esmMin(() => {
6391
- import_jsQR$1 = __toESM(require_jsQR(), 1);
6392
- import_png$1 = require_png();
6348
+ import_jsQR = __toESM(require_jsQR(), 1);
6393
6349
  QRCodeScanner = class {
6394
6350
  static async scanFromUrl(imageUrl) {
6395
6351
  try {
6396
6352
  const response = await axios.get(imageUrl, { responseType: "arraybuffer" });
6397
6353
  const buffer = Buffer.from(response.data);
6398
- return await this.scanFromBuffer(buffer);
6354
+ return this.scanFromBuffer(buffer);
6399
6355
  } catch (error) {
6400
6356
  logger.error("识别二维码时发生错误:", error);
6401
6357
  return null;
@@ -6472,10 +6428,10 @@ var init_QRCodeScanner = __esmMin(() => {
6472
6428
  ]) try {
6473
6429
  logger.debug(` 尝试策略: ${strategy.name}`);
6474
6430
  const processedData = strategy.enhance ? this.enhanceContrast(imageData) : imageData;
6475
- const code = (0, import_jsQR$1.default)(processedData.data, processedData.width, processedData.height, strategy.options);
6431
+ const code = (0, import_jsQR.default)(processedData.data, processedData.width, processedData.height, strategy.options);
6476
6432
  if (code && code.data) {
6477
- logger.mark(`✓ 成功识别二维码 [区域: ${regionName}] [策略: ${strategy.name}]`);
6478
- logger.mark(` 二维码内容: ${code.data}`);
6433
+ logger.debug(`✓ 成功识别二维码 [区域: ${regionName}] [策略: ${strategy.name}]`);
6434
+ logger.debug(` 二维码内容: ${code.data}`);
6479
6435
  return code.data;
6480
6436
  } else logger.debug(` 策略 ${strategy.name} 未识别到二维码`);
6481
6437
  } catch (err) {
@@ -6484,81 +6440,120 @@ var init_QRCodeScanner = __esmMin(() => {
6484
6440
  logger.debug(` 区域 ${regionName} 识别失败,尝试下一个区域`);
6485
6441
  return null;
6486
6442
  }
6487
- static async scanFromBuffer(buffer) {
6443
+ static parseImageBuffer(buffer) {
6488
6444
  try {
6489
- let imageData;
6490
- try {
6491
- const png = import_png$1.PNG.sync.read(buffer);
6492
- imageData = {
6493
- width: png.width,
6494
- height: png.height,
6495
- data: Uint8ClampedArray.from(png.data)
6445
+ const transformer = new Transformer(buffer);
6446
+ const metadata = transformer.metadataSync();
6447
+ const width = metadata.width;
6448
+ const height = metadata.height;
6449
+ const rawPixels = transformer.rawPixelsSync();
6450
+ logger.debug(`图片解析成功: ${width}x${height}, 格式: ${metadata.format}`);
6451
+ logger.debug(`原始像素数据长度: ${rawPixels.length}, 预期长度(RGBA): ${width * height * 4}`);
6452
+ const expectedRGBA = width * height * 4;
6453
+ const expectedRGB = width * height * 3;
6454
+ if (rawPixels.length === expectedRGB) {
6455
+ logger.debug("检测到 RGB 格式,转换为 RGBA");
6456
+ const rgbaData = new Uint8ClampedArray(expectedRGBA);
6457
+ for (let i = 0, j = 0; i < rawPixels.length; i += 3, j += 4) {
6458
+ rgbaData[j] = rawPixels[i];
6459
+ rgbaData[j + 1] = rawPixels[i + 1];
6460
+ rgbaData[j + 2] = rawPixels[i + 2];
6461
+ rgbaData[j + 3] = 255;
6462
+ }
6463
+ return {
6464
+ width,
6465
+ height,
6466
+ data: rgbaData
6467
+ };
6468
+ } else if (rawPixels.length === expectedRGBA) {
6469
+ logger.debug("检测到 RGBA 格式");
6470
+ return {
6471
+ width,
6472
+ height,
6473
+ data: Uint8ClampedArray.from(rawPixels)
6496
6474
  };
6497
- logger.mark(`图片解析成功: ${png.width}x${png.height}, 数据大小: ${(png.data.length / 1024 / 1024).toFixed(2)}MB`);
6498
- } catch (err) {
6499
- logger.warn("图片格式不支持,目前仅支持 PNG 格式的二维码识别", err);
6475
+ } else {
6476
+ logger.warn(`像素数据长度不匹配: ${rawPixels.length}, 预期 RGB: ${expectedRGB}, RGBA: ${expectedRGBA}`);
6500
6477
  return null;
6501
6478
  }
6479
+ } catch (err) {
6480
+ logger.warn("图片解析失败:", err);
6481
+ return null;
6482
+ }
6483
+ }
6484
+ static scanFromBuffer(buffer) {
6485
+ try {
6486
+ const imageData = this.parseImageBuffer(buffer);
6487
+ if (!imageData) return null;
6502
6488
  const { width, height } = imageData;
6489
+ const dataSizeMB = (width * height * 4 / 1024 / 1024).toFixed(2);
6490
+ logger.debug(`图片数据: ${width}x${height}, 内存占用: ${dataSizeMB}MB`);
6503
6491
  if (width <= 2048 && height <= 2048) {
6504
6492
  logger.debug("图片尺寸适中,使用全图识别策略");
6505
6493
  const result = this.tryRecognizeInRegion(imageData, "全图");
6506
6494
  if (result) return result;
6507
6495
  }
6508
- logger.mark(`图片尺寸较大 (${width}x${height}),使用分块扫描策略`);
6496
+ logger.debug(`图片尺寸较大 (${width}x${height}),使用分块扫描策略`);
6509
6497
  const scanRegions = [];
6510
6498
  const blockSize = 1024;
6511
- logger.debug("添加顶部扫描区域");
6499
+ logger.debug("添加四角扫描区域");
6512
6500
  scanRegions.push({
6513
- name: "顶部左",
6501
+ name: "左上角",
6514
6502
  x: 0,
6515
6503
  y: 0,
6516
6504
  w: Math.min(blockSize, width),
6517
6505
  h: Math.min(blockSize, height)
6518
6506
  });
6519
6507
  if (width > blockSize) scanRegions.push({
6520
- name: "顶部右",
6521
- x: width - blockSize,
6508
+ name: "右上角",
6509
+ x: width - Math.min(blockSize, width),
6522
6510
  y: 0,
6523
- w: blockSize,
6511
+ w: Math.min(blockSize, width),
6524
6512
  h: Math.min(blockSize, height)
6525
6513
  });
6526
- if (width > blockSize * 2) scanRegions.push({
6527
- name: "顶部中",
6528
- x: Math.floor((width - blockSize) / 2),
6529
- y: 0,
6530
- w: blockSize,
6514
+ if (height > blockSize) scanRegions.push({
6515
+ name: "左下角",
6516
+ x: 0,
6517
+ y: height - Math.min(blockSize, height),
6518
+ w: Math.min(blockSize, width),
6531
6519
  h: Math.min(blockSize, height)
6532
6520
  });
6533
- if (height > blockSize) {
6534
- logger.debug("添加底部扫描区域");
6521
+ if (width > blockSize && height > blockSize) scanRegions.push({
6522
+ name: "右下角",
6523
+ x: width - Math.min(blockSize, width),
6524
+ y: height - Math.min(blockSize, height),
6525
+ w: Math.min(blockSize, width),
6526
+ h: Math.min(blockSize, height)
6527
+ });
6528
+ if (width > blockSize * 2) {
6529
+ logger.debug("添加顶部/底部中间扫描区域");
6535
6530
  scanRegions.push({
6536
- name: "底部左",
6537
- x: 0,
6538
- y: height - blockSize,
6539
- w: Math.min(blockSize, width),
6540
- h: blockSize
6531
+ name: "顶部中",
6532
+ x: Math.floor((width - blockSize) / 2),
6533
+ y: 0,
6534
+ w: blockSize,
6535
+ h: Math.min(blockSize, height)
6541
6536
  });
6542
- if (width > blockSize) scanRegions.push({
6543
- name: "底部右",
6544
- x: width - blockSize,
6537
+ if (height > blockSize) scanRegions.push({
6538
+ name: "底部中",
6539
+ x: Math.floor((width - blockSize) / 2),
6545
6540
  y: height - blockSize,
6546
6541
  w: blockSize,
6547
6542
  h: blockSize
6548
6543
  });
6549
6544
  }
6550
6545
  if (height > blockSize * 2) {
6551
- logger.debug("添加中部扫描区域");
6546
+ logger.debug("添加左右中间扫描区域");
6552
6547
  const middleY = Math.floor((height - blockSize) / 2);
6553
6548
  scanRegions.push({
6554
- name: "中部左",
6549
+ name: "左中",
6555
6550
  x: 0,
6556
6551
  y: middleY,
6557
6552
  w: Math.min(blockSize, width),
6558
6553
  h: blockSize
6559
6554
  });
6560
6555
  if (width > blockSize) scanRegions.push({
6561
- name: "中部右",
6556
+ name: "右中",
6562
6557
  x: width - blockSize,
6563
6558
  y: middleY,
6564
6559
  w: blockSize,
@@ -6582,14 +6577,14 @@ var init_QRCodeScanner = __esmMin(() => {
6582
6577
  break;
6583
6578
  }
6584
6579
  }
6585
- logger.mark(`共生成 ${scanRegions.length} 个扫描区域,开始逐个扫描`);
6580
+ logger.debug(`共生成 ${scanRegions.length} 个扫描区域,开始逐个扫描`);
6586
6581
  for (let i = 0; i < scanRegions.length; i++) {
6587
6582
  const region = scanRegions[i];
6588
6583
  logger.debug(`[${i + 1}/${scanRegions.length}] 扫描区域: ${region.name} (位置: ${region.x},${region.y}, 尺寸: ${region.w}x${region.h})`);
6589
6584
  const regionData = this.extractRegion(imageData, region.x, region.y, region.w, region.h);
6590
6585
  const result = this.tryRecognizeInRegion(regionData, region.name);
6591
6586
  if (result) {
6592
- logger.mark(`二维码识别完成,共扫描了 ${i + 1}/${scanRegions.length} 个区域`);
6587
+ logger.debug(`二维码识别完成,共扫描了 ${i + 1}/${scanRegions.length} 个区域`);
6593
6588
  return result;
6594
6589
  }
6595
6590
  }
@@ -6771,6 +6766,9 @@ var init_Common = __esmMin(async () => {
6771
6766
  };
6772
6767
  Common = new Tools();
6773
6768
  });
6769
+ var init_EmojiReaction = __esmMin(() => {
6770
+ init_Config();
6771
+ });
6774
6772
  async function detectEncoder$1(codec) {
6775
6773
  if (cachedEncoders$1[codec]) return cachedEncoders$1[codec];
6776
6774
  logger.debug(`[BiliDanmaku] 开始检测 ${codec.toUpperCase()} 编码器...`);
@@ -7474,6 +7472,16 @@ async function getMediaDuration(path$1) {
7474
7472
  const { stdout } = await ffprobe(`-v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "${path$1}"`);
7475
7473
  return parseFloat(parseFloat(stdout.trim()).toFixed(2));
7476
7474
  }
7475
+ async function loopVideo(inputPath, outputPath, loopCount) {
7476
+ if (loopCount <= 1) {
7477
+ fs.copyFileSync(inputPath, outputPath);
7478
+ return true;
7479
+ }
7480
+ const result = await ffmpeg(`-y -stream_loop ${loopCount - 1} -i "${inputPath}" -c copy "${outputPath}"`);
7481
+ if (result.status) logger.mark(`视频重放成功: ${outputPath}`);
7482
+ else logger.error("视频重放失败", result);
7483
+ return result.status;
7484
+ }
7477
7485
  async function mergeVideoAudio(videoPath, audioPath, resultPath) {
7478
7486
  const result = await ffmpeg(`-y -i "${videoPath}" -i "${audioPath}" -c copy "${resultPath}"`);
7479
7487
  if (result.status) logger.mark(`视频合成成功: ${resultPath}`);
@@ -7527,38 +7535,6 @@ async function mergeLiveImageContinuous(options, context) {
7527
7535
  }
7528
7536
  };
7529
7537
  }
7530
- async function batchMergeLiveImages(videos, bgmPath, outputDir, mode = "independent", loopCount = 3) {
7531
- const results = [];
7532
- if (mode === "independent") for (const video of videos) {
7533
- const outputPath = `${outputDir}${video.outputName}`;
7534
- const success = await mergeLiveImageIndependent({
7535
- videoPath: video.path,
7536
- outputPath,
7537
- loopCount
7538
- }, bgmPath);
7539
- results.push({
7540
- success,
7541
- outputPath
7542
- });
7543
- }
7544
- else {
7545
- let context = await createLiveImageContext(bgmPath);
7546
- for (const video of videos) {
7547
- const outputPath = `${outputDir}${video.outputName}`;
7548
- const result = await mergeLiveImageContinuous({
7549
- videoPath: video.path,
7550
- outputPath,
7551
- loopCount
7552
- }, context);
7553
- context = result.context;
7554
- results.push({
7555
- success: result.success,
7556
- outputPath
7557
- });
7558
- }
7559
- }
7560
- return results;
7561
- }
7562
7538
  var init_FFmpeg = __esmMin(async () => {
7563
7539
  await init_utils$1();
7564
7540
  await init_danmaku$1();
@@ -8103,12 +8079,12 @@ var init_Network$1 = __esmMin(() => {
8103
8079
  };
8104
8080
  });
8105
8081
  var init_Network = __esmMin(() => {
8106
- init_constants();
8107
8082
  init_Downloader();
8108
8083
  init_helpers();
8109
8084
  init_Network$1();
8110
8085
  init_ThrottleStream();
8111
8086
  init_types();
8087
+ init_constants();
8112
8088
  });
8113
8089
  var init_Networks = __esmMin(() => {
8114
8090
  init_Network();
@@ -8293,48 +8269,12 @@ var init_Render = __esmMin(async () => {
8293
8269
  return ret;
8294
8270
  };
8295
8271
  });
8296
- var utils_exports = __export({
8297
- BASE_HEADERS: () => BASE_HEADERS,
8298
- Base: () => Base,
8299
- Common: () => Common,
8300
- Count: () => Count,
8301
- DEFAULT_THROTTLE_CONFIG: () => DEFAULT_THROTTLE_CONFIG,
8302
- Downloader: () => Downloader,
8303
- Network: () => Network,
8304
- Networks: () => Network,
8305
- QRCodeScanner: () => QRCodeScanner,
8306
- Render: () => Render,
8307
- Root: () => Root,
8308
- ThrottleStream: () => ThrottleStream,
8309
- baseHeaders: () => BASE_HEADERS,
8310
- batchMergeLiveImages: () => batchMergeLiveImages,
8311
- burnDanmakuToVideo: () => burnBiliDanmaku,
8312
- burnDouyinDanmaku: () => burnDouyinDanmaku,
8313
- compressVideo: () => compressVideo,
8314
- createLiveImageContext: () => createLiveImageContext,
8315
- danmakuToASS: () => generateBiliASS,
8316
- downloadFile: () => downloadFile,
8317
- downloadVideo: () => downloadVideo,
8318
- formatBuildTime: () => formatBuildTime,
8319
- formatBytes: () => formatBytes,
8320
- getBuildMetadata: () => getBuildMetadata,
8321
- getErrorDescription: () => getErrorDescription,
8322
- getMediaDuration: () => getMediaDuration,
8323
- isRecoverableNetworkError: () => isRecoverableNetworkError,
8324
- isThrottlingError: () => isThrottlingError,
8325
- mergeAndBurnDanmaku: () => mergeAndBurnBili,
8326
- mergeLiveImageContinuous: () => mergeLiveImageContinuous,
8327
- mergeLiveImageIndependent: () => mergeLiveImageIndependent,
8328
- mergeVideoAudio: () => mergeVideoAudio,
8329
- sanitizeHeaders: () => sanitizeHeaders,
8330
- statBotId: () => statBotId$1,
8331
- uploadFile: () => uploadFile
8332
- }, 1);
8333
8272
  var init_utils$1 = __esmMin(async () => {
8334
8273
  init_root();
8335
8274
  init_Base();
8336
8275
  init_build_metadata();
8337
8276
  init_Common();
8277
+ init_EmojiReaction();
8338
8278
  init_FFmpeg();
8339
8279
  init_Networks();
8340
8280
  init_QRCodeScanner();
@@ -8502,6 +8442,14 @@ var init_bilibili = __esmMin(async () => {
8502
8442
  async unsubscribeBilibiliUser(groupId, host_mid) {
8503
8443
  const result = await this.runQuery("DELETE FROM GroupUserSubscriptions WHERE groupId = ? AND host_mid = ?", [groupId, host_mid]);
8504
8444
  await this.runQuery("DELETE FROM DynamicCaches WHERE groupId = ? AND host_mid = ?", [groupId, host_mid]);
8445
+ const remainingSubscriptions = await this.getQuery("SELECT COUNT(*) as count FROM GroupUserSubscriptions WHERE host_mid = ?", [host_mid]);
8446
+ if (remainingSubscriptions && remainingSubscriptions.count === 0) {
8447
+ logger.info(`[BilibiliDB] 用户 ${host_mid} 已无任何群组订阅,清理相关数据`);
8448
+ await this.runQuery("DELETE FROM BilibiliUsers WHERE host_mid = ?", [host_mid]);
8449
+ await this.runQuery("DELETE FROM FilterWords WHERE host_mid = ?", [host_mid]);
8450
+ await this.runQuery("DELETE FROM FilterTags WHERE host_mid = ?", [host_mid]);
8451
+ await this.runQuery("DELETE FROM DynamicCaches WHERE host_mid = ?", [host_mid]);
8452
+ }
8505
8453
  return result.changes > 0;
8506
8454
  }
8507
8455
  async addDynamicCache(dynamic_id, host_mid, groupId, dynamic_type) {
@@ -8880,11 +8828,24 @@ var init_douyin = __esmMin(async () => {
8880
8828
  logger.info("[DouyinDB] 检测到 AwemeCaches 表缺少 pushType 字段,开始执行迁移...");
8881
8829
  await this.runQuery("ALTER TABLE AwemeCaches ADD COLUMN pushType TEXT DEFAULT 'post'");
8882
8830
  await this.runQuery("ALTER TABLE AwemeCaches RENAME TO AwemeCaches_old");
8883
- await this.runQuery(`\n CREATE TABLE IF NOT EXISTS AwemeCaches (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n aweme_id TEXT NOT NULL,\n sec_uid TEXT NOT NULL,\n groupId TEXT NOT NULL,\n pushType TEXT DEFAULT 'post',\n createdAt TEXT DEFAULT CURRENT_TIMESTAMP,\n updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid),\n FOREIGN KEY (groupId) REFERENCES Groups(id),\n UNIQUE(aweme_id, sec_uid, groupId, pushType)\n )\n `);
8831
+ await this.runQuery(`\n CREATE TABLE IF NOT EXISTS AwemeCaches (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n aweme_id TEXT NOT NULL,\n sec_uid TEXT NOT NULL,\n groupId TEXT NOT NULL,\n pushType TEXT DEFAULT 'post',\n createdAt TEXT DEFAULT CURRENT_TIMESTAMP,\n updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid),\n UNIQUE(aweme_id, sec_uid, groupId, pushType)\n )\n `);
8884
8832
  await this.runQuery(`\n INSERT INTO AwemeCaches (id, aweme_id, sec_uid, groupId, pushType, createdAt, updatedAt)\n SELECT id, aweme_id, sec_uid, groupId, pushType, createdAt, updatedAt\n FROM AwemeCaches_old\n `);
8885
8833
  await this.runQuery("DROP TABLE AwemeCaches_old");
8886
8834
  logger.info("[DouyinDB] AwemeCaches 表迁移完成");
8887
8835
  }
8836
+ try {
8837
+ const tableInfo = await this.allQuery("SELECT sql FROM sqlite_master WHERE type='table' AND name='AwemeCaches'");
8838
+ if (tableInfo.length > 0 && tableInfo[0].sql.includes("FOREIGN KEY (groupId) REFERENCES Groups(id)")) {
8839
+ logger.info("[DouyinDB] 检测到 AwemeCaches 表存在错误的外键约束,开始修复...");
8840
+ await this.runQuery("ALTER TABLE AwemeCaches RENAME TO AwemeCaches_old");
8841
+ await this.runQuery(`\n CREATE TABLE IF NOT EXISTS AwemeCaches (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n aweme_id TEXT NOT NULL,\n sec_uid TEXT NOT NULL,\n groupId TEXT NOT NULL,\n pushType TEXT DEFAULT 'post',\n createdAt TEXT DEFAULT CURRENT_TIMESTAMP,\n updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid),\n UNIQUE(aweme_id, sec_uid, groupId, pushType)\n )\n `);
8842
+ await this.runQuery(`\n INSERT INTO AwemeCaches (id, aweme_id, sec_uid, groupId, pushType, createdAt, updatedAt)\n SELECT id, aweme_id, sec_uid, groupId, pushType, createdAt, updatedAt\n FROM AwemeCaches_old\n `);
8843
+ await this.runQuery("DROP TABLE AwemeCaches_old");
8844
+ logger.info("[DouyinDB] AwemeCaches 表外键约束修复完成");
8845
+ }
8846
+ } catch (error) {
8847
+ logger.debug("[DouyinDB] 外键约束检查/修复跳过:", error);
8848
+ }
8888
8849
  } catch (error) {
8889
8850
  logger.error("[DouyinDB] 数据库迁移失败:", error);
8890
8851
  }
@@ -8895,7 +8856,7 @@ var init_douyin = __esmMin(async () => {
8895
8856
  `CREATE TABLE IF NOT EXISTS Groups (\n id TEXT NOT NULL,\n botId TEXT NOT NULL,\n createdAt TEXT DEFAULT CURRENT_TIMESTAMP,\n updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (id, botId),\n FOREIGN KEY (botId) REFERENCES Bots(id)\n )`,
8896
8857
  `CREATE TABLE IF NOT EXISTS DouyinUsers (\n sec_uid TEXT PRIMARY KEY,\n short_id TEXT,\n remark TEXT,\n living INTEGER DEFAULT 0,\n filterMode TEXT DEFAULT 'blacklist',\n createdAt TEXT DEFAULT CURRENT_TIMESTAMP,\n updatedAt TEXT DEFAULT CURRENT_TIMESTAMP\n )`,
8897
8858
  `CREATE TABLE IF NOT EXISTS GroupUserSubscriptions (\n groupId TEXT,\n sec_uid TEXT,\n createdAt TEXT DEFAULT CURRENT_TIMESTAMP,\n updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (groupId, sec_uid),\n FOREIGN KEY (groupId) REFERENCES Groups(id),\n FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid)\n )`,
8898
- `CREATE TABLE IF NOT EXISTS AwemeCaches (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n aweme_id TEXT NOT NULL,\n sec_uid TEXT NOT NULL,\n groupId TEXT NOT NULL,\n pushType TEXT DEFAULT 'post',\n createdAt TEXT DEFAULT CURRENT_TIMESTAMP,\n updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid),\n FOREIGN KEY (groupId) REFERENCES Groups(id),\n UNIQUE(aweme_id, sec_uid, groupId, pushType)\n )`,
8859
+ `CREATE TABLE IF NOT EXISTS AwemeCaches (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n aweme_id TEXT NOT NULL,\n sec_uid TEXT NOT NULL,\n groupId TEXT NOT NULL,\n pushType TEXT DEFAULT 'post',\n createdAt TEXT DEFAULT CURRENT_TIMESTAMP,\n updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid),\n UNIQUE(aweme_id, sec_uid, groupId, pushType)\n )`,
8899
8860
  `CREATE TABLE IF NOT EXISTS FilterWords (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n sec_uid TEXT NOT NULL,\n word TEXT NOT NULL,\n createdAt TEXT DEFAULT CURRENT_TIMESTAMP,\n updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid),\n UNIQUE(sec_uid, word)\n )`,
8900
8861
  `CREATE TABLE IF NOT EXISTS FilterTags (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n sec_uid TEXT NOT NULL,\n tag TEXT NOT NULL,\n createdAt TEXT DEFAULT CURRENT_TIMESTAMP,\n updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid),\n UNIQUE(sec_uid, tag)\n )`,
8901
8862
  `CREATE TABLE IF NOT EXISTS ListSnapshots (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n sec_uid TEXT NOT NULL,\n pushType TEXT NOT NULL,\n aweme_id TEXT NOT NULL,\n createdAt TEXT DEFAULT CURRENT_TIMESTAMP,\n updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid),\n UNIQUE(sec_uid, pushType, aweme_id)\n )`
@@ -9038,6 +8999,14 @@ var init_douyin = __esmMin(async () => {
9038
8999
  async unsubscribeDouyinUser(groupId, sec_uid) {
9039
9000
  const result = await this.runQuery("DELETE FROM GroupUserSubscriptions WHERE groupId = ? AND sec_uid = ?", [groupId, sec_uid]);
9040
9001
  await this.runQuery("DELETE FROM AwemeCaches WHERE groupId = ? AND sec_uid = ?", [groupId, sec_uid]);
9002
+ const remainingSubscriptions = await this.getQuery("SELECT COUNT(*) as count FROM GroupUserSubscriptions WHERE sec_uid = ?", [sec_uid]);
9003
+ if (remainingSubscriptions && remainingSubscriptions.count === 0) {
9004
+ logger.info(`[DouyinDB] 用户 ${sec_uid} 已无任何群组订阅,清理相关数据`);
9005
+ await this.runQuery("DELETE FROM DouyinUsers WHERE sec_uid = ?", [sec_uid]);
9006
+ await this.runQuery("DELETE FROM FilterWords WHERE sec_uid = ?", [sec_uid]);
9007
+ await this.runQuery("DELETE FROM FilterTags WHERE sec_uid = ?", [sec_uid]);
9008
+ await this.runQuery("DELETE FROM AwemeCaches WHERE sec_uid = ?", [sec_uid]);
9009
+ }
9041
9010
  return result.changes > 0;
9042
9011
  }
9043
9012
  async addAwemeCache(aweme_id, sec_uid, groupId, pushType = "post") {
@@ -12926,15 +12895,16 @@ var init_setup = __esmMin(async () => {
12926
12895
  });
12927
12896
  await init_module();
12928
12897
  await init_semver();
12929
- var requireVersion = "1.14.1";
12898
+ var requireVersion = "1.14.4";
12930
12899
  if (process.env.NODE_ENV !== "development" && isSemverGreater(requireVersion, Root.karinVersion)) {
12931
12900
  const msg = `[karin-plugin-kkk] 插件构建时的 karin 版本 (${requireVersion}) 高于当前运行版本 (${Root.karinVersion}),可能会出现兼容性问题!`;
12932
12901
  logger.warn(msg);
12933
12902
  const notifiedSet = /* @__PURE__ */ new Set();
12934
12903
  karin$1.on(BOT_CONNECT, async (bot) => {
12904
+ const botId = bot.selfId;
12905
+ if (botId === "console") return;
12935
12906
  await new Promise((resolve$1) => setTimeout(resolve$1, 2e3));
12936
12907
  const masters = config.master();
12937
- const botId = bot.selfId;
12938
12908
  logger.info(`[karin-plugin-kkk] 监测到 Bot 连接: ${botId}, 准备发送版本警告`);
12939
12909
  let warningImage = null;
12940
12910
  try {
@@ -17220,20 +17190,23 @@ var DouYin = class extends Base {
17220
17190
  const processedImages = [];
17221
17191
  const temp = [];
17222
17192
  g_title = VideoData.data.aweme_detail.preview_title.substring(0, 50).replace(/[\\/:*?"<>|\r\n]/g, " ");
17223
- let mp3Path = "";
17224
- if (VideoData.data.aweme_detail.music.play_url.uri === "") mp3Path = JSON.parse(VideoData.data.aweme_detail.music.extra).original_song_url;
17225
- else mp3Path = VideoData.data.aweme_detail.music.play_url.uri;
17226
- const liveimgbgm = await downloadFile(mp3Path, {
17227
- title: `Douyin_tmp_A_${Date.now()}.mp3`,
17228
- headers: this.headers
17229
- });
17230
- temp.push(liveimgbgm);
17231
- const mergeMode = Config.douyin.liveImageMergeMode ?? "independent";
17193
+ let liveimgbgm = null;
17232
17194
  let bgmContext = null;
17233
- if (mergeMode === "continuous") bgmContext = await createLiveImageContext(liveimgbgm.filepath);
17195
+ const mergeMode = Config.douyin.liveImageMergeMode ?? "independent";
17196
+ if (VideoData.data.aweme_detail.music) {
17197
+ let mp3Path = "";
17198
+ if (VideoData.data.aweme_detail.music.play_url.uri === "") mp3Path = JSON.parse(VideoData.data.aweme_detail.music.extra).original_song_url;
17199
+ else mp3Path = VideoData.data.aweme_detail.music.play_url.uri;
17200
+ liveimgbgm = await downloadFile(mp3Path, {
17201
+ title: `Douyin_tmp_A_${Date.now()}.mp3`,
17202
+ headers: this.headers
17203
+ });
17204
+ temp.push(liveimgbgm);
17205
+ if (mergeMode === "continuous") bgmContext = await createLiveImageContext(liveimgbgm.filepath);
17206
+ }
17234
17207
  for (const [index, imageItem] of images.entries()) {
17235
17208
  imagenum++;
17236
- if (imageItem.clip_type === 2) {
17209
+ if (imageItem.clip_type === 2 || imageItem.clip_type === void 0) {
17237
17210
  image_url = imageItem.url_list[2] || imageItem.url_list[1];
17238
17211
  processedImages.push(segment.image(image_url));
17239
17212
  if (Config.app.removeCache === false) {
@@ -17254,7 +17227,12 @@ var DouYin = class extends Base {
17254
17227
  const outputPath = Common.tempDri.video + `Douyin_Result_${Date.now()}.mp4`;
17255
17228
  let success;
17256
17229
  const loopCount = imageItem.clip_type === 4 ? 1 : 3;
17257
- if (mergeMode === "continuous" && bgmContext) {
17230
+ if (!liveimgbgm) if (loopCount > 1) success = await loopVideo(liveimg.filepath, outputPath, loopCount);
17231
+ else {
17232
+ fs.renameSync(liveimg.filepath, outputPath);
17233
+ success = true;
17234
+ }
17235
+ else if (mergeMode === "continuous" && bgmContext) {
17258
17236
  const result = await mergeLiveImageContinuous({
17259
17237
  videoPath: liveimg.filepath,
17260
17238
  outputPath,
@@ -17327,22 +17305,25 @@ var DouYin = class extends Base {
17327
17305
  case VideoData.data.aweme_detail.is_slides === true && VideoData.data.aweme_detail.images !== null: {
17328
17306
  const images = [];
17329
17307
  const temp = [];
17330
- let mp3Path = "";
17331
- if (VideoData.data.aweme_detail.music.play_url.uri === "") mp3Path = JSON.parse(VideoData.data.aweme_detail.music.extra).original_song_url;
17332
- else mp3Path = VideoData.data.aweme_detail.music.play_url.uri;
17333
- const liveimgbgm = await downloadFile(mp3Path, {
17334
- title: `Douyin_tmp_A_${Date.now()}.mp3`,
17335
- headers: this.headers
17336
- });
17337
- temp.push(liveimgbgm);
17308
+ let liveimgbgm = null;
17309
+ let bgmContext = null;
17310
+ const mergeMode = Config.douyin.liveImageMergeMode ?? "independent";
17311
+ if (VideoData.data.aweme_detail.music) {
17312
+ let mp3Path = "";
17313
+ if (VideoData.data.aweme_detail.music.play_url.uri === "") mp3Path = JSON.parse(VideoData.data.aweme_detail.music.extra).original_song_url;
17314
+ else mp3Path = VideoData.data.aweme_detail.music.play_url.uri;
17315
+ liveimgbgm = await downloadFile(mp3Path, {
17316
+ title: `Douyin_tmp_A_${Date.now()}.mp3`,
17317
+ headers: this.headers
17318
+ });
17319
+ temp.push(liveimgbgm);
17320
+ if (mergeMode === "continuous") bgmContext = await createLiveImageContext(liveimgbgm.filepath);
17321
+ }
17338
17322
  const images1 = VideoData.data.aweme_detail.images ?? [];
17339
17323
  if (!images1.length) logger.debug("未获取到合辑的图片数据");
17340
- const mergeMode = Config.douyin.liveImageMergeMode ?? "independent";
17341
- let bgmContext = null;
17342
- if (mergeMode === "continuous") bgmContext = await createLiveImageContext(liveimgbgm.filepath);
17343
17324
  for (const item of images1) {
17344
17325
  imagenum++;
17345
- if (item.clip_type === 2) {
17326
+ if (item.clip_type === 2 || item.clip_type === void 0) {
17346
17327
  images.push(segment.image(item.url_list[0]));
17347
17328
  continue;
17348
17329
  }
@@ -17354,7 +17335,12 @@ var DouYin = class extends Base {
17354
17335
  const outputPath = Common.tempDri.video + `Douyin_Result_${Date.now()}.mp4`;
17355
17336
  let success;
17356
17337
  const loopCount = item.clip_type === 4 ? 1 : 3;
17357
- if (mergeMode === "continuous" && bgmContext) {
17338
+ if (!liveimgbgm) if (loopCount > 1) success = await loopVideo(liveimg.filepath, outputPath, loopCount);
17339
+ else {
17340
+ fs.renameSync(liveimg.filepath, outputPath);
17341
+ success = true;
17342
+ }
17343
+ else if (mergeMode === "continuous" && bgmContext) {
17358
17344
  const result = await mergeLiveImageContinuous({
17359
17345
  videoPath: liveimg.filepath,
17360
17346
  outputPath,
@@ -17471,7 +17457,7 @@ var DouYin = class extends Base {
17471
17457
  gender: userProfile.data.user.gender ?? 0,
17472
17458
  user_age: userProfile.data.user.user_age ?? 0
17473
17459
  } : void 0,
17474
- image_url: this.is_mp4 ? VideoData.data.aweme_detail.video.animated_cover?.url_list[0] ?? VideoData.data.aweme_detail.video.cover_original_scale.url_list[0] : VideoData.data.aweme_detail.images[0].url_list[0],
17460
+ image_url: this.is_mp4 ? VideoData.data.aweme_detail.video.animated_cover?.url_list[0] ?? VideoData.data.aweme_detail.video.cover_original_scale?.url_list[0] ?? VideoData.data.aweme_detail.video.cover.url_list[0] : VideoData.data.aweme_detail.images[0].url_list[0],
17475
17461
  cover_size: this.is_mp4 ? VideoData.data.aweme_detail.video.cover ? {
17476
17462
  width: VideoData.data.aweme_detail.video.cover_original_scale.width,
17477
17463
  height: VideoData.data.aweme_detail.video.cover_original_scale.height
@@ -17569,13 +17555,12 @@ var DouYin = class extends Base {
17569
17555
  await Common.removeFile(videoFile.filepath, true);
17570
17556
  const stats = fs.statSync(filePath);
17571
17557
  const fileSizeInMB = Number((stats.size / (1024 * 1024)).toFixed(2));
17572
- const { uploadFile: uploadFile$1 } = await init_utils$1().then(() => utils_exports);
17573
- if (fileSizeInMB > Config.upload.groupfilevalue) await uploadFile$1(this.e, {
17558
+ if (fileSizeInMB > Config.upload.groupfilevalue) await uploadFile(this.e, {
17574
17559
  filepath: filePath,
17575
17560
  totalBytes: fileSizeInMB,
17576
17561
  originTitle: g_title || ""
17577
17562
  }, "", { useGroupFile: true });
17578
- else await uploadFile$1(this.e, {
17563
+ else await uploadFile(this.e, {
17579
17564
  filepath: filePath,
17580
17565
  totalBytes: fileSizeInMB,
17581
17566
  originTitle: g_title || ""
@@ -18223,7 +18208,7 @@ var DouYinpush = class extends Base {
18223
18208
  if (pushItem.pushType === "favorite") {
18224
18209
  const authorUserInfo = "author_user_info" in Detail_Data ? Detail_Data.author_user_info : void 0;
18225
18210
  img$2 = await Render("douyin/favorite-list", {
18226
- image_url: iddata.is_mp4 ? Detail_Data.video.animated_cover?.url_list[0] ?? Detail_Data.video.cover_original_scale.url_list[0] : Detail_Data.images[0].url_list[0],
18211
+ image_url: iddata.is_mp4 ? Detail_Data.video.animated_cover?.url_list[0] ?? Detail_Data.video.cover_original_scale?.url_list[0] ?? Detail_Data.video.cover.url_list[0] : Detail_Data.images[0].url_list[0],
18227
18212
  desc: this.desc(Detail_Data, Detail_Data.desc),
18228
18213
  dianzan: this.count(Detail_Data.statistics.digg_count),
18229
18214
  pinglun: this.count(Detail_Data.statistics.comment_count),
@@ -18242,7 +18227,7 @@ var DouYinpush = class extends Base {
18242
18227
  } else if (pushItem.pushType === "recommend") {
18243
18228
  const authorUserInfo = "author_user_info" in Detail_Data ? Detail_Data.author_user_info : void 0;
18244
18229
  img$2 = await Render("douyin/recommend-list", {
18245
- image_url: iddata.is_mp4 ? Detail_Data.video.animated_cover?.url_list[0] ?? Detail_Data.video.cover_original_scale.url_list[0] : Detail_Data.images[0].url_list[0],
18230
+ image_url: iddata.is_mp4 ? Detail_Data.video.animated_cover?.url_list[0] ?? Detail_Data.video.cover_original_scale?.url_list[0] ?? Detail_Data.video.cover.url_list[0] : Detail_Data.images[0].url_list[0],
18246
18231
  desc: this.desc(Detail_Data, Detail_Data.desc),
18247
18232
  dianzan: this.count(Detail_Data.statistics.digg_count),
18248
18233
  pinglun: this.count(Detail_Data.statistics.comment_count),
@@ -18298,7 +18283,7 @@ var DouYinpush = class extends Base {
18298
18283
  })()
18299
18284
  });
18300
18285
  }
18301
- for (const target of pushItem.targets) try {
18286
+ for (const target of pushItem.targets) {
18302
18287
  let status = { message_id: "" };
18303
18288
  const { groupId, botId } = target;
18304
18289
  if (!skip) {
@@ -18336,24 +18321,24 @@ var DouYinpush = class extends Base {
18336
18321
  if (Detail_Data.is_slides === true && Detail_Data.images) {
18337
18322
  const images = [];
18338
18323
  const temp = [];
18339
- let mp3Path = "";
18340
- if (Detail_Data.music.play_url.uri === "") mp3Path = JSON.parse(Detail_Data.music.extra).original_song_url;
18341
- else mp3Path = Detail_Data.music.play_url.uri;
18342
- const liveimgbgm = await downloadFile(mp3Path, {
18343
- title: `Douyin_tmp_A_${Date.now()}.mp3`,
18344
- headers: douyinBaseHeaders
18345
- });
18346
- temp.push(liveimgbgm);
18347
- const images1 = Detail_Data.images ?? [];
18348
- if (!images1.length) logger.debug("未获取到合辑的图片数据");
18349
- const mergeMode = Config.douyin.liveImageMergeMode ?? "independent";
18324
+ let liveimgbgm = null;
18350
18325
  let bgmContext = null;
18351
- if (mergeMode === "continuous") {
18352
- const { createLiveImageContext: createLiveImageContext$1 } = await init_utils$1().then(() => utils_exports);
18353
- bgmContext = await createLiveImageContext$1(liveimgbgm.filepath);
18326
+ const mergeMode = Config.douyin.liveImageMergeMode ?? "independent";
18327
+ if (Detail_Data.music) {
18328
+ let mp3Path = "";
18329
+ if (Detail_Data.music.play_url.uri === "") mp3Path = JSON.parse(Detail_Data.music.extra).original_song_url;
18330
+ else mp3Path = Detail_Data.music.play_url.uri;
18331
+ liveimgbgm = await downloadFile(mp3Path, {
18332
+ title: `Douyin_tmp_A_${Date.now()}.mp3`,
18333
+ headers: douyinBaseHeaders
18334
+ });
18335
+ temp.push(liveimgbgm);
18336
+ if (mergeMode === "continuous") bgmContext = await createLiveImageContext(liveimgbgm.filepath);
18354
18337
  }
18338
+ const images1 = Detail_Data.images ?? [];
18339
+ if (!images1.length) logger.debug("未获取到合辑的图片数据");
18355
18340
  for (const item of images1) {
18356
- if (item.clip_type === 2) {
18341
+ if (item.clip_type === 2 || item.clip_type === void 0) {
18357
18342
  images.push(segment.image(item.url_list[0]));
18358
18343
  continue;
18359
18344
  }
@@ -18362,37 +18347,40 @@ var DouYinpush = class extends Base {
18362
18347
  headers: douyinBaseHeaders
18363
18348
  });
18364
18349
  if (liveimg.filepath) {
18365
- const { Common: Common$1, mergeLiveImageContinuous: mergeLiveImageContinuous$1, mergeLiveImageIndependent: mergeLiveImageIndependent$1 } = await init_utils$1().then(() => utils_exports);
18366
- const outputPath = Common$1.tempDri.video + `Douyin_Result_${Date.now()}.mp4`;
18350
+ const outputPath = Common.tempDri.video + `Douyin_Result_${Date.now()}.mp4`;
18367
18351
  let success;
18368
18352
  const loopCount = item.clip_type === 4 ? 1 : 3;
18369
- if (mergeMode === "continuous" && bgmContext) {
18370
- const result = await mergeLiveImageContinuous$1({
18353
+ if (!liveimgbgm) if (loopCount > 1) success = await loopVideo(liveimg.filepath, outputPath, loopCount);
18354
+ else {
18355
+ fs.renameSync(liveimg.filepath, outputPath);
18356
+ success = true;
18357
+ }
18358
+ else if (mergeMode === "continuous" && bgmContext) {
18359
+ const result = await mergeLiveImageContinuous({
18371
18360
  videoPath: liveimg.filepath,
18372
18361
  outputPath,
18373
18362
  loopCount
18374
18363
  }, bgmContext);
18375
18364
  success = result.success;
18376
18365
  bgmContext = result.context;
18377
- } else success = await mergeLiveImageIndependent$1({
18366
+ } else success = await mergeLiveImageIndependent({
18378
18367
  videoPath: liveimg.filepath,
18379
18368
  outputPath,
18380
18369
  loopCount
18381
18370
  }, liveimgbgm.filepath);
18382
18371
  if (success) {
18383
- const fs$1 = await import("node:fs");
18384
- const filePath = Common$1.tempDri.video + `tmp_${Date.now()}.mp4`;
18385
- fs$1.default.renameSync(outputPath, filePath);
18372
+ const filePath = Common.tempDri.video + `tmp_${Date.now()}.mp4`;
18373
+ fs.renameSync(outputPath, filePath);
18386
18374
  logger.mark(`视频文件重命名完成: ${outputPath.split("/").pop()} -> ${filePath.split("/").pop()}`);
18387
18375
  logger.mark("正在尝试删除缓存文件");
18388
- await Common$1.removeFile(liveimg.filepath, true);
18376
+ await Common.removeFile(liveimg.filepath, true);
18389
18377
  temp.push({
18390
18378
  filepath: filePath,
18391
18379
  totalBytes: 0
18392
18380
  });
18393
18381
  images.push(segment.video("file://" + filePath));
18394
18382
  if (item.clip_type === 5 && item.url_list?.[0]) images.push(segment.image(item.url_list[0]));
18395
- } else await Common$1.removeFile(liveimg.filepath, true);
18383
+ } else await Common.removeFile(liveimg.filepath, true);
18396
18384
  }
18397
18385
  }
18398
18386
  const bot = karin$1.getBot(botId);
@@ -18407,30 +18395,27 @@ var DouYinpush = class extends Base {
18407
18395
  } catch (error) {
18408
18396
  logger.error(`发送合辑失败: ${error}`);
18409
18397
  } finally {
18410
- for (const item of temp) {
18411
- const { Common: Common$1 } = await init_utils$1().then(() => utils_exports);
18412
- await Common$1.removeFile(item.filepath, true);
18413
- }
18398
+ for (const item of temp) await Common.removeFile(item.filepath, true);
18414
18399
  }
18415
- } else if (Detail_Data.images) if (Detail_Data.images.some((item) => item.clip_type !== 2)) {
18400
+ } else if (Detail_Data.images) if (Detail_Data.images.some((item) => item.clip_type !== 2 && item.clip_type !== void 0)) {
18416
18401
  const processedImages = [];
18417
18402
  const temp = [];
18418
- let mp3Path = "";
18419
- if (Detail_Data.music.play_url.uri === "") mp3Path = JSON.parse(Detail_Data.music.extra).original_song_url;
18420
- else mp3Path = Detail_Data.music.play_url.uri;
18421
- const liveimgbgm = await downloadFile(mp3Path, {
18422
- title: `Douyin_tmp_A_${Date.now()}.mp3`,
18423
- headers: douyinBaseHeaders
18424
- });
18425
- temp.push(liveimgbgm);
18426
- const mergeMode = Config.douyin.liveImageMergeMode ?? "independent";
18403
+ let liveimgbgm = null;
18427
18404
  let bgmContext = null;
18428
- if (mergeMode === "continuous") {
18429
- const { createLiveImageContext: createLiveImageContext$1 } = await init_utils$1().then(() => utils_exports);
18430
- bgmContext = await createLiveImageContext$1(liveimgbgm.filepath);
18405
+ const mergeMode = Config.douyin.liveImageMergeMode ?? "independent";
18406
+ if (Detail_Data.music) {
18407
+ let mp3Path = "";
18408
+ if (Detail_Data.music.play_url.uri === "") mp3Path = JSON.parse(Detail_Data.music.extra).original_song_url;
18409
+ else mp3Path = Detail_Data.music.play_url.uri;
18410
+ liveimgbgm = await downloadFile(mp3Path, {
18411
+ title: `Douyin_tmp_A_${Date.now()}.mp3`,
18412
+ headers: douyinBaseHeaders
18413
+ });
18414
+ temp.push(liveimgbgm);
18415
+ if (mergeMode === "continuous") bgmContext = await createLiveImageContext(liveimgbgm.filepath);
18431
18416
  }
18432
18417
  for (const item of Detail_Data.images) {
18433
- if (item.clip_type === 2) {
18418
+ if (item.clip_type === 2 || item.clip_type === void 0) {
18434
18419
  const image_url = item.url_list[2] ?? item.url_list[1];
18435
18420
  processedImages.push(segment.image(image_url));
18436
18421
  continue;
@@ -18440,37 +18425,40 @@ var DouYinpush = class extends Base {
18440
18425
  headers: douyinBaseHeaders
18441
18426
  });
18442
18427
  if (liveimg.filepath) {
18443
- const { Common: Common$1, mergeLiveImageContinuous: mergeLiveImageContinuous$1, mergeLiveImageIndependent: mergeLiveImageIndependent$1 } = await init_utils$1().then(() => utils_exports);
18444
- const outputPath = Common$1.tempDri.video + `Douyin_Result_${Date.now()}.mp4`;
18428
+ const outputPath = Common.tempDri.video + `Douyin_Result_${Date.now()}.mp4`;
18445
18429
  let success;
18446
18430
  const loopCount = item.clip_type === 4 ? 1 : 3;
18447
- if (mergeMode === "continuous" && bgmContext) {
18448
- const result = await mergeLiveImageContinuous$1({
18431
+ if (!liveimgbgm) if (loopCount > 1) success = await loopVideo(liveimg.filepath, outputPath, loopCount);
18432
+ else {
18433
+ fs.renameSync(liveimg.filepath, outputPath);
18434
+ success = true;
18435
+ }
18436
+ else if (mergeMode === "continuous" && bgmContext) {
18437
+ const result = await mergeLiveImageContinuous({
18449
18438
  videoPath: liveimg.filepath,
18450
18439
  outputPath,
18451
18440
  loopCount
18452
18441
  }, bgmContext);
18453
18442
  success = result.success;
18454
18443
  bgmContext = result.context;
18455
- } else success = await mergeLiveImageIndependent$1({
18444
+ } else success = await mergeLiveImageIndependent({
18456
18445
  videoPath: liveimg.filepath,
18457
18446
  outputPath,
18458
18447
  loopCount
18459
18448
  }, liveimgbgm.filepath);
18460
18449
  if (success) {
18461
- const fs$1 = await import("node:fs");
18462
- const filePath = Common$1.tempDri.video + `tmp_${Date.now()}.mp4`;
18463
- fs$1.default.renameSync(outputPath, filePath);
18450
+ const filePath = Common.tempDri.video + `tmp_${Date.now()}.mp4`;
18451
+ fs.renameSync(outputPath, filePath);
18464
18452
  logger.mark(`视频文件重命名完成: ${outputPath.split("/").pop()} -> ${filePath.split("/").pop()}`);
18465
18453
  logger.mark("正在尝试删除缓存文件");
18466
- await Common$1.removeFile(liveimg.filepath, true);
18454
+ await Common.removeFile(liveimg.filepath, true);
18467
18455
  temp.push({
18468
18456
  filepath: filePath,
18469
18457
  totalBytes: 0
18470
18458
  });
18471
18459
  processedImages.push(segment.video("file://" + filePath));
18472
18460
  if (item.clip_type === 5 && item.url_list?.[0]) processedImages.push(segment.image(item.url_list[0]));
18473
- } else await Common$1.removeFile(liveimg.filepath, true);
18461
+ } else await Common.removeFile(liveimg.filepath, true);
18474
18462
  }
18475
18463
  }
18476
18464
  const bot = karin$1.getBot(botId);
@@ -18485,10 +18473,7 @@ var DouYinpush = class extends Base {
18485
18473
  } catch (error) {
18486
18474
  logger.error(`发送图集失败: ${error}`);
18487
18475
  } finally {
18488
- for (const item of temp) {
18489
- const { Common: Common$1 } = await init_utils$1().then(() => utils_exports);
18490
- await Common$1.removeFile(item.filepath, true);
18491
- }
18476
+ for (const item of temp) await Common.removeFile(item.filepath, true);
18492
18477
  }
18493
18478
  } else {
18494
18479
  const imageres = [];
@@ -18513,8 +18498,6 @@ var DouYinpush = class extends Base {
18513
18498
  }
18514
18499
  }
18515
18500
  if (skip || pushItem.pushType !== "live" && status.message_id) await douyinDBInstance.addAwemeCache(actualAwemeId, pushItem.sec_uid, groupId, pushItem.pushType);
18516
- } catch (error) {
18517
- throw new Error(`${error}`);
18518
18501
  }
18519
18502
  }
18520
18503
  return true;
@@ -18939,8 +18922,6 @@ var Kuaishou = class extends Base {
18939
18922
  return true;
18940
18923
  }
18941
18924
  };
18942
- var import_jsQR = __toESM(require_jsQR(), 1);
18943
- var import_png = require_png();
18944
18925
  await init_module();
18945
18926
  await init_Config();
18946
18927
  var safeScreenshot = async (page, screenshotPath) => {
@@ -19280,12 +19261,11 @@ var waitQrcode = async (page) => {
19280
19261
  const response = await fetch(originalImage);
19281
19262
  imageBuffer = Buffer.from(await response.arrayBuffer());
19282
19263
  }
19283
- const png = import_png.PNG.sync.read(imageBuffer);
19284
- const code = (0, import_jsQR.default)(Uint8ClampedArray.from(png.data), png.width, png.height);
19285
- if (code && code.data) {
19286
- logger.mark("二维码解码成功:", code.data);
19264
+ const qrContent = QRCodeScanner.scanFromBuffer(imageBuffer);
19265
+ if (qrContent) {
19266
+ logger.mark("二维码解码成功:", qrContent);
19287
19267
  return {
19288
- url: code.data,
19268
+ url: qrContent,
19289
19269
  originalImage
19290
19270
  };
19291
19271
  }