karin-plugin-kkk 2.34.0 → 2.36.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.
Files changed (34) hide show
  1. package/CHANGELOG.md +869 -1165
  2. package/README.md +4 -5
  3. package/config/default_config/app.yaml +1 -1
  4. package/config/default_config/bilibili.yaml +2 -2
  5. package/config/default_config/config.json +136 -0
  6. package/config/default_config/cookies.yaml +2 -2
  7. package/config/default_config/douyin.yaml +3 -3
  8. package/config/default_config/kuaishou.yaml +1 -1
  9. package/config/default_config/pushlist.yaml +6 -6
  10. package/config/default_config/request.yaml +1 -1
  11. package/config/default_config/xiaohongshu.yaml +2 -2
  12. package/lib/build-metadata.json +5 -5
  13. package/lib/core_chunk/amagi.d.mts +18 -18
  14. package/lib/core_chunk/amagi.js +1 -1
  15. package/lib/core_chunk/amagiClient.js +1 -1
  16. package/lib/core_chunk/main.js +752 -1928
  17. package/lib/core_chunk/vendor.js +4738 -2417
  18. package/lib/karin-plugin-kkk.css +198 -236
  19. package/lib/root.js +1 -1
  20. package/lib/web/assets/CronEditor-KiEwYMrF.js +1 -0
  21. package/lib/web/assets/DesktopLayout-Cdla4l6u.js +1 -0
  22. package/lib/web/assets/MobileLayout-DfXVuyA-.js +1 -0
  23. package/lib/web/assets/ThemeSwitch-DcAlzu8R.js +1 -0
  24. package/lib/web/assets/index-3mh4kVq1.css +2 -0
  25. package/lib/web/assets/index-zS6iiHmV.js +47 -0
  26. package/lib/web/index.html +3 -3
  27. package/package.json +28 -28
  28. package/resources/font/bilifont/font.css +3 -3
  29. package/resources/font/fansmedal-num/font.css +1 -1
  30. package/lib/web/assets/AboutPanel-DFjZYu60.js +0 -1
  31. package/lib/web/assets/DesktopLayout-Cr5AitGP.js +0 -1
  32. package/lib/web/assets/MobileLayout-BW-vN-VU.js +0 -2
  33. package/lib/web/assets/index-8KaTMCj2.css +0 -2
  34. package/lib/web/assets/index-CQhoJUBv.js +0 -28
@@ -1,10 +1,10 @@
1
1
  import { i as __toESM } from "./rolldown-runtime.js";
2
- import { $ as SiGooglephotos, $n as ArrowDownToLine, $t as RefreshCw, A as RiHashtag, An as Download, At as Users, B as RiThumbUpFill, Bn as CircleFadingArrowUp, Bt as Square, C as AiFillHeart, Cn as Gift, Ct as format, D as Markdown, Dn as Eye, Dt as X, E as AiOutlineVideoCamera, En as FilePlay, Et as Zap, F as RiMessage3Fill, Fn as Code, Ft as TriangleAlert, G as RiVideoLine, Gn as Check, Gt as Shield, H as RiTrophyFill, Hn as CircleCheck, Ht as Sparkles, I as RiPieChart2Fill, In as Clock, It as Trash2, J as SiSamsung, Jn as Calendar, Jt as Settings2, K as SiXiaomi, Kn as ChartColumn, Kt as ShieldCheck, L as RiShareForwardFill, Ln as Clock3, Lt as Terminal, M as RiHeart3Line, Mn as Cpu, Mt as UsersRound, N as RiHeartLine, Nn as CornerDownLeft, Nt as UserPlus, O as RiArrowRightFill, On as EyeOff, Ot as WandSparkles, P as RiLiveLine, Pn as Copy, Pt as Upload, Q as SiHonor, Qn as BellRing, Qt as RotateCcw, R as RiStarFill, Rn as Clapperboard, Rt as Sun, S as BiImage, Sn as GitBranch, St as formatDistanceToNow, T as AiFillStar, Tn as FileText, Tt as twMerge, U as RiUserFollowLine, Un as CircleCheckBig, Ut as Smartphone, V as RiTiktokFill, Vn as CircleEllipsis, Vt as SquarePen, W as RiVerifiedBadgeFill, Wn as CircleAlert, Wt as ShoppingBag, X as SiOneplus, Xn as Bot, Xt as ScanLine, Y as SiOppo, Yn as Box, Yt as Search, Z as SiHuawei, Zn as Bookmark, Zt as Save, _ as MdSchedule, _n as LayoutTemplate, _t as t, a as VictoryScatter, an as Play, ar as require_react, at as r$1, b as FaTiktok, bn as Heart, bt as parse, c as VictoryChart, cn as Music, cr as Xhshow, ct as e$1, d as VictoryTheme, dn as MessageCircle, dt as n, en as Radio, er as Chip, et as SiGithub, f as rehypeHighlight, fn as Menu, ft as e, g as MdLocationOn, gn as Link, gt as c, h as MdLightbulbOutline, hn as LoaderCircle, ht as o$3, i as require_heic_decode, in as Plus, ir as require_server_node, it as o$2, j as RiHeart3Fill, jn as Crown, jt as User, k as RiGroupLine, kn as ExternalLink, kt as Video, l as VictoryAxis, ln as Moon, lr as zod_default, lt as m, m as MdInfoOutline, mn as MapPin, mt as o$1, n as require_lib, nn as QrCode, nr as clsx, nt as SiApple, o as VictoryPie, on as Pencil, or as require_protobufjs, ot as o, p as MdFitScreen, pn as Maximize, pt as a$1, q as SiVivo, qn as Camera, qt as Share2, r as require_jpeg_js, rn as Puzzle, rr as require_jsx_runtime, rt as SiAnthropic, s as VictoryLine, sn as Palette, sr as Chalk, st as o$4, t as createProxyMiddleware, tn as Quote, tr as Button, tt as SiBilibili, u as VictoryLabel, un as Monitor, ut as a, v as FaCommentDots, vn as Info, vt as r, w as AiFillPushpin, wn as Gamepad2, wt as differenceInSeconds, x as FaUserGroup, xn as Hash, xt as fromUnixTime, y as FaMusic, yn as Image$1, yt as zhCN, z as RiStarLine, zn as CircleQuestionMark, zt as Star } from "./vendor.js";
2
+ import { $ as SiGooglephotos, $n as require_jsx_runtime, $t as Puzzle, A as RiHashtag, An as Copy, At as Upload, B as RiThumbUpFill, Bn as CircleAlert, Bt as ShoppingBag, C as AiFillHeart, Cn as Eye, Ct as X, D as Markdown, Dn as Crown, Dt as UsersRound, E as AiOutlineVideoCamera, En as Download, Et as Users, F as RiMessage3Fill, Fn as CircleQuestionMark, Ft as Star, G as RiVideoLine, Gn as Box, Gt as Search, H as RiTrophyFill, Hn as ChartColumn, Ht as ShieldCheck, I as RiPieChart2Fill, In as CircleFadingArrowUp, It as Square, J as SiSamsung, Jn as BellRing, Jt as RotateCcw, K as SiXiaomi, Kn as Bot, Kt as ScanLine, L as RiShareForwardFill, Ln as CircleEllipsis, Lt as SquarePen, M as RiHeart3Line, Mn as Clock, Mt as Trash2, N as RiHeartLine, Nn as Clock3, Nt as Terminal, O as RiArrowRightFill, On as Cpu, Ot as User, P as RiLiveLine, Pn as Clapperboard, Pt as Sun, Q as SiHonor, Qn as clsx, Qt as QrCode, R as RiStarFill, Rn as CircleCheck, Rt as Sparkles, S as BiImage, Sn as FilePlay, St as Zap, T as AiFillStar, Tn as ExternalLink, Tt as Video, U as RiUserFollowLine, Un as Camera, Ut as Share2, V as RiTiktokFill, Vn as Check, Vt as Shield, W as RiVerifiedBadgeFill, Wn as Calendar, Wt as Settings2, X as SiOneplus, Xn as Chip, Xt as Radio, Y as SiOppo, Yn as ArrowDownToLine, Yt as RefreshCw, Z as SiHuawei, Zn as Button, Zt as Quote, _ as MdSchedule, _n as Hash, _t as fromUnixTime, a as VictoryScatter, an as Moon, ar as zod_default, at as r, b as FaTiktok, bn as Gamepad2, bt as differenceInSeconds, c as VictoryChart, cn as Menu, ct as m, d as VictoryTheme, dn as LoaderCircle, dt as e, en as Plus, er as require_server_node, et as SiGithub, f as rehypeHighlight, fn as Link, ft as a$1, g as MdLocationOn, gn as Heart, gt as parse, h as MdLightbulbOutline, hn as Image$1, ht as zhCN, i as require_heic_decode, in as Music, ir as Xhshow, it as o$2, j as RiHeart3Fill, jn as Code, jt as TriangleAlert, k as RiGroupLine, kn as CornerDownLeft, kt as UserPlus, l as VictoryAxis, ln as Maximize, lt as a, m as MdInfoOutline, mn as Info, mt as c, n as require_lib, nn as Pencil, nr as require_protobufjs, nt as SiApple, o as VictoryPie, on as Monitor, ot as o, p as MdFitScreen, pn as LayoutTemplate, pt as o$1, q as SiVivo, qn as Bookmark, qt as Save, r as require_jpeg_js, rn as Palette, rr as Chalk, rt as SiAnthropic, s as VictoryLine, sn as MessageCircle, st as o$3, t as createProxyMiddleware, tn as Play, tr as require_react, tt as SiBilibili, u as VictoryLabel, un as MapPin, ut as n, v as FaCommentDots, vn as GitBranch, vt as formatDistanceToNow, w as AiFillPushpin, wn as EyeOff, wt as WandSparkles, x as FaUserGroup, xn as FileText, xt as twMerge, y as FaMusic, yn as Gift, yt as format, z as RiStarLine, zn as CircleCheckBig, zt as Smartphone } from "./vendor.js";
3
3
  import "node:module";
4
4
  import fs from "node:fs";
5
5
  import path, { resolve } from "node:path";
6
- import URL$2, { fileURLToPath } from "node:url";
7
- import karin$1, { BOT_CONNECT, app, authMiddleware, checkPkgUpdate, checkPort, common, config, copyConfigSync, createBadRequestResponse, createNotFoundResponse, createServerErrorResponse, createSuccessResponse, db, defineConfig, ffmpeg, ffprobe, filesByExt, getBot, hooks, karin, karinPathHtml, karinPathTemp, logger, logger as logger$1, logs, mkdirSync, parseChangelog, range, render, requireFileSync, restart, segment, updatePkg, watch } from "node-karin";
6
+ import URL$1, { fileURLToPath } from "node:url";
7
+ import karin$1, { BOT_CONNECT, app, authMiddleware, checkPkgUpdate, checkPort, common, config, createBadRequestResponse, createNotFoundResponse, createServerErrorResponse, createSuccessResponse, db, defineConfig, ffmpeg, ffprobe, hooks, karin, karinPathHtml, karinPathTemp, logger, logger as logger$1, logs, mkdirSync, parseChangelog, range, render, restart, segment, updatePkg, watch } from "node-karin";
8
8
  import { EventEmitter } from "node:events";
9
9
  import crypto from "node:crypto";
10
10
  import axios, { AxiosError } from "node-karin/axios";
