karin-plugin-kkk 2.21.0 → 2.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [2.21.1](https://github.com/ikenxuan/karin-plugin-kkk/compare/v2.21.0...v2.21.1) (2026-03-02)
6
+
7
+
8
+ ### 🐛 错误修复
9
+
10
+ * 在线图片的上传方式配置 ([5d80aa5](https://github.com/ikenxuan/karin-plugin-kkk/commit/5d80aa571095e3851daa7120c724b75b38df83e7))
11
+
5
12
  ## [2.21.0](https://github.com/ikenxuan/karin-plugin-kkk/compare/v2.20.3...v2.21.0) (2026-03-02)
6
13
 
7
14
 
@@ -7,9 +7,6 @@ priority: 800
7
7
  # 缓存删除,非必要不修改!
8
8
  removeCache: true
9
9
 
10
- # 发送图片时由插件本地下载后使用 file 协议传递,而非直接传递 HTTP 链接给上游下载
11
- downloadImageLocally: false
12
-
13
10
  # 渲染精度,可选值50~200,建议100。设置高精度会提高图片的精细度,过高可能会影响渲染与发送速度
14
11
  renderScale: 100
15
12
 
@@ -22,6 +22,12 @@ usegroupfile: false
22
22
  # 群文件上传阈值,当文件大小超过该值时将使用群文件上传,单位:MB,「使用群文件上传」开启后才会生效
23
23
  groupfilevalue: 100
24
24
 
25
+ # 网络图片发送方式,可选值:url / file / base64
26
+ # url: 直接传递 HTTP 链接给上游下载(可能因上游网络问题导致下载超时)
27
+ # file: 下载到本地使用 file 协议发送(需 Karin 与协议端在同一系统)
28
+ # base64: 下载后转换为 base64 发送(传输数据增大约 1/3,不在同一网络环境可能导致额外带宽成本)
29
+ imageSendMode: url
30
+
25
31
  # 下载限速开关,开启后会限制下载速度,避免触发服务器风控导致连接被重置(ECONNRESET)
26
32
  # 如果你的网络带宽很大且下载时经常报错"连接被重置",建议开启此选项
27
33
  downloadThrottle: false
package/lib/apps/admin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "../core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { C as removeOldFiles, S as dylogin, w as task, x as biLogin } from "../core_chunk/main-eqK3KHoJ.js";
2
+ import { C as removeOldFiles, S as dylogin, w as task, x as biLogin } from "../core_chunk/main-B-QVNIL7.js";
3
3
  import "../core_chunk/vendor-CYCcUtqE.js";
4
4
  import "../core_chunk/template-n3eb7E79.js";
5
5
  export { biLogin, dylogin, removeOldFiles, task };
package/lib/apps/help.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "../core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { b as version, y as help } from "../core_chunk/main-eqK3KHoJ.js";
2
+ import { b as version, y as help } from "../core_chunk/main-B-QVNIL7.js";
3
3
  import "../core_chunk/vendor-CYCcUtqE.js";
4
4
  import "../core_chunk/template-n3eb7E79.js";
5
5
  export { help, version };
package/lib/apps/push.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "../core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { _ as setdyPush, d as bilibiliPushList, f as changeBotID, g as setbiliPush, h as forcePush, m as douyinPushList, p as douyinPush, u as bilibiliPush, v as testDouyinPush } from "../core_chunk/main-eqK3KHoJ.js";
2
+ import { _ as setdyPush, d as bilibiliPushList, f as changeBotID, g as setbiliPush, h as forcePush, m as douyinPushList, p as douyinPush, u as bilibiliPush, v as testDouyinPush } from "../core_chunk/main-B-QVNIL7.js";
3
3
  import "../core_chunk/vendor-CYCcUtqE.js";
4
4
  import "../core_chunk/template-n3eb7E79.js";
5
5
  export { bilibiliPush, bilibiliPushList, changeBotID, douyinPush, douyinPushList, forcePush, setbiliPush, setdyPush, testDouyinPush };
@@ -1,5 +1,5 @@
1
1
  import "../core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { l as qrLogin } from "../core_chunk/main-eqK3KHoJ.js";
2
+ import { l as qrLogin } from "../core_chunk/main-B-QVNIL7.js";
3
3
  import "../core_chunk/vendor-CYCcUtqE.js";
4
4
  import "../core_chunk/template-n3eb7E79.js";
5
5
  export { qrLogin };
package/lib/apps/tools.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "../core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { a as douyinAPP, c as xiaohongshuAPP, i as bilibiliAPP, o as kuaishouAPP, s as prefix } from "../core_chunk/main-eqK3KHoJ.js";
2
+ import { a as douyinAPP, c as xiaohongshuAPP, i as bilibiliAPP, o as kuaishouAPP, s as prefix } from "../core_chunk/main-B-QVNIL7.js";
3
3
  import "../core_chunk/vendor-CYCcUtqE.js";
4
4
  import "../core_chunk/template-n3eb7E79.js";
5
5
  export { bilibiliAPP, douyinAPP, kuaishouAPP, prefix, xiaohongshuAPP };
@@ -1,5 +1,5 @@
1
1
  import "../core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { n as kkkUpdateCommand, r as update, t as kkkUpdate } from "../core_chunk/main-eqK3KHoJ.js";
2
+ import { n as kkkUpdateCommand, r as update, t as kkkUpdate } from "../core_chunk/main-B-QVNIL7.js";
3
3
  import "../core_chunk/vendor-CYCcUtqE.js";
4
4
  import "../core_chunk/template-n3eb7E79.js";
5
5
  export { kkkUpdate, kkkUpdateCommand, update };
@@ -1,10 +1,10 @@
1
1
  {
2
- "version": "2.21.0",
3
- "buildTime": "2026-03-02T08:01:57.263Z",
4
- "buildTimestamp": 1772438517263,
2
+ "version": "2.21.1",
3
+ "buildTime": "2026-03-02T12:56:19.212Z",
4
+ "buildTimestamp": 1772456179213,
5
5
  "name": "karin-plugin-kkk",
6
6
  "description": "Karin 的「抖音」「B 站」视频解析/动态推送插件",
7
7
  "homepage": "https://github.com/ikenxuan/karin-plugin-kkk",
8
- "commitHash": "6c66bfcb4ea81d16b9a251c0fa14e5dddee3e718",
9
- "shortCommitHash": "6c66bfcb"
8
+ "commitHash": "e43173f44582ab306a63ac13df2c7a3c4fb33200",
9
+ "shortCommitHash": "e43173f4"
10
10
  }
@@ -6225,7 +6225,7 @@ var init_Base = __esmMin(() => {
6225
6225
  } catch (error) {
6226
6226
  if (options && options.active === false) await event.reply("视频文件上传失败" + JSON.stringify(error, null, 2));
6227
6227
  logger.error("视频文件上传错误," + String(error));
6228
- return false;
6228
+ throw error;
6229
6229
  } finally {
6230
6230
  const filePath = file.filepath;
6231
6231
  logger.mark(`临时预览地址:http://localhost:${process.env.HTTP_PORT}/api/kkk/video/${encodeURIComponent(filePath.split("/").pop() ?? "")}`);
@@ -8279,29 +8279,55 @@ var init_ImageDownloader = __esmMin(() => {
8279
8279
  ImageDownloader = class {
8280
8280
  axiosInstance;
8281
8281
  tempDir;
8282
+ maxRetries = 3;
8283
+ retryDelay = 1e3;
8282
8284
  constructor(axiosInstance, tempDir) {
8283
8285
  this.axiosInstance = axiosInstance;
8284
8286
  this.tempDir = tempDir;
8285
8287
  if (!fs.existsSync(this.tempDir)) fs.mkdirSync(this.tempDir, { recursive: true });
8286
8288
  }
8287
8289
  async processImage(imageUrl, title, index) {
8288
- if (!Config.app.downloadImageLocally) return imageUrl;
8290
+ switch (Config.upload.imageSendMode) {
8291
+ case "base64": try {
8292
+ return await this.downloadAndConvertToBase64(imageUrl);
8293
+ } catch (error) {
8294
+ logger.error(`图片转换 base64 失败,回退到原始 URL: ${imageUrl}`, error);
8295
+ return imageUrl;
8296
+ }
8297
+ case "file": try {
8298
+ const result = await this.downloadImage(imageUrl, title, index);
8299
+ if (result.shouldDelete) this.scheduleDelete(result.filePath);
8300
+ return result.filePath;
8301
+ } catch (error) {
8302
+ logger.error(`图片下载失败,回退到原始 URL: ${imageUrl}`, error);
8303
+ return imageUrl;
8304
+ }
8305
+ case "url":
8306
+ default: return imageUrl;
8307
+ }
8308
+ }
8309
+ async downloadAndConvertToBase64(imageUrl) {
8310
+ const filename = this.generateFilename(imageUrl);
8311
+ const filePath = path.join(this.tempDir, filename);
8289
8312
  try {
8290
- const result = await this.downloadImage(imageUrl, title, index);
8291
- if (result.shouldDelete) this.scheduleDelete(result.filePath);
8292
- return result.filePath;
8293
- } catch (error) {
8294
- logger.error(`图片下载失败,回退到原始 URL: ${imageUrl}`, error);
8295
- return imageUrl;
8313
+ const response = await this.downloadWithRetry(imageUrl);
8314
+ fs.writeFileSync(filePath, response.data);
8315
+ const base64 = fs.readFileSync(filePath).toString("base64");
8316
+ logger.debug(`图片已下载并转换为 base64: ${imageUrl.substring(0, 50)}...`);
8317
+ return `base64://${base64}`;
8318
+ } finally {
8319
+ if (Config.app.removeCache && fs.existsSync(filePath)) try {
8320
+ fs.unlinkSync(filePath);
8321
+ logger.debug(`临时文件已删除: ${filePath}`);
8322
+ } catch (error) {
8323
+ logger.error(`删除临时文件失败: ${filePath}`, error);
8324
+ }
8296
8325
  }
8297
8326
  }
8298
8327
  async downloadImage(imageUrl, title, index) {
8299
8328
  const filename = this.generateFilename(imageUrl, title, index);
8300
8329
  const filePath = path.join(this.tempDir, filename);
8301
- const response = await this.axiosInstance.get(imageUrl, {
8302
- responseType: "arraybuffer",
8303
- timeout: 3e4
8304
- });
8330
+ const response = await this.downloadWithRetry(imageUrl);
8305
8331
  fs.writeFileSync(filePath, response.data);
8306
8332
  logger.debug(`图片已下载: ${filePath}`);
8307
8333
  return {
@@ -8309,6 +8335,28 @@ var init_ImageDownloader = __esmMin(() => {
8309
8335
  shouldDelete: true
8310
8336
  };
8311
8337
  }
8338
+ async downloadWithRetry(imageUrl, retryCount = 0) {
8339
+ try {
8340
+ const response = await this.axiosInstance.get(imageUrl, {
8341
+ responseType: "arraybuffer",
8342
+ timeout: 3e4
8343
+ });
8344
+ if (retryCount > 0) logger.info(`图片下载成功(重试 ${retryCount} 次后): ${imageUrl.substring(0, 50)}...`);
8345
+ return response;
8346
+ } catch (error) {
8347
+ if (retryCount < this.maxRetries) {
8348
+ const delay$1 = this.retryDelay * Math.pow(2, retryCount);
8349
+ logger.warn(`图片下载失败,${delay$1}ms 后进行第 ${retryCount + 1}/${this.maxRetries} 次重试: ${imageUrl.substring(0, 50)}...`, error);
8350
+ await this.sleep(delay$1);
8351
+ return this.downloadWithRetry(imageUrl, retryCount + 1);
8352
+ }
8353
+ logger.error(`图片下载失败,已重试 ${this.maxRetries} 次: ${imageUrl}`, error);
8354
+ throw error;
8355
+ }
8356
+ }
8357
+ sleep(ms) {
8358
+ return new Promise((resolve$1) => setTimeout(resolve$1, ms));
8359
+ }
8312
8360
  generateFilename(imageUrl, title, index) {
8313
8361
  const ext = this.getExtension(imageUrl);
8314
8362
  if (Config.app.removeCache) return `tmp_${Date.now()}${index !== void 0 ? `_${index}` : ""}${ext}`;
@@ -11225,12 +11273,6 @@ var init_app_schema = __esmMin(() => {
11225
11273
  label: "缓存删除",
11226
11274
  description: "下载的视频缓存自动删除,非必要不修改!"
11227
11275
  },
11228
- {
11229
- key: "downloadImageLocally",
11230
- type: "switch",
11231
- label: "本地下载图片",
11232
- description: "发送图片时由插件本地下载后使用 file 协议传递,而非直接传递 HTTP 链接给上游下载"
11233
- },
11234
11276
  {
11235
11277
  type: "divider",
11236
11278
  title: "解析优先级设置"
@@ -12622,6 +12664,26 @@ var init_upload_schema = __esmMin(() => {
12622
12664
  disabled: $or($not("usegroupfile"), $var("sendbase64")),
12623
12665
  rules: [{ min: 1 }]
12624
12666
  },
12667
+ {
12668
+ key: "imageSendMode",
12669
+ type: "radio",
12670
+ label: "网络图片发送方式",
12671
+ description: "选择发送网络图片的方式:\n• URL - 直接传递链接给上游(可能因上游网络问题超时)\n• File - 下载后用 file 协议发送(需 Karin 与协议端同系统)\n• Base64 - 转 base64 发送(传输数据增大 1/3,不在同一网络环境可能导致额外带宽成本)",
12672
+ options: [
12673
+ {
12674
+ label: "URL 链接(直接传递)",
12675
+ value: "url"
12676
+ },
12677
+ {
12678
+ label: "File 协议(本地文件)",
12679
+ value: "file"
12680
+ },
12681
+ {
12682
+ label: "Base64(编码传输)",
12683
+ value: "base64"
12684
+ }
12685
+ ]
12686
+ },
12625
12687
  {
12626
12688
  type: "divider",
12627
12689
  title: "上传拦截配置"
@@ -13272,22 +13334,27 @@ var init_handler = __esmMin(async () => {
13272
13334
  };
13273
13335
  wrapWithErrorHandler = (fn, options) => async (e, next) => {
13274
13336
  const emojiManager = e ? new EmojiReactionManager(e) : void 0;
13337
+ let processingTimer = null;
13338
+ let successTimer = null;
13275
13339
  if (emojiManager) {
13276
13340
  await emojiManager.add("EYES");
13277
- setTimeout(() => {
13341
+ processingTimer = setTimeout(() => {
13278
13342
  emojiManager.add("PROCESSING").catch(() => {});
13279
13343
  }, 1500);
13280
13344
  }
13281
13345
  const ctx = logger.runContext(async () => fn(e, next));
13282
13346
  try {
13283
13347
  const result = await ctx.run();
13284
- if (emojiManager) setTimeout(() => {
13348
+ if (emojiManager) successTimer = setTimeout(() => {
13285
13349
  emojiManager.replace("PROCESSING", "SUCCESS").catch(() => {});
13286
13350
  }, 1500);
13287
13351
  return result;
13288
13352
  } catch (error) {
13353
+ if (processingTimer) clearTimeout(processingTimer);
13354
+ if (successTimer) clearTimeout(successTimer);
13289
13355
  if (emojiManager) {
13290
- await emojiManager.clearAll();
13356
+ const processingEmojiId = emojiManager["getPlatformEmojiId"]("PROCESSING");
13357
+ if (emojiManager.has(processingEmojiId)) await emojiManager.remove("PROCESSING");
13291
13358
  await emojiManager.add("ERROR");
13292
13359
  }
13293
13360
  await new Promise((resolve$1) => setTimeout(resolve$1, 100));
@@ -14787,6 +14854,28 @@ const webConfig = defineConfig({
14787
14854
  rules: [{ min: 1 }],
14788
14855
  isDisabled: !all.upload.usegroupfile || all.upload.sendbase64
14789
14856
  }),
14857
+ components.radio.group("imageSendMode", {
14858
+ label: "网络图片发送方式",
14859
+ orientation: "vertical",
14860
+ defaultValue: all.upload.imageSendMode,
14861
+ radio: [
14862
+ components.radio.create("imageSendMode:radio-1", {
14863
+ label: "URL 链接(直接传递)",
14864
+ description: "直接传递 HTTP 链接给上游下载,可能因上游网络问题导致下载超时",
14865
+ value: "url"
14866
+ }),
14867
+ components.radio.create("imageSendMode:radio-2", {
14868
+ label: "File 协议(本地文件)",
14869
+ description: "下载到本地后使用 file 协议发送,需 Karin 与协议端在同一系统",
14870
+ value: "file"
14871
+ }),
14872
+ components.radio.create("imageSendMode:radio-3", {
14873
+ label: "Base64(编码传输)",
14874
+ description: "下载后转换为 base64 发送,传输数据增大约 1/3,不在同一网络环境可能导致额外带宽成本",
14875
+ value: "base64"
14876
+ })
14877
+ ]
14878
+ }),
14790
14879
  components.divider.create("divider-upload-limit", {
14791
14880
  description: "上传拦截配置",
14792
14881
  descPosition: 20
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "./core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import "./core_chunk/main-eqK3KHoJ.js";
2
+ import "./core_chunk/main-B-QVNIL7.js";
3
3
  import "./core_chunk/vendor-CYCcUtqE.js";
4
4
  import "./core_chunk/template-n3eb7E79.js";
5
5
  export {};
package/lib/root.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import "./core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { D as Root, O as init_root } from "./core_chunk/main-eqK3KHoJ.js";
2
+ import { D as Root, O as init_root } from "./core_chunk/main-B-QVNIL7.js";
3
3
  init_root();
4
4
  export { Root };
package/lib/web.config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "./core_chunk/rolldown-runtime-BMXAG3ag.js";
2
- import { E as web_config_default, T as webConfig } from "./core_chunk/main-eqK3KHoJ.js";
2
+ import { E as web_config_default, T as webConfig } from "./core_chunk/main-B-QVNIL7.js";
3
3
  import "./core_chunk/vendor-CYCcUtqE.js";
4
4
  import "./core_chunk/template-n3eb7E79.js";
5
5
  export { web_config_default as default, webConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "karin-plugin-kkk",
3
- "version": "2.21.0",
3
+ "version": "2.21.1",
4
4
  "description": "Karin 的「抖音」「B 站」视频解析/动态推送插件",
5
5
  "keywords": [
6
6
  "karin-plugin",