@@ -1597,7 +1597,7 @@ var xiaohongshuApiUrls = {
1597
1597
  * @param data - 请求参数
1598
1598
  * @returns 完整的接口URL
1599
1599
  */
1600
- emojiList(data) {
1600
+ emojiList(_data) {
1601
1601
  return {
1602
1602
  apiPath: "/api/im/redmoji/detail",
1603
1603
  Url: "https://edith.xiaohongshu.com/api/im/redmoji/detail"
@@ -2503,7 +2503,7 @@ var generateSecChUa = (userAgent) => {
2503
2503
  */
2504
2504
  var getDouyinDefaultConfig = (cookie, requestConfig) => {
2505
2505
  let finalUserAgent = requestConfig?.headers?.["User-Agent"] ?? "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36";
2506
- finalUserAgent = finalUserAgent.replace(/\s+Edg\/[\d\.]+/g, "");
2506
+ finalUserAgent = finalUserAgent.replace(/\s+Edg\/[\d.]+/g, "");
2507
2507
  const defHeaders = {
2508
2508
  Accept: "application/json, text/plain, */*",
2509
2509
  "Accept-Encoding": "gzip, deflate, br, zstd",
@@ -2800,7 +2800,8 @@ var SM3 = class {
2800
2800
  this.chunk = this.chunk.concat(a.slice(0, f));
2801
2801
  while (this.chunk.length >= 64) {
2802
2802
  this._compress(this.chunk);
2803
- f < a.length ? this.chunk = a.slice(f, Math.min(f + 64, a.length)) : this.chunk = [];
2803
+ if (f < a.length) this.chunk = a.slice(f, Math.min(f + 64, a.length));
2804
+ else this.chunk = [];
2804
2805
  f += 64;
2805
2806
  }
2806
2807
  }
@@ -2836,10 +2837,17 @@ var SM3 = class {
2836
2837
  if (t.length < 64) console.error("compress error: not enough data");
2837
2838
  else {
2838
2839
  for (var f = ((e) => {
2839
- for (var r = new Array(132), t = 0; t < 16; t++) r[t] = e[4 * t] << 24, r[t] |= e[4 * t + 1] << 16, r[t] |= e[4 * t + 2] << 8, r[t] |= e[4 * t + 3], r[t] >>>= 0;
2840
+ for (var r = new Array(132), t = 0; t < 16; t++) {
2841
+ r[t] = e[4 * t] << 24;
2842
+ r[t] |= e[4 * t + 1] << 16;
2843
+ r[t] |= e[4 * t + 2] << 8;
2844
+ r[t] |= e[4 * t + 3];
2845
+ r[t] >>>= 0;
2846
+ }
2840
2847
  for (var n = 16; n < 68; n++) {
2841
2848
  let a = r[n - 16] ^ r[n - 9] ^ this.le(r[n - 3], 15);
2842
- a = a ^ this.le(a, 15) ^ this.le(a, 23), r[n] = (a ^ this.le(r[n - 13], 7) ^ r[n - 6]) >>> 0;
2849
+ a = a ^ this.le(a, 15) ^ this.le(a, 23);
2850
+ r[n] = (a ^ this.le(r[n - 13], 7) ^ r[n - 6]) >>> 0;
2843
2851
  }
2844
2852
  for (n = 0; n < 64; n++) r[n + 68] = (r[n] ^ r[n + 4]) >>> 0;
2845
2853
  return r;
@@ -2849,7 +2857,15 @@ var SM3 = class {
2849
2857
  let u = this.pe(c, i[0], i[1], i[2]);
2850
2858
  u = (4294967295 & (u = u + i[3] + s + f[c + 68])) >>> 0;
2851
2859
  let b = this.he(c, i[4], i[5], i[6]);
2852
- b = (4294967295 & (b = b + i[7] + o + f[c])) >>> 0, i[3] = i[2], i[2] = this.le(i[1], 9), i[1] = i[0], i[0] = u, i[7] = i[6], i[6] = this.le(i[5], 19), i[5] = i[4], i[4] = (b ^ this.le(b, 9) ^ this.le(b, 17)) >>> 0;
2860
+ b = (4294967295 & (b = b + i[7] + o + f[c])) >>> 0;
2861
+ i[3] = i[2];
2862
+ i[2] = this.le(i[1], 9);
2863
+ i[1] = i[0];
2864
+ i[0] = u;
2865
+ i[7] = i[6];
2866
+ i[6] = this.le(i[5], 19);
2867
+ i[5] = i[4];
2868
+ i[4] = (b ^ this.le(b, 9) ^ this.le(b, 17)) >>> 0;
2853
2869
  }
2854
2870
  for (let l = 0; l < 8; l++) this.reg[l] = (this.reg[l] ^ i[l]) >>> 0;
2855
2871
  }
@@ -3160,7 +3176,7 @@ function generate_random_str() {
3160
3176
  * @returns 清理后的User-Agent字符串
3161
3177
  */
3162
3178
  var cleanUserAgentForSigning = (userAgent) => {
3163
- return userAgent.replace(/\s+Edg\/[\d\.]+/g, "");
3179
+ return userAgent.replace(/\s+Edg\/[\d.]+/g, "");
3164
3180
  };
3165
3181
  /**
3166
3182
  * 抖音a_bogus签名算法
@@ -3272,7 +3288,7 @@ var XBogus = class {
3272
3288
  * @returns 包含完整URL、X-Bogus值和使用的User-Agent的元组
3273
3289
  */
3274
3290
  getXBogus(url, ua) {
3275
- const parsedUrl = new URL$2.URL(url);
3291
+ const parsedUrl = new URL$1.URL(url);
3276
3292
  const urlPath = parsedUrl.pathname + parsedUrl.search;
3277
3293
  const currentUa = ua ?? this.defaultUa;
3278
3294
  const rc4EncryptedUa = this.rc4Encrypt(this.uaKey, currentUa);
@@ -4957,7 +4973,7 @@ async function fetchTextWork(options, cookie, requestConfig) {
4957
4973
  * console.log(result.data) // 自动解析的作品数据
4958
4974
  * ```
4959
4975
  */
4960
- async function parseWork$1(options, cookie, requestConfig) {
4976
+ async function parseWork(options, cookie, requestConfig) {
4961
4977
  return fetchDouyinInternal("parseWork", options, {
4962
4978
  cookie,
4963
4979
  requestConfig
@@ -5003,7 +5019,7 @@ function createBoundDouyinFetcher(cookie, requestConfig) {
5003
5019
  fetchImageAlbumWork: (options, reqConfig) => fetchImageAlbumWork(options, cookie, reqConfig ?? requestConfig),
5004
5020
  fetchSlidesWork: (options, reqConfig) => fetchSlidesWork(options, cookie, reqConfig ?? requestConfig),
5005
5021
  fetchTextWork: (options, reqConfig) => fetchTextWork(options, cookie, reqConfig ?? requestConfig),
5006
- parseWork: (options, reqConfig) => parseWork$1(options, cookie, reqConfig ?? requestConfig),
5022
+ parseWork: (options, reqConfig) => parseWork(options, cookie, reqConfig ?? requestConfig),
5007
5023
  fetchDanmakuList: (options, reqConfig) => fetchDanmakuList(options, cookie, reqConfig ?? requestConfig),
5008
5024
  fetchWorkComments: (options, reqConfig) => fetchWorkComments$1(options, cookie, reqConfig ?? requestConfig),
5009
5025
  fetchCommentReplies: (options, reqConfig) => fetchCommentReplies(options, cookie, reqConfig ?? requestConfig),
@@ -5041,7 +5057,7 @@ var douyinFetcher$1 = {
5041
5057
  fetchImageAlbumWork,
5042
5058
  fetchSlidesWork,
5043
5059
  fetchTextWork,
5044
- parseWork: parseWork$1,
5060
+ parseWork,
5045
5061
  fetchDanmakuList,
5046
5062
  fetchWorkComments: fetchWorkComments$1,
5047
5063
  fetchCommentReplies,
@@ -7919,7 +7935,7 @@ var createNetworkErrorResult = (error, retries) => {
7919
7935
  * @returns 清理后的User-Agent字符串
7920
7936
  */
7921
7937
  var cleanUserAgent = (userAgent) => {
7922
- return userAgent.replace(/\s+Edg\/[\d\.]+/g, "");
7938
+ return userAgent.replace(/\s+Edg\/[\d.]+/g, "");
7923
7939
  };
7924
7940
  /**
7925
7941
  * 执行网络请求并返回数据(带自动重试)
@@ -8171,8 +8187,7 @@ var qtparam = async (BASEURL, cookie) => {
8171
8187
  126,
8172
8188
  127
8173
8189
  ];
8174
- let isvip;
8175
- logininfo.data.vipStatus === 1 ? isvip = true : isvip = false;
8190
+ const isvip = logininfo.data.vipStatus === 1;
8176
8191
  if (isvip) return {
8177
8192
  QUERY: `&fnval=4048&fourk=1&${sign}`,
8178
8193
  STATUS: "isLogin",
@@ -8313,7 +8328,7 @@ var DmSegMobileReplyType = null;
8313
8328
  */
8314
8329
  function getProtoType() {
8315
8330
  if (DmSegMobileReplyType) return DmSegMobileReplyType;
8316
- DmSegMobileReplyType = import_protobufjs.default.Root.fromJSON(DANMAKU_PROTO_JSON).lookupType("bilibili.community.service.dm.v1.DmSegMobileReply");
8331
+ DmSegMobileReplyType = import_protobufjs.Root.fromJSON(DANMAKU_PROTO_JSON).lookupType("bilibili.community.service.dm.v1.DmSegMobileReply");
8317
8332
  return DmSegMobileReplyType;
8318
8333
  }
8319
8334
  /**
@@ -9330,15 +9345,6 @@ var kuaishouUtils = {
9330
9345
  //#endregion
9331
9346
  //#region ../amagi/packages/core/src/platform/xiaohongshu/XiaohongshuApi.ts
9332
9347
  /**
9333
- * 小红书 API 模块 (已废弃)
9334
- *
9335
- * 此模块中的 API 已在 v6 版本废弃
9336
- * 请使用 xiaohongshuFetcher 或 client.xiaohongshu.fetcher 替代
9337
- *
9338
- * @module platform/xiaohongshu/XiaohongshuApi
9339
- * @deprecated v6 已废弃,请使用 fetcher API 替代
9340
- */
9341
- /**
9342
9348
  * 创建废弃的 API 存根函数
9343
9349
  */
9344
9350
  var createDeprecatedStub = (methodName) => {
@@ -10200,62 +10206,79 @@ var Cfg = class {
10200
10206
  dirCfgPath;
10201
10207
  /** 默认配置文件路径 */
10202
10208
  defCfgPath;
10209
+ /** JSON 配置文件路径 */
10210
+ get jsonConfigPath() {
10211
+ return `${this.dirCfgPath}/config.json`;
10212
+ }
10213
+ get defJsonConfigPath() {
10214
+ return `${this.defCfgPath}/config.json`;
10215
+ }
10203
10216
  constructor() {
10204
10217
  this.dirCfgPath = `${karinPathBase}/${Root.pluginName}/config`;
10205
10218
  this.defCfgPath = `${Root.pluginPath}/config/default_config/`;
10206
10219
  }
10207
10220
  /** 初始化配置 */
10208
10221
  initCfg() {
10209
- copyConfigSync(this.defCfgPath, this.dirCfgPath);
10210
- const files = filesByExt(this.dirCfgPath, ".yaml", "name");
10211
- for (const file of files) {
10212
- const config = YAML.parseDocument(fs.readFileSync(`${this.dirCfgPath}/${file}`, "utf8"));
10213
- const defConfig = YAML.parseDocument(fs.readFileSync(`${this.defCfgPath}/${file}`, "utf8"));
10214
- const { differences, result } = this.mergeObjectsWithPriority(config, defConfig);
10215
- if (differences) fs.writeFileSync(`${this.dirCfgPath}/${file}`, result.toString({ lineWidth: -1 }));
10222
+ if (!fs.existsSync(this.dirCfgPath)) fs.mkdirSync(this.dirCfgPath, { recursive: true });
10223
+ const hasJson = fs.existsSync(this.jsonConfigPath);
10224
+ const hasYaml = [
10225
+ "app",
10226
+ "bilibili",
10227
+ "cookies",
10228
+ "douyin",
10229
+ "kuaishou",
10230
+ "pushlist",
10231
+ "request",
10232
+ "upload",
10233
+ "xiaohongshu"
10234
+ ].some((name) => fs.existsSync(`${this.dirCfgPath}/${name}.yaml`));
10235
+ if (!hasJson && !hasYaml && fs.existsSync(this.defJsonConfigPath)) fs.copyFileSync(this.defJsonConfigPath, this.jsonConfigPath);
10236
+ if (fs.existsSync(this.jsonConfigPath) && fs.existsSync(this.defJsonConfigPath)) {
10237
+ const userConfig = this.getJson();
10238
+ const defConfig = JSON.parse(fs.readFileSync(this.defJsonConfigPath, "utf8"));
10239
+ const merged = this.mergeJsonConfigs(defConfig, userConfig);
10240
+ if (JSON.stringify(merged) !== JSON.stringify(userConfig)) this.setJson(merged);
10216
10241
  }
10217
- /**
10218
- * @description 监听配置文件
10219
- */
10220
10242
  setTimeout(() => {
10221
- filesByExt(this.dirCfgPath, ".yaml", "abs").forEach((file) => watch(file, (_old, _now) => {
10222
- const fileName = path.basename(file, ".yaml");
10223
- if (fileName === "cookies" || fileName === "request") {
10224
- logger.debug(`[Config] 检测到 ${fileName} 配置变化,正在重载 Amagi Client...`);
10225
- import("./amagiClient.js").then(({ reloadAmagiConfig }) => {
10226
- reloadAmagiConfig();
10227
- }).catch((error) => {
10228
- logger.error(`[Config] 重载 Amagi Client 失败: ${error}`);
10229
- });
10243
+ if (fs.existsSync(this.jsonConfigPath)) watch(this.jsonConfigPath, (old, now) => {
10244
+ if (old?.amagi !== now?.amagi) {
10245
+ logger.debug("[karin-plugin-kkk][Config] 检测到 amagi 配置变化,正在重载 Amagi Client...");
10246
+ import("./amagiClient.js").then(({ reloadAmagiConfig }) => reloadAmagiConfig()).catch((error) => logger.error(`[karin-plugin-kkk][Config] 重载 Amagi Client 失败: ${error}`));
10230
10247
  }
10231
- }));
10248
+ });
10232
10249
  }, 2e3);
10233
10250
  return this;
10234
10251
  }
10252
+ /** 读取 JSON 配置 */
10253
+ getJson() {
10254
+ return JSON.parse(fs.readFileSync(this.jsonConfigPath, "utf8"));
10255
+ }
10256
+ /** 写入 JSON 配置 */
10257
+ setJson(config) {
10258
+ fs.writeFileSync(this.jsonConfigPath, JSON.stringify(config, null, 2), "utf8");
10259
+ }
10260
+ /** 深度合并 JSON 配置(保留用户值,补充默认值) */
10261
+ mergeJsonConfigs(def, user) {
10262
+ if (!def || typeof def !== "object" || Array.isArray(def)) return user ?? def;
10263
+ if (!user || typeof user !== "object" || Array.isArray(user)) return user ?? def;
10264
+ const result = { ...def };
10265
+ for (const key in user) if (typeof user[key] === "object" && !Array.isArray(user[key]) && user[key] !== null) result[key] = this.mergeJsonConfigs(def[key], user[key]);
10266
+ else result[key] = user[key];
10267
+ return result;
10268
+ }
10235
10269
  /**
10236
- * 获取默认配置和用户配置
10237
- * @param name 配置文件名
10238
- * @returns 返回合并后的配置
10270
+ * 获取配置
10271
+ * @param name 配置键名
10239
10272
  */
10240
10273
  getDefOrConfig(name) {
10241
- const def = this.getYaml("default_config", name);
10242
- const config = this.getYaml("config", name);
10243
- return {
10244
- ...def,
10245
- ...config
10246
- };
10274
+ return this.getJson()[name] || {};
10247
10275
  }
10248
10276
  /** 获取所有配置文件 */
10249
10277
  async All() {
10250
10278
  const { getDouyinDB, getBilibiliDB } = await import("./db.js");
10251
10279
  const douyinDB = await getDouyinDB();
10252
10280
  const bilibiliDB = await getBilibiliDB();
10253
- const allConfig = {};
10254
- const files = fs.readdirSync(this.defCfgPath);
10255
- for (const file of files) {
10256
- const fileName = path.basename(file, ".yaml");
10257
- allConfig[fileName] = this.getDefOrConfig(fileName) || {};
10258
- }
10281
+ const allConfig = this.getJson();
10259
10282
  if (allConfig.pushlist) try {
10260
10283
  if (allConfig.pushlist.douyin) for (const item of allConfig.pushlist.douyin) {
10261
10284
  const filterWords = await douyinDB.getFilterWords(item.sec_uid);
@@ -10279,71 +10302,62 @@ var Cfg = class {
10279
10302
  return allConfig;
10280
10303
  }
10281
10304
  /**
10282
- * 获取 YAML 文件内容
10283
- * @param type 配置文件类型
10284
- * @param name 配置文件名
10285
- * @returns 返回 YAML 文件内容
10286
- */
10287
- getYaml(type, name) {
10288
- return requireFileSync(type === "config" ? `${this.dirCfgPath}/${name}.yaml` : `${this.defCfgPath}/${name}.yaml`, { force: true });
10289
- }
10290
- /**
10291
- * 修改配置文件
10292
- * @param name 文件名
10293
- * @param key 键
10294
- * @param value 值
10295
- * @param type 配置文件类型,默认为用户配置文件 `config`
10296
- */
10297
- Modify(name, key, value, type = "config") {
10298
- const path = type === "config" ? `${this.dirCfgPath}/${name}.yaml` : `${this.defCfgPath}/${name}.yaml`;
10299
- const yamlData = YAML.parseDocument(fs.readFileSync(path, "utf8"));
10300
- const keys = key.split(".");
10301
- yamlData.setIn(keys, value);
10302
- fs.writeFileSync(path, yamlData.toString({ lineWidth: -1 }), "utf8");
10303
- }
10304
- /**
10305
- * 修改整个配置文件,保留注释
10305
+ * 修改整个配置文件
10306
10306
  * @param name 文件名
10307
10307
  * @param config 完整的配置对象
10308
- * @param type 配置文件类型,默认为用户配置文件 `config`
10309
10308
  */
10310
10309
  async ModifyPro(name, config, type = "config") {
10310
+ if (type !== "config") return false;
10311
10311
  const { getDouyinDB, getBilibiliDB } = await import("./db.js");
10312
10312
  const douyinDB = await getDouyinDB();
10313
10313
  const bilibiliDB = await getBilibiliDB();
10314
- const filePath = type === "config" ? `${this.dirCfgPath}/${name}.yaml` : `${this.defCfgPath}/${name}.yaml`;
10315
- try {
10316
- const existingContent = fs.readFileSync(filePath, "utf8");
10317
- const doc = YAML.parseDocument(existingContent);
10318
- let filterCfg = config;
10319
- if (name === "pushlist" && ("douyin" in config || "bilibili" in config)) {
10320
- const cleanedConfig = { ...config };
10321
- if ("douyin" in cleanedConfig) cleanedConfig.douyin = cleanedConfig.douyin.map((item) => {
10322
- const { Keywords, Tags, filterMode, ...rest } = item;
10323
- return rest;
10324
- });
10325
- if ("bilibili" in cleanedConfig) cleanedConfig.bilibili = cleanedConfig.bilibili.map((item) => {
10326
- const { Keywords, Tags, filterMode, ...rest } = item;
10327
- return rest;
10328
- });
10329
- filterCfg = cleanedConfig;
10330
- }
10331
- const newConfigNode = YAML.parseDocument(YAML.stringify(filterCfg)).contents;
10332
- this.deepMergeYaml(doc.contents, newConfigNode);
10333
- fs.writeFileSync(filePath, doc.toString({ lineWidth: -1 }), "utf8");
10334
- if ("douyin" in config) {
10335
- await this.syncFilterConfigToDb(config.douyin, douyinDB, "sec_uid");
10336
- logger.debug("已同步抖音过滤配置到数据库");
10337
- }
10338
- if ("bilibili" in config) {
10339
- await this.syncFilterConfigToDb(config.bilibili, bilibiliDB, "host_mid");
10340
- logger.debug("已同步B站过滤配置到数据库");
10341
- }
10342
- return true;
10343
- } catch (error) {
10344
- logger.error(`修改配置文件时发生错误:${error}`);
10345
- return false;
10314
+ const jsonConfig = this.getJson();
10315
+ jsonConfig[name] = config;
10316
+ this.setJson(jsonConfig);
10317
+ if ("douyin" in config) {
10318
+ await this.syncFilterConfigToDb(config.douyin, douyinDB, "sec_uid");
10319
+ logger.debug("[karin-plugin-kkk][Config] 已同步抖音过滤配置到数据库");
10346
10320
  }
10321
+ if ("bilibili" in config) {
10322
+ await this.syncFilterConfigToDb(config.bilibili, bilibiliDB, "host_mid");
10323
+ logger.debug("[karin-plugin-kkk][Config] 已同步B站过滤配置到数据库");
10324
+ }
10325
+ return true;
10326
+ }
10327
+ /**
10328
+ * 修改配置字段(支持深层嵌套路径,包括数组索引)
10329
+ * @param moduleName 模块名
10330
+ * @param path 字段路径,如 'push.switch' 或 'cookies.douyin' 或 'list[0].name'
10331
+ * @param value 新值
10332
+ */
10333
+ async Modify(moduleName, path, value) {
10334
+ const jsonConfig = this.getJson();
10335
+ const pathKeys = this.parsePath(path);
10336
+ let target = jsonConfig[moduleName];
10337
+ for (let i = 0; i < pathKeys.length - 1; i++) {
10338
+ const key = pathKeys[i];
10339
+ if (!(key in target)) target[key] = {};
10340
+ target = target[key];
10341
+ }
10342
+ const lastKey = pathKeys[pathKeys.length - 1];
10343
+ target[lastKey] = value;
10344
+ this.setJson(jsonConfig);
10345
+ if (moduleName === "pushlist") {
10346
+ const { getDouyinDB, getBilibiliDB } = await import("./db.js");
10347
+ const douyinDB = await getDouyinDB();
10348
+ const bilibiliDB = await getBilibiliDB();
10349
+ if ("douyin" in jsonConfig[moduleName]) await this.syncFilterConfigToDb(jsonConfig[moduleName].douyin, douyinDB, "sec_uid");
10350
+ if ("bilibili" in jsonConfig[moduleName]) await this.syncFilterConfigToDb(jsonConfig[moduleName].bilibili, bilibiliDB, "host_mid");
10351
+ }
10352
+ return true;
10353
+ }
10354
+ /**
10355
+ * 解析路径字符串,支持点号和数组索引
10356
+ * 'a.b.c' => ['a', 'b', 'c']
10357
+ * 'a[0].b' => ['a', '0', 'b']
10358
+ */
10359
+ parsePath(path) {
10360
+ return path.replace(/\[(\d+)\]/g, ".$1").split(".");
10347
10361
  }
10348
10362
  /**
10349
10363
  * 同步过滤配置到数据库
@@ -10369,86 +10383,115 @@ var Cfg = class {
10369
10383
  }
10370
10384
  }
10371
10385
  /**
10372
- * 深度合并YAML节点(保留目标注释)
10373
- * @param target 目标节点(保留注释的原始节点)
10374
- * @param source 源节点(提供新值的节点)
10375
- */
10376
- deepMergeYaml(target, source) {
10377
- if (YAML.isMap(target) && YAML.isMap(source)) for (const pair of source.items) {
10378
- const key = pair.key;
10379
- const sourceVal = pair.value;
10380
- const targetVal = target.get(key);
10381
- if (targetVal === void 0) target.set(key, sourceVal);
10382
- else if (YAML.isMap(targetVal) && YAML.isMap(sourceVal)) this.deepMergeYaml(targetVal, sourceVal);
10383
- else if (YAML.isSeq(targetVal) && YAML.isSeq(sourceVal)) {
10384
- targetVal.items = sourceVal.items;
10385
- targetVal.flow = sourceVal.flow;
10386
- } else target.set(key, sourceVal);
10387
- }
10388
- }
10389
- mergeObjectsWithPriority(userDoc, defaultDoc) {
10390
- let differences = false;
10391
- /** 合并 YAML 对象,确保注释保留 */
10392
- const mergeYamlNodes = (target, source) => {
10393
- if (YAML.isMap(target) && YAML.isMap(source)) for (const pair of source.items) {
10394
- const key = pair.key;
10395
- const value = pair.value;
10396
- const existing = target.get(key);
10397
- if (existing === void 0) {
10398
- differences = true;
10399
- target.set(key, value);
10400
- } else if (YAML.isMap(value) && YAML.isMap(existing)) mergeYamlNodes(existing, value);
10401
- else if (existing !== value) {
10402
- differences = true;
10403
- target.set(key, value);
10404
- }
10405
- }
10406
- };
10407
- mergeYamlNodes(defaultDoc.contents, userDoc.contents);
10408
- return {
10409
- differences,
10410
- result: defaultDoc
10411
- };
10412
- }
10413
- /**
10414
10386
  * 同步配置到数据库
10415
- * 这个方法应该在所有模块都初始化完成后调用
10416
10387
  */
10417
10388
  async syncConfigToDatabase() {
10418
10389
  try {
10419
10390
  const { getDouyinDB, getBilibiliDB } = await import("./db.js");
10420
10391
  const douyinDB = await getDouyinDB();
10421
10392
  const bilibiliDB = await getBilibiliDB();
10422
- const pushCfg = this.getYaml("config", "pushlist");
10423
- if (pushCfg.bilibili) await bilibiliDB.syncConfigSubscriptions(pushCfg.bilibili);
10424
- if (pushCfg.douyin) await douyinDB.syncConfigSubscriptions(pushCfg.douyin);
10425
- logger.debug("[BilibiliDB] + [DouyinDB] 配置已同步到数据库");
10393
+ const pushCfg = this.getJson().pushlist;
10394
+ if (pushCfg?.bilibili) await bilibiliDB.syncConfigSubscriptions(pushCfg.bilibili);
10395
+ if (pushCfg?.douyin) await douyinDB.syncConfigSubscriptions(pushCfg.douyin);
10396
+ logger.debug("[karin-plugin-kkk][BilibiliDB] + [DouyinDB] 配置已同步到数据库");
10426
10397
  } catch (error) {
10427
- logger.error("同步配置到数据库失败:", error);
10398
+ logger.error("[karin-plugin-kkk]同步配置到数据库失败:", error);
10428
10399
  }
10429
10400
  }
10430
10401
  };
10431
- /**
10432
- * 配置实例缓存
10433
- */
10434
10402
  var configInstance = null;
10435
- /**
10436
- * 获取配置实例(延迟初始化)
10437
- * @returns 配置实例
10438
- */
10403
+ var migrationExecuted = false;
10439
10404
  var getConfigInstance = () => {
10440
- if (!configInstance) configInstance = new Proxy(new Cfg().initCfg(), { get(target, prop) {
10441
- if (prop in target) return target[prop];
10442
- return target.getDefOrConfig(prop);
10443
- } });
10405
+ if (!configInstance) {
10406
+ if (!migrationExecuted) {
10407
+ migrateConfigFromYaml();
10408
+ migrationExecuted = true;
10409
+ }
10410
+ configInstance = new Proxy(new Cfg().initCfg(), { get(target, prop) {
10411
+ if (prop in target) return target[prop];
10412
+ return target.getDefOrConfig(prop);
10413
+ } });
10414
+ }
10444
10415
  return configInstance;
10445
10416
  };
10446
- /**
10447
- * 配置对象代理
10448
- */
10449
10417
  var Config = new Proxy({}, { get(target, prop) {
10450
10418
  return getConfigInstance()[prop];
10451
10419
  } });
10420
+ /**
10421
+ * 从 YAML 迁移到 JSON
10422
+ */
10423
+ var migrateConfigFromYaml = () => {
10424
+ const dirCfgPath = `${karinPathBase}/${Root.pluginName}/config`;
10425
+ const defCfgPath = `${Root.pluginPath}/config/default_config/`;
10426
+ const jsonConfigPath = `${dirCfgPath}/config.json`;
10427
+ if (fs.existsSync(jsonConfigPath)) return;
10428
+ const yamlFiles = [
10429
+ "app",
10430
+ "bilibili",
10431
+ "cookies",
10432
+ "douyin",
10433
+ "kuaishou",
10434
+ "pushlist",
10435
+ "request",
10436
+ "upload",
10437
+ "xiaohongshu"
10438
+ ];
10439
+ if (!yamlFiles.some((name) => fs.existsSync(`${dirCfgPath}/${name}.yaml`))) return;
10440
+ try {
10441
+ const defYaml = {};
10442
+ for (const name of yamlFiles) {
10443
+ const file = `${defCfgPath}/${name}.yaml`;
10444
+ if (fs.existsSync(file)) defYaml[name] = YAML.parse(fs.readFileSync(file, "utf8"));
10445
+ }
10446
+ const userYaml = {};
10447
+ for (const name of yamlFiles) {
10448
+ const file = `${dirCfgPath}/${name}.yaml`;
10449
+ if (fs.existsSync(file)) userYaml[name] = filterKeys(YAML.parse(fs.readFileSync(file, "utf8")), defYaml[name]);
10450
+ }
10451
+ const jsonConfig = {
10452
+ amagi: {
10453
+ timeout: userYaml.request?.timeout ?? defYaml.request?.timeout,
10454
+ "User-Agent": userYaml.request?.["User-Agent"] ?? defYaml.request?.["User-Agent"],
10455
+ proxy: userYaml.request?.proxy ?? defYaml.request?.proxy,
10456
+ cookies: userYaml.cookies || defYaml.cookies || {},
10457
+ APIServer: userYaml.app?.APIServer ?? defYaml.app?.APIServer,
10458
+ APIServerMount: userYaml.app?.APIServerMount ?? defYaml.app?.APIServerMount,
10459
+ APIServerPort: userYaml.app?.APIServerPort ?? defYaml.app?.APIServerPort
10460
+ },
10461
+ app: {
10462
+ ...defYaml.app,
10463
+ ...defYaml.upload,
10464
+ ...userYaml.app,
10465
+ ...userYaml.upload
10466
+ },
10467
+ douyin: userYaml.douyin || defYaml.douyin,
10468
+ bilibili: userYaml.bilibili || defYaml.bilibili,
10469
+ kuaishou: userYaml.kuaishou || defYaml.kuaishou,
10470
+ xiaohongshu: userYaml.xiaohongshu || defYaml.xiaohongshu,
10471
+ pushlist: userYaml.pushlist || defYaml.pushlist
10472
+ };
10473
+ fs.writeFileSync(jsonConfigPath, JSON.stringify(jsonConfig, null, 2), "utf8");
10474
+ const backupDir = `${dirCfgPath}/yaml_backup_${Date.now()}`;
10475
+ fs.mkdirSync(backupDir, { recursive: true });
10476
+ for (const name of yamlFiles) {
10477
+ const file = `${dirCfgPath}/${name}.yaml`;
10478
+ if (fs.existsSync(file)) {
10479
+ fs.copyFileSync(file, `${backupDir}/${name}.yaml`);
10480
+ fs.unlinkSync(file);
10481
+ }
10482
+ }
10483
+ logger.info(`[karin-plugin-kkk][Config] YAML 配置已迁移到 config.json,备份保存在 ${backupDir}`);
10484
+ } catch (error) {
10485
+ logger.error(`[karin-plugin-kkk][Config] YAML 迁移失败: ${error}`);
10486
+ }
10487
+ };
10488
+ var filterKeys = (user, template) => {
10489
+ if (!template || typeof template !== "object") return user;
10490
+ if (Array.isArray(template)) return user;
10491
+ const result = {};
10492
+ for (const key in template) if (key in user) result[key] = typeof template[key] === "object" && !Array.isArray(template[key]) ? filterKeys(user[key], template[key]) : user[key];
10493
+ return result;
10494
+ };
10452
10495
  //#endregion
10453
10496
  //#region src/module/utils/amagiClient.ts
10454
10497
  /**
@@ -10476,48 +10519,24 @@ var AmagiBase = class {
10476
10519
  }
10477
10520
  /** 创建解析库实例 */
10478
10521
  createAmagiClient = () => {
10522
+ const amagi = Config.amagi;
10479
10523
  return Client({
10480
- cookies: {
10481
- douyin: Config.cookies.douyin,
10482
- bilibili: Config.cookies.bilibili,
10483
- kuaishou: Config.cookies.kuaishou,
10484
- xiaohongshu: Config.cookies.xiaohongshu
10485
- },
10524
+ cookies: amagi.cookies || {},
10486
10525
  request: {
10487
- timeout: Config.request.timeout,
10488
- headers: { "User-Agent": Config.request["User-Agent"] },
10489
- proxy: Config.request.proxy?.switch ? Config.request.proxy : false
10526
+ timeout: amagi.timeout,
10527
+ headers: { "User-Agent": amagi["User-Agent"] },
10528
+ proxy: amagi.proxy?.switch ? amagi.proxy : false
10490
10529
  }
10491
10530
  });
10492
10531
  };
10493
10532
  /**
10494
10533
  * 重载配置 - 重新创建 Amagi Client 实例
10495
- * 当配置文件中的 cookies 或 request 配置更新后,调用此方法使新配置生效
10496
10534
  */
10497
10535
  reloadConfig() {
10498
10536
  logger.debug("[AmagiClient] 检测到配置变化,正在重载...");
10499
- const oldCookies = {
10500
- douyin: Config.cookies.douyin?.substring(0, 20) + "...",
10501
- bilibili: Config.cookies.bilibili?.substring(0, 20) + "...",
10502
- kuaishou: Config.cookies.kuaishou?.substring(0, 20) + "...",
10503
- xiaohongshu: Config.cookies.xiaohongshu?.substring(0, 20) + "..."
10504
- };
10505
10537
  const client = this.createAmagiClient();
10506
10538
  this.amagi = this.wrapAmagiClient(client);
10507
- const newCookies = {
10508
- douyin: Config.cookies.douyin?.substring(0, 20) + "...",
10509
- bilibili: Config.cookies.bilibili?.substring(0, 20) + "...",
10510
- kuaishou: Config.cookies.kuaishou?.substring(0, 20) + "...",
10511
- xiaohongshu: Config.cookies.xiaohongshu?.substring(0, 20) + "..."
10512
- };
10513
10539
  logger.debug("[AmagiClient] 配置重载完成");
10514
- logger.debug(`[AmagiClient] Cookie 变化对比:\n${util.inspect({
10515
- 旧配置: oldCookies,
10516
- 新配置: newCookies
10517
- }, {
10518
- colors: true,
10519
- depth: 2
10520
- })}`);
10521
10540
  }
10522
10541
  /** 包装解析库实例,递归代理所有嵌套对象的方法 */
10523
10542
  wrapAmagiClient = (client) => {
@@ -10559,20 +10578,14 @@ var AmagiBase = class {
10559
10578
  };
10560
10579
  };
10561
10580
  /**
10562
- * 已知的软性错误码 — 这些接口响应属于正常业务边缘情况,不应中断执行流程。
10563
- * 在 softFetch 中配置后,对应接口调用不会抛出异常,而是原样返回 Result,
10564
- * 由业务代码根据 code 决定后续处理逻辑。
10565
- *
10581
+ * 软错误码常量
10566
10582
  * Bilibili:
10567
10583
  * 12061 - UP主已关闭评论区
10568
10584
  */
10569
10585
  var SOFT_ERROR_CODES = { BILIBILI_COMMENTS_DISABLED: 12061 };
10570
10586
  /**
10571
- * 调用 amagi fetcher 方法,允许特定错误码不抛出异常而是以 Result 形式返回。
10572
- * 用于处理已知的非致命接口响应(例如评论区已关闭)。
10573
- * 业务代码收到返回值后,通过判断 result.code 决定继续解析还是返回提示。
10574
- *
10575
- * @param fn - 经过代理包装的 amagi 方法调用
10587
+ * 调用 amagi fetcher 方法,允许特定错误码不抛出异常而是以 Result 形式返回
10588
+ * @param fn - 经过代理包装的 amagi 方法调用
10576
10589
  * @param allowedCodes - 不应抛出异常的错误码列表
10577
10590
  */
10578
10591
  var softFetch = async (fn, allowedCodes) => {
@@ -10589,37 +10602,13 @@ var softFetch = async (fn, allowedCodes) => {
10589
10602
  throw err;
10590
10603
  }
10591
10604
  };
10592
- /** 获取已初始化的解析库实例(单例) */
10593
- var createLiveProxy = (getter) => {
10594
- return new Proxy({}, { get(_target, prop) {
10595
- const current = getter();
10596
- const value = Reflect.get(current, prop);
10597
- if (typeof value === "function") return value.bind(current);
10598
- if (value && typeof value === "object") return createLiveProxy(() => Reflect.get(getter(), prop));
10599
- return value;
10600
- } });
10601
- };
10602
10605
  var amagiClientInstance = new AmagiBase();
10603
- /** 导出 Amagi Client 实例 */
10604
- var amagiClient = createLiveProxy(() => amagiClientInstance.amagi);
10605
- /**
10606
- * 重载 Amagi 配置
10607
- * 当 cookies 或 request 配置更新后调用此方法,使新配置立即生效
10608
- * @example
10609
- * ```typescript
10610
- * // 更新配置后
10611
- * await Config.Modify('cookies', 'douyin', newCookie)
10612
- * reloadAmagiConfig() // 重载配置
10613
- * ```
10614
- */
10606
+ var amagiClient = amagiClientInstance.amagi;
10615
10607
  var reloadAmagiConfig = () => {
10616
10608
  amagiClientInstance.reloadConfig();
10617
10609
  };
10618
- /** B站 Fetcher 实例 */
10619
10610
  var bilibiliFetcher = amagiClient.bilibili.fetcher;
10620
- /** 抖音 Fetcher 实例 */
10621
10611
  var douyinFetcher = amagiClient.douyin.fetcher;
10622
- /** 快手 Fetcher 实例 */
10623
10612
  var kuaishouFetcher = amagiClient.kuaishou.fetcher;
10624
10613
  amagiClient.xiaohongshu.fetcher;
10625
10614
  //#endregion
@@ -10646,7 +10635,7 @@ var Base = class extends AmagiBase {
10646
10635
  /**
10647
10636
  * 统计每个平台使用最多的机器人ID和使用次数
10648
10637
  * @param pushList - 推送列表配置
10649
- * @returns
10638
+ * @returns
10650
10639
  */
10651
10640
  var statBotId$1 = (pushList) => {
10652
10641
  const platformBotCount = {
@@ -10719,12 +10708,12 @@ var uploadFile = async (event, file, videoUrl, options) => {
10719
10708
  selfId = event.selfId;
10720
10709
  contact = event.contact;
10721
10710
  }
10722
- if (Config.upload.compress && file.totalBytes > Config.upload.compresstrigger) {
10711
+ if (Config.app.compress && file.totalBytes > Config.app.compresstrigger) {
10723
10712
  const Duration = await getMediaDuration(file.filepath);
10724
- logger.warn(logger.yellow(`视频大小 (${file.totalBytes} MB) 触发压缩条件(设定值:${Config.upload.compresstrigger} MB),正在进行压缩至${Config.upload.compressvalue} MB...`));
10725
- const message = [segment.text(`视频大小 (${file.totalBytes} MB) 触发压缩条件(设定值:${Config.upload.compresstrigger} MB),正在进行压缩至${Config.upload.compressvalue} MB...`), options?.message_id ? segment.reply(options.message_id) : segment.text("")];
10713
+ logger.warn(logger.yellow(`视频大小 (${file.totalBytes} MB) 触发压缩条件(设定值:${Config.app.compresstrigger} MB),正在进行压缩至${Config.app.compressvalue} MB...`));
10714
+ const message = [segment.text(`视频大小 (${file.totalBytes} MB) 触发压缩条件(设定值:${Config.app.compresstrigger} MB),正在进行压缩至${Config.app.compressvalue} MB...`), options?.message_id ? segment.reply(options.message_id) : segment.text("")];
10726
10715
  const msg1 = await karin$1.sendMsg(selfId, contact, message);
10727
- const targetBitrate = Common.calculateBitrate(Config.upload.compresstrigger, Duration) * .75;
10716
+ const targetBitrate = Common.calculateBitrate(Config.app.compresstrigger, Duration) * .75;
10728
10717
  const startTime = Date.now();
10729
10718
  const outputPath = `${Common.tempDri.video}tmp_${Date.now()}.mp4`;
10730
10719
  await compressVideo({
@@ -10739,8 +10728,8 @@ var uploadFile = async (event, file, videoUrl, options) => {
10739
10728
  const message2 = [segment.text(`压缩后最终视频大小为: ${newFileSize.toFixed(1)} MB,压缩耗时:${((endTime - startTime) / 1e3).toFixed(1)} 秒`), segment.reply(msg1.messageId)];
10740
10729
  await karin$1.sendMsg(selfId, contact, message2);
10741
10730
  }
10742
- if (options) options.useGroupFile = Config.upload.usegroupfile && newFileSize > Config.upload.groupfilevalue;
10743
- if (Config.upload.videoSendMode === "base64" && !options?.useGroupFile) {
10731
+ if (options) options.useGroupFile = Config.app.usegroupfile && newFileSize > Config.app.groupfilevalue;
10732
+ if (Config.app.videoSendMode === "base64" && !options?.useGroupFile) {
10744
10733
  File = `base64://${fs.readFileSync(file.filepath).toString("base64")}`;
10745
10734
  logger.mark(`已开启视频文件 base64转换 正在进行${logger.yellow("base64转换中")}...`);
10746
10735
  } else File = options?.useGroupFile ? file.filepath : `file://${file.filepath}`;
@@ -10751,14 +10740,14 @@ var uploadFile = async (event, file, videoUrl, options) => {
10751
10740
  await bot.uploadFile(contact, File, file.originTitle ? `${file.originTitle}.mp4` : `${File.split("/").pop()}`);
10752
10741
  } else {
10753
10742
  logger.mark(`${logger.blue("主动消息:")} 视频大小: ${newFileSize.toFixed(1)}MB 正在通过${logger.yellow("karin.sendMsg")}回复...`);
10754
- (await karin$1.sendMsg(selfId, contact, [segment.video(File)])).messageId ? sendStatus = true : sendStatus = false;
10743
+ sendStatus = (await karin$1.sendMsg(selfId, contact, [segment.video(File)])).messageId ? true : false;
10755
10744
  }
10756
10745
  else if (options?.useGroupFile) {
10757
10746
  logger.mark(`${logger.blue("被动消息:")} 视频大小: ${newFileSize.toFixed(1)}MB 正在通过${logger.yellow("e.bot.uploadFile")}回复...`);
10758
10747
  await event.bot.uploadFile(event.contact, File, file.originTitle ? `${file.originTitle}.mp4` : `${File.split("/").pop()}`);
10759
10748
  } else {
10760
10749
  logger.mark(`${logger.blue("被动消息:")} 视频大小: ${newFileSize.toFixed(1)}MB 正在通过${logger.yellow("e.reply")}回复...`);
10761
- (await event.reply(segment.video(File) || videoUrl)).messageId ? sendStatus = true : sendStatus = false;
10750
+ sendStatus = (await event.reply(segment.video(File) || videoUrl)).messageId ? true : false;
10762
10751
  }
10763
10752
  return sendStatus;
10764
10753
  } catch (error) {
@@ -10768,8 +10757,8 @@ var uploadFile = async (event, file, videoUrl, options) => {
10768
10757
  } finally {
10769
10758
  const filePath = file.filepath;
10770
10759
  Common.registerVideoPreview(filePath, Config.app.removeCache, 1800 * 1e3);
10771
- logger.mark(`临时预览地址:http://localhost:${process.env.HTTP_PORT}/api/kkk/video/${encodeURIComponent(filePath.split("/").pop() ?? "")}`);
10772
- Config.app.removeCache && logger.info(`文件 ${filePath} 将在 30 分钟后删除`);
10760
+ logger.mark(`临时预览地址:http://localhost:${process.env.HTTP_PORT}/kkk/ssr/video/${encodeURIComponent(filePath.split("/").pop() ?? "")}`);
10761
+ if (Config.app.removeCache) logger.info(`文件 ${filePath} 将在 30 分钟后删除`);
10773
10762
  setTimeout(async () => {
10774
10763
  if (await Common.removeFile(filePath)) Common.markVideoPreviewRemoved(filePath);
10775
10764
  }, 1800 * 1e3);
@@ -10789,8 +10778,8 @@ var downloadVideo = async (event, downloadOpt, uploadOpt) => {
10789
10778
  }).getHeaders());
10790
10779
  const fileSizeInMB = (fileSizeContent / (1024 * 1024)).toFixed(2);
10791
10780
  const fileSize = parseInt(parseFloat(fileSizeInMB).toFixed(2));
10792
- if (fileSizeContent > 0 && Config.upload.usefilelimit && fileSize > Config.upload.filelimit) {
10793
- const message = segment.text(`视频:「${downloadOpt.title.originTitle ?? "Error: 文件名获取失败"}」大小 (${fileSizeInMB} MB) 超出最大限制(设定值:${Config.upload.filelimit} MB),已取消上传`);
10781
+ if (fileSizeContent > 0 && Config.app.usefilelimit && fileSize > Config.app.filelimit) {
10782
+ const message = segment.text(`视频:「${downloadOpt.title.originTitle ?? "Error: 文件名获取失败"}」大小 (${fileSizeInMB} MB) 超出最大限制(设定值:${Config.app.filelimit} MB),已取消上传`);
10794
10783
  const selfId = event.selfId || uploadOpt?.activeOption?.uin;
10795
10784
  const contact = event.contact || karin$1.contactGroup(uploadOpt?.activeOption?.group_id) || karin$1.contactFriend(selfId);
10796
10785
  await karin$1.sendMsg(selfId, contact, message);
@@ -10816,7 +10805,7 @@ var downloadVideo = async (event, downloadOpt, uploadOpt) => {
10816
10805
  */
10817
10806
  var downloadFile = async (videoUrl, opt) => {
10818
10807
  const startTime = Date.now();
10819
- const uploadConfig = Config.upload;
10808
+ const uploadConfig = Config.app;
10820
10809
  const throttleConfig = {
10821
10810
  enabled: uploadConfig.downloadThrottle ?? false,
10822
10811
  maxSpeed: (uploadConfig.downloadMaxSpeed ?? 10) * 1024 * 1024,
@@ -11583,7 +11572,7 @@ var buildGoogleMotionPhoto = async (options) => {
11583
11572
  };
11584
11573
  //#endregion
11585
11574
  //#region src/module/utils/FFmpeg.ts
11586
- /**
11575
+ /**
11587
11576
  * 修复 m4s 文件为标准 MP4 格式
11588
11577
  * B站的 DASH 流使用 m4s 格式,缺少 moov atom,需要转换
11589
11578
  */
@@ -11980,7 +11969,7 @@ var extractTotalBytesFromHeaders = (headers) => {
11980
11969
  * @returns 安全的文件名
11981
11970
  */
11982
11971
  var sanitizeFilename = (filename) => {
11983
- return filename.replace(/[<>:"/\\|?*\x00-\x1f]/g, "_").replace(/^\.+/, "").replace(/\.+$/, "").replace(/\s+/g, "_").substring(0, 200);
11972
+ return filename.replace(/[<>:"/\\|?*\p{Cc}]/gu, "_").replace(/^\.+/, "").replace(/\.+$/, "").replace(/\s+/g, "_").substring(0, 200);
11984
11973
  };
11985
11974
  //#endregion
11986
11975
  //#region src/module/utils/Network/ThrottleStream.ts
@@ -12341,7 +12330,7 @@ var ImageDownloader = class {
12341
12330
  * @returns 处理后的图片路径(HTTP URL / file:// 协议 / base64://)
12342
12331
  */
12343
12332
  async processImage(imageUrl, title, index) {
12344
- switch (Config.upload.imageSendMode) {
12333
+ switch (Config.app.imageSendMode) {
12345
12334
  case "base64": try {
12346
12335
  return await this.downloadAndConvertToBase64(imageUrl);
12347
12336
  } catch (error) {
@@ -12766,18 +12755,18 @@ function getImageDownloader() {
12766
12755
  /**
12767
12756
  * 处理图片 URL,根据配置决定是否本地下载
12768
12757
  * 这是一个便捷函数,用于快速处理单个图片
12769
- *
12758
+ *
12770
12759
  * @param imageUrl - 图片 URL
12771
12760
  * @param title - 作品标题(用于文件命名)
12772
12761
  * @param index - 图片索引(用于多图场景)
12773
12762
  * @returns 处理后的图片路径(可能是 file:// 协议或原始 HTTP URL)
12774
- *
12763
+ *
12775
12764
  * @example
12776
12765
  * ```ts
12777
12766
  * // 单张图片
12778
12767
  * const imagePath = await processImageUrl('https://example.com/image.jpg', '作品标题')
12779
12768
  * await e.reply(segment.image(imagePath))
12780
- *
12769
+ *
12781
12770
  * // 多张图片
12782
12771
  * const imagePaths = await Promise.all(
12783
12772
  * imageUrls.map((url, i) => processImageUrl(url, '作品标题', i))
@@ -12791,7 +12780,7 @@ async function processImageUrl(imageUrl, title, index) {
12791
12780
  //#region ../template/src/dev/preview/utils/time.ts
12792
12781
  var import_react = /* @__PURE__ */ __toESM(require_react(), 1);
12793
12782
  var import_server_node = require_server_node();
12794
- var formatDuration$5 = (ms) => {
12783
+ var formatDuration$3 = (ms) => {
12795
12784
  const totalSeconds = Math.max(Math.floor(ms / 1e3), 0);
12796
12785
  const hours = Math.floor(totalSeconds / 3600);
12797
12786
  const minutes = Math.floor(totalSeconds % 3600 / 60);
@@ -12815,7 +12804,7 @@ var buildStatus = (state) => {
12815
12804
  statusText: `文件 ${filePath} 将在未知时间后删除`,
12816
12805
  countdownText: "--:--"
12817
12806
  };
12818
- const countdownText = formatDuration$5(state.remainingMs);
12807
+ const countdownText = formatDuration$3(state.remainingMs);
12819
12808
  return {
12820
12809
  statusText: `文件 ${filePath} 将在 ${countdownText} 后删除`,
12821
12810
  countdownText
@@ -17345,7 +17334,7 @@ var OriginalAVContent = ({ content }) => {
17345
17334
  children: content.duration_text
17346
17335
  }),
17347
17336
  content.play,
17348
- "观看 ",
17337
+ "观看 ",
17349
17338
  content.danmaku,
17350
17339
  "弹幕"
17351
17340
  ]
@@ -19043,6 +19032,69 @@ var BilibiliVideoInfo = import_react.memo((props) => {
19043
19032
  });
19044
19033
  BilibiliVideoInfo.displayName = "BilibiliVideoInfo";
19045
19034
  //#endregion
19035
+ //#region ../template/src/components/platforms/douyin/Icons.tsx
19036
+ var svgProps = (size, { weight: _weight, ...props }) => ({
19037
+ width: size,
19038
+ height: size,
19039
+ fill: "none",
19040
+ xmlns: "http://www.w3.org/2000/svg",
19041
+ ...props
19042
+ });
19043
+ /** 抖音官方点赞图标,提取自 packages/template/icon.html。 */
19044
+ var DouyinLikeIcon = ({ size = 36, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
19045
+ viewBox: "0 0 891 816",
19046
+ ...svgProps(size, props),
19047
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
19048
+ fill: "currentColor",
19049
+ d: "M453.036 88.712C493.774 30.664 560.66 0 634.251 0 774.403 0 890.972 121.59 890.972 266.137v.054c.01-.02.019-.04.028-.06 0 4.121-.07 7.241-.122 9.537-.072 3.151-.108 4.747.122 5.247-.531 30.403-5.778 55.522-15.101 88.712-5.289 5.96-10.204 17.184-15.1 29.572-7.724 11.998-10.647 17.81-15.101 29.57a546.67 546.67 0 0 1-14.452 22.974c-37.449 56.8-87.537 113.33-137.579 163.509-78.331 79.025-158.123 144.805-192.835 173.422-9.585 7.901-15.732 12.969-17.464 14.7-12.301 12.303-24.603 12.611-36.905 12.619-.324.004-.649.006-.977.006-25.236 0-37.854-12.619-50.472-25.237-.963-.963-3.413-2.984-7.156-5.979-38.233-28.184-124.273-96.997-205.116-180C121.066 542.02 61.622 470.007 29.092 399.588 16.474 374.351.731 314.264 0 280.922c.269-.267.227-1.873.144-5.078C.083 273.498 0 270.297 0 266.137 0 121.524 116.502 0 256.721 0c73.458 0 140.405 30.664 196.315 88.712Z"
19050
+ })
19051
+ });
19052
+ /** 抖音官方评论图标,提取自 packages/template/icon.html。 */
19053
+ var DouyinCommentIcon = ({ size = 36, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
19054
+ viewBox: "-24.75 -11.25 22.5 22.31",
19055
+ ...svgProps(size, props),
19056
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
19057
+ fill: "currentColor",
19058
+ d: " M-4.644999980926514,4.482999801635742 C-7.25,7.818999767303467 -13.482000350952148,8.300000190734863 -13.482000350952148,8.300000190734863 C-13.482000350952148,8.300000190734863 -14.413999557495117,11.48799991607666 -12.135000228881836,10.821000099182129 C-9.855999946594238,10.154000282287598 -7.25,8.152000427246094 -4.644999980926514,4.482999801635742z M-17.36,-1.04 C-17.36,-0.16 -18.07,0.56 -18.96,0.56 C-18.96,0.56 -18.96,0.56 -18.96,0.56 C-19.85,0.56 -20.57,-0.16 -20.57,-1.04 C-20.57,-1.92 -19.85,-2.64 -18.96,-2.64 C-18.07,-2.64 -17.36,-1.92 -17.36,-1.04z M-11.89,-1.04 C-11.89,-0.16 -12.61,0.56 -13.5,0.56 C-13.5,0.56 -13.5,0.56 -13.5,0.56 C-14.39,0.56 -15.11,-0.16 -15.11,-1.04 C-15.11,-1.92 -14.39,-2.64 -13.5,-2.64 C-12.61,-2.64 -11.89,-1.92 -11.89,-1.04z M-6.43,-1.04 C-6.43,-0.16 -7.15,0.56 -8.04,0.56 C-8.04,0.56 -8.04,0.56 -8.04,0.56 C-8.92,0.56 -9.64,-0.16 -9.64,-1.04 C-9.64,-1.92 -8.92,-2.64 -8.04,-2.64 C-7.15,-2.64 -6.43,-1.92 -6.43,-1.04z M-5.79,5.98 C-3.56,3.75 -2.25,1.42 -2.25,-1.29 C-2.25,-6.79 -7.29,-11.25 -13.5,-11.25 C-19.71,-11.25 -24.75,-6.79 -24.75,-1.29 C-24.75,4.21 -19.55,7.99 -13.34,7.99 C-13.34,7.99 -13.34,11.06 -13.34,11.06 C-13.34,11.06 -8.51,8.72 -5.79,5.98z"
19059
+ })
19060
+ });
19061
+ /** 抖音官方收藏图标,提取自 packages/template/icon.html。 */
19062
+ var DouyinFavoriteIcon = ({ size = 36, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
19063
+ viewBox: "-2.285 -1.439 104.57 99.703",
19064
+ ...svgProps(size, props),
19065
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
19066
+ fill: "currentColor",
19067
+ d: "M43.79199981689453,4.316999912261963 C45.93899917602539,-1.4390000104904175 54.06100082397461,-1.4390000104904175 56.20800018310547,4.316999912261963 C56.20800018310547,4.316999912261963 65.927001953125,30.375 65.927001953125,30.375 C65.927001953125,30.375 93.6510009765625,31.59000015258789 93.6510009765625,31.59000015258789 C99.7750015258789,31.857999801635742 102.28500366210938,39.60200119018555 97.48799896240234,43.428001403808594 C97.48799896240234,43.428001403808594 75.77100372314453,60.74700164794922 75.77100372314453,60.74700164794922 C75.77100372314453,60.74700164794922 83.18599700927734,87.55599975585938 83.18599700927734,87.55599975585938 C84.8239974975586,93.47799682617188 78.25299835205078,98.26399993896484 73.14099884033203,94.87200164794922 C73.14099884033203,94.87200164794922 50,79.51799774169922 50,79.51799774169922 C50,79.51799774169922 26.858999252319336,94.87200164794922 26.858999252319336,94.87200164794922 C21.746999740600586,98.26399993896484 15.175999641418457,93.47799682617188 16.81399917602539,87.55599975585938 C16.81399917602539,87.55599975585938 24.229000091552734,60.74700164794922 24.229000091552734,60.74700164794922 C24.229000091552734,60.74700164794922 2.51200008392334,43.428001403808594 2.51200008392334,43.428001403808594 C-2.2850000858306885,39.60200119018555 0.22499999403953552,31.857999801635742 6.348999977111816,31.59000015258789 C6.348999977111816,31.59000015258789 34.073001861572266,30.375 34.073001861572266,30.375 C34.073001861572266,30.375 43.79199981689453,4.316999912261963 43.79199981689453,4.316999912261963z"
19068
+ })
19069
+ });
19070
+ /** 抖音官方分享图标,提取自 packages/template/icon.html。 */
19071
+ var DouyinShareIcon = ({ size = 36, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
19072
+ viewBox: "-13.517 -10.558 25.734 22.375",
19073
+ ...svgProps(size, props),
19074
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
19075
+ fill: "currentColor",
19076
+ d: "M.815 4.762V-4.93s-6.033-.564-10.06 3.755c-4.027 4.319-4.272 12.992-1.597 8.919C-8.062 3.511.815 4.762.815 4.762Zm9.699-1.767c1.655-1.461 1.703-3.887.107-5.413L3.706-9.032C2.11-10.558.815-10.264.815-8.375v3.421c.009 6.839 0 6.039 0 6.039 0 2.208 0 5.452 0 7.24 0 1.788 1.343 2.053 2.998.591l6.701-5.921Z"
19077
+ })
19078
+ });
19079
+ /** 抖音官方推荐图标。 */
19080
+ var DouyinRecommendIcon = ({ size = 36, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
19081
+ viewBox: "0 0 27 27",
19082
+ ...svgProps(size, props),
19083
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
19084
+ fill: "currentColor",
19085
+ d: "M18.544 22.86H8.42V9.697l5.821-5.821a1.79 1.79 0 0 1 2.531 0c.118.118.215.278.291.48.076.203.114.397.114.583v.354l-1.114 4.404h5.518c.54 0 1.012.203 1.417.608.405.405.608.877.608 1.417v2.025a2.059 2.059 0 0 1-.152.759l-3.037 7.137c-.152.338-.405.625-.76.861a1.978 1.978 0 0 1-1.113.354ZM6.79 9.697V22.86H4.366a1.625 1.625 0 0 1-1.625-1.625v-9.91c0-.898.728-1.626 1.625-1.626H6.79Z"
19086
+ })
19087
+ });
19088
+ /** 抖音官方更多图标,提取自 packages/template/icon.html。 */
19089
+ var DouyinMoreIcon = ({ size = 36, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
19090
+ viewBox: "0 0 36 36",
19091
+ ...svgProps(size, props),
19092
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", {
19093
+ fill: "currentColor",
19094
+ d: "M13.556 17.778a1.778 1.778 0 1 1-3.556 0 1.778 1.778 0 0 1 3.556 0Zm6.222 0a1.778 1.778 0 1 1-3.556 0 1.778 1.778 0 0 1 3.556 0Zm4.444 1.778a1.778 1.778 0 1 0 0-3.556 1.778 1.778 0 0 0 0 3.556Z"
19095
+ })
19096
+ });
19097
+ //#endregion
19046
19098
  //#region ../template/src/components/platforms/douyin/ArticleWork.tsx
19047
19099
  /**
19048
19100
  * 抖音Logo头部组件
@@ -19105,7 +19157,6 @@ var ContentSection = ({ markdown, images }) => {
19105
19157
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
19106
19158
  className: "prose prose-lg max-w-none text-foreground select-text",
19107
19159
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Markdown, {
19108
- children: markdown.replace(/!\[([^\]]*)\]\(([^\s)]+)(?:\s+width=\d+)?(?:\s+height=\d+)?\)/g, "![$1]($2)"),
19109
19160
  components: {
19110
19161
  h1: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
19111
19162
  className: "text-8xl font-bold mb-8 mt-12 text-foreground",
@@ -19168,7 +19219,8 @@ var ContentSection = ({ markdown, images }) => {
19168
19219
  className: "bg-surface px-3 py-1 rounded text-5xl text-accent font-mono",
19169
19220
  children
19170
19221
  })
19171
- }
19222
+ },
19223
+ children: markdown.replace(/!\[([^\]]*)\]\(([^\s)]+)(?:\s+width=\d+)?(?:\s+height=\d+)?\)/g, "![$1]($2)")
19172
19224
  })
19173
19225
  })
19174
19226
  });
@@ -19184,22 +19236,22 @@ var InfoSection$2 = (props) => {
19184
19236
  children: [
19185
19237
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
19186
19238
  className: "flex gap-2 items-center",
19187
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heart, { className: "w-11 h-11 text-like" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.dianzan, "点赞"] })]
19239
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 44 }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.dianzan, "点赞"] })]
19188
19240
  }),
19189
19241
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "·" }),
19190
19242
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
19191
19243
  className: "flex gap-2 items-center",
19192
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MessageCircle, { className: "w-11 h-11 text-comment" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.pinglun, "评论"] })]
19244
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinCommentIcon, { size: 44 }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.pinglun, "评论"] })]
19193
19245
  }),
19194
19246
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "·" }),
19195
19247
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
19196
19248
  className: "flex gap-2 items-center",
19197
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Bookmark, { className: "w-11 h-11" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.shouchang, "收藏"] })]
19249
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinFavoriteIcon, { size: 44 }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.shouchang, "收藏"] })]
19198
19250
  }),
19199
19251
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "·" }),
19200
19252
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
19201
19253
  className: "flex gap-2 items-center",
19202
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Share2, { className: "w-11 h-11 text-success" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.share, "分享"] })]
19254
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinShareIcon, { size: 44 }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.share, "分享"] })]
19203
19255
  })
19204
19256
  ]
19205
19257
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -19247,7 +19299,7 @@ var UserInfoSection$1 = (props) => {
19247
19299
  children: [
19248
19300
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
19249
19301
  className: "flex gap-1 items-center",
19250
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heart, { className: "w-7 h-7 text-like" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
19302
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 28 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
19251
19303
  className: "text-muted",
19252
19304
  children: "获赞"
19253
19305
  })]
@@ -19433,28 +19485,28 @@ var VideoInfoHeader = (props) => {
19433
19485
  children: [
19434
19486
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
19435
19487
  className: "flex items-center gap-4 text-foreground/50",
19436
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heart, { size: 48 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
19488
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 48 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
19437
19489
  className: "text-5xl font-medium text-foreground/90",
19438
19490
  children: formatDouyinCommentDiggCount(props.Statistics.digg_count)
19439
19491
  })]
19440
19492
  }),
19441
19493
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
19442
19494
  className: "flex items-center gap-4 text-foreground/50",
19443
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MessageCircle, { size: 48 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
19495
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinCommentIcon, { size: 48 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
19444
19496
  className: "text-5xl font-medium text-foreground/90",
19445
19497
  children: formatDouyinCommentDiggCount(props.Statistics.comment_count)
19446
19498
  })]
19447
19499
  }),
19448
19500
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
19449
19501
  className: "flex items-center gap-4 text-foreground/50",
19450
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Share2, { size: 48 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
19502
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinShareIcon, { size: 48 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
19451
19503
  className: "text-5xl font-medium text-foreground/90",
19452
19504
  children: formatDouyinCommentDiggCount(props.Statistics.share_count)
19453
19505
  })]
19454
19506
  }),
19455
19507
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
19456
19508
  className: "flex items-center gap-4 text-foreground/50",
19457
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Star, { size: 48 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
19509
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinFavoriteIcon, { size: 48 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
19458
19510
  className: "text-5xl font-medium text-foreground/90",
19459
19511
  children: formatDouyinCommentDiggCount(props.Statistics.collect_count)
19460
19512
  })]
@@ -19594,7 +19646,7 @@ var ReplyItemComponent = ({ reply, depth = 0, isLast, maxDepth = 6 }) => {
19594
19646
  className: "flex items-center h-12.5",
19595
19647
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
19596
19648
  className: "flex items-center text-muted",
19597
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CircleEllipsis, {
19649
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinMoreIcon, {
19598
19650
  size: 45,
19599
19651
  className: "mr-5"
19600
19652
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
@@ -19700,10 +19752,7 @@ var ReplyItemComponent = ({ reply, depth = 0, isLast, maxDepth = 6 }) => {
19700
19752
  }),
19701
19753
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
19702
19754
  className: "flex gap-2 items-center",
19703
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heart, {
19704
- size: 40,
19705
- className: "text-muted"
19706
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
19755
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 40 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
19707
19756
  className: "text-4xl select-text",
19708
19757
  children: formatDouyinCommentDiggCount(reply.digg_count)
19709
19758
  })]
@@ -19807,10 +19856,7 @@ var CommentItemComponent$1 = (props) => {
19807
19856
  }),
19808
19857
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
19809
19858
  className: "flex gap-2 items-center transition-colors cursor-pointer",
19810
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heart, {
19811
- size: 44,
19812
- className: "text-muted"
19813
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
19859
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 44 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
19814
19860
  className: "text-4xl select-text",
19815
19861
  children: formatDouyinCommentDiggCount(props.digg_count)
19816
19862
  })]
@@ -19952,22 +19998,22 @@ var InfoSection$1 = (props) => {
19952
19998
  children: [
19953
19999
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
19954
20000
  className: "flex gap-2 items-center",
19955
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heart, { className: "w-11 h-11 text-like" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.dianzan, "点赞"] })]
20001
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 44 }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.dianzan, "点赞"] })]
19956
20002
  }),
19957
20003
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "·" }),
19958
20004
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
19959
20005
  className: "flex gap-2 items-center",
19960
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MessageCircle, { className: "w-11 h-11 text-comment" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.pinglun, "评论"] })]
20006
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinCommentIcon, { size: 44 }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.pinglun, "评论"] })]
19961
20007
  }),
19962
20008
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "·" }),
19963
20009
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
19964
20010
  className: "flex gap-2 items-center",
19965
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Bookmark, { className: "w-11 h-11" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.shouchang, "收藏"] })]
20011
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinFavoriteIcon, { size: 44 }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.shouchang, "收藏"] })]
19966
20012
  }),
19967
20013
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "·" }),
19968
20014
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
19969
20015
  className: "flex gap-2 items-center",
19970
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Share2, { className: "w-11 h-11 text-success" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.share, "分享"] })]
20016
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinShareIcon, { size: 44 }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [props.data.share, "分享"] })]
19971
20017
  })
19972
20018
  ]
19973
20019
  }),
@@ -20026,10 +20072,7 @@ var UserInfoSection = (props) => {
20026
20072
  children: [
20027
20073
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20028
20074
  className: "flex gap-1 items-center",
20029
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heart, {
20030
- size: 28,
20031
- className: "text-like"
20032
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
20075
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 28 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
20033
20076
  className: "text-muted",
20034
20077
  children: "获赞"
20035
20078
  })]
@@ -20298,10 +20341,7 @@ var DouyinFavoriteList = (props) => {
20298
20341
  }),
20299
20342
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20300
20343
  className: "absolute -bottom-3 -right-3 bg-rose-500 text-white px-5 py-2 rounded-full border-[5px] border-white font-bold text-xl shadow-xl flex items-center gap-2",
20301
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RiHeart3Fill, {
20302
- size: 20,
20303
- className: "fill-current"
20304
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "赞了" })]
20344
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 20 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "赞了" })]
20305
20345
  })
20306
20346
  ]
20307
20347
  }),
@@ -20325,10 +20365,7 @@ var DouyinFavoriteList = (props) => {
20325
20365
  className: "mt-6 flex flex-col items-center gap-2 text-rose-500",
20326
20366
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20327
20367
  className: "flex items-center gap-3",
20328
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RiHeart3Fill, {
20329
- size: 40,
20330
- className: "fill-current"
20331
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
20368
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 40 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
20332
20369
  className: "text-4xl font-bold",
20333
20370
  children: "刚刚赞了这个作品"
20334
20371
  })]
@@ -20435,24 +20472,24 @@ var DouyinFavoriteList = (props) => {
20435
20472
  className: "grid grid-cols-5 gap-6 z-10",
20436
20473
  children: [
20437
20474
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$3, {
20438
- icon: RiHeart3Fill,
20475
+ icon: DouyinLikeIcon,
20439
20476
  value: props.data.dianzan,
20440
- iconClassName: "text-rose-500/80"
20477
+ iconColor: "#FE2C55"
20441
20478
  }),
20442
20479
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$3, {
20443
- icon: RiThumbUpFill,
20480
+ icon: DouyinRecommendIcon,
20444
20481
  value: props.data.tuijian
20445
20482
  }),
20446
20483
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$3, {
20447
- icon: RiMessage3Fill,
20484
+ icon: DouyinCommentIcon,
20448
20485
  value: props.data.pinglun
20449
20486
  }),
20450
20487
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$3, {
20451
- icon: RiStarFill,
20488
+ icon: DouyinFavoriteIcon,
20452
20489
  value: props.data.shouchang
20453
20490
  }),
20454
20491
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$3, {
20455
- icon: RiShareForwardFill,
20492
+ icon: DouyinShareIcon,
20456
20493
  value: props.data.share
20457
20494
  })
20458
20495
  ]
@@ -20465,11 +20502,12 @@ var DouyinFavoriteList = (props) => {
20465
20502
  });
20466
20503
  };
20467
20504
  /** 统计项小组件 */
20468
- var StatItem$3 = ({ icon: IconComponent, value, iconClassName }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20505
+ var StatItem$3 = ({ icon: IconComponent, value, iconColor }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20469
20506
  className: "flex flex-col items-center justify-center gap-2 py-6 rounded-3xl bg-surface",
20470
20507
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconComponent, {
20471
20508
  size: 48,
20472
- className: `opacity-90 ${iconClassName || "text-muted"}`
20509
+ color: iconColor,
20510
+ className: "opacity-90"
20473
20511
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
20474
20512
  className: "text-2xl font-bold text-foreground/70 mt-1",
20475
20513
  children: value
@@ -20738,22 +20776,22 @@ var DouyinDynamicStatus$1 = (props) => {
20738
20776
  className: "flex gap-6 items-center text-5xl tracking-normal select-text text-foreground/70",
20739
20777
  children: [
20740
20778
  {
20741
- icon: o$3,
20779
+ icon: DouyinLikeIcon,
20742
20780
  value: dianzan,
20743
20781
  label: "点赞"
20744
20782
  },
20745
20783
  {
20746
- icon: t,
20784
+ icon: DouyinCommentIcon,
20747
20785
  value: pinglun,
20748
20786
  label: "评论"
20749
20787
  },
20750
20788
  {
20751
- icon: r,
20789
+ icon: DouyinFavoriteIcon,
20752
20790
  value: shouchang,
20753
20791
  label: "收藏"
20754
20792
  },
20755
20793
  {
20756
- icon: e$1,
20794
+ icon: DouyinShareIcon,
20757
20795
  value: share,
20758
20796
  label: "分享"
20759
20797
  }
@@ -20922,19 +20960,19 @@ var DouyinDynamicFooter$1 = (props) => {
20922
20960
  className: "text-3xl flex gap-6 items-center text-foreground/70",
20923
20961
  children: [
20924
20962
  {
20925
- icon: o$3,
20963
+ icon: DouyinLikeIcon,
20926
20964
  iconSize: 36,
20927
20965
  label: "获赞",
20928
20966
  value: 获赞
20929
20967
  },
20930
20968
  {
20931
- icon: o$4,
20969
+ icon: o$3,
20932
20970
  iconSize: 36,
20933
20971
  label: "关注",
20934
20972
  value: 关注
20935
20973
  },
20936
20974
  {
20937
- icon: r$1,
20975
+ icon: r,
20938
20976
  iconSize: 36,
20939
20977
  label: "粉丝",
20940
20978
  value: 粉丝
@@ -21197,10 +21235,7 @@ var InfoSection = ({ data }) => {
21197
21235
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
21198
21236
  className: "flex items-center gap-1.5",
21199
21237
  children: [
21200
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heart, {
21201
- size: 28,
21202
- className: "text-foreground/20"
21203
- }),
21238
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 28 }),
21204
21239
  data.like_count,
21205
21240
  "点赞"
21206
21241
  ]
@@ -21312,7 +21347,7 @@ var BottomSection = ({ data }) => {
21312
21347
  className: "flex flex-col gap-2 px-6 py-3 rounded-2xl bg-default/45",
21313
21348
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
21314
21349
  className: "flex items-center gap-2",
21315
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heart, { size: 28 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
21350
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 28 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
21316
21351
  className: "text-foreground/40",
21317
21352
  children: "获赞"
21318
21353
  })]
@@ -21474,7 +21509,7 @@ var MusicAuthorInfoSection = ({ avatarUrl, username, userShortId, totalFavorited
21474
21509
  }),
21475
21510
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
21476
21511
  className: "flex gap-2 items-center",
21477
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heart, { className: "w-8 h-8 text-like" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: ["获赞: ", totalFavorited] })]
21512
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 32 }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: ["获赞: ", totalFavorited] })]
21478
21513
  }),
21479
21514
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
21480
21515
  className: "flex gap-2 items-center",
@@ -21866,10 +21901,7 @@ var DouyinRecommendList = (props) => {
21866
21901
  }),
21867
21902
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
21868
21903
  className: "absolute -bottom-3 -right-3 bg-emerald-500 text-white px-5 py-2 rounded-full border-[5px] border-white font-bold text-xl shadow-xl flex items-center gap-2",
21869
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RiThumbUpFill, {
21870
- size: 20,
21871
- className: "fill-current"
21872
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "推荐" })]
21904
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinRecommendIcon, { size: 20 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "推荐" })]
21873
21905
  })
21874
21906
  ]
21875
21907
  }),
@@ -21893,10 +21925,7 @@ var DouyinRecommendList = (props) => {
21893
21925
  className: "mt-6 flex flex-col items-center gap-2 text-emerald-500",
21894
21926
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
21895
21927
  className: "flex items-center gap-3",
21896
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RiThumbUpFill, {
21897
- size: 40,
21898
- className: "fill-current"
21899
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
21928
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinRecommendIcon, { size: 40 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
21900
21929
  className: "text-4xl font-bold",
21901
21930
  children: "刚刚推荐了这个作品"
21902
21931
  })]
@@ -22003,24 +22032,24 @@ var DouyinRecommendList = (props) => {
22003
22032
  className: "grid grid-cols-5 gap-6 z-10",
22004
22033
  children: [
22005
22034
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$2, {
22006
- icon: RiHeart3Fill,
22035
+ icon: DouyinLikeIcon,
22007
22036
  value: props.data.dianzan
22008
22037
  }),
22009
22038
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$2, {
22010
- icon: RiThumbUpFill,
22039
+ icon: DouyinRecommendIcon,
22011
22040
  value: props.data.tuijian,
22012
- iconClassName: "text-emerald-500/80"
22041
+ iconColor: "#13C15A"
22013
22042
  }),
22014
22043
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$2, {
22015
- icon: RiMessage3Fill,
22044
+ icon: DouyinCommentIcon,
22016
22045
  value: props.data.pinglun
22017
22046
  }),
22018
22047
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$2, {
22019
- icon: RiStarFill,
22048
+ icon: DouyinFavoriteIcon,
22020
22049
  value: props.data.shouchang
22021
22050
  }),
22022
22051
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$2, {
22023
- icon: RiShareForwardFill,
22052
+ icon: DouyinShareIcon,
22024
22053
  value: props.data.share
22025
22054
  })
22026
22055
  ]
@@ -22033,11 +22062,12 @@ var DouyinRecommendList = (props) => {
22033
22062
  });
22034
22063
  };
22035
22064
  /** 统计项小组件 */
22036
- var StatItem$2 = ({ icon: IconComponent, value, iconClassName }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
22065
+ var StatItem$2 = ({ icon: IconComponent, value, iconColor }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
22037
22066
  className: "flex flex-col items-center justify-center gap-2 py-6 rounded-3xl bg-surface",
22038
22067
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconComponent, {
22039
22068
  size: 48,
22040
- className: `opacity-90 ${iconClassName || "text-muted"}`
22069
+ color: iconColor,
22070
+ className: "opacity-90"
22041
22071
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
22042
22072
  className: "text-2xl font-bold text-foreground/70 mt-1",
22043
22073
  children: value
@@ -22054,12 +22084,12 @@ var pushTypeConfig = {
22054
22084
  favorite: {
22055
22085
  label: "喜欢列表",
22056
22086
  color: "bg-[#d94f50]/5 text-[#b04546] border-[#d94f50]/12",
22057
- icon: Heart
22087
+ icon: DouyinLikeIcon
22058
22088
  },
22059
22089
  recommend: {
22060
22090
  label: "推荐列表",
22061
22091
  color: "bg-[#c9943a]/5 text-[#a07d30] border-[#c9943a]/12",
22062
- icon: Sparkles
22092
+ icon: DouyinRecommendIcon
22063
22093
  },
22064
22094
  live: {
22065
22095
  label: "直播状态",
@@ -22193,7 +22223,7 @@ var DouyinUserItem = (props) => {
22193
22223
  label: "粉丝"
22194
22224
  },
22195
22225
  {
22196
- icon: Heart,
22226
+ icon: DouyinLikeIcon,
22197
22227
  value: props.total_favorited,
22198
22228
  label: "获赞"
22199
22229
  },
@@ -22388,7 +22418,7 @@ var DouyinUserList = (props) => {
22388
22418
  /**
22389
22419
  * 格式化数字显示 (使用中文单位:万、亿)
22390
22420
  */
22391
- var formatCount$2 = (count) => {
22421
+ var formatCount = (count) => {
22392
22422
  if (count >= 1e8) return (count / 1e8).toFixed(1) + "亿";
22393
22423
  if (count >= 1e4) return (count / 1e4).toFixed(1) + "万";
22394
22424
  return count.toString();
@@ -22397,7 +22427,7 @@ var formatCount$2 = (count) => {
22397
22427
  * 格式化视频时长显示 (如: 6:20)
22398
22428
  * @param milliseconds 毫秒数
22399
22429
  */
22400
- var formatDuration$4 = (milliseconds) => {
22430
+ var formatDuration$2 = (milliseconds) => {
22401
22431
  const seconds = Math.floor(milliseconds / 1e3);
22402
22432
  return `${Math.floor(seconds / 60)}:${(seconds % 60).toString().padStart(2, "0")}`;
22403
22433
  };
@@ -22433,7 +22463,7 @@ var VideoCard = ({ video }) => {
22433
22463
  }),
22434
22464
  video.is_video && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
22435
22465
  className: "absolute bottom-4 right-4 px-8 py-3 rounded-2xl text-4xl bg-white/50 text-black backdrop-blur-xs shadow-lg",
22436
- children: formatDuration$4(video.duration)
22466
+ children: formatDuration$2(video.duration)
22437
22467
  }),
22438
22468
  video.music && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
22439
22469
  className: "absolute bottom-4 left-4 flex items-center gap-2 px-6 py-3 rounded-2xl text-xl bg-white/50 text-black backdrop-blur-xs shadow-lg",
@@ -22465,19 +22495,19 @@ var VideoCard = ({ video }) => {
22465
22495
  children: [
22466
22496
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
22467
22497
  className: "flex items-center gap-2 text-foreground/70",
22468
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AiFillHeart, { size: 34 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: formatCount$2(video.statistics.like_count) })]
22498
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 34 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: formatCount(video.statistics.like_count) })]
22469
22499
  }),
22470
22500
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
22471
22501
  className: "flex items-center gap-2 text-foreground/70",
22472
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FaCommentDots, { size: 34 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: formatCount$2(video.statistics.comment_count) })]
22502
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinCommentIcon, { size: 34 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: formatCount(video.statistics.comment_count) })]
22473
22503
  }),
22474
22504
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
22475
22505
  className: "flex items-center gap-2 text-foreground/70",
22476
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AiFillStar, { size: 34 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: formatCount$2(video.statistics.collect_count) })]
22506
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinFavoriteIcon, { size: 34 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: formatCount(video.statistics.collect_count) })]
22477
22507
  }),
22478
22508
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
22479
22509
  className: "flex items-center gap-2 text-foreground/70",
22480
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RiShareForwardFill, { size: 34 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: formatCount$2(video.statistics.share_count) })]
22510
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinShareIcon, { size: 34 }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: formatCount(video.statistics.share_count) })]
22481
22511
  })
22482
22512
  ]
22483
22513
  })
@@ -22559,7 +22589,7 @@ var DouyinUserVideoList = (prpos) => {
22559
22589
  }),
22560
22590
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
22561
22591
  className: "font-medium text-4xl text-foreground",
22562
- children: [" ", formatCount$2(prpos.data.user.following_count)]
22592
+ children: [" ", formatCount(prpos.data.user.following_count)]
22563
22593
  })
22564
22594
  ]
22565
22595
  }),
@@ -22576,24 +22606,21 @@ var DouyinUserVideoList = (prpos) => {
22576
22606
  }),
22577
22607
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
22578
22608
  className: "font-medium text-4xl text-foreground",
22579
- children: [" ", formatCount$2(prpos.data.user.follower_count)]
22609
+ children: [" ", formatCount(prpos.data.user.follower_count)]
22580
22610
  })
22581
22611
  ]
22582
22612
  }),
22583
22613
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
22584
22614
  className: "flex items-center gap-2",
22585
22615
  children: [
22586
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AiFillHeart, {
22587
- size: 28,
22588
- className: "text-muted"
22589
- }),
22616
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 28 }),
22590
22617
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
22591
22618
  className: "text-muted",
22592
22619
  children: "获赞"
22593
22620
  }),
22594
22621
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
22595
22622
  className: "font-medium text-4xl text-foreground",
22596
- children: [" ", formatCount$2(prpos.data.user.total_favorited)]
22623
+ children: [" ", formatCount(prpos.data.user.total_favorited)]
22597
22624
  })
22598
22625
  ]
22599
22626
  })
@@ -22621,7 +22648,8 @@ var DouyinUserVideoList = (prpos) => {
22621
22648
  className: "text-accent font-bold",
22622
22649
  children: ["1~", prpos.data.videos.length]
22623
22650
  }),
22624
- " 之间的数字解析对应作品。例如发送“1”解析第一个作品"
22651
+ " ",
22652
+ "之间的数字解析对应作品。例如发送“1”解析第一个作品"
22625
22653
  ] })]
22626
22654
  })
22627
22655
  }),
@@ -22647,12 +22675,12 @@ var formatNumber$1 = (num) => {
22647
22675
  if (num >= 1e4) return `${(num / 1e4).toFixed(1)}万`;
22648
22676
  return num.toLocaleString();
22649
22677
  };
22650
- var formatDuration$3 = (ms) => {
22678
+ var formatDuration$1 = (ms) => {
22651
22679
  const seconds = Math.floor(ms / 1e3);
22652
22680
  return `${Math.floor(seconds / 60)}:${(seconds % 60).toString().padStart(2, "0")}`;
22653
22681
  };
22654
22682
  var DouyinVideoInfo = import_react.memo((props) => {
22655
- const duration = (0, import_react.useMemo)(() => props.data.video ? formatDuration$3(props.data.video.duration) : null, [props.data.video]);
22683
+ const duration = (0, import_react.useMemo)(() => props.data.video ? formatDuration$1(props.data.video.duration) : null, [props.data.video]);
22656
22684
  const coverMaskStyle = (0, import_react.useMemo)(() => ({
22657
22685
  maskImage: `linear-gradient(to bottom,
22658
22686
  transparent 0%,
@@ -22802,19 +22830,19 @@ var DouyinVideoInfo = import_react.memo((props) => {
22802
22830
  className: "flex items-center gap-14 shrink-0",
22803
22831
  children: [
22804
22832
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$1, {
22805
- icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heart, { size: 48 }),
22833
+ icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinLikeIcon, { size: 48 }),
22806
22834
  value: props.data.statistics.digg_count
22807
22835
  }),
22808
22836
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$1, {
22809
- icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MessageCircle, { size: 48 }),
22837
+ icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinCommentIcon, { size: 48 }),
22810
22838
  value: props.data.statistics.comment_count
22811
22839
  }),
22812
22840
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$1, {
22813
- icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Star, { size: 48 }),
22841
+ icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinFavoriteIcon, { size: 48 }),
22814
22842
  value: props.data.statistics.collect_count
22815
22843
  }),
22816
22844
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatItem$1, {
22817
- icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Share2, { size: 48 }),
22845
+ icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DouyinShareIcon, { size: 48 }),
22818
22846
  value: props.data.statistics.share_count
22819
22847
  })
22820
22848
  ]
@@ -22883,7 +22911,7 @@ var StatItem$1 = ({ icon, value }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx
22883
22911
  DouyinVideoInfo.displayName = "DouyinVideoInfo";
22884
22912
  //#endregion
22885
22913
  //#region ../template/src/components/platforms/douyin/VideoWork.tsx
22886
- function formatDuration$2(duration) {
22914
+ function formatDuration(duration) {
22887
22915
  if (typeof duration !== "number" || !Number.isFinite(duration) || duration < 0) return void 0;
22888
22916
  const totalSeconds = Math.floor(duration / 1e3);
22889
22917
  const hours = Math.floor(totalSeconds / 3600);
@@ -22948,7 +22976,7 @@ var DouyinAvatarUserInfo = (props) => {
22948
22976
  };
22949
22977
  var DouyinVideoCover = (props) => {
22950
22978
  const { image_url, music, duration } = props.data;
22951
- const durationText = formatDuration$2(duration);
22979
+ const durationText = formatDuration(duration);
22952
22980
  const musicBadge = music && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
22953
22981
  className: "absolute left-7 bottom-7 z-20 flex items-center gap-4 max-w-[72%] p-3 rounded-3xl bg-black/45 backdrop-blur-2xl border border-white/20 shadow-large overflow-hidden",
22954
22982
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
@@ -23070,22 +23098,22 @@ var DouyinDynamicStatus = (props) => {
23070
23098
  className: "flex gap-6 items-center text-5xl tracking-normal select-text text-foreground/70",
23071
23099
  children: [
23072
23100
  {
23073
- icon: o$3,
23101
+ icon: DouyinLikeIcon,
23074
23102
  value: dianzan,
23075
23103
  label: "点赞"
23076
23104
  },
23077
23105
  {
23078
- icon: t,
23106
+ icon: DouyinCommentIcon,
23079
23107
  value: pinglun,
23080
23108
  label: "评论"
23081
23109
  },
23082
23110
  {
23083
- icon: r,
23111
+ icon: DouyinFavoriteIcon,
23084
23112
  value: shouchang,
23085
23113
  label: "收藏"
23086
23114
  },
23087
23115
  {
23088
- icon: e$1,
23116
+ icon: DouyinShareIcon,
23089
23117
  value: share,
23090
23118
  label: "分享"
23091
23119
  }
@@ -23095,7 +23123,7 @@ var DouyinDynamicStatus = (props) => {
23095
23123
  className: "flex gap-2 items-end",
23096
23124
  children: [
23097
23125
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, {
23098
- size: 50,
23126
+ size: 46,
23099
23127
  weight: "fill",
23100
23128
  className: "mt-2"
23101
23129
  }),
@@ -23255,19 +23283,19 @@ var DouyinDynamicFooter = (props) => {
23255
23283
  className: "text-3xl flex gap-6 items-center text-foreground/70",
23256
23284
  children: [
23257
23285
  {
23258
- icon: o$3,
23286
+ icon: DouyinLikeIcon,
23259
23287
  iconSize: 36,
23260
23288
  label: "获赞",
23261
23289
  value: 获赞
23262
23290
  },
23263
23291
  {
23264
- icon: o$4,
23292
+ icon: o$3,
23265
23293
  iconSize: 36,
23266
23294
  label: "关注",
23267
23295
  value: 关注
23268
23296
  },
23269
23297
  {
23270
- icon: r$1,
23298
+ icon: r,
23271
23299
  iconSize: 36,
23272
23300
  label: "粉丝",
23273
23301
  value: 粉丝
@@ -24987,7 +25015,8 @@ var GroupStatistics = (props) => {
24987
25015
  className: "font-black text-foreground",
24988
25016
  children: props.data.globalTotalGroups
24989
25017
  }),
24990
- " 个群组 · 解析 ",
25018
+ " 个群组 · 解析",
25019
+ " ",
24991
25020
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
24992
25021
  className: "font-black text-foreground",
24993
25022
  children: props.data.globalTotalParses
@@ -25270,15 +25299,16 @@ var ansi256ToColor = (colorCode) => {
25270
25299
  return `#${hex}${hex}${hex}`;
25271
25300
  };
25272
25301
  var convertAnsiToHtml = (text) => {
25273
- const ansiRegex = /\x1b\[([0-9;]+)m/g;
25302
+ const ansiRegex = /* @__PURE__ */ new RegExp("\x1B\\[([0-9;]+)m", "g");
25274
25303
  let result = "", lastIndex = 0;
25275
25304
  let currentStyles = { classes: [] };
25276
25305
  let match;
25277
25306
  const escapeHtml = (str) => str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
25307
+ const formatLogContent = (content) => escapeHtml(content).replace(/([\u3400-\u9fff\uf900-\ufaff\u3000-\u303f\uff00-\uffef]+)/g, "<span class=\"font-[HarmonyOSHans-Regular]\">$1</span>");
25278
25308
  const makeSpan = (content) => {
25279
25309
  const hasClass = currentStyles.classes.length > 0, hasInline = currentStyles.inlineColor;
25280
- if (!hasClass && !hasInline) return escapeHtml(content);
25281
- return `<span${hasClass ? ` class="${currentStyles.classes.join(" ")}"` : ""}${hasInline ? ` style="color: ${currentStyles.inlineColor}"` : ""}>${escapeHtml(content)}</span>`;
25310
+ if (!hasClass && !hasInline) return formatLogContent(content);
25311
+ return `<span${hasClass ? ` class="${currentStyles.classes.join(" ")}"` : ""}${hasInline ? ` style="color: ${currentStyles.inlineColor}"` : ""}>${formatLogContent(content)}</span>`;
25282
25312
  };
25283
25313
  while ((match = ansiRegex.exec(text)) !== null) {
25284
25314
  if (match.index > lastIndex) result += makeSpan(text.substring(lastIndex, match.index));
@@ -25318,7 +25348,7 @@ var convertAnsiToHtml = (text) => {
25318
25348
  };
25319
25349
  var getLogLevelTheme = (level, isDark) => {
25320
25350
  const themeMap = {
25321
- "TRAC": {
25351
+ TRAC: {
25322
25352
  bgClass: isDark ? "bg-muted/10" : "bg-muted/5",
25323
25353
  borderClass: "border-muted/20",
25324
25354
  textClass: "text-muted",
@@ -25326,7 +25356,7 @@ var getLogLevelTheme = (level, isDark) => {
25326
25356
  levelClass: isDark ? "text-muted/10" : "text-muted/10",
25327
25357
  dotClass: "bg-muted/40"
25328
25358
  },
25329
- "DEBU": {
25359
+ DEBU: {
25330
25360
  bgClass: isDark ? "bg-cyan-400/10" : "bg-cyan-500/5",
25331
25361
  borderClass: isDark ? "border-cyan-400/20" : "border-cyan-500/20",
25332
25362
  textClass: isDark ? "text-cyan-400" : "text-cyan-600",
@@ -25334,7 +25364,7 @@ var getLogLevelTheme = (level, isDark) => {
25334
25364
  levelClass: isDark ? "text-cyan-400/10" : "text-cyan-600/10",
25335
25365
  dotClass: isDark ? "bg-cyan-400/40" : "bg-cyan-500/40"
25336
25366
  },
25337
- "MARK": {
25367
+ MARK: {
25338
25368
  bgClass: isDark ? "bg-muted/10" : "bg-muted/5",
25339
25369
  borderClass: "border-muted/20",
25340
25370
  textClass: "text-muted",
@@ -25342,7 +25372,7 @@ var getLogLevelTheme = (level, isDark) => {
25342
25372
  levelClass: isDark ? "text-muted/10" : "text-muted/10",
25343
25373
  dotClass: "bg-muted/40"
25344
25374
  },
25345
- "INFO": {
25375
+ INFO: {
25346
25376
  bgClass: "bg-success-soft",
25347
25377
  borderClass: "border-success/25",
25348
25378
  textClass: "text-success",
@@ -25350,7 +25380,7 @@ var getLogLevelTheme = (level, isDark) => {
25350
25380
  levelClass: isDark ? "text-success/10" : "text-success/10",
25351
25381
  dotClass: "bg-success/40"
25352
25382
  },
25353
- "WARN": {
25383
+ WARN: {
25354
25384
  bgClass: "bg-warning-soft",
25355
25385
  borderClass: "border-warning/25",
25356
25386
  textClass: "text-warning",
@@ -25358,7 +25388,7 @@ var getLogLevelTheme = (level, isDark) => {
25358
25388
  levelClass: isDark ? "text-warning/10" : "text-warning-soft",
25359
25389
  dotClass: "bg-warning/40"
25360
25390
  },
25361
- "ERRO": {
25391
+ ERRO: {
25362
25392
  bgClass: "bg-danger-soft",
25363
25393
  borderClass: "border-danger/25",
25364
25394
  textClass: "text-danger",
@@ -25366,7 +25396,7 @@ var getLogLevelTheme = (level, isDark) => {
25366
25396
  levelClass: isDark ? "text-danger/10" : "text-danger/10",
25367
25397
  dotClass: "bg-danger/40"
25368
25398
  },
25369
- "FATA": {
25399
+ FATA: {
25370
25400
  bgClass: isDark ? "bg-pink-400/10" : "bg-pink-500/5",
25371
25401
  borderClass: isDark ? "border-pink-400/25" : "border-pink-500/25",
25372
25402
  textClass: isDark ? "text-pink-400" : "text-pink-500",
@@ -26024,7 +26054,8 @@ var handlerError = (props) => {
26024
26054
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MdSchedule, { size: 24 }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
26025
26055
  "Built Time: ",
26026
26056
  data.buildTime,
26027
- " 于 ",
26057
+ " 于",
26058
+ " ",
26028
26059
  formatDistanceToNow(parse(data.buildTime, "yyyy年MM月dd日 HH:mm", /* @__PURE__ */ new Date()), { locale: zhCN }),
26029
26060
  "前"
26030
26061
  ] })]
@@ -27904,13 +27935,15 @@ var VersionWarning = (props) => {
27904
27935
  className: "text-[26px] leading-relaxed",
27905
27936
  style: { color: secondaryColor },
27906
27937
  children: [
27907
- "当前插件版本基于 ",
27938
+ "当前插件版本基于",
27939
+ " ",
27908
27940
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
27909
27941
  className: "font-bold font-mono",
27910
27942
  style: { color: accentColor },
27911
27943
  children: ["node-karin v", props.data.requireVersion]
27912
27944
  }),
27913
- " 开发,低版本运行时可能出现功能异常或兼容性问题,请及时更新以获得最佳体验"
27945
+ " ",
27946
+ "开发,低版本运行时可能出现功能异常或兼容性问题,请及时更新以获得最佳体验"
27914
27947
  ]
27915
27948
  })]
27916
27949
  })]
@@ -29829,11 +29862,11 @@ var SSRRender = class {
29829
29862
  };
29830
29863
  /**
29831
29864
  * SSR 预渲染组件为 HTML 的具体实现
29832
- *
29865
+ *
29833
29866
  * @template K 模板类型键,用于类型推断
29834
29867
  * @param options 渲染配置选项
29835
29868
  * @returns 渲染结果 Promise
29836
- *
29869
+ *
29837
29870
  * # Example
29838
29871
  * ```typescript
29839
29872
  * // 基础使用
@@ -29845,7 +29878,7 @@ var SSRRender = class {
29845
29878
  * },
29846
29879
  * outputDir: './output'
29847
29880
  * })
29848
- *
29881
+ *
29849
29882
  * // 使用插件
29850
29883
  * const result = await reactServerRender({
29851
29884
  * request: renderRequest,
@@ -30313,7 +30346,7 @@ var embedWatermark = (pngBytes, watermarkText) => {
30313
30346
  /**
30314
30347
  * 渲染函数
30315
30348
  * 将指定路径的模板渲染为图片元素数组
30316
- *
30349
+ *
30317
30350
  * @param event 消息事件对象,用于获取机器人账号信息
30318
30351
  * @template P 渲染路径,必须是有效的动态路径
30319
30352
  * @param path 渲染路径,格式为 "平台/组件ID" 或 "平台/分类/组件ID"
@@ -32598,7 +32631,7 @@ var webConfig = defineConfig({
32598
32631
  }]
32599
32632
  },
32600
32633
  page: {
32601
- url: process.env.NODE_ENV === "development" ? "http://192.168.1.8:5176/kkk/karin-config" : "/kkk/karin-config",
32634
+ url: process.env.NODE_ENV === "development" ? "http://localhost:5176/kkk/assets/karin-config" : "/kkk/assets/karin-config",
32602
32635
  title: "kkk插件配置管理",
32603
32636
  description: "使用 kkk 插件自带的配置管理页面"
32604
32637
  }
@@ -32707,7 +32740,7 @@ var renderErrorImage = async (ctx, opts = {}) => {
32707
32740
  //#region src/module/utils/bot.ts
32708
32741
  /**
32709
32742
  * 获取候选机器人
32710
- * @returns
32743
+ * @returns
32711
32744
  */
32712
32745
  var getCandidateBots = () => {
32713
32746
  return karin$1.getAllBotList().map((item) => item.bot).filter((bot) => bot.account.name !== "console");
@@ -32715,7 +32748,7 @@ var getCandidateBots = () => {
32715
32748
  /**
32716
32749
  * 获取非console主机器人ID列表
32717
32750
  * @param masters - 主机器人ID列表
32718
- * @returns
32751
+ * @returns
32719
32752
  */
32720
32753
  var getNonConsoleMasters = (masters = config.master()) => {
32721
32754
  return masters.filter((id) => id !== "console");
@@ -32723,7 +32756,7 @@ var getNonConsoleMasters = (masters = config.master()) => {
32723
32756
  /**
32724
32757
  * 获取可访问的主机器人
32725
32758
  * @param masters - 主机器人ID列表
32726
- * @returns
32759
+ * @returns
32727
32760
  */
32728
32761
  var getReachableMasterBots = async (masters = config.master()) => {
32729
32762
  const owners = getNonConsoleMasters(masters);
@@ -32754,7 +32787,7 @@ var getReachableMasterBots = async (masters = config.master()) => {
32754
32787
  /**
32755
32788
  * 获取一个最少能用的机器人实例,优先级:1. selfId 参数指定的机器人 2. 可访问主人机器人中的第一个 3. pushlist 中活跃的机器人 4. 任意一个在线机器人
32756
32789
  * @param selfId - 机器人ID
32757
- * @returns
32790
+ * @returns
32758
32791
  */
32759
32792
  var resolveUsableBot = async (selfId) => {
32760
32793
  if (selfId) {
@@ -32959,7 +32992,7 @@ var sendErrorToAllMasters = async (ctx, img, customPrefix) => {
32959
32992
  * @param event - 错误事件上下文
32960
32993
  * @param isPush - 是否为推送任务
32961
32994
  *
32962
- * @returns
32995
+ * @returns
32963
32996
  */
32964
32997
  var resolveSingleMasterTarget = async (event, isPush) => {
32965
32998
  if (isPush) {
@@ -33987,10 +34020,10 @@ var Bilibili = class extends Base {
33987
34020
  this.downloadfilename = "";
33988
34021
  this.forceBurnDanmaku = options?.forceBurnDanmaku ?? false;
33989
34022
  this.headers.Referer = "https://www.bilibili.com/";
33990
- this.headers.Cookie = Config.cookies.bilibili;
34023
+ this.headers.Cookie = Config.amagi.cookies.bilibili;
33991
34024
  }
33992
34025
  async BilibiliHandler(iddata) {
33993
- Config.app.parseTip && await this.e.reply("检测到B站链接,开始解析");
34026
+ if (Config.app.parseTip) this.e.reply("检测到B站链接,开始解析");
33994
34027
  switch (this.Type) {
33995
34028
  case "one_video": {
33996
34029
  const infoData = await this.amagi.bilibili.fetcher.fetchVideoInfo({
@@ -34037,9 +34070,12 @@ var Bilibili = class extends Base {
34037
34070
  host_mid: infoData.data.data.owner.mid,
34038
34071
  typeMode: "strict"
34039
34072
  });
34040
- const danmakuCid = iddata.p ? infoData.data.data.pages[iddata.p - 1]?.cid ?? infoData.data.data.cid : infoData.data.data.cid;
34041
- const danmakuDuration = iddata.p ? infoData.data.data.pages[iddata.p - 1]?.duration ?? infoData.data.data.duration : infoData.data.data.duration;
34042
- const hotDanmaku = getHotDanmaku(await this.fetchVideoDanmakuList(danmakuCid, danmakuDuration), 20);
34073
+ let hotDanmaku;
34074
+ if (Config.bilibili.showDanmakuInVideoInfo) {
34075
+ const danmakuCid = iddata.p ? infoData.data.data.pages[iddata.p - 1]?.cid ?? infoData.data.data.cid : infoData.data.data.cid;
34076
+ const danmakuDuration = iddata.p ? infoData.data.data.pages[iddata.p - 1]?.duration ?? infoData.data.data.duration : infoData.data.data.duration;
34077
+ hotDanmaku = getHotDanmaku(await this.fetchVideoDanmakuList(danmakuCid, danmakuDuration), 20);
34078
+ }
34043
34079
  const img = await Render(this.e, "bilibili/videoInfo", {
34044
34080
  share_url: "https://b23.tv/" + infoData.data.data.bvid,
34045
34081
  title: infoData.data.data.title,
@@ -34120,7 +34156,7 @@ var Bilibili = class extends Base {
34120
34156
  }
34121
34157
  }
34122
34158
  }
34123
- if (Config.bilibili.sendContent.some((content) => content === "video")) if (Config.upload.usefilelimit && Number(videoSize) > Number(Config.upload.filelimit) && !Config.upload.compress) this.e.reply(`设定的最大上传大小为 ${Config.upload.filelimit}MB\n当前解析到的视频大小为 ${Number(videoSize)}MB\n视频太大了,还是去B站看吧~`, { reply: true });
34159
+ if (Config.bilibili.sendContent.some((content) => content === "video")) if (Config.app.usefilelimit && Number(videoSize) > Number(Config.app.filelimit) && !Config.app.compress) this.e.reply(`设定的最大上传大小为 ${Config.app.filelimit}MB\n当前解析到的视频大小为 ${Number(videoSize)}MB\n视频太大了,还是去B站看吧~`, { reply: true });
34124
34160
  else {
34125
34161
  if (Config.bilibili.videoQuality !== 0 && Config.bilibili.videoQuality < 64) this.islogin = false;
34126
34162
  let danmakuList = [];
@@ -34284,7 +34320,7 @@ var Bilibili = class extends Base {
34284
34320
  filepath: filePath,
34285
34321
  totalBytes: 0
34286
34322
  });
34287
- const videoPath = Config.upload.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
34323
+ const videoPath = Config.app.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
34288
34324
  imgArray.push(segment.video(videoPath));
34289
34325
  }
34290
34326
  }
@@ -34301,7 +34337,7 @@ var Bilibili = class extends Base {
34301
34337
  filepath: motionPhotoCoverPath,
34302
34338
  totalBytes: 0
34303
34339
  });
34304
- const motionPhotoCover = Config.upload.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
34340
+ const motionPhotoCover = Config.app.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
34305
34341
  imgArray.push(segment.image(motionPhotoCover));
34306
34342
  hasPushedMotionPhotoCover = true;
34307
34343
  }
@@ -34880,7 +34916,7 @@ var Bilibili = class extends Base {
34880
34916
  await Common.removeFile(bmp3.filepath, true);
34881
34917
  const stats = fs.statSync(filePath);
34882
34918
  const fileSizeInMB = Number((stats.size / (1024 * 1024)).toFixed(2));
34883
- if (fileSizeInMB > Config.upload.groupfilevalue) await uploadFile(this.e, {
34919
+ if (fileSizeInMB > Config.app.groupfilevalue) await uploadFile(this.e, {
34884
34920
  filepath: filePath,
34885
34921
  totalBytes: fileSizeInMB,
34886
34922
  originTitle: this.downloadfilename
@@ -34922,7 +34958,7 @@ var Bilibili = class extends Base {
34922
34958
  await Common.removeFile(videoFile.filepath, true);
34923
34959
  const stats = fs.statSync(filePath);
34924
34960
  const fileSizeInMB = Number((stats.size / (1024 * 1024)).toFixed(2));
34925
- if (fileSizeInMB > Config.upload.groupfilevalue) await uploadFile(this.e, {
34961
+ if (fileSizeInMB > Config.app.groupfilevalue) await uploadFile(this.e, {
34926
34962
  filepath: filePath,
34927
34963
  totalBytes: fileSizeInMB,
34928
34964
  originTitle: this.downloadfilename
@@ -35123,7 +35159,7 @@ var mapping_table = (type) => {
35123
35159
  * 根据动态类型获取对应的oid(对象ID),用于后续评论接口调用
35124
35160
  * @param dynamicType 动态类型
35125
35161
  * @param dynamicData 动态数据
35126
- * @returns
35162
+ * @returns
35127
35163
  */
35128
35164
  var oid = (dynamicType, dynamicData) => {
35129
35165
  switch (dynamicType) {
@@ -35204,7 +35240,7 @@ var getvideosize = async (videourl, audiourl, bvid) => {
35204
35240
  headers: {
35205
35241
  ...BASE_HEADERS,
35206
35242
  Referer: `https://www.bilibili.com/video/${bvid}`,
35207
- Cookie: Config.cookies.bilibili
35243
+ Cookie: Config.amagi.cookies.bilibili
35208
35244
  }
35209
35245
  }).getHeaders();
35210
35246
  const audioheaders = await new Network({
@@ -35212,7 +35248,7 @@ var getvideosize = async (videourl, audiourl, bvid) => {
35212
35248
  headers: {
35213
35249
  ...BASE_HEADERS,
35214
35250
  Referer: `https://www.bilibili.com/video/${bvid}`,
35215
- Cookie: Config.cookies.bilibili
35251
+ Cookie: Config.amagi.cookies.bilibili
35216
35252
  }
35217
35253
  }).getHeaders();
35218
35254
  const videoSize = extractTotalBytesFromHeaders(videoheaders);
@@ -35269,8 +35305,8 @@ var getStringDisplayWidth$1 = (str) => {
35269
35305
  };
35270
35306
  /**
35271
35307
  * 提取专栏中的所有图片URL
35272
- * @param content
35273
- * @returns
35308
+ * @param content
35309
+ * @returns
35274
35310
  */
35275
35311
  var extractArticleImages = (content) => {
35276
35312
  const images = [];
@@ -35544,9 +35580,9 @@ var extractFansDetail = (member) => {
35544
35580
  * @returns
35545
35581
  */
35546
35582
  var genParams = async (apiURL) => {
35547
- if (Config.cookies.bilibili === "" || Config.cookies.bilibili === null) return "&platform=html5";
35583
+ if (Config.amagi.cookies.bilibili === "" || Config.amagi.cookies.bilibili === null) return "&platform=html5";
35548
35584
  const loginInfo = await bilibiliFetcher.fetchLoginStatus({ typeMode: "strict" });
35549
- const genSign = await wbi_sign(apiURL, Config.cookies.bilibili);
35585
+ const genSign = await wbi_sign(apiURL, Config.amagi.cookies.bilibili);
35550
35586
  const qn = [
35551
35587
  6,
35552
35588
  16,
@@ -35561,19 +35597,15 @@ var genParams = async (apiURL) => {
35561
35597
  126,
35562
35598
  127
35563
35599
  ];
35564
- let isvip;
35565
- loginInfo.data.data.vipStatus === 1 ? isvip = true : isvip = false;
35566
- if (isvip) return `&fnval=16&fourk=1&${genSign}`;
35600
+ if (loginInfo.data.data.vipStatus === 1) return `&fnval=16&fourk=1&${genSign}`;
35567
35601
  else return `&qn=${qn[3]}&fnval=16`;
35568
35602
  };
35569
35603
  var checkCk = async () => {
35570
- if (Config.cookies.bilibili === "" || Config.cookies.bilibili === null) return {
35604
+ if (Config.amagi.cookies.bilibili === "" || Config.amagi.cookies.bilibili === null) return {
35571
35605
  Status: "!isLogin",
35572
35606
  isVIP: false
35573
35607
  };
35574
- const loginInfo = await bilibiliFetcher.fetchLoginStatus({ typeMode: "strict" });
35575
- let isVIP;
35576
- loginInfo.data.data.vipStatus === 1 ? isVIP = true : isVIP = false;
35608
+ const isVIP = (await bilibiliFetcher.fetchLoginStatus({ typeMode: "strict" })).data.data.vipStatus === 1;
35577
35609
  if (isVIP) return {
35578
35610
  Status: "isLogin",
35579
35611
  isVIP
@@ -35734,8 +35766,8 @@ var bilibiliLogin = async (e) => {
35734
35766
  let cookieString;
35735
35767
  if (Array.isArray(setCookieHeader)) cookieString = setCookieHeader.join("; ");
35736
35768
  else cookieString = setCookieHeader || "";
35737
- Config.Modify("cookies", "bilibili", cookieString);
35738
- await e.reply("登录成功!用户登录凭证已保存至cookies.yaml", { reply: true });
35769
+ Config.Modify("amagi", "cookies.bilibili", cookieString);
35770
+ await e.reply("登录成功!用户登录凭证已保存至配置", { reply: true });
35739
35771
  await recallMessages();
35740
35772
  };
35741
35773
  /**
@@ -35810,7 +35842,7 @@ var allBilibiliPushTypes = [
35810
35842
  var bilibiliBaseHeaders = {
35811
35843
  ...BASE_HEADERS,
35812
35844
  Referer: "https://www.bilibili.com",
35813
- Cookie: Config.cookies.bilibili
35845
+ Cookie: Config.amagi.cookies.bilibili
35814
35846
  };
35815
35847
  var Bilibilipush = class extends Base {
35816
35848
  force = false;
@@ -35914,7 +35946,7 @@ var Bilibilipush = class extends Base {
35914
35946
  ${logger.cyan("动态id")}:${logger.yellow(dynamicId)}
35915
35947
  ${logger.cyan("访问地址")}:${logger.green("https://t.bilibili.com/" + dynamicId)}`);
35916
35948
  let skip = await skipDynamic$1(data[dynamicId]);
35917
- skip && logger.warn(`动态 https://t.bilibili.com/${dynamicId} 已被处理,跳过`);
35949
+ if (skip) logger.warn(`动态 https://t.bilibili.com/${dynamicId} 已被处理,跳过`);
35918
35950
  let send_video = true;
35919
35951
  let img = [];
35920
35952
  this.injectBotToEventForRender(data[dynamicId].targets);
@@ -36320,8 +36352,8 @@ var Bilibilipush = class extends Base {
36320
36352
  playUrlData.data.data.accept_description = correctList.accept_description;
36321
36353
  /** 获取第一个视频流的大小 */
36322
36354
  videoSize = await getvideosize(correctList.videoList[0].base_url, playUrlData.data.data.dash.audio[0].base_url, data[dynamicId].Dynamic_Data.modules.module_dynamic.major.archive.bvid);
36323
- if (Config.upload.usefilelimit && Number(videoSize) > Number(Config.upload.filelimit) && !Config.upload.compress) {
36324
- await karin$1.sendMsg(botId, Contact, [segment.text(`设定的最大上传大小为 ${Config.upload.filelimit}MB\n当前解析到的视频大小为 ${Number(videoSize)}MB\n视频太大了,还是去B站看吧~`), segment.reply(status.messageId)]);
36355
+ if (Config.app.usefilelimit && Number(videoSize) > Number(Config.app.filelimit) && !Config.app.compress) {
36356
+ await karin$1.sendMsg(botId, Contact, [segment.text(`设定的最大上传大小为 ${Config.app.filelimit}MB\n当前解析到的视频大小为 ${Number(videoSize)}MB\n视频太大了,还是去B站看吧~`), segment.reply(status.messageId)]);
36325
36357
  break;
36326
36358
  }
36327
36359
  logger.mark(`当前处于自动推送状态,解析到的视频大小为 ${logger.yellow(Number(videoSize))} MB`);
@@ -36348,7 +36380,7 @@ var Bilibilipush = class extends Base {
36348
36380
  await Common.removeFile(mp3File.filepath, true);
36349
36381
  const stats = fs.statSync(filePath);
36350
36382
  const fileSizeInMB = Number((stats.size / (1024 * 1024)).toFixed(2));
36351
- if (fileSizeInMB > Config.upload.groupfilevalue) await uploadFile(this.e, {
36383
+ if (fileSizeInMB > Config.app.groupfilevalue) await uploadFile(this.e, {
36352
36384
  filepath: filePath,
36353
36385
  totalBytes: fileSizeInMB,
36354
36386
  originTitle: `${infoData.data.data.desc.substring(0, 50).replace(/[\\/:\\*\\?"<>\\|\r\n\s]/g, " ")}`
@@ -36427,7 +36459,7 @@ var Bilibilipush = class extends Base {
36427
36459
  filepath: filePath,
36428
36460
  totalBytes: 0
36429
36461
  });
36430
- const videoPath = Config.upload.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
36462
+ const videoPath = Config.app.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
36431
36463
  imgArray.push(segment.video(videoPath));
36432
36464
  }
36433
36465
  }
@@ -36444,7 +36476,7 @@ var Bilibilipush = class extends Base {
36444
36476
  filepath: motionPhotoCoverPath,
36445
36477
  totalBytes: 0
36446
36478
  });
36447
- const motionPhotoCover = Config.upload.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
36479
+ const motionPhotoCover = Config.app.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
36448
36480
  imgArray.push(segment.image(motionPhotoCover));
36449
36481
  hasPushedMotionPhotoCover = true;
36450
36482
  }
@@ -37505,7 +37537,7 @@ var DouYin = class DouYin extends Base {
37505
37537
  return true;
37506
37538
  }
37507
37539
  async DouyinHandler(data) {
37508
- Config.app.parseTip && this.e.reply("检测到抖音链接,开始解析");
37540
+ if (Config.app.parseTip) this.e.reply("检测到抖音链接,开始解析");
37509
37541
  switch (this.type) {
37510
37542
  case "one_work": {
37511
37543
  const VideoData = await this.amagi.douyin.fetcher.parseWork({
@@ -37615,7 +37647,7 @@ var DouYin = class DouYin extends Base {
37615
37647
  filepath: filePath,
37616
37648
  totalBytes: 0
37617
37649
  });
37618
- const videoPath = Config.upload.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
37650
+ const videoPath = Config.app.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
37619
37651
  processedImages.push(segment.video(videoPath));
37620
37652
  }
37621
37653
  }
@@ -37632,7 +37664,7 @@ var DouYin = class DouYin extends Base {
37632
37664
  filepath: motionPhotoCoverPath,
37633
37665
  totalBytes: 0
37634
37666
  });
37635
- const motionPhotoCover = Config.upload.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
37667
+ const motionPhotoCover = Config.app.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
37636
37668
  processedImages.push(segment.image(motionPhotoCover));
37637
37669
  hasPushedMotionPhotoCover = true;
37638
37670
  }
@@ -37770,7 +37802,7 @@ var DouYin = class DouYin extends Base {
37770
37802
  filepath: filePath,
37771
37803
  totalBytes: 0
37772
37804
  });
37773
- const videoPath = Config.upload.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
37805
+ const videoPath = Config.app.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
37774
37806
  images.push(segment.video(videoPath));
37775
37807
  }
37776
37808
  }
@@ -37787,7 +37819,7 @@ var DouYin = class DouYin extends Base {
37787
37819
  filepath: motionPhotoCoverPath,
37788
37820
  totalBytes: 0
37789
37821
  });
37790
- const motionPhotoCover = Config.upload.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
37822
+ const motionPhotoCover = Config.app.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
37791
37823
  images.push(segment.image(motionPhotoCover));
37792
37824
  hasPushedMotionPhotoCover = true;
37793
37825
  }
@@ -37941,7 +37973,9 @@ var DouYin = class DouYin extends Base {
37941
37973
  else {
37942
37974
  const suggest = [];
37943
37975
  if (VideoData.data.aweme_detail?.suggest_words?.suggest_words) {
37944
- for (const item of VideoData.data.aweme_detail.suggest_words.suggest_words) if (item.words && item.scene === "comment_top_rec") for (const v of item.words) v.word && suggest.push(v.word);
37976
+ for (const item of VideoData.data.aweme_detail.suggest_words.suggest_words) if (item.words && item.scene === "comment_top_rec") {
37977
+ for (const v of item.words) if (v.word) suggest.push(v.word);
37978
+ }
37945
37979
  }
37946
37980
  const aweme = VideoData.data.aweme_detail;
37947
37981
  const img = await Render(this.e, "douyin/comment", {
@@ -38024,7 +38058,7 @@ var DouYin = class DouYin extends Base {
38024
38058
  await Common.removeFile(videoFile.filepath, true);
38025
38059
  const stats = fs.statSync(filePath);
38026
38060
  const fileSizeInMB = Number((stats.size / (1024 * 1024)).toFixed(2));
38027
- if (fileSizeInMB > Config.upload.groupfilevalue) await uploadFile(this.e, {
38061
+ if (fileSizeInMB > Config.app.groupfilevalue) await uploadFile(this.e, {
38028
38062
  filepath: filePath,
38029
38063
  totalBytes: fileSizeInMB,
38030
38064
  originTitle: g_title || ""
@@ -38258,7 +38292,7 @@ var douyinProcessVideos = (videos, videoQuality, maxAutoVideoSize) => {
38258
38292
  videos.sort((a, b) => b.play_addr.data_size - a.play_addr.data_size);
38259
38293
  });
38260
38294
  if (videoQuality === "adapt") {
38261
- const sizeLimitBytes = (maxAutoVideoSize || Config.upload.filelimit) * 1024 * 1024;
38295
+ const sizeLimitBytes = (maxAutoVideoSize || Config.app.filelimit) * 1024 * 1024;
38262
38296
  for (const quality of [
38263
38297
  "4k",
38264
38298
  "2k",
@@ -38538,7 +38572,7 @@ var getDouyinID = async (event, url, log = true) => {
38538
38572
  logger.warn("无法获取作品ID");
38539
38573
  break;
38540
38574
  }
38541
- log && console.log(result);
38575
+ if (log) console.log(result);
38542
38576
  return result;
38543
38577
  };
38544
38578
  //#endregion
@@ -39265,7 +39299,7 @@ async function renderLiveImage(options) {
39265
39299
  var douyinBaseHeaders = {
39266
39300
  ...BASE_HEADERS,
39267
39301
  Referer: "https://www.douyin.com",
39268
- Cookie: Config.cookies.douyin
39302
+ Cookie: Config.amagi.cookies.douyin
39269
39303
  };
39270
39304
  var DouYinpush = class extends Base {
39271
39305
  force = false;
@@ -39279,7 +39313,7 @@ var DouYinpush = class extends Base {
39279
39313
  constructor(e = {}, force = false) {
39280
39314
  super(e);
39281
39315
  this.headers.Referer = "https://www.douyin.com";
39282
- this.headers.Cookie = Config.cookies.douyin;
39316
+ this.headers.Cookie = Config.amagi.cookies.douyin;
39283
39317
  this.force = force;
39284
39318
  }
39285
39319
  injectBotToEventForRender(targets) {
@@ -39299,7 +39333,7 @@ var DouYinpush = class extends Base {
39299
39333
  const deletedCount = await cleanOldDynamicCache("douyin");
39300
39334
  if (deletedCount > 0) logger.info(`已清理 ${deletedCount} 条过期的抖音作品缓存记录`);
39301
39335
  if (await this.checkremark()) return true;
39302
- this.ensureConfigFields(Config.pushlist.douyin);
39336
+ await this.ensureConfigFields(Config.pushlist.douyin);
39303
39337
  const registeredBotIds = karin$1.getAllBotID();
39304
39338
  const filteredPushList = this.filterPushListByRegisteredBots(Config.pushlist.douyin, registeredBotIds);
39305
39339
  if (filteredPushList.length === 0) {
@@ -39315,10 +39349,30 @@ var DouYinpush = class extends Base {
39315
39349
  * 检查并补全配置文件中缺失的字段
39316
39350
  * @param pushList 推送配置列表
39317
39351
  */
39318
- ensureConfigFields(pushList) {
39352
+ async ensureConfigFields(pushList) {
39319
39353
  if (!pushList || pushList.length === 0) return;
39320
39354
  let hasChanges = false;
39321
39355
  for (const item of pushList) {
39356
+ if (!item.sec_uid && item.short_id) try {
39357
+ logger.info(`自动获取用户 ${item.remark || item.short_id} 的 sec_uid`);
39358
+ const searchResult = await this.amagi.douyin.fetcher.searchContent({
39359
+ query: item.short_id,
39360
+ type: "user",
39361
+ typeMode: "strict"
39362
+ });
39363
+ let matchedUser = null;
39364
+ for (const userItem of searchResult.data.user_list) if ((userItem.user_info.unique_id || userItem.user_info.short_id) === item.short_id) {
39365
+ matchedUser = userItem.user_info;
39366
+ break;
39367
+ }
39368
+ if (matchedUser?.sec_uid) {
39369
+ item.sec_uid = matchedUser.sec_uid;
39370
+ hasChanges = true;
39371
+ logger.info(`已为 ${item.remark || item.short_id} 补全 sec_uid: ${item.sec_uid}`);
39372
+ } else logger.warn(`无法获取用户 ${item.short_id} 的 sec_uid`);
39373
+ } catch (error) {
39374
+ logger.error(`获取 ${item.short_id} 的 sec_uid 失败: ${error}`);
39375
+ }
39322
39376
  if (!item.pushTypes || item.pushTypes.length === 0) {
39323
39377
  item.pushTypes = [
39324
39378
  "post",
@@ -39402,7 +39456,7 @@ var DouYinpush = class extends Base {
39402
39456
  `);
39403
39457
  const Detail_Data = pushItem.Detail_Data;
39404
39458
  const skip = await skipDynamic(pushItem);
39405
- skip && logger.warn(`作品 https://www.douyin.com/video/${actualAwemeId} 已被处理,跳过`);
39459
+ if (skip) logger.warn(`作品 https://www.douyin.com/video/${actualAwemeId} 已被处理,跳过`);
39406
39460
  let img = [];
39407
39461
  let iddata = { type: "one_work" };
39408
39462
  this.injectBotToEventForRender(pushItem.targets);
@@ -39583,7 +39637,7 @@ var DouYinpush = class extends Base {
39583
39637
  filepath: filePath,
39584
39638
  totalBytes: 0
39585
39639
  });
39586
- const videoPath = Config.upload.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
39640
+ const videoPath = Config.app.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
39587
39641
  images.push(segment.video(videoPath));
39588
39642
  }
39589
39643
  }
@@ -39600,7 +39654,7 @@ var DouYinpush = class extends Base {
39600
39654
  filepath: motionPhotoCoverPath,
39601
39655
  totalBytes: 0
39602
39656
  });
39603
- const motionPhotoCover = Config.upload.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
39657
+ const motionPhotoCover = Config.app.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
39604
39658
  images.push(segment.image(motionPhotoCover));
39605
39659
  hasPushedMotionPhotoCover = true;
39606
39660
  }
@@ -39706,7 +39760,7 @@ var DouYinpush = class extends Base {
39706
39760
  filepath: filePath,
39707
39761
  totalBytes: 0
39708
39762
  });
39709
- const videoPath = Config.upload.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
39763
+ const videoPath = Config.app.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
39710
39764
  processedImages.push(segment.video(videoPath));
39711
39765
  }
39712
39766
  }
@@ -39723,7 +39777,7 @@ var DouYinpush = class extends Base {
39723
39777
  filepath: motionPhotoCoverPath,
39724
39778
  totalBytes: 0
39725
39779
  });
39726
- const motionPhotoCover = Config.upload.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
39780
+ const motionPhotoCover = Config.app.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
39727
39781
  processedImages.push(segment.image(motionPhotoCover));
39728
39782
  hasPushedMotionPhotoCover = true;
39729
39783
  }
@@ -39799,6 +39853,10 @@ var DouYinpush = class extends Base {
39799
39853
  const filteredUserList = userList.filter((item) => item.switch !== false);
39800
39854
  for (const item of filteredUserList) {
39801
39855
  await common.sleep(2e3);
39856
+ if (!item.sec_uid) {
39857
+ logger.warn(`用户 ${item.remark || item.short_id} 缺少 sec_uid,跳过`);
39858
+ continue;
39859
+ }
39802
39860
  const sec_uid = item.sec_uid;
39803
39861
  const pushTypes = item.pushTypes || ["post"];
39804
39862
  logger.debug(`开始获取用户:${item.remark}(${sec_uid})的内容,推送类型:${pushTypes.join(", ")}`);
@@ -39920,7 +39978,7 @@ var DouYinpush = class extends Base {
39920
39978
  });
39921
39979
  /** 处理抖音号 */
39922
39980
  let user_shortid;
39923
- UserInfoData.data.user.unique_id === "" ? user_shortid = UserInfoData.data.user.short_id : user_shortid = UserInfoData.data.user.unique_id;
39981
+ user_shortid = UserInfoData.data.user.unique_id === "" ? UserInfoData.data.user.short_id : UserInfoData.data.user.unique_id;
39924
39982
  config.douyin ??= [];
39925
39983
  const existingItem = config.douyin.find((item) => item.sec_uid === sec_uid);
39926
39984
  const isSubscribed = await douyinDBInstance.isSubscribed(sec_uid, groupId);
@@ -40210,7 +40268,7 @@ var getKuaishouID = async (url, log = true) => {
40210
40268
  logger.warn("无法获取作品ID");
40211
40269
  break;
40212
40270
  }
40213
- log && console.log(result);
40271
+ if (log) console.log(result);
40214
40272
  return result;
40215
40273
  };
40216
40274
  //#endregion
@@ -40229,7 +40287,7 @@ var Kuaishou = class extends Base {
40229
40287
  await this.e.reply("不支持解析的视频");
40230
40288
  return true;
40231
40289
  }
40232
- Config.app.parseTip && await this.e.reply("检测到快手链接,开始解析");
40290
+ if (Config.app.parseTip) this.e.reply("检测到快手链接,开始解析");
40233
40291
  const video_url = data.VideoData.data.data.visionVideoDetail.photo.photoUrl;
40234
40292
  const transformedData = Object.entries(data.EmojiData.data.data.visionBaseEmoticons.iconUrls).map(([name, path]) => {
40235
40293
  return {
@@ -40346,7 +40404,7 @@ var getXiaohongshuID = async (url, log = true) => {
40346
40404
  break;
40347
40405
  }
40348
40406
  if (result.type === "unknown") throw new Error("无法从链接中提取小红书笔记ID");
40349
- log && console.log(result);
40407
+ if (log) console.log(result);
40350
40408
  return result;
40351
40409
  };
40352
40410
  //#endregion
@@ -40493,8 +40551,8 @@ var Xiaohongshu = class extends Base {
40493
40551
  this.type = iddata?.type;
40494
40552
  }
40495
40553
  async XiaohongshuHandler(data) {
40496
- if (Config.cookies.xiaohongshu === "") throw new Error("我还没有小红书的 Cookies,暂时无法解析呢 ~");
40497
- Config.app.parseTip && await this.e.reply("检测到小红书链接,开始解析");
40554
+ if (Config.amagi.cookies.xiaohongshu === "") throw new Error("我还没有小红书的 Cookies,暂时无法解析呢 ~");
40555
+ if (Config.app.parseTip) await this.e.reply("检测到小红书链接,开始解析");
40498
40556
  const NoteData = await this.amagi.xiaohongshu.fetcher.fetchNoteDetail({
40499
40557
  typeMode: "strict",
40500
40558
  note_id: data.note_id,
@@ -40553,7 +40611,7 @@ var Xiaohongshu = class extends Base {
40553
40611
  headers: {
40554
40612
  ...BASE_HEADERS,
40555
40613
  Referer: "https://www.xiaohongshu.com",
40556
- Cookie: Config.cookies.xiaohongshu
40614
+ Cookie: Config.amagi.cookies.xiaohongshu
40557
40615
  }
40558
40616
  });
40559
40617
  let staticImgPath = "";
@@ -40567,7 +40625,7 @@ var Xiaohongshu = class extends Base {
40567
40625
  headers: {
40568
40626
  ...BASE_HEADERS,
40569
40627
  Referer: "https://www.xiaohongshu.com",
40570
- Cookie: Config.cookies.xiaohongshu
40628
+ Cookie: Config.amagi.cookies.xiaohongshu
40571
40629
  }
40572
40630
  });
40573
40631
  if (livePhoto.filepath) {
@@ -40594,7 +40652,7 @@ var Xiaohongshu = class extends Base {
40594
40652
  filepath: filePath,
40595
40653
  totalBytes: 0
40596
40654
  });
40597
- const videoPath = Config.upload.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
40655
+ const videoPath = Config.app.videoSendMode === "base64" ? `base64://${fs.readFileSync(filePath).toString("base64")}` : `file://${filePath}`;
40598
40656
  processedImages.push(segment.video(videoPath));
40599
40657
  }
40600
40658
  }
@@ -40611,7 +40669,7 @@ var Xiaohongshu = class extends Base {
40611
40669
  filepath: motionPhotoCoverPath,
40612
40670
  totalBytes: 0
40613
40671
  });
40614
- const motionPhotoCover = Config.upload.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
40672
+ const motionPhotoCover = Config.app.imageSendMode === "base64" ? `base64://${fs.readFileSync(motionPhotoCoverPath).toString("base64")}` : `file://${motionPhotoCoverPath}`;
40615
40673
  processedImages.push(segment.image(motionPhotoCover));
40616
40674
  hasPushedMotionPhotoCover = true;
40617
40675
  hasGeneratedLivePhoto = true;
@@ -40667,7 +40725,7 @@ var Xiaohongshu = class extends Base {
40667
40725
  headers: {
40668
40726
  ...BASE_HEADERS,
40669
40727
  Referer: "https://www.xiaohongshu.com",
40670
- Cookie: Config.cookies.xiaohongshu
40728
+ Cookie: Config.amagi.cookies.xiaohongshu
40671
40729
  }
40672
40730
  }, { message_id: this.e.messageId });
40673
40731
  else await this.e.reply(segment.video(video.url_default));
@@ -40741,7 +40799,7 @@ var xiaohongshuProcessVideos = (streamData, videoQuality, maxAutoVideoSize) => {
40741
40799
  videos.sort((a, b) => b.size - a.size);
40742
40800
  });
40743
40801
  if (videoQuality === "adapt") {
40744
- const sizeLimitBytes = (maxAutoVideoSize || Config.upload.filelimit) * 1024 * 1024;
40802
+ const sizeLimitBytes = (maxAutoVideoSize || Config.app.filelimit) * 1024 * 1024;
40745
40803
  for (const quality of [
40746
40804
  "4k",
40747
40805
  "2k",
@@ -40986,9 +41044,9 @@ var douyinLogin = async (e) => {
40986
41044
  if (!hasTtwid) logger.warn(" - 缺少 ttwid");
40987
41045
  }
40988
41046
  logger.debug("开始保存 cookies...");
40989
- Config.Modify("cookies", "douyin", cookieString);
41047
+ Config.Modify("amagi", "cookies.douyin", cookieString);
40990
41048
  logger.debug("cookies 保存完成");
40991
- await e.reply("登录成功!用户登录凭证已保存至cookies.yaml", { reply: true });
41049
+ await e.reply("登录成功!用户登录凭证已保存至配置", { reply: true });
40992
41050
  await Promise.all(msg_id.map(async (id) => {
40993
41051
  await e.bot.recallMsg(e.contact, id);
40994
41052
  }));
@@ -41194,7 +41252,7 @@ var handleCacheCleanup = wrapWithErrorHandler(async () => {
41194
41252
  const twoHoursAgo = Date.now() - 7200 * 1e3;
41195
41253
  const videoDeleted = removeOldFiles(Common.tempDri.video, twoHoursAgo);
41196
41254
  logger.debug(`${Common.tempDri.video} 目录下已删除 ${videoDeleted} 个文件`);
41197
- if (Config.upload.imageSendMode === "file") {
41255
+ if (Config.app.imageSendMode === "file") {
41198
41256
  const imageDeleted = removeOldFiles(Common.tempDri.images, twoHoursAgo);
41199
41257
  logger.debug(`${Common.tempDri.images} 目录下已删除 ${imageDeleted} 个文件`);
41200
41258
  }
@@ -41463,7 +41521,7 @@ var handleSetBilibiliPush = wrapWithErrorHandler(async (e) => {
41463
41521
  logger.info(`B站推送已${enable ? "开启" : "关闭"}`);
41464
41522
  return true;
41465
41523
  }
41466
- if (!Config.cookies.bilibili) {
41524
+ if (!Config.amagi.cookies.bilibili) {
41467
41525
  await e.reply("\n请先配置B站Cookie", { at: true });
41468
41526
  return true;
41469
41527
  }
@@ -42045,7 +42103,7 @@ var handleDouyin = wrapWithErrorHandler(async (e, next) => {
42045
42103
  var handleBilibili = wrapWithErrorHandler(async (e, next) => {
42046
42104
  e.msg = e.msg.replace(/\\/g, "");
42047
42105
  const forceBurnDanmaku = /^#?弹幕解析/.test(e.msg);
42048
- const urlRegex = /(https?:\/\/(?:(?:www\.|m\.|t\.)?bilibili\.com|b23\.tv|bili2233\.cn)\/[a-zA-Z0-9_\-.~:\/?#[\]@!$&'()*+,;=]+)/;
42106
+ const urlRegex = /(https?:\/\/(?:(?:www\.|m\.|t\.)?bilibili\.com|b23\.tv|bili2233\.cn)\/[a-zA-Z0-9_\-.~:/?#[\]@!$&'()*+,;=]+)/;
42049
42107
  const bvRegex = /^BV[1-9a-zA-Z]{10}$/;
42050
42108
  const avRegex = /^av\d+$/i;
42051
42109
  let url = null;
@@ -42407,1091 +42465,40 @@ var update = karin$1.task("kkk-更新检测", "*/3 * * * *", Handler, {
42407
42465
  log: false
42408
42466
  });
42409
42467
  //#endregion
42410
- //#region src/platform/bilibili/api/contents.ts
42411
- var import_lib = /* @__PURE__ */ __toESM(require_lib(), 1);
42412
- /**
42413
- * B站内容管理 API
42414
- */
42415
- /**
42416
- * 获取B站内容列表
42417
- * GET /api/v1/platforms/bilibili/contents?groupId=xxx
42418
- */
42419
- var getContents$1 = async (req, res) => {
42420
- try {
42421
- const { groupId } = req.query;
42422
- if (!groupId || typeof groupId !== "string") return createBadRequestResponse(res, "请提供群组ID");
42423
- const caches = await (await getBilibiliDB()).dynamicCacheRepository.find({
42424
- where: { groupId },
42425
- relations: ["bilibiliUser"],
42426
- order: { createdAt: "DESC" },
42427
- take: 100
42428
- });
42429
- const uniqueMids = [...new Set(caches.map((c) => c.host_mid))];
42430
- const avatarCache = /* @__PURE__ */ new Map();
42431
- let hitRiskControl = false;
42432
- for (const mid of uniqueMids) {
42433
- if (hitRiskControl) {
42434
- avatarCache.set(mid, "");
42435
- continue;
42436
- }
42437
- try {
42438
- const userProfile = await bilibiliFetcher.fetchUserCard({
42439
- host_mid: mid,
42440
- typeMode: "strict"
42441
- });
42442
- avatarCache.set(mid, userProfile.data?.data?.card?.face || "");
42443
- } catch (error) {
42444
- if (error instanceof AmagiError && (error.code === -352 || error.code === -412)) {
42445
- logger.warn(`[BilibiliAPI] 获取头像时遇到风控(${error.code})`);
42446
- hitRiskControl = true;
42447
- }
42448
- avatarCache.set(mid, "");
42449
- }
42450
- }
42451
- return createSuccessResponse(res, caches.map((cache) => {
42452
- const authorName = cache.bilibiliUser?.remark || cache.host_mid.toString();
42453
- return {
42454
- id: cache.dynamic_id,
42455
- platform: "bilibili",
42456
- title: `B站动态 ${cache.dynamic_id}`,
42457
- author: authorName,
42458
- authorId: cache.host_mid.toString(),
42459
- avatar: avatarCache.get(cache.host_mid) || "",
42460
- thumbnail: "",
42461
- type: "dynamic",
42462
- dynamicType: cache.dynamic_type,
42463
- createdAt: cache.createdAt.getTime()
42464
- };
42465
- }));
42466
- } catch (error) {
42467
- logger.error("[BilibiliAPI] 获取内容列表失败:", error);
42468
- return createServerErrorResponse(res, "获取内容列表失败");
42469
- }
42470
- };
42471
- /**
42472
- * 添加B站内容
42473
- * POST /api/v1/platforms/bilibili/contents
42474
- * Body: { contentId, groupId, authorId }
42475
- */
42476
- var addContent$1 = async (req, res) => {
42477
- try {
42478
- const { contentId, groupId, authorId } = req.body;
42479
- if (!contentId || !groupId || !authorId) return createBadRequestResponse(res, "请提供 contentId、groupId 和 authorId");
42480
- const bilibiliDB = await getBilibiliDB();
42481
- const hostMid = parseInt(authorId);
42482
- if (!await bilibiliDB.getBilibiliUser(hostMid)) return createBadRequestResponse(res, "该UP主未在订阅列表中,请先添加订阅");
42483
- await bilibiliDB.addDynamicCache(contentId, hostMid, groupId, "manual");
42484
- return createSuccessResponse(res, { message: "添加成功" });
42485
- } catch (error) {
42486
- logger.error("[BilibiliAPI] 添加内容失败:", error);
42487
- return createServerErrorResponse(res, "添加内容失败");
42488
- }
42489
- };
42490
- /**
42491
- * 删除B站内容
42492
- * POST /api/kkk/v1/platforms/bilibili/contents/:id/delete
42493
- * Body: { groupId: string }
42494
- */
42495
- var deleteContent$1 = async (req, res) => {
42496
- try {
42497
- const { id } = req.params;
42498
- const { groupId } = req.body;
42499
- if (!id || Array.isArray(id) && !id[0] || !groupId) return createBadRequestResponse(res, "请提供内容ID和群组ID");
42500
- const result = await (await getBilibiliDB()).dynamicCacheRepository.delete({
42501
- dynamic_id: Array.isArray(id) ? id[0] : id,
42502
- groupId
42503
- });
42504
- if (result.affected === 0) return createBadRequestResponse(res, "未找到要删除的内容");
42505
- return createSuccessResponse(res, {
42506
- message: "删除成功",
42507
- affected: result.affected
42508
- });
42509
- } catch (error) {
42510
- logger.error("[BilibiliAPI] 删除内容失败:", error);
42511
- return createServerErrorResponse(res, "删除内容失败");
42512
- }
42513
- };
42514
- //#endregion
42515
- //#region src/platform/bilibili/api/risk-control.ts
42516
- /**
42517
- * B站风控处理 API
42518
- */
42519
- /**
42520
- * 创建风控错误响应
42521
- */
42522
- var createRiskControlResponse = (res, geetest, token, v_voucher) => {
42523
- return res.status(452).json({
42524
- message: "B站风控验证",
42525
- code: -352,
42526
- data: {
42527
- type: "bilibili_risk_control",
42528
- geetest,
42529
- token,
42530
- v_voucher
42531
- }
42532
- });
42533
- };
42534
- /**
42535
- * 创建无法验证的风控错误响应
42536
- */
42537
- var createRiskControlNoVoucherResponse = (res, code) => {
42538
- return res.status(452).json({
42539
- message: "B站风控",
42540
- code,
42541
- data: {
42542
- type: "bilibili_risk_control_no_voucher",
42543
- message: code === -352 ? "B站风控校验失败,请稍后重试或更换 Cookie" : "当前IP被B站风控,请稍后重试或更换网络"
42544
- }
42545
- });
42546
- };
42547
- /**
42548
- * 处理 B站风控错误
42549
- * 检测 -352 和 -412 错误并申请验证码
42550
- */
42551
- var handleBilibiliRiskControl = async (error, res) => {
42552
- if (!(error instanceof AmagiError)) return false;
42553
- if (error.code !== -352 && error.code !== -412) return false;
42554
- const v_voucher = error.data?.data?.v_voucher;
42555
- if (!v_voucher) {
42556
- logger.info(`[BilibiliAPI] 检测到风控(${error.code}),但无 v_voucher`);
42557
- createRiskControlNoVoucherResponse(res, error.code);
42558
- return true;
42559
- }
42560
- try {
42561
- logger.info(`[BilibiliAPI] 检测到风控(${error.code}),申请验证码...`);
42562
- const verification = await bilibiliFetcher.requestCaptchaFromVoucher({
42563
- v_voucher,
42564
- typeMode: "strict"
42565
- });
42566
- if (!verification.data?.data?.geetest) {
42567
- logger.error("[BilibiliAPI] 申请验证码失败");
42568
- createRiskControlNoVoucherResponse(res, error.code);
42569
- return true;
42570
- }
42571
- const geetest = verification.data.data.geetest;
42572
- const token = verification.data.data.token;
42573
- createRiskControlResponse(res, {
42574
- gt: geetest.gt,
42575
- challenge: geetest.challenge
42576
- }, token, v_voucher);
42577
- return true;
42578
- } catch (err) {
42579
- logger.error("[BilibiliAPI] 申请验证码异常:", err);
42580
- createRiskControlNoVoucherResponse(res, error.code);
42581
- return true;
42582
- }
42583
- };
42584
- /**
42585
- * B站风控验证结果提交
42586
- * POST /api/v1/platforms/bilibili/verify
42587
- * Body: { challenge, token, validate, seccode }
42588
- */
42589
- var verifyCaptcha = async (req, res) => {
42590
- try {
42591
- const { challenge, token, validate, seccode } = req.body;
42592
- if (!challenge || !token || !validate || !seccode) return createBadRequestResponse(res, "验证参数不完整");
42593
- logger.info("[BilibiliAPI] 提交风控验证结果...");
42594
- const verifyResult = await bilibiliFetcher.validateCaptchaResult({
42595
- challenge,
42596
- token,
42597
- validate,
42598
- seccode,
42599
- typeMode: "strict"
42600
- });
42601
- if (verifyResult.success && verifyResult.data?.data?.grisk_id) {
42602
- logger.info(`[BilibiliAPI] 验证成功,grisk_id: ${verifyResult.data.data.grisk_id}`);
42603
- return createSuccessResponse(res, {
42604
- success: true,
42605
- message: "验证成功",
42606
- grisk_id: verifyResult.data.data.grisk_id
42607
- });
42608
- }
42609
- return createSuccessResponse(res, {
42610
- success: false,
42611
- message: "验证失败,请重试"
42612
- });
42613
- } catch (error) {
42614
- logger.error("[BilibiliAPI] 验证请求失败:", error);
42615
- if (error instanceof AmagiError) {
42616
- if (error.code === -111) return createSuccessResponse(res, {
42617
- success: false,
42618
- message: "验证失败,建议重新配置 B站 Cookie"
42619
- });
42620
- return createSuccessResponse(res, {
42621
- success: false,
42622
- message: error.rawError?.errorDescription || "验证失败"
42623
- });
42624
- }
42625
- return createServerErrorResponse(res, "验证请求失败");
42626
- }
42627
- };
42628
- //#endregion
42629
- //#region src/platform/bilibili/api/parse.ts
42630
- /**
42631
- * 判断是否为图文动态
42632
- */
42633
- function isDynamicTypeDraw(data) {
42634
- return data.data.item.type === DynamicType.DRAW;
42635
- }
42636
- /**
42637
- * 判断是否为文字动态
42638
- */
42639
- function isDynamicTypeWord(data) {
42640
- return data.data.item.type === DynamicType.WORD;
42641
- }
42642
- /**
42643
- * 判断是否为视频动态
42644
- */
42645
- function isDynamicTypeAV(data) {
42646
- return data.data.item.type === DynamicType.AV;
42647
- }
42648
- /**
42649
- * 判断是否为转发动态
42650
- */
42651
- function isDynamicTypeForward(data) {
42652
- return data.data.item.type === DynamicType.FORWARD;
42653
- }
42654
- /**
42655
- * 判断是否为专栏文章动态
42656
- */
42657
- function isDynamicTypeArticle(data) {
42658
- return data.data.item.type === DynamicType.ARTICLE;
42659
- }
42660
- /**
42661
- * 判断是否为直播推荐动态
42662
- */
42663
- function isDynamicTypeLiveRcmd(data) {
42664
- return data.data.item.type === DynamicType.LIVE_RCMD;
42665
- }
42666
- /**
42667
- * 格式化数字
42668
- */
42669
- var formatCount$1 = (count) => {
42670
- if (count >= 1e8) return `${(count / 1e8).toFixed(1)}亿`;
42671
- if (count >= 1e4) return `${(count / 1e4).toFixed(1)}万`;
42672
- return count.toString();
42673
- };
42674
- /**
42675
- * 格式化时长
42676
- */
42677
- var formatDuration$1 = (seconds) => {
42678
- return `${Math.floor(seconds / 60)}:${Math.floor(seconds % 60).toString().padStart(2, "0")}`;
42679
- };
42680
- /**
42681
- * 格式化时间戳
42682
- */
42683
- var formatTimestamp$1 = (timestamp) => {
42684
- const date = /* @__PURE__ */ new Date(timestamp * 1e3);
42685
- const diff = (/* @__PURE__ */ new Date()).getTime() - date.getTime();
42686
- if (diff < 6e4) return "刚刚";
42687
- if (diff < 36e5) return `${Math.floor(diff / 6e4)}分钟前`;
42688
- if (diff < 864e5) return `${Math.floor(diff / 36e5)}小时前`;
42689
- if (diff < 2592e6) return `${Math.floor(diff / 864e5)}天前`;
42690
- return `${date.getMonth() + 1}月${date.getDate()}日`;
42691
- };
42692
- /**
42693
- * 处理评论中的表情
42694
- */
42695
- var processCommentEmojis$1 = (text, emojiData) => {
42696
- if (!text || !emojiData?.data?.packages) return text;
42697
- const emojiMap = /* @__PURE__ */ new Map();
42698
- emojiData.data.packages.forEach((pkg) => {
42699
- pkg.emote.forEach((emote) => {
42700
- emojiMap.set(emote.text, emote.url);
42701
- });
42702
- });
42703
- let processedText = text;
42704
- processedText = processedText.replace(/\[([^\]]+)\]/g, (match, emojiName) => {
42705
- const emojiUrl = emojiMap.get(match) || emojiMap.get(emojiName);
42706
- if (emojiUrl) return `<img src="${emojiUrl}" alt="${emojiName}" class="emoji" />`;
42707
- return match;
42708
- });
42709
- return processedText.split(/(<img[^>]*>)/).map((part) => {
42710
- if (part.startsWith("<img")) return part;
42711
- if (part.trim()) return `<span>${part}</span>`;
42712
- return part;
42713
- }).join("");
42714
- };
42715
- /**
42716
- * 解析评论数据
42717
- */
42718
- var parseComments$1 = (commentsData, emojiData) => {
42719
- if (!commentsData || !Array.isArray(commentsData)) return [];
42720
- return commentsData.map((comment, index) => {
42721
- let processedContent = comment.content?.message || "";
42722
- if (emojiData && processedContent) processedContent = processCommentEmojis$1(processedContent, emojiData);
42723
- const pictures = comment.content?.pictures;
42724
- return {
42725
- id: comment.rpid?.toString() || index.toString(),
42726
- author: comment.member?.uname || "匿名用户",
42727
- avatar: comment.member?.avatar || "",
42728
- content: processedContent,
42729
- images: pictures?.map((pic) => pic.img_src) || [],
42730
- likes: comment.like || 0,
42731
- timestamp: formatTimestamp$1(comment.ctime || 0)
42732
- };
42733
- });
42734
- };
42735
- /**
42736
- * 获取动态评论类型
42737
- * 参考 mapping_table 函数的映射关系
42738
- */
42739
- var getDynamicCommentType = (dynamicType) => {
42740
- switch (dynamicType) {
42741
- case DynamicType.AV: return 1;
42742
- case DynamicType.DRAW: return 11;
42743
- case DynamicType.ARTICLE: return 12;
42744
- case DynamicType.LIVE_RCMD:
42745
- case DynamicType.FORWARD:
42746
- case DynamicType.WORD:
42747
- default: return 17;
42748
- }
42749
- };
42750
- /**
42751
- * 获取动态 OID(用于评论接口)
42752
- */
42753
- var getDynamicOid = (dynamicInfo, dynamicCard) => {
42754
- const item = dynamicInfo.data.item;
42755
- if (isDynamicTypeAV(dynamicInfo)) return (dynamicInfo.data.item.modules.module_dynamic.major?.archive)?.aid?.toString() || item.id_str;
42756
- if (isDynamicTypeWord(dynamicInfo) || isDynamicTypeForward(dynamicInfo)) return item.id_str;
42757
- return dynamicCard.card?.desc?.rid?.toString() || item.id_str;
42758
- };
42759
- /**
42760
- * 解析图文动态内容
42761
- */
42762
- var parseDrawDynamic = (data) => {
42763
- const moduleDynamic = data.data.item.modules.module_dynamic;
42764
- const description = moduleDynamic.major?.opus?.summary?.text || "";
42765
- const images = (moduleDynamic.major?.opus?.pics || []).map((pic) => pic.url).filter((url) => typeof url === "string");
42766
- return {
42767
- title: "图文动态",
42768
- description,
42769
- thumbnail: images[0] || "",
42770
- images,
42771
- type: "note"
42772
- };
42773
- };
42774
- /**
42775
- * 解析文字动态内容
42776
- */
42777
- var parseWordDynamic = (data) => {
42778
- return {
42779
- title: "文字动态",
42780
- description: data.data.item.modules.module_dynamic.major?.opus?.summary?.text || "",
42781
- thumbnail: "",
42782
- images: [],
42783
- type: "note"
42784
- };
42785
- };
42786
- /**
42787
- * 解析视频动态内容
42788
- */
42789
- var parseAVDynamic = (data) => {
42790
- const archive = data.data.item.modules.module_dynamic.major?.archive;
42791
- return {
42792
- title: archive?.title || "视频动态",
42793
- description: archive?.desc || "",
42794
- thumbnail: archive?.cover || "",
42795
- images: [],
42796
- type: "video"
42797
- };
42798
- };
42799
- /**
42800
- * 解析转发动态内容
42801
- */
42802
- var parseForwardDynamic = (data) => {
42803
- return {
42804
- title: "转发动态",
42805
- description: data.data.item.modules.module_dynamic.desc?.text || "",
42806
- thumbnail: "",
42807
- images: [],
42808
- type: "forward"
42809
- };
42810
- };
42811
- /**
42812
- * 解析专栏文章动态内容
42813
- */
42814
- var parseArticleDynamic = (data) => {
42815
- const opus = data.data.item.modules.module_dynamic.major?.opus;
42816
- return {
42817
- title: opus?.title || "专栏文章",
42818
- description: opus?.summary?.text || "",
42819
- thumbnail: "",
42820
- images: [],
42821
- type: "article"
42822
- };
42823
- };
42824
- /**
42825
- * 解析直播推荐动态内容
42826
- */
42827
- var parseLiveRcmdDynamic = (data) => {
42828
- const liveRcmd = data.data.item.modules.module_dynamic.major?.live_rcmd;
42829
- let title = "直播推荐";
42830
- let thumbnail = "";
42831
- if (liveRcmd?.content) try {
42832
- const liveInfo = JSON.parse(liveRcmd.content);
42833
- title = liveInfo.live_play_info?.title || "直播推荐";
42834
- thumbnail = liveInfo.live_play_info?.cover || "";
42835
- } catch {}
42836
- return {
42837
- title,
42838
- description: "",
42839
- thumbnail,
42840
- images: [],
42841
- type: "live"
42842
- };
42843
- };
42844
- /**
42845
- * 解析动态内容(主入口)
42846
- * 使用类型守卫进行类型安全的分发
42847
- */
42848
- var parseDynamicContent = (dynamicInfo) => {
42849
- if (isDynamicTypeDraw(dynamicInfo)) return parseDrawDynamic(dynamicInfo);
42850
- if (isDynamicTypeWord(dynamicInfo)) return parseWordDynamic(dynamicInfo);
42851
- if (isDynamicTypeAV(dynamicInfo)) return parseAVDynamic(dynamicInfo);
42852
- if (isDynamicTypeForward(dynamicInfo)) return parseForwardDynamic(dynamicInfo);
42853
- if (isDynamicTypeArticle(dynamicInfo)) return parseArticleDynamic(dynamicInfo);
42854
- if (isDynamicTypeLiveRcmd(dynamicInfo)) return parseLiveRcmdDynamic(dynamicInfo);
42855
- return {
42856
- title: "动态内容",
42857
- description: "",
42858
- thumbnail: "",
42859
- images: [],
42860
- type: "dynamic"
42861
- };
42862
- };
42863
- /**
42864
- * 获取动态作者信息
42865
- */
42866
- var getDynamicAuthor = (dynamicInfo) => {
42867
- const moduleAuthor = dynamicInfo.data.item.modules.module_author;
42868
- return {
42869
- name: moduleAuthor.name || "未知用户",
42870
- avatar: moduleAuthor.face || "",
42871
- id: moduleAuthor.mid?.toString() || ""
42872
- };
42873
- };
42874
- /**
42875
- * 获取动态统计信息
42876
- */
42877
- var getDynamicStats = (dynamicInfo) => {
42878
- const moduleStat = dynamicInfo.data.item.modules.module_stat;
42879
- return {
42880
- views: formatCount$1(moduleStat.forward?.count || 0),
42881
- likes: formatCount$1(moduleStat.like?.count || 0),
42882
- commentCount: moduleStat.comment?.count || 0
42883
- };
42884
- };
42885
- /**
42886
- * 解析B站视频
42887
- * POST /api/kkk/v1/platforms/bilibili/parse/video
42888
- * Body: { bvid?: string, aid?: string }
42889
- */
42890
- var parseVideo = async (req, res) => {
42891
- try {
42892
- const { bvid, aid } = req.body;
42893
- if (!bvid && !aid) return createBadRequestResponse(res, "请提供 bvid 或 aid");
42894
- const videoDetail = (await bilibiliFetcher.fetchVideoInfo({
42895
- bvid: bvid || "",
42896
- typeMode: "strict"
42897
- })).data.data;
42898
- const streamData = (await bilibiliFetcher.fetchVideoStreamUrl({
42899
- avid: videoDetail.aid,
42900
- cid: videoDetail.cid,
42901
- typeMode: "strict"
42902
- })).data.data;
42903
- const [commentsResponse, emojiResponse] = await Promise.all([bilibiliFetcher.fetchComments({
42904
- oid: videoDetail.aid.toString(),
42905
- type: 1,
42906
- number: 50,
42907
- typeMode: "strict"
42908
- }), bilibiliFetcher.fetchEmojiList({ typeMode: "strict" })]);
42909
- const commentsData = commentsResponse.data.data;
42910
- const emojiData = emojiResponse.data;
42911
- const comments = parseComments$1(commentsData.replies || [], emojiData);
42912
- return createSuccessResponse(res, {
42913
- id: videoDetail.bvid,
42914
- title: videoDetail.title || "无标题",
42915
- description: videoDetail.desc || "",
42916
- thumbnail: videoDetail.pic || "",
42917
- duration: formatDuration$1(videoDetail.duration || 0),
42918
- views: formatCount$1(videoDetail.stat?.view || 0),
42919
- likes: formatCount$1(videoDetail.stat?.like || 0),
42920
- author: {
42921
- name: videoDetail.owner?.name || "未知用户",
42922
- avatar: videoDetail.owner?.face || "",
42923
- id: videoDetail.owner?.mid?.toString() || ""
42924
- },
42925
- type: "video",
42926
- downloadUrl: {
42927
- video: streamData.dash?.video?.[0]?.baseUrl,
42928
- audio: streamData.dash?.audio?.[0]?.baseUrl
42929
- },
42930
- tags: videoDetail.tname ? [videoDetail.tname] : [],
42931
- comments,
42932
- commentCount: videoDetail.stat?.reply || 0
42933
- });
42934
- } catch (error) {
42935
- const err = error;
42936
- if (await handleBilibiliRiskControl(err, res)) return;
42937
- logger.error("[BilibiliAPI] 解析视频失败:", err);
42938
- return createServerErrorResponse(res, `解析失败: ${err.message}`);
42939
- }
42940
- };
42941
- /**
42942
- * 解析B站动态(返回原始数据)
42943
- * POST /api/kkk/v1/platforms/bilibili/parse/dynamic/raw
42944
- * Body: { dynamic_id: string }
42945
- *
42946
- * 返回原始 API 数据,前端自行处理不同动态类型
42947
- * 注意:不获取评论数据,减少请求开销
42948
- */
42949
- var parseDynamicRaw = async (req, res) => {
42950
- try {
42951
- const { dynamic_id } = req.body;
42952
- if (!dynamic_id) return createBadRequestResponse(res, "请提供动态ID (dynamic_id)");
42953
- const dynamicInfo = (await bilibiliFetcher.fetchDynamicDetail({
42954
- dynamic_id,
42955
- typeMode: "strict"
42956
- })).data;
42957
- const [dynamicCardResponse, userProfileResponse] = await Promise.all([bilibiliFetcher.fetchDynamicCard({
42958
- dynamic_id: dynamicInfo.data.item.id_str,
42959
- typeMode: "strict"
42960
- }), bilibiliFetcher.fetchUserCard({
42961
- host_mid: dynamicInfo.data.item.modules.module_author.mid,
42962
- typeMode: "strict"
42963
- })]);
42964
- const dynamicCard = dynamicCardResponse.data.data;
42965
- return createSuccessResponse(res, {
42966
- dynamicInfo: dynamicInfo.data,
42967
- dynamicCard,
42968
- userProfile: userProfileResponse.data.data,
42969
- comments: null,
42970
- emoji: null
42971
- });
42972
- } catch (error) {
42973
- const err = error;
42974
- if (await handleBilibiliRiskControl(err, res)) return;
42975
- logger.error("[BilibiliAPI] 解析动态失败:", err);
42976
- return createServerErrorResponse(res, `解析失败: ${err.message}`);
42977
- }
42978
- };
42979
- /**
42980
- * 解析B站动态
42981
- * POST /api/kkk/v1/platforms/bilibili/parse/dynamic
42982
- * Body: { dynamic_id: string }
42983
- */
42984
- var parseDynamic = async (req, res) => {
42985
- try {
42986
- const { dynamic_id } = req.body;
42987
- if (!dynamic_id) return createBadRequestResponse(res, "请提供动态ID (dynamic_id)");
42988
- const dynamicInfo = (await bilibiliFetcher.fetchDynamicDetail({
42989
- dynamic_id,
42990
- typeMode: "strict"
42991
- })).data;
42992
- const dynamicCard = (await bilibiliFetcher.fetchDynamicCard({
42993
- dynamic_id: dynamicInfo.data.item.id_str,
42994
- typeMode: "strict"
42995
- })).data.data;
42996
- const dynamicContent = parseDynamicContent(dynamicInfo);
42997
- const author = getDynamicAuthor(dynamicInfo);
42998
- const stats = getDynamicStats(dynamicInfo);
42999
- let comments = [];
43000
- const itemType = dynamicInfo.data.item.type;
43001
- if (itemType !== DynamicType.LIVE_RCMD) try {
43002
- const [commentsResponse, emojiResponse] = await Promise.all([bilibiliFetcher.fetchComments({
43003
- type: getDynamicCommentType(itemType),
43004
- oid: getDynamicOid(dynamicInfo, dynamicCard),
43005
- typeMode: "strict"
43006
- }), bilibiliFetcher.fetchEmojiList({ typeMode: "strict" })]);
43007
- const commentsData = commentsResponse.data.data;
43008
- const emojiData = emojiResponse.data;
43009
- comments = parseComments$1(commentsData.replies || [], emojiData);
43010
- } catch (error) {
43011
- logger.warn("[BilibiliAPI] 获取动态评论失败:", error);
43012
- }
43013
- return createSuccessResponse(res, {
43014
- id: dynamicInfo.data.item.id_str,
43015
- title: dynamicContent.title,
43016
- description: dynamicContent.description,
43017
- thumbnail: dynamicContent.thumbnail || author.avatar,
43018
- duration: "0:00",
43019
- views: stats.views,
43020
- likes: stats.likes,
43021
- author,
43022
- type: dynamicContent.type,
43023
- dynamicType: itemType,
43024
- images: dynamicContent.images,
43025
- tags: [],
43026
- comments,
43027
- commentCount: stats.commentCount
43028
- });
43029
- } catch (error) {
43030
- const err = error;
43031
- if (await handleBilibiliRiskControl(err, res)) return;
43032
- logger.error("[BilibiliAPI] 解析动态失败:", err);
43033
- return createServerErrorResponse(res, `解析失败: ${err.message}`);
43034
- }
43035
- };
43036
- //#endregion
43037
- //#region src/platform/bilibili/api/video.ts
43038
- var QUALITY_MAP = {
43039
- 6: "240P 极速",
43040
- 16: "360P 流畅",
43041
- 32: "480P 清晰",
43042
- 64: "720P 高清",
43043
- 74: "720P60 高帧率",
43044
- 80: "1080P 高清",
43045
- 112: "1080P+ 高码率",
43046
- 116: "1080P60 高帧率",
43047
- 120: "4K 超清",
43048
- 125: "HDR 真彩色",
43049
- 126: "杜比视界",
43050
- 127: "8K 超高清"
43051
- };
43052
- /**
43053
- * 获取视频播放地址
43054
- * GET /api/kkk/v1/platforms/bilibili/video/playurl?bvid=xxx&p=1
43055
- *
43056
- * 返回视频播放地址,前端直接使用这些 URL 播放
43057
- * - DASH 格式:返回视频流和音频流列表,前端使用支持 DASH 的播放器
43058
- * - DURL 格式:返回合并好的单流地址,可直接播放
43059
- */
43060
- var getVideoPlayUrl = async (req, res) => {
43061
- try {
43062
- const { bvid, p } = req.query;
43063
- if (!bvid) return createBadRequestResponse(res, "请提供视频 BV 号 (bvid)");
43064
- const pageNum = p ? parseInt(p, 10) : 1;
43065
- const videoInfo = (await bilibiliFetcher.fetchVideoInfo({
43066
- bvid,
43067
- typeMode: "strict"
43068
- })).data.data;
43069
- const cid = pageNum > 1 && videoInfo.pages[pageNum - 1] ? videoInfo.pages[pageNum - 1].cid : videoInfo.cid;
43070
- const playUrlData = (await bilibiliFetcher.fetchVideoStreamUrl({
43071
- avid: videoInfo.aid,
43072
- cid,
43073
- typeMode: "strict"
43074
- })).data.data;
43075
- const response = {
43076
- bvid: videoInfo.bvid,
43077
- aid: videoInfo.aid,
43078
- cid,
43079
- title: videoInfo.title,
43080
- duration: videoInfo.duration,
43081
- cover: videoInfo.pic
43082
- };
43083
- if (playUrlData.dash) {
43084
- response.streamType = "dash";
43085
- const videoMap = /* @__PURE__ */ new Map();
43086
- for (const video of playUrlData.dash.video) if (!videoMap.has(video.id)) videoMap.set(video.id, {
43087
- url: video.baseUrl || video.base_url,
43088
- quality: video.id,
43089
- qualityDesc: QUALITY_MAP[video.id] || `${video.id}P`,
43090
- codecs: video.codecs,
43091
- width: video.width,
43092
- height: video.height,
43093
- bandwidth: video.bandwidth
43094
- });
43095
- response.videoStreams = Array.from(videoMap.values()).sort((a, b) => b.quality - a.quality);
43096
- response.audioStreams = playUrlData.dash.audio.map((audio) => ({
43097
- url: audio.baseUrl || audio.base_url,
43098
- quality: audio.id,
43099
- bandwidth: audio.bandwidth
43100
- })).sort((a, b) => b.bandwidth - a.bandwidth);
43101
- } else if (playUrlData.durl && playUrlData.durl.length > 0) {
43102
- response.streamType = "durl";
43103
- response.durlUrl = playUrlData.durl[0].url;
43104
- response.durlQuality = playUrlData.accept_description?.[0] || "默认清晰度";
43105
- } else return createServerErrorResponse(res, "无法获取视频播放地址");
43106
- return createSuccessResponse(res, response);
43107
- } catch (error) {
43108
- const err = error;
43109
- if (await handleBilibiliRiskControl(err, res)) return;
43110
- logger.error("[BilibiliAPI] 获取视频播放地址失败:", err);
43111
- return createServerErrorResponse(res, `获取播放地址失败: ${err.message}`);
43112
- }
43113
- };
43114
- //#endregion
43115
- //#region src/platform/bilibili/api/index.ts
43116
- /**
43117
- * B站平台 API 路由
43118
- */
43119
- var router$1 = express.Router();
43120
- router$1.get("/contents", getContents$1);
43121
- router$1.post("/contents", addContent$1);
43122
- router$1.post("/contents/:id/delete", deleteContent$1);
43123
- router$1.post("/parse/video", parseVideo);
43124
- router$1.post("/parse/dynamic", parseDynamic);
43125
- router$1.post("/parse/dynamic/raw", parseDynamicRaw);
43126
- router$1.get("/video/playurl", getVideoPlayUrl);
43127
- router$1.post("/verify", verifyCaptcha);
43128
- //#endregion
43129
- //#region src/platform/douyin/api/contents.ts
43130
- /**
43131
- * 抖音内容管理 API
43132
- */
43133
- /**
43134
- * 获取抖音内容列表
43135
- * GET /api/v1/platforms/douyin/contents?groupId=xxx
43136
- */
43137
- var getContents = async (req, res) => {
43138
- try {
43139
- const { groupId } = req.query;
43140
- if (!groupId || typeof groupId !== "string") return createBadRequestResponse(res, "请提供群组ID");
43141
- const caches = await (await getDouyinDB()).awemeCacheRepository.find({
43142
- where: { groupId },
43143
- relations: ["douyinUser"],
43144
- order: { createdAt: "DESC" },
43145
- take: 100
43146
- });
43147
- const userAvatarMap = /* @__PURE__ */ new Map();
43148
- const uniqueSecUids = [...new Set(caches.map((c) => c.sec_uid))];
43149
- for (let i = 0; i < uniqueSecUids.length; i += 5) {
43150
- const batch = uniqueSecUids.slice(i, i + 5);
43151
- await Promise.all(batch.map(async (secUid) => {
43152
- try {
43153
- const userProfile = await douyinFetcher.fetchUserProfile({
43154
- sec_uid: secUid,
43155
- typeMode: "strict"
43156
- });
43157
- userAvatarMap.set(secUid, userProfile.data?.user?.avatar_larger?.url_list[0] || "");
43158
- } catch {
43159
- userAvatarMap.set(secUid, "");
43160
- }
43161
- }));
43162
- }
43163
- return createSuccessResponse(res, caches.map((cache) => {
43164
- const cacheWithUser = cache;
43165
- const authorName = cacheWithUser.douyinUser?.remark || cacheWithUser.douyinUser?.short_id || cache.sec_uid;
43166
- return {
43167
- id: cache.aweme_id,
43168
- platform: "douyin",
43169
- title: `抖音作品 ${cache.aweme_id}`,
43170
- author: authorName,
43171
- authorId: cache.sec_uid,
43172
- avatar: userAvatarMap.get(cache.sec_uid) || "",
43173
- thumbnail: "",
43174
- type: "video",
43175
- createdAt: cache.createdAt.getTime()
43176
- };
43177
- }));
43178
- } catch (error) {
43179
- logger.error("[DouyinAPI] 获取内容列表失败:", error);
43180
- return createServerErrorResponse(res, "获取内容列表失败");
43181
- }
43182
- };
43183
- /**
43184
- * 添加抖音内容
43185
- * POST /api/v1/platforms/douyin/contents
43186
- * Body: { contentId, groupId, authorId }
43187
- */
43188
- var addContent = async (req, res) => {
43189
- try {
43190
- const { contentId, groupId, authorId } = req.body;
43191
- if (!contentId || !groupId || !authorId) return createBadRequestResponse(res, "请提供 contentId、groupId 和 authorId");
43192
- const douyinDB = await getDouyinDB();
43193
- if (!await douyinDB.getDouyinUser(authorId)) return createBadRequestResponse(res, "该作者未在订阅列表中,请先添加订阅");
43194
- await douyinDB.addAwemeCache(contentId, authorId, groupId);
43195
- return createSuccessResponse(res, { message: "添加成功" });
43196
- } catch (error) {
43197
- logger.error("[DouyinAPI] 添加内容失败:", error);
43198
- return createServerErrorResponse(res, "添加内容失败");
43199
- }
43200
- };
43201
- /**
43202
- * 删除抖音内容
43203
- * POST /api/kkk/v1/platforms/douyin/contents/:id/delete
43204
- * Body: { groupId: string }
43205
- */
43206
- var deleteContent = async (req, res) => {
43207
- try {
43208
- const { id } = req.params;
43209
- const { groupId } = req.body;
43210
- if (!id || Array.isArray(id) && !id[0] || !groupId) return createBadRequestResponse(res, "请提供内容ID和群组ID");
43211
- const result = await (await getDouyinDB()).awemeCacheRepository.delete({
43212
- aweme_id: Array.isArray(id) ? id[0] : id,
43213
- groupId
43214
- });
43215
- if (result.affected === 0) return createBadRequestResponse(res, "未找到要删除的内容");
43216
- return createSuccessResponse(res, {
43217
- message: "删除成功",
43218
- affected: result.affected
43219
- });
43220
- } catch (error) {
43221
- logger.error("[DouyinAPI] 删除内容失败:", error);
43222
- return createServerErrorResponse(res, "删除内容失败");
43223
- }
43224
- };
43225
- //#endregion
43226
- //#region src/platform/douyin/api/parse.ts
43227
- /**
43228
- * 格式化数字
43229
- */
43230
- var formatCount = (count) => {
43231
- if (count >= 1e8) return `${(count / 1e8).toFixed(1)}亿`;
43232
- if (count >= 1e4) return `${(count / 1e4).toFixed(1)}万`;
43233
- return count.toString();
43234
- };
43235
- /**
43236
- * 格式化时长
43237
- */
43238
- var formatDuration = (seconds) => {
43239
- return `${Math.floor(seconds / 60)}:${Math.floor(seconds % 60).toString().padStart(2, "0")}`;
43240
- };
43241
- /**
43242
- * 格式化时间戳
43243
- */
43244
- var formatTimestamp = (timestamp) => {
43245
- const date = /* @__PURE__ */ new Date(timestamp * 1e3);
43246
- const diff = (/* @__PURE__ */ new Date()).getTime() - date.getTime();
43247
- if (diff < 6e4) return "刚刚";
43248
- if (diff < 36e5) return `${Math.floor(diff / 6e4)}分钟前`;
43249
- if (diff < 864e5) return `${Math.floor(diff / 36e5)}小时前`;
43250
- if (diff < 2592e6) return `${Math.floor(diff / 864e5)}天前`;
43251
- return `${date.getMonth() + 1}月${date.getDate()}日`;
43252
- };
43253
- /**
43254
- * 从标题中移除标签
43255
- */
43256
- var removeTags = (title, tags) => {
43257
- if (!title || !tags || tags.length === 0) return title;
43258
- let cleanTitle = title;
43259
- tags.forEach((tag) => {
43260
- if (tag) {
43261
- const hashtagPattern = new RegExp(`#${tag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:\\s|$)`, "gi");
43262
- cleanTitle = cleanTitle.replace(hashtagPattern, "");
43263
- const atPattern = new RegExp(`@${tag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:\\s|$)`, "gi");
43264
- cleanTitle = cleanTitle.replace(atPattern, "");
43265
- }
43266
- });
43267
- return cleanTitle.replace(/\s+/g, " ").trim();
43268
- };
43269
- /**
43270
- * 处理评论中的表情
43271
- */
43272
- var processCommentEmojis = (text, emojiData) => {
43273
- if (!text || !emojiData?.emoji_list) return text;
43274
- let processedText = text;
43275
- processedText = processedText.replace(/\[([^\]]+)\]/g, (match, emojiName) => {
43276
- const emojiInfo = emojiData.emoji_list.find((emoji) => emoji.display_name === `[${emojiName}]`);
43277
- if (emojiInfo && emojiInfo.emoji_url?.url_list?.[0]) return `<img src="${emojiInfo.emoji_url.url_list[0]}" alt="${emojiName}" class="emoji" />`;
43278
- return match;
43279
- });
43280
- return processedText.split(/(<img[^>]*>)/).map((part) => {
43281
- if (part.startsWith("<img")) return part;
43282
- if (part.trim()) return `<span>${part}</span>`;
43283
- return part;
43284
- }).join("");
43285
- };
43286
- /**
43287
- * 解析评论数据
43288
- */
43289
- var parseComments = (commentsData, emojiData) => {
43290
- if (!commentsData || !Array.isArray(commentsData)) return [];
43291
- return commentsData.map((comment, index) => {
43292
- let processedText = comment.text || "";
43293
- if (emojiData && comment.text) processedText = processCommentEmojis(comment.text, emojiData);
43294
- return {
43295
- id: comment.cid?.toString() || index.toString(),
43296
- author: comment.user?.nickname || "匿名用户",
43297
- avatar: comment.user?.avatar_thumb?.url_list?.[0] || "",
43298
- content: processedText,
43299
- images: comment.image_list?.map((img) => img.origin_url?.url_list?.[3]).filter(Boolean) || [],
43300
- likes: comment.digg_count || 0,
43301
- timestamp: formatTimestamp(comment.create_time || 0)
43302
- };
43303
- });
43304
- };
43305
- /**
43306
- * 解析抖音作品
43307
- * POST /api/v1/platforms/douyin/parse
43308
- * Body: { aweme_id: string }
43309
- */
43310
- var parseWork = async (req, res) => {
43311
- try {
43312
- const { aweme_id } = req.body;
43313
- if (!aweme_id) return createBadRequestResponse(res, "请提供作品ID (aweme_id)");
43314
- const [workResponse, commentsResponse, emojiResponse] = await Promise.all([
43315
- douyinFetcher.parseWork({
43316
- aweme_id,
43317
- typeMode: "strict"
43318
- }),
43319
- douyinFetcher.fetchWorkComments({
43320
- aweme_id,
43321
- typeMode: "strict"
43322
- }),
43323
- douyinFetcher.fetchEmojiList({ typeMode: "strict" })
43324
- ]);
43325
- const awemeDetail = workResponse.data.aweme_detail;
43326
- const commentsData = commentsResponse.data.comments;
43327
- const emojiData = emojiResponse.data;
43328
- const isSlides = awemeDetail.is_slides === true && awemeDetail.images !== null;
43329
- const isVideo = !awemeDetail.images && !isSlides;
43330
- const workType = isSlides ? "slides" : isVideo ? "video" : "note";
43331
- const comments = parseComments(commentsData, emojiData);
43332
- const tags = Array.isArray(awemeDetail.text_extra) ? awemeDetail.text_extra.map((tag) => typeof tag === "string" ? tag : tag.hashtag_name).filter(Boolean) : [];
43333
- const cleanTitle = removeTags(awemeDetail.desc || "无标题", tags);
43334
- let slides;
43335
- if (isSlides && awemeDetail.images) slides = awemeDetail.images.map((item) => {
43336
- if (item.clip_type === 2) return {
43337
- type: "image",
43338
- url: item.url_list[2],
43339
- thumbnail: item.url_list[2]
43340
- };
43341
- else if (item.clip_type === 3) {
43342
- const videoUri = item.video?.play_addr_h264?.uri;
43343
- const videoUrl = videoUri ? `https://aweme.snssdk.com/aweme/v1/play/?video_id=${videoUri}&ratio=1080p&line=0` : item.video?.play_addr_h264?.url_list?.[0];
43344
- return {
43345
- type: "livephoto",
43346
- url: item.url_list[2],
43347
- videoUrl,
43348
- thumbnail: item.url_list[2],
43349
- duration: formatDuration(item.video?.duration / 1e3 || 0)
43350
- };
43351
- } else return {
43352
- type: "video",
43353
- url: item.video.play_addr_h264.url_list[0],
43354
- thumbnail: item.url_list[2],
43355
- duration: formatDuration(item.video?.duration / 1e3 || 0)
43356
- };
43357
- });
43358
- return createSuccessResponse(res, {
43359
- id: awemeDetail.aweme_id,
43360
- title: cleanTitle,
43361
- description: awemeDetail.desc || "",
43362
- thumbnail: isVideo ? awemeDetail.video?.cover?.url_list?.[0] : awemeDetail.images?.[0]?.url_list?.[0] || "",
43363
- duration: isVideo ? formatDuration(awemeDetail.video?.duration / 1e3 || 0) : "0:00",
43364
- views: formatCount(awemeDetail.statistics?.play_count || 0),
43365
- likes: formatCount(awemeDetail.statistics?.digg_count || 0),
43366
- author: {
43367
- name: awemeDetail.author?.nickname || "未知用户",
43368
- avatar: awemeDetail.author?.avatar_thumb?.url_list?.[0] || "",
43369
- id: awemeDetail.author?.sec_uid || ""
43370
- },
43371
- type: workType,
43372
- downloadUrl: {
43373
- video: isVideo ? awemeDetail.video?.play_addr?.url_list?.[0] : isSlides && slides ? slides.find((slide) => slide.type === "video")?.url : void 0,
43374
- audio: isSlides && awemeDetail.video ? awemeDetail.video?.play_addr?.url_list?.[0] : awemeDetail.music?.play_url?.uri
43375
- },
43376
- images: workType === "note" ? awemeDetail.images?.map((img) => img.url_list?.[2]).filter(Boolean) : void 0,
43377
- slides,
43378
- tags,
43379
- comments,
43380
- commentCount: awemeDetail.statistics?.comment_count || 0
43381
- });
43382
- } catch (error) {
43383
- logger.error("[DouyinAPI] 解析作品失败:", error);
43384
- return createServerErrorResponse(res, `解析失败: ${error.message}`);
43385
- }
43386
- };
43387
- //#endregion
43388
- //#region src/platform/douyin/api/index.ts
43389
- /**
43390
- * 抖音平台 API 路由
43391
- */
43392
- var router = express.Router();
43393
- router.get("/contents", getContents);
43394
- router.post("/contents", addContent);
43395
- router.post("/contents/:id/delete", deleteContent);
43396
- router.post("/parse", parseWork);
43397
- //#endregion
43398
- //#region src/module/server/auth.ts
43399
- /**
43400
- * Base64解码
43401
- * @param str Base64编码的字符串
43402
- * @returns 解码后的字符串
43403
- */
43404
- var base64Decode = (str) => {
43405
- return Buffer.from(str, "base64").toString("utf8");
43406
- };
43407
- /**
43408
- * URL解码
43409
- * @param str URL编码的字符串
43410
- * @returns 解码后的字符串
43411
- */
43412
- var urlDecode = (str) => {
43413
- return decodeURIComponent(str);
43414
- };
43415
- /**
43416
- * 十六进制解码
43417
- * @param str 十六进制编码的字符串
43418
- * @returns 解码后的字符串
43419
- */
43420
- var hexDecode = (str) => {
43421
- return Buffer.from(str, "hex").toString("utf8");
43422
- };
43423
- /**
43424
- * 反转字符串
43425
- * @param str 待反转字符串
43426
- * @returns 反转后的字符串
43427
- */
43428
- var reverseString = (str) => {
43429
- return str.split("").reverse().join("");
43430
- };
43431
- /**
43432
- * 字符偏移解码
43433
- * @param str 编码的字符串
43434
- * @param offset 偏移量
43435
- * @returns 解码后的字符串
43436
- */
43437
- var charOffsetDecode = (str, offset = 5) => {
43438
- return str.split("").map((char) => {
43439
- const code = char.charCodeAt(0);
43440
- return String.fromCharCode(code - offset);
43441
- }).join("");
43442
- };
43443
- /**
43444
- * 多层解码解密
43445
- * @param str 多层编码的字符串
43446
- * @returns 解码后的原始字符串
43447
- */
43448
- var multiLayerDecode = (str) => {
43449
- try {
43450
- let decoded = base64Decode(str);
43451
- decoded = urlDecode(decoded);
43452
- decoded = base64Decode(decoded);
43453
- decoded = reverseString(decoded);
43454
- decoded = hexDecode(decoded);
43455
- decoded = charOffsetDecode(decoded, 5);
43456
- return decoded;
43457
- } catch (error) {
43458
- throw new Error("多层解码失败:" + error);
43459
- }
43460
- };
43461
- /**
43462
- * HMAC-SHA256签名验证中间件
43463
- * @param req 请求对象
43464
- * @param res 响应对象
43465
- * @param next 下一个中间件函数
43466
- */
43467
- var signatureVerificationMiddleware = (req, res, next) => {
43468
- try {
43469
- const encodedSignature = req.headers["x-signature"];
43470
- const timestamp = req.headers["x-timestamp"];
43471
- const nonce = req.headers["x-nonce"];
43472
- const token = req.headers["authorization"]?.replace("Bearer ", "") || "";
43473
- if (!encodedSignature || !timestamp || !nonce) return createBadRequestResponse(res, "缺少必要的签名参数");
43474
- if (Math.abs(Date.now() - parseInt(timestamp)) > 300 * 1e3) return createBadRequestResponse(res, "请求时间戳已过期");
43475
- let decodedSignature;
43476
- try {
43477
- decodedSignature = multiLayerDecode(encodedSignature);
43478
- } catch (error) {
43479
- return createBadRequestResponse(res, "签名格式错误:" + error);
43480
- }
43481
- const signatureString = `${req.method.toUpperCase()}|${req.headers["x-original-url"] || req.originalUrl}|${req.method === "GET" ? "" : JSON.stringify(req.body || {})}|${timestamp}|${nonce}`;
43482
- const expectedSignature = crypto.createHmac("sha256", token).update(signatureString).digest("hex");
43483
- if (decodedSignature !== expectedSignature) {
43484
- logger.warn(`签名验证失败: 期望=${expectedSignature}, 解码后实际=${decodedSignature}, 签名字符串=${signatureString}`);
43485
- return createBadRequestResponse(res, "签名验证失败");
43486
- }
43487
- next();
43488
- } catch (error) {
43489
- logger.error("签名验证中间件错误:", error);
43490
- return createServerErrorResponse(res, "签名验证失败");
43491
- }
42468
+ //#region src/module/server/constants/routes.ts
42469
+ /**
42470
+ * 路由常量定义
42471
+ */
42472
+ var KKK_PREFIX = "/kkk";
42473
+ /** 静态文件 */
42474
+ var ASSETS_PREFIX = "/assets";
42475
+ /** SSR 页面 */
42476
+ var SSR_PREFIX = "/ssr";
42477
+ `${KKK_PREFIX}${ASSETS_PREFIX}`;
42478
+ `${KKK_PREFIX}${SSR_PREFIX}`;
42479
+ `${KKK_PREFIX}`;
42480
+ var ROUTES = {
42481
+ /** 获取所有 Bot */
42482
+ BOTS: "/bots",
42483
+ /** 获取指定 Bot 信息 */
42484
+ BOT_INFO: "/bots/:botId",
42485
+ /** 获取指定 Bot 的群列表 */
42486
+ BOT_GROUPS: "/bots/:botId/groups",
42487
+ /** 获取指定 Bot 群信息 */
42488
+ BOT_GROUP_INFO: "/bots/:botId/groups/:groupId",
42489
+ /** 获取所有群组 */
42490
+ GROUPS_BATCH: "/groups/batch",
42491
+ /** 获取插件所有配置 */
42492
+ CONFIG: "/config",
42493
+ /** 获取视频流 */
42494
+ VIDEO_STREAM: "/stream/:filename",
42495
+ /** 获取视频事件 */
42496
+ VIDEO_EVENTS: "/video/:filename/events",
42497
+ /** 视频播放页面 */
42498
+ VIDEO_PAGE: "/video/:filename"
43492
42499
  };
43493
42500
  //#endregion
43494
- //#region src/module/server/api/bots.ts
42501
+ //#region src/module/server/controllers/bots.ts
43495
42502
  /**
43496
42503
  * Bot 管理 API
43497
42504
  */
@@ -43543,7 +42550,7 @@ var getBots = async (_req, res) => {
43543
42550
  */
43544
42551
  var getBotInfo = async (req, res) => {
43545
42552
  try {
43546
- const { botId } = req.params;
42553
+ const botId = Array.isArray(req.params.botId) ? req.params.botId[0] : req.params.botId;
43547
42554
  if (!botId) return createServerErrorResponse(res, "缺少 botId 参数");
43548
42555
  const bot = getOnlineBotById(botId);
43549
42556
  if (!bot || bot.account.name === "console") return createServerErrorResponse(res, "Bot 不存在或不在线");
@@ -43564,7 +42571,7 @@ var getBotInfo = async (req, res) => {
43564
42571
  */
43565
42572
  var getBotGroups = async (req, res) => {
43566
42573
  try {
43567
- const { botId } = req.params;
42574
+ const botId = Array.isArray(req.params.botId) ? req.params.botId[0] : req.params.botId;
43568
42575
  if (!botId) return createServerErrorResponse(res, "缺少 botId 参数");
43569
42576
  const botItem = karin.getAllBotList().find((item) => item.bot.account.selfId === botId);
43570
42577
  if (!botItem) return createServerErrorResponse(res, "Bot 不存在或不在线");
@@ -43594,7 +42601,8 @@ var getBotGroups = async (req, res) => {
43594
42601
  */
43595
42602
  var getBotGroupInfo = async (req, res) => {
43596
42603
  try {
43597
- const { botId, groupId } = req.params;
42604
+ const botId = Array.isArray(req.params.botId) ? req.params.botId[0] : req.params.botId;
42605
+ const groupId = Array.isArray(req.params.groupId) ? req.params.groupId[0] : req.params.groupId;
43598
42606
  if (!botId || !groupId) return createServerErrorResponse(res, "缺少 botId 或 groupId 参数");
43599
42607
  const bot = getOnlineBotById(botId);
43600
42608
  if (!bot) return createServerErrorResponse(res, "Bot 不存在或不在线");
@@ -43679,141 +42687,31 @@ var getGroupsBatch = async (req, res) => {
43679
42687
  }
43680
42688
  };
43681
42689
  //#endregion
43682
- //#region src/module/server/api/config.ts
42690
+ //#region src/module/server/controllers/config.ts
43683
42691
  /**
43684
- * 获取所有配置
43685
- * GET /api/kkk/v1/config
42692
+ * 配置管理 API
42693
+ * 提供配置的增删改查接口,供 APP 端使用
43686
42694
  */
43687
- var getAllConfig = async (_req, res) => {
43688
- try {
43689
- const config = await Config.All();
43690
- res.json({
43691
- success: true,
43692
- message: "获取配置成功",
43693
- data: config
43694
- });
43695
- } catch (error) {
43696
- res.status(500).json({
43697
- success: false,
43698
- message: `获取配置失败: ${error.message}`,
43699
- data: null
43700
- });
43701
- }
43702
- };
43703
- /**
43704
- * 获取指定配置模块
43705
- * GET /api/kkk/v1/config/:module
43706
- */
43707
- var getConfigModule = async (req, res) => {
43708
- try {
43709
- const { module } = req.params;
43710
- const allConfig = await Config.All();
43711
- if (!(module in allConfig)) return res.status(400).json({
43712
- success: false,
43713
- message: `配置模块 "${module}" 不存在`,
43714
- data: null
43715
- });
43716
- res.json({
43717
- success: true,
43718
- message: "获取配置成功",
43719
- data: allConfig[module]
43720
- });
43721
- } catch (error) {
43722
- res.status(500).json({
43723
- success: false,
43724
- message: `获取配置失败: ${error.message}`,
43725
- data: null
43726
- });
43727
- }
43728
- };
43729
42695
  /**
43730
- * 更新指定配置模块
43731
- * PUT/POST /api/kkk/v1/config/:module
43732
- */
43733
- var updateConfigModule = async (req, res) => {
43734
- try {
43735
- const { module } = req.params;
43736
- const newConfig = req.body?.config || req.body;
43737
- if (!newConfig || typeof newConfig !== "object") return res.status(400).json({
43738
- success: false,
43739
- message: "请求体必须是有效的配置对象",
43740
- data: null
43741
- });
43742
- if ("_method" in newConfig) delete newConfig._method;
43743
- if (!(module in await Config.All())) return res.status(400).json({
43744
- success: false,
43745
- message: `配置模块 "${module}" 不存在`,
43746
- data: null
43747
- });
43748
- if (await Config.ModifyPro(module, newConfig)) {
43749
- if (module === "pushlist") await Config.syncConfigToDatabase();
43750
- if (module === "cookies" || module === "request") reloadAmagiConfig();
43751
- const updatedConfig = await Config.All();
43752
- res.json({
43753
- success: true,
43754
- message: "配置更新成功",
43755
- data: updatedConfig[module]
43756
- });
43757
- } else res.status(500).json({
43758
- success: false,
43759
- message: "配置更新失败",
43760
- data: null
43761
- });
43762
- } catch (error) {
43763
- res.status(500).json({
43764
- success: false,
43765
- message: `配置更新失败: ${error.message}`,
43766
- data: null
43767
- });
43768
- }
43769
- };
43770
- /**
43771
- * 更新单个配置项
43772
- * PATCH /api/kkk/v1/config/:module
43773
- * Body: { key: string, value: any }
42696
+ * 获取所有配置
42697
+ * GET /kkk/web/v1/config
43774
42698
  */
43775
- var patchConfigItem = async (req, res) => {
42699
+ var getAllConfig = async (_req, res) => {
43776
42700
  try {
43777
- const { module } = req.params;
43778
- const { key, value } = req.body;
43779
- if (!key) return res.status(400).json({
43780
- success: false,
43781
- message: "缺少配置项 key",
43782
- data: null
43783
- });
43784
- if (!(module in await Config.All())) return res.status(400).json({
43785
- success: false,
43786
- message: `配置模块 "${module}" 不存在`,
43787
- data: null
43788
- });
43789
- Config.Modify(module, key, value);
43790
- const updatedConfig = await Config.All();
43791
- res.json({
43792
- success: true,
43793
- message: "配置项更新成功",
43794
- data: updatedConfig[module]
43795
- });
42701
+ return createSuccessResponse(res, await Config.All());
43796
42702
  } catch (error) {
43797
- res.status(500).json({
43798
- success: false,
43799
- message: `配置项更新失败: ${error.message}`,
43800
- data: null
43801
- });
42703
+ return createServerErrorResponse(res, `获取配置失败: ${error.message}`);
43802
42704
  }
43803
42705
  };
43804
42706
  /**
43805
42707
  * 批量更新配置
43806
- * PUT /api/kkk/v1/config
42708
+ * POST /kkk/web/v1/config
43807
42709
  * Body: Partial<ConfigType>
43808
42710
  */
43809
42711
  var updateAllConfig = async (req, res) => {
43810
42712
  try {
43811
42713
  const newConfig = req.body;
43812
- if (!newConfig || typeof newConfig !== "object") return res.status(400).json({
43813
- success: false,
43814
- message: "请求体必须是有效的配置对象",
43815
- data: null
43816
- });
42714
+ if (!newConfig || typeof newConfig !== "object") return createBadRequestResponse(res, "请求体必须是有效的配置对象");
43817
42715
  const oldConfig = await Config.All();
43818
42716
  const results = [];
43819
42717
  let needReloadAmagi = false;
@@ -43832,7 +42730,7 @@ var updateAllConfig = async (req, res) => {
43832
42730
  module,
43833
42731
  success
43834
42732
  });
43835
- if (success && (moduleName === "cookies" || moduleName === "request")) needReloadAmagi = true;
42733
+ if (success && moduleName === "amagi") needReloadAmagi = true;
43836
42734
  } catch (error) {
43837
42735
  results.push({
43838
42736
  module,
@@ -43843,211 +42741,23 @@ var updateAllConfig = async (req, res) => {
43843
42741
  if ("pushlist" in newConfig) await Config.syncConfigToDatabase();
43844
42742
  if (needReloadAmagi) reloadAmagiConfig();
43845
42743
  const allSuccess = results.every((r) => r.success);
43846
- const updatedConfig = await Config.All();
43847
- res.json({
43848
- success: allSuccess,
43849
- message: allSuccess ? "所有配置更新成功" : "部分配置更新失败",
43850
- data: {
43851
- config: updatedConfig,
43852
- results
43853
- }
43854
- });
43855
- } catch (error) {
43856
- res.status(500).json({
43857
- success: false,
43858
- message: `配置更新失败: ${error.message}`,
43859
- data: null
43860
- });
43861
- }
43862
- };
43863
- //#endregion
43864
- //#region src/module/server/api/groups.ts
43865
- /**
43866
- * 群组管理 API
43867
- */
43868
- /**
43869
- * 获取所有已订阅推送功能的群组列表
43870
- * GET /api/v1/groups
43871
- */
43872
- var getGroups = async (_req, res) => {
43873
- try {
43874
- const douyinDB = await getDouyinDB();
43875
- const bilibiliDB = await getBilibiliDB();
43876
- const [douyinGroups, bilibiliGroups] = await Promise.all([douyinDB.groupRepository.find(), bilibiliDB.groupRepository.find()]);
43877
- const allGroupsMap = /* @__PURE__ */ new Map();
43878
- douyinGroups.forEach((group) => {
43879
- allGroupsMap.set(group.id, {
43880
- id: group.id,
43881
- botId: group.botId
43882
- });
43883
- });
43884
- bilibiliGroups.forEach((group) => {
43885
- if (!allGroupsMap.has(group.id)) allGroupsMap.set(group.id, {
43886
- id: group.id,
43887
- botId: group.botId
43888
- });
43889
- });
43890
- const groupList = [];
43891
- for (const group of allGroupsMap.values()) {
43892
- const [douyinSubscriptions, bilibiliSubscriptions] = await Promise.all([douyinDB.getGroupSubscriptions(group.id), bilibiliDB.getGroupSubscriptions(group.id)]);
43893
- if (douyinSubscriptions.length > 0 || bilibiliSubscriptions.length > 0) {
43894
- const bot = getBot(group.botId);
43895
- let groupName = group.id;
43896
- let groupAvatarUrl = "";
43897
- let botAvatarUrl = "";
43898
- let isOnline = true;
43899
- if (!bot) isOnline = false;
43900
- else try {
43901
- const groupInfo = await bot.getGroupInfo(group.id);
43902
- if (groupInfo) groupName = groupInfo.groupName || groupName;
43903
- groupAvatarUrl = await bot.getGroupAvatarUrl(group.id) || "";
43904
- botAvatarUrl = await bot.getAvatarUrl(group.botId) || "";
43905
- } catch (e) {
43906
- logger.warn(`[GroupsAPI] 获取群组信息失败 ${group.id}:`, e);
43907
- }
43908
- groupList.push({
43909
- id: group.id,
43910
- name: groupName,
43911
- avatar: groupAvatarUrl,
43912
- botId: group.botId,
43913
- botAvatar: botAvatarUrl,
43914
- isOnline,
43915
- subscriptionCount: {
43916
- douyin: douyinSubscriptions.length,
43917
- bilibili: bilibiliSubscriptions.length
43918
- }
43919
- });
43920
- }
43921
- }
43922
- return createSuccessResponse(res, groupList);
43923
- } catch (error) {
43924
- logger.error("[GroupsAPI] 获取群组列表失败:", error);
43925
- return createServerErrorResponse(res, "获取群组列表失败");
43926
- }
43927
- };
43928
- //#endregion
43929
- //#region src/module/server/api/link.ts
43930
- /**
43931
- * 链接解析 API
43932
- */
43933
- /**
43934
- * 识别平台类型
43935
- */
43936
- var detectPlatform = (url) => {
43937
- if (url.includes("douyin.com") || url.includes("iesdouyin.com") || url.includes("webcast.amemv.com") || url.includes("live.douyin.com")) return "douyin";
43938
- if (url.includes("bilibili.com") || url.includes("b23.tv")) return "bilibili";
43939
- return "unknown";
43940
- };
43941
- /**
43942
- * 从 URL 中提取作品 ID
43943
- */
43944
- var extractWorkId = (url, platform) => {
43945
- if (platform === "douyin") {
43946
- const videoMatch = /video\/(\d+)/.exec(url);
43947
- if (videoMatch) return {
43948
- type: "video",
43949
- id: videoMatch[1]
43950
- };
43951
- const noteMatch = /note\/(\d+)/.exec(url);
43952
- if (noteMatch) return {
43953
- type: "note",
43954
- id: noteMatch[1]
43955
- };
43956
- const modalMatch = /modal_id=(\d+)/.exec(url);
43957
- if (modalMatch) return {
43958
- type: "video",
43959
- id: modalMatch[1]
43960
- };
43961
- }
43962
- if (platform === "bilibili") {
43963
- const bvidMatch = /\/video\/(BV[a-zA-Z0-9]+)/.exec(url);
43964
- if (bvidMatch) return {
43965
- type: "video",
43966
- id: bvidMatch[1]
43967
- };
43968
- const aidMatch = /\/video\/av(\d+)/.exec(url);
43969
- if (aidMatch) return {
43970
- type: "video",
43971
- id: aidMatch[1]
43972
- };
43973
- const tMatch = /^https:\/\/t\.bilibili\.com\/(\d+)/.exec(url);
43974
- if (tMatch) return {
43975
- type: "dynamic",
43976
- id: tMatch[1]
43977
- };
43978
- const opusMatch = /\/opus\/(\d+)/.exec(url);
43979
- if (opusMatch) return {
43980
- type: "dynamic",
43981
- id: opusMatch[1]
43982
- };
43983
- }
43984
- return null;
43985
- };
43986
- /**
43987
- * 解析短链接并获取最终 URL
43988
- * POST /api/v1/link/resolve
43989
- * Body: { link: string }
43990
- */
43991
- var resolveLink = async (req, res) => {
43992
- try {
43993
- const { link } = req.body;
43994
- if (!link || typeof link !== "string") return createBadRequestResponse(res, "请提供有效的链接");
43995
- const finalUrl = (await axios.get(link, {
43996
- headers: { "User-Agent": "Apifox/1.0.0 (https://apifox.com)" },
43997
- maxRedirects: 10,
43998
- validateStatus: () => true
43999
- })).request.res?.responseUrl || link;
44000
- if (finalUrl.includes("403 Forbidden")) return createServerErrorResponse(res, "无法获取链接的重定向地址");
44001
- const platform = detectPlatform(finalUrl);
44002
- const workInfo = extractWorkId(finalUrl, platform);
44003
- logger.debug(`[LinkAPI] 链接解析: ${link} -> ${platform} (${workInfo?.type}: ${workInfo?.id})`);
44004
42744
  return createSuccessResponse(res, {
44005
- originalUrl: link,
44006
- finalUrl,
44007
- platform,
44008
- workType: workInfo?.type || null,
44009
- workId: workInfo?.id || null
44010
- });
42745
+ config: await Config.All(),
42746
+ results
42747
+ }, allSuccess ? "所有配置更新成功" : "部分配置更新失败");
44011
42748
  } catch (error) {
44012
- logger.error("[LinkAPI] 链接解析失败:", error);
44013
- return createServerErrorResponse(res, `链接解析失败: ${error.message}`);
42749
+ return createServerErrorResponse(res, `配置更新失败: ${error.message}`);
44014
42750
  }
44015
42751
  };
44016
42752
  //#endregion
44017
- //#region src/module/server/api/index.ts
44018
- /**
44019
- * API 路由聚合
44020
- */
44021
- var apiRouter = express.Router();
44022
- var authMiddlewares = [authMiddleware, signatureVerificationMiddleware];
44023
- apiRouter.get("/bots", ...authMiddlewares, getBots);
44024
- apiRouter.get("/bots/:botId", ...authMiddlewares, getBotInfo);
44025
- apiRouter.get("/bots/:botId/groups", ...authMiddlewares, getBotGroups);
44026
- apiRouter.get("/bots/:botId/groups/:groupId", ...authMiddlewares, getBotGroupInfo);
44027
- apiRouter.get("/groups", ...authMiddlewares, getGroups);
44028
- apiRouter.post("/groups/batch", ...authMiddlewares, getGroupsBatch);
44029
- apiRouter.post("/link/resolve", ...authMiddlewares, resolveLink);
44030
- apiRouter.get("/config", ...authMiddlewares, getAllConfig);
44031
- apiRouter.put("/config", ...authMiddlewares, updateAllConfig);
44032
- apiRouter.post("/config", ...authMiddlewares, updateAllConfig);
44033
- apiRouter.get("/config/:module", ...authMiddlewares, getConfigModule);
44034
- apiRouter.put("/config/:module", ...authMiddlewares, updateConfigModule);
44035
- apiRouter.post("/config/:module", ...authMiddlewares, updateConfigModule);
44036
- apiRouter.patch("/config/:module", ...authMiddlewares, patchConfigItem);
44037
- apiRouter.use("/platforms/douyin", ...authMiddlewares, router);
44038
- apiRouter.use("/platforms/bilibili", ...authMiddlewares, router$1);
44039
- //#endregion
44040
- //#region src/module/server/router.ts
42753
+ //#region src/module/server/controllers/video.ts
44041
42754
  /**
44042
- * 视频流服务路由
42755
+ * 视频控制器
44043
42756
  */
44044
42757
  /**
44045
42758
  * 视频文件流传输
44046
- * GET /api/kkk/stream/:filename
44047
- * @param req 请求对象。
44048
- * @param res 响应对象。
44049
42759
  */
44050
- var videoStreamRouter = (req, res) => {
42760
+ var getVideoStream = (req, res) => {
44051
42761
  const filenameParam = req.params.filename;
44052
42762
  const filename = Array.isArray(filenameParam) ? filenameParam[0] : filenameParam;
44053
42763
  if (!filename) {
@@ -44064,7 +42774,7 @@ var videoStreamRouter = (req, res) => {
44064
42774
  const start = parseInt(parts[0], 10);
44065
42775
  const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;
44066
42776
  if (start >= fileSize || end >= fileSize || start > end) {
44067
- res.status(416).send("Requested range not satisfiable");
42777
+ res.status(416).send("请求范围不满足");
44068
42778
  return;
44069
42779
  }
44070
42780
  const chunksize = end - start + 1;
@@ -44096,7 +42806,7 @@ var videoStreamRouter = (req, res) => {
44096
42806
  file.on("error", (err) => {
44097
42807
  logger.error(`读取视频文件流时出错 (Full): ${err.message}`);
44098
42808
  if (!res.headersSent) try {
44099
- createNotFoundResponse(res, "读取视频文件时出错");
42809
+ createNotFoundResponse(res, "读取视频文件失败");
44100
42810
  } catch (e) {
44101
42811
  logger.error("发送读取错误响应失败:", e);
44102
42812
  if (!res.writableEnded) res.end();
@@ -44110,18 +42820,15 @@ var videoStreamRouter = (req, res) => {
44110
42820
  else if (!res.writableEnded) res.end();
44111
42821
  } else {
44112
42822
  logger.error(`处理视频数据请求时发生错误: ${error.message}`);
44113
- if (!res.headersSent) createNotFoundResponse(res, "服务器内部错误");
42823
+ if (!res.headersSent) createNotFoundResponse(res, "服务器错误");
44114
42824
  else if (!res.writableEnded) res.end();
44115
42825
  }
44116
42826
  }
44117
42827
  };
44118
42828
  /**
44119
- * 视频播放页面
44120
- * GET /api/kkk/video/:filename
44121
- * @param req 请求对象。
44122
- * @param res 响应对象。
42829
+ * 视频播放页面(SSR)
44123
42830
  */
44124
- var getVideoRouter = (req, res) => {
42831
+ var getVideoPage = (req, res) => {
44125
42832
  const filenameParam = req.params.filename;
44126
42833
  const filename = Array.isArray(filenameParam) ? filenameParam[0] : filenameParam;
44127
42834
  if (!filename) {
@@ -44130,7 +42837,7 @@ var getVideoRouter = (req, res) => {
44130
42837
  }
44131
42838
  const videoPath = Common.validateVideoRequest(filename, res);
44132
42839
  if (!videoPath) return;
44133
- const videoDataUrl = `/api/kkk/stream/${encodeURIComponent(filename)}`;
42840
+ const videoDataUrl = `/kkk/v1/stream/${encodeURIComponent(filename)}`;
44134
42841
  const previewInfo = Common.getVideoPreview(filename);
44135
42842
  const removeCache = previewInfo?.removeCache ?? Config.app.removeCache;
44136
42843
  const createdAt = previewInfo?.createdAt ?? Date.now();
@@ -44142,19 +42849,16 @@ var getVideoRouter = (req, res) => {
44142
42849
  removeCache,
44143
42850
  createdAt,
44144
42851
  expireAt,
44145
- eventsUrl: `/api/kkk/video/${encodeURIComponent(filename)}/events`
42852
+ eventsUrl: `/kkk/v1/video/${encodeURIComponent(filename)}/events`
44146
42853
  });
44147
42854
  res.setHeader("Cache-Control", "no-cache");
44148
42855
  res.setHeader("Content-Type", "text/html; charset=utf-8");
44149
42856
  res.send(htmlContent);
44150
42857
  };
44151
42858
  /**
44152
- * 视频预览状态事件流。
44153
- * 持续向前端推送剩余有效期与删除状态,并在预览失效后自动结束连接。
44154
- * @param req 请求对象。
44155
- * @param res 响应对象。
42859
+ * 视频预览状态事件流(SSE)
44156
42860
  */
44157
- var videoPreviewEventsRouter = (req, res) => {
42861
+ var getVideoEvents = (req, res) => {
44158
42862
  const filenameParam = req.params.filename;
44159
42863
  const filename = Array.isArray(filenameParam) ? filenameParam[0] : filenameParam;
44160
42864
  if (!filename) {
@@ -44175,10 +42879,6 @@ var videoPreviewEventsRouter = (req, res) => {
44175
42879
  res.setHeader("Connection", "keep-alive");
44176
42880
  res.flushHeaders?.();
44177
42881
  let timer = null;
44178
- /**
44179
- * 推送一次最新的预览状态到客户端。
44180
- * @returns 当前预览是否已被删除或失效。
44181
- */
44182
42882
  const sendPayload = () => {
44183
42883
  const currentInfo = Common.getVideoPreview(filename) ?? previewInfo;
44184
42884
  const now = Date.now();
@@ -44220,12 +42920,130 @@ var videoPreviewEventsRouter = (req, res) => {
44220
42920
  });
44221
42921
  };
44222
42922
  //#endregion
44223
- //#region src/module/server/Register.ts
44224
- var server = express();
44225
- var proxyOptions = {
44226
- target: "https://developer.huawei.com",
44227
- changeOrigin: true
42923
+ //#region src/module/server/middlewares/auth.ts
42924
+ /**
42925
+ * Base64解码
42926
+ * @param str Base64编码的字符串
42927
+ * @returns 解码后的字符串
42928
+ */
42929
+ var base64Decode = (str) => {
42930
+ return Buffer.from(str, "base64").toString("utf8");
44228
42931
  };
42932
+ /**
42933
+ * URL解码
42934
+ * @param str URL编码的字符串
42935
+ * @returns 解码后的字符串
42936
+ */
42937
+ var urlDecode = (str) => {
42938
+ return decodeURIComponent(str);
42939
+ };
42940
+ /**
42941
+ * 十六进制解码
42942
+ * @param str 十六进制编码的字符串
42943
+ * @returns 解码后的字符串
42944
+ */
42945
+ var hexDecode = (str) => {
42946
+ return Buffer.from(str, "hex").toString("utf8");
42947
+ };
42948
+ /**
42949
+ * 反转字符串
42950
+ * @param str 待反转字符串
42951
+ * @returns 反转后的字符串
42952
+ */
42953
+ var reverseString = (str) => {
42954
+ return str.split("").reverse().join("");
42955
+ };
42956
+ /**
42957
+ * 字符偏移解码
42958
+ * @param str 编码的字符串
42959
+ * @param offset 偏移量
42960
+ * @returns 解码后的字符串
42961
+ */
42962
+ var charOffsetDecode = (str, offset = 5) => {
42963
+ return str.split("").map((char) => {
42964
+ const code = char.charCodeAt(0);
42965
+ return String.fromCharCode(code - offset);
42966
+ }).join("");
42967
+ };
42968
+ /**
42969
+ * 多层解码解密
42970
+ * @param str 多层编码的字符串
42971
+ * @returns 解码后的原始字符串
42972
+ */
42973
+ var multiLayerDecode = (str) => {
42974
+ try {
42975
+ let decoded = base64Decode(str);
42976
+ decoded = urlDecode(decoded);
42977
+ decoded = base64Decode(decoded);
42978
+ decoded = reverseString(decoded);
42979
+ decoded = hexDecode(decoded);
42980
+ decoded = charOffsetDecode(decoded, 5);
42981
+ return decoded;
42982
+ } catch (error) {
42983
+ throw new Error("多层解码失败:" + error);
42984
+ }
42985
+ };
42986
+ /**
42987
+ * HMAC-SHA256签名验证中间件
42988
+ * @param req 请求对象
42989
+ * @param res 响应对象
42990
+ * @param next 下一个中间件函数
42991
+ */
42992
+ var signatureVerificationMiddleware = (req, res, next) => {
42993
+ try {
42994
+ const encodedSignature = req.headers["x-signature"];
42995
+ const timestamp = req.headers["x-timestamp"];
42996
+ const nonce = req.headers["x-nonce"];
42997
+ const token = req.headers["authorization"]?.replace("Bearer ", "") || "";
42998
+ if (!encodedSignature || !timestamp || !nonce) return createBadRequestResponse(res, "缺少必要的签名参数");
42999
+ if (Math.abs(Date.now() - parseInt(timestamp)) > 300 * 1e3) return createBadRequestResponse(res, "请求时间戳已过期");
43000
+ let decodedSignature;
43001
+ try {
43002
+ decodedSignature = multiLayerDecode(encodedSignature);
43003
+ } catch (error) {
43004
+ return createBadRequestResponse(res, "签名格式错误:" + error);
43005
+ }
43006
+ const signatureString = `${req.method.toUpperCase()}|${req.headers["x-original-url"] || req.originalUrl}|${req.method === "GET" ? "" : JSON.stringify(req.body || {})}|${timestamp}|${nonce}`;
43007
+ const expectedSignature = crypto.createHmac("sha256", token).update(signatureString).digest("hex");
43008
+ if (decodedSignature !== expectedSignature) {
43009
+ logger.warn(`签名验证失败: 期望=${expectedSignature}, 解码后实际=${decodedSignature}, 签名字符串=${signatureString}`);
43010
+ return createBadRequestResponse(res, "签名验证失败");
43011
+ }
43012
+ next();
43013
+ } catch (error) {
43014
+ logger.error("签名验证中间件错误:", error);
43015
+ return createServerErrorResponse(res, "签名验证失败");
43016
+ }
43017
+ };
43018
+ //#endregion
43019
+ //#region src/module/server/routes/api.ts
43020
+ var import_lib = /* @__PURE__ */ __toESM(require_lib(), 1);
43021
+ /**
43022
+ * API 路由注册
43023
+ */
43024
+ var apiRouter = express.Router();
43025
+ var authMiddlewares = [authMiddleware, signatureVerificationMiddleware];
43026
+ apiRouter.get(ROUTES.BOTS, ...authMiddlewares, getBots);
43027
+ apiRouter.get(ROUTES.BOT_INFO, ...authMiddlewares, getBotInfo);
43028
+ apiRouter.get(ROUTES.BOT_GROUPS, ...authMiddlewares, getBotGroups);
43029
+ apiRouter.get(ROUTES.BOT_GROUP_INFO, ...authMiddlewares, getBotGroupInfo);
43030
+ apiRouter.post(ROUTES.GROUPS_BATCH, ...authMiddlewares, getGroupsBatch);
43031
+ apiRouter.get(ROUTES.CONFIG, ...authMiddlewares, getAllConfig);
43032
+ apiRouter.post(ROUTES.CONFIG, ...authMiddlewares, updateAllConfig);
43033
+ apiRouter.get(ROUTES.VIDEO_STREAM, getVideoStream);
43034
+ apiRouter.get(ROUTES.VIDEO_EVENTS, getVideoEvents);
43035
+ //#endregion
43036
+ //#region src/module/server/routes/ssr.ts
43037
+ /**
43038
+ * SSR 路由注册
43039
+ */
43040
+ var ssrRouter = express.Router();
43041
+ ssrRouter.get(ROUTES.VIDEO_PAGE, getVideoPage);
43042
+ //#endregion
43043
+ //#region src/module/server/routes/static.ts
43044
+ /**
43045
+ * 静态文件路由注册
43046
+ */
44229
43047
  var webDistPath = path.join(Root.pluginPath, "lib", "web");
44230
43048
  var webIndexPath = path.join(webDistPath, "index.html");
44231
43049
  var sendWebIndex = (res) => {
@@ -44234,10 +43052,34 @@ var sendWebIndex = (res) => {
44234
43052
  res.setHeader("Cache-Control", "no-cache");
44235
43053
  res.type("html").send(html);
44236
43054
  } catch (error) {
44237
- const message = `[karin-plugin-kkk] Failed to read Web UI entry: ${webIndexPath}`;
44238
- logger.error(error instanceof Error ? `${message}\n${error.stack ?? error.message}` : `${message}\n${String(error)}`);
44239
- res.status(500).type("text/plain").send(message);
43055
+ logger.error("[karin-plugin-kkk] 读取 Web UI 入口文件失败:", error);
43056
+ createServerErrorResponse(res, "加载 Web UI 失败");
43057
+ }
43058
+ };
43059
+ var staticRouter = express.Router();
43060
+ var webStatic = express.static(webDistPath, {
43061
+ redirect: false,
43062
+ setHeaders: (res, filePath) => {
43063
+ if (filePath.endsWith(".html")) {
43064
+ res.setHeader("Cache-Control", "no-cache");
43065
+ return;
43066
+ }
43067
+ res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
44240
43068
  }
43069
+ });
43070
+ staticRouter.use(webStatic);
43071
+ staticRouter.use((_req, res) => {
43072
+ sendWebIndex(res);
43073
+ });
43074
+ //#endregion
43075
+ //#region src/module/server/routes/index.ts
43076
+ /**
43077
+ * 主路由注册
43078
+ */
43079
+ var server = express();
43080
+ var proxyOptions = {
43081
+ target: "https://developer.huawei.com",
43082
+ changeOrigin: true
44241
43083
  };
44242
43084
  server.use(import_lib.default());
44243
43085
  server.use("/", createProxyMiddleware(proxyOptions));
@@ -44254,39 +43096,21 @@ if (process.env.NODE_ENV !== "test") checkPort(3780).then((isOpen) => {
44254
43096
  var app$1 = express.Router();
44255
43097
  app$1.use(express.json());
44256
43098
  app$1.use(express.urlencoded({ extended: true }));
44257
- if (Config.app.APIServer && Config.app.APIServerMount) {
44258
- app$1.use("/amagi/api/bilibili", createBilibiliRoutes(Config.cookies.bilibili));
44259
- app$1.use("/amagi/api/douyin", createDouyinRoutes(Config.cookies.douyin));
44260
- app$1.use("/amagi/api/kuaishou", createKuaishouRoutes(Config.cookies.kuaishou));
44261
- app$1.use("/amagi/api/xiaohongshu", createXiaohongshuRoutes(Config.cookies.xiaohongshu));
44262
- } else if (Config.app.APIServer) new Client({ cookies: {
44263
- bilibili: Config.cookies.bilibili,
44264
- douyin: Config.cookies.douyin,
44265
- kuaishou: Config.cookies.kuaishou,
44266
- xiaohongshu: Config.cookies.xiaohongshu
44267
- } }).startServer(Config.app.APIServerPort);
44268
- app$1.get("/stream/:filename", videoStreamRouter);
44269
- app$1.get("/video/:filename", getVideoRouter);
44270
- app$1.get("/video/:filename/events", videoPreviewEventsRouter);
43099
+ if (Config.amagi.APIServer && Config.amagi.APIServerMount) {
43100
+ app$1.use("/amagi/api/bilibili", createBilibiliRoutes(Config.amagi.cookies.bilibili));
43101
+ app$1.use("/amagi/api/douyin", createDouyinRoutes(Config.amagi.cookies.douyin));
43102
+ app$1.use("/amagi/api/kuaishou", createKuaishouRoutes(Config.amagi.cookies.kuaishou));
43103
+ app$1.use("/amagi/api/xiaohongshu", createXiaohongshuRoutes(Config.amagi.cookies.xiaohongshu));
43104
+ } else if (Config.amagi.APIServer) new Client({ cookies: {
43105
+ bilibili: Config.amagi.cookies.bilibili,
43106
+ douyin: Config.amagi.cookies.douyin,
43107
+ kuaishou: Config.amagi.cookies.kuaishou,
43108
+ xiaohongshu: Config.amagi.cookies.xiaohongshu
43109
+ } }).startServer(Config.amagi.APIServerPort);
44271
43110
  app$1.use("/v1", apiRouter);
44272
- var staticRouter = express.Router();
44273
- var webStatic = express.static(webDistPath, {
44274
- redirect: false,
44275
- setHeaders: (res, filePath) => {
44276
- if (filePath.endsWith(".html")) {
44277
- res.setHeader("Cache-Control", "no-cache");
44278
- return;
44279
- }
44280
- res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
44281
- }
44282
- });
44283
- staticRouter.use(webStatic);
44284
- staticRouter.use((_req, res) => {
44285
- sendWebIndex(res);
44286
- });
44287
- /** 将子路由挂载到主路由上 */
44288
- app.use("/kkk", staticRouter);
44289
- app.use("/api/kkk", app$1);
43111
+ app$1.use(SSR_PREFIX, ssrRouter);
43112
+ app$1.use(ASSETS_PREFIX, staticRouter);
43113
+ app.use(KKK_PREFIX, app$1);
44290
43114
  //#endregion
44291
43115
  //#region src/setup.ts
44292
43116
  var requireVersion = "1.16.1";
@@ -44333,4 +43157,4 @@ mkdirSync(`${karinPathBase}/${Root.pluginName}/data`);
44333
43157
  mkdirSync(Common.tempDri.images);
44334
43158
  mkdirSync(Common.tempDri.video);
44335
43159
  //#endregion
44336
- export { createLineBreakNode as $, xiaohongshuApiUrls as $n, emitLog as $r, MajorType as $t, template_default as A, fetchResponse as An, BilibiliBangumiStreamParamsSchema as Ar, DouyinMethodMapping as At, DouyinDBBase as B, createBoundDouyinFetcher as Bn, BilibiliMethodRoutes as Br, DouyinFetcherMethods as Bt, help as C, bv2av as Cn, DouyinWorkParamsSchema as Cr, softFetch as Ct, removeOldFiles as D, logMiddleware as Dn, BilibiliArticleParamsSchema as Dr, BilibiliApiRoutes as Dt, dylogin as E, initLogger as En, BilibiliArticleInfoParamsSchema as Er, amagiClient$1 as Et, getBilibiliDB as F, createBoundKuaishouFetcher as Fn, BilibiliDanmakuParamsSchema as Fr, getApiRoute as Ft, createAtNode as G, createErrorResponse as Gn, BilibiliValidationSchemas as Gr, KuaishouMethodToFetcher as Gt, reactServerRender as H, douyinSign as Hn, BilibiliQrcodeStatusParamsSchema as Hr, DouyinMethodToFetcher as Ht, getDouyinDB as I, kuaishouFetcher$1 as In, BilibiliDynamicParamsSchema as Ir, getEnglishMethodName as It, createEmojiNode as J, validateDouyinParams as Jn, amagiEvents as Jr, XiaohongshuInternalMethods as Jt, createBlockquoteNode as K, createSuccessResponse$1 as Kn, BilibiliVideoDownloadParamsSchema as Kr, MethodMaps as Kt, getStatisticsDB as L, kuaishouSign as Ln, BilibiliEmojiParamsSchema as Lr, BilibiliFetcherMethods as Lt, bilibiliDBInstance as M, isNetworkErrorResult as Mn, BilibiliColumnInfoParamsSchema as Mr, KuaishouMethodMapping as Mt, cleanOldDynamicCache as N, createBoundXiaohongshuFetcher as Nn, BilibiliCommentParamsSchema as Nr, XiaohongshuApiRoutes as Nt, task as O, logger$2 as On, BilibiliAv2BvParamsSchema as Or, BilibiliMethodMapping as Ot, douyinDBInstance as P, xiaohongshuFetcher$1 as Pn, BilibiliCommentReplyParamsSchema as Pr, XiaohongshuMethodMapping as Pt, createImageNode as Q, XiaohongshuValidationSchemas as Qn, emitHttpResponse as Qr, AdditionalType as Qt, initAllDatabases as R, kuaishouApiUrls as Rn, BilibiliLiveParamsSchema as Rr, BilibiliInternalMethods as Rt, setdyPush as S, av2bv as Sn, DouyinValidationSchemas as Sr, reloadAmagiConfig as St, biLogin as T, httpLogger as Tn, BilibiliArticleCardParamsSchema as Tr, amagi as Tt, renderVideoPreviewPage as U, bilibiliFetcher$1 as Un, BilibiliUserParamsSchema as Ur, KuaishouFetcherMethods as Ut, BilibiliDBBase as V, douyinApiUrls as Vn, BilibiliQrcodeParamsSchema as Vr, DouyinInternalMethods as Vt, renderRichTextToReact as W, createBoundBilibiliFetcher as Wn, BilibiliValidateCaptchaParamsSchema as Wr, KuaishouInternalMethods as Wt, createHeadingNode as X, validateXiaohongshuParams as Xn, emitApiSuccess as Xr, toFetcherMethod as Xt, createHashtagNode as Y, validateKuaishouParams as Yn, emitApiError as Yr, XiaohongshuMethodToFetcher as Yt, createHorizontalRuleNode as Z, XiaohongshuMethodRoutes as Zn, emitHttpRequest as Zr, DynamicType as Zt, douyinPush as _, ValidationError as _n, DouyinMusicParamsSchema as _r, SOFT_ERROR_CODES as _t, bilibiliAPP as a, emitNetworkError as ai, xiaohongshu$1 as an, KuaishouUserProfileParamsSchema as ar, createParagraphNode as at, globalIgnore as b, wbi_sign as bn, DouyinUserListParamsSchema as br, douyinFetcher as bt, prefix as c, createBoundBilibiliApi as ci, createBoundKuaishouApi as cn, KuaishouVideoParamsSchema as cr, createTextNode as ct, globalStatistics as d, getDouyinData as di, createDouyinRoutes as dn, DouyinDanmakuParamsSchema as dr, createVoteNode as dt, emitLogDebug as ei, CommentType as en, xiaohongshuSign as er, createLinkCardNode as et, groupStatistics as f, getKuaishouData as fi, createBoundDouyinApi as fn, DouyinEmojiListParamsSchema as fr, createWebLinkNode as ft, changeBotID as g, ApiError as gn, DouyinMethodRoutes as gr, AmagiError as gt, bilibiliPushList as h, createBilibiliRoutes as hn, DouyinLiveRoomParamsSchema as hr, AmagiBase as ht, update as i, emitLogWarn as ii, createBoundXiaohongshuApi as in, KuaishouMethodRoutes as ir, createMentionNode as it, webConfig as j, getHeadersAndData as jn, BilibiliBv2AvParamsSchema as jr, KuaishouApiRoutes as jt, testWrapWithErrorHandler as k, fetchData as kn, BilibiliBangumiInfoParamsSchema as kr, DouyinApiRoutes as kt, xiaohongshuAPP as l, bilibiliApiUrls as li, kuaishou$1 as ln, DouyinCommentParamsSchema as lr, createTopicNode as lt, bilibiliPush as m, bilibiliUtils as mn, DouyinHotWordsParamsSchema as mr, normalizeRichTextNodes as mt, kkkUpdateCommand as n, emitLogInfo as ni, xiaohongshuUtils as nn, KuaishouEmojiParamsSchema as nr, createListNode as nt, douyinAPP as o, emitNetworkRetry as oi, kuaishouUtils as on, KuaishouUserWorkListParamsSchema as or, createRichTextDocument as ot, qrLogin as p, Root as pi, douyin$1 as pn, DouyinEmojiProParamsSchema as pr, extractRichTextPlainText as pt, createCodeBlockNode as q, validateBilibiliParams as qn, BilibiliVideoParamsSchema as qr, XiaohongshuFetcherMethods as qt, kkkUpdateTest as r, emitLogMark as ri, createXiaohongshuRoutes as rn, KuaishouLiveRoomInfoParamsSchema as rr, createLotteryNode as rt, kuaishouAPP as s, bilibili$1 as si, createKuaishouRoutes as sn, KuaishouValidationSchemas as sr, createSearchKeywordNode as st, kkkUpdate as t, emitLogError as ti, createAmagiClient as tn, KuaishouCommentParamsSchema as tr, createListItemNode as tt, testPush as u, getBilibiliData as ui, douyinUtils as un, DouyinCommentReplyParamsSchema as ur, createViewPictureNode as ut, douyinPushList as v, handleError as vn, DouyinQrcodeParamsSchema as vr, amagiClient as vt, version as w, qtparam as wn, BilibiliApplyCaptchaParamsSchema as wr, CreateApp as wt, setbiliPush as x, parseDmSegMobileReply as xn, DouyinUserParamsSchema as xr, kuaishouFetcher as xt, forcePush as y, bilibiliErrorCodeMap as yn, DouyinSearchParamsSchema as yr, bilibiliFetcher as yt, StatisticsDBBase as z, douyinFetcher$1 as zn, BilibiliLoginParamsSchema as zr, BilibiliMethodToFetcher as zt };
43160
+ export { createLineBreakNode as $, xiaohongshuSign as $n, emitLogDebug as $r, CommentType as $t, template_default as A, getHeadersAndData as An, BilibiliBv2AvParamsSchema as Ar, KuaishouApiRoutes as At, DouyinDBBase as B, douyinApiUrls as Bn, BilibiliQrcodeParamsSchema as Br, DouyinInternalMethods as Bt, help as C, qtparam as Cn, BilibiliApplyCaptchaParamsSchema as Cr, CreateApp as Ct, removeOldFiles as D, logger$2 as Dn, BilibiliAv2BvParamsSchema as Dr, BilibiliMethodMapping as Dt, dylogin as E, logMiddleware as En, BilibiliArticleParamsSchema as Er, BilibiliApiRoutes as Et, getBilibiliDB as F, kuaishouFetcher$1 as Fn, BilibiliDynamicParamsSchema as Fr, getEnglishMethodName as Ft, createAtNode as G, createSuccessResponse$1 as Gn, BilibiliVideoDownloadParamsSchema as Gr, MethodMaps as Gt, reactServerRender as H, bilibiliFetcher$1 as Hn, BilibiliUserParamsSchema as Hr, KuaishouFetcherMethods as Ht, getDouyinDB as I, kuaishouSign as In, BilibiliEmojiParamsSchema as Ir, BilibiliFetcherMethods as It, createEmojiNode as J, validateKuaishouParams as Jn, emitApiError as Jr, XiaohongshuMethodToFetcher as Jt, createBlockquoteNode as K, validateBilibiliParams as Kn, BilibiliVideoParamsSchema as Kr, XiaohongshuFetcherMethods as Kt, getStatisticsDB as L, kuaishouApiUrls as Ln, BilibiliLiveParamsSchema as Lr, BilibiliInternalMethods as Lt, bilibiliDBInstance as M, createBoundXiaohongshuFetcher as Mn, BilibiliCommentParamsSchema as Mr, XiaohongshuApiRoutes as Mt, cleanOldDynamicCache as N, xiaohongshuFetcher$1 as Nn, BilibiliCommentReplyParamsSchema as Nr, XiaohongshuMethodMapping as Nt, task as O, fetchData as On, BilibiliBangumiInfoParamsSchema as Or, DouyinApiRoutes as Ot, douyinDBInstance as P, createBoundKuaishouFetcher as Pn, BilibiliDanmakuParamsSchema as Pr, getApiRoute as Pt, createImageNode as Q, xiaohongshuApiUrls as Qn, emitLog as Qr, MajorType as Qt, initAllDatabases as R, douyinFetcher$1 as Rn, BilibiliLoginParamsSchema as Rr, BilibiliMethodToFetcher as Rt, setdyPush as S, bv2av as Sn, DouyinWorkParamsSchema as Sr, softFetch as St, biLogin as T, initLogger as Tn, BilibiliArticleInfoParamsSchema as Tr, amagiClient$1 as Tt, renderVideoPreviewPage as U, createBoundBilibiliFetcher as Un, BilibiliValidateCaptchaParamsSchema as Ur, KuaishouInternalMethods as Ut, BilibiliDBBase as V, douyinSign as Vn, BilibiliQrcodeStatusParamsSchema as Vr, DouyinMethodToFetcher as Vt, renderRichTextToReact as W, createErrorResponse as Wn, BilibiliValidationSchemas as Wr, KuaishouMethodToFetcher as Wt, createHeadingNode as X, XiaohongshuMethodRoutes as Xn, emitHttpRequest as Xr, DynamicType as Xt, createHashtagNode as Y, validateXiaohongshuParams as Yn, emitApiSuccess as Yr, toFetcherMethod as Yt, createHorizontalRuleNode as Z, XiaohongshuValidationSchemas as Zn, emitHttpResponse as Zr, AdditionalType as Zt, douyinPush as _, handleError as _n, DouyinQrcodeParamsSchema as _r, SOFT_ERROR_CODES as _t, bilibiliAPP as a, emitNetworkRetry as ai, kuaishouUtils as an, KuaishouUserWorkListParamsSchema as ar, createParagraphNode as at, globalIgnore as b, parseDmSegMobileReply as bn, DouyinUserParamsSchema as br, kuaishouFetcher as bt, prefix as c, bilibiliApiUrls as ci, kuaishou$1 as cn, DouyinCommentParamsSchema as cr, createTextNode as ct, globalStatistics as d, getKuaishouData as di, createBoundDouyinApi as dn, DouyinEmojiListParamsSchema as dr, createVoteNode as dt, emitLogError as ei, createAmagiClient as en, KuaishouCommentParamsSchema as er, createLinkCardNode as et, groupStatistics as f, Root as fi, douyin$1 as fn, DouyinEmojiProParamsSchema as fr, createWebLinkNode as ft, changeBotID as g, ValidationError as gn, DouyinMusicParamsSchema as gr, AmagiError as gt, bilibiliPushList as h, ApiError as hn, DouyinMethodRoutes as hr, AmagiBase as ht, update as i, emitNetworkError as ii, xiaohongshu$1 as in, KuaishouUserProfileParamsSchema as ir, createMentionNode as it, webConfig as j, isNetworkErrorResult as jn, BilibiliColumnInfoParamsSchema as jr, KuaishouMethodMapping as jt, testWrapWithErrorHandler as k, fetchResponse as kn, BilibiliBangumiStreamParamsSchema as kr, DouyinMethodMapping as kt, xiaohongshuAPP as l, getBilibiliData as li, douyinUtils as ln, DouyinCommentReplyParamsSchema as lr, createTopicNode as lt, bilibiliPush as m, createBilibiliRoutes as mn, DouyinLiveRoomParamsSchema as mr, normalizeRichTextNodes as mt, kkkUpdateCommand as n, emitLogMark as ni, createXiaohongshuRoutes as nn, KuaishouLiveRoomInfoParamsSchema as nr, createListNode as nt, douyinAPP as o, bilibili$1 as oi, createKuaishouRoutes as on, KuaishouValidationSchemas as or, createRichTextDocument as ot, qrLogin as p, bilibiliUtils as pn, DouyinHotWordsParamsSchema as pr, extractRichTextPlainText as pt, createCodeBlockNode as q, validateDouyinParams as qn, amagiEvents as qr, XiaohongshuInternalMethods as qt, kkkUpdateTest as r, emitLogWarn as ri, createBoundXiaohongshuApi as rn, KuaishouMethodRoutes as rr, createLotteryNode as rt, kuaishouAPP as s, createBoundBilibiliApi as si, createBoundKuaishouApi as sn, KuaishouVideoParamsSchema as sr, createSearchKeywordNode as st, kkkUpdate as t, emitLogInfo as ti, xiaohongshuUtils as tn, KuaishouEmojiParamsSchema as tr, createListItemNode as tt, testPush as u, getDouyinData as ui, createDouyinRoutes as un, DouyinDanmakuParamsSchema as ur, createViewPictureNode as ut, douyinPushList as v, bilibiliErrorCodeMap as vn, DouyinSearchParamsSchema as vr, bilibiliFetcher as vt, version as w, httpLogger as wn, BilibiliArticleCardParamsSchema as wr, amagi as wt, setbiliPush as x, av2bv as xn, DouyinValidationSchemas as xr, reloadAmagiConfig as xt, forcePush as y, wbi_sign as yn, DouyinUserListParamsSchema as yr, douyinFetcher as yt, StatisticsDBBase as z, createBoundDouyinFetcher as zn, BilibiliMethodRoutes as zr, DouyinFetcherMethods as zt };