karin-plugin-kkk 2.32.3 → 2.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,12 +4,11 @@ import "node:module";
4
4
  import fs from "node:fs";
5
5
  import path, { resolve } from "node:path";
6
6
  import URL$2, { fileURLToPath } from "node:url";
7
- import os, { platform } from "node:os";
8
- import karin$1, { BOT_CONNECT, app, authMiddleware, checkPkgUpdate, checkPort, common, components, config, copyConfigSync, createBadRequestResponse, createNotFoundResponse, createServerErrorResponse, createSuccessResponse, db, defineConfig, ffmpeg, ffprobe, filesByExt, getBot, hooks, karin, karinPathHtml, karinPathTemp, logger, logger as logger$1, logs, mkdirSync, parseChangelog, range, render, requireFileSync, restart, segment, updatePkg, watch } from "node-karin";
9
- import _ from "node-karin/lodash";
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";
10
8
  import { EventEmitter } from "node:events";
11
9
  import crypto from "node:crypto";
12
10
  import axios, { AxiosError } from "node-karin/axios";
11
+ import os, { platform } from "node:os";
13
12
  import express from "node-karin/express";
14
13
  import { karinPathBase, karinPathTemp as karinPathTemp$1 } from "node-karin/root";
15
14
  import sqlite3 from "node-karin/sqlite3";
@@ -18,6 +17,7 @@ import util from "node:util";
18
17
  import { generate, scan } from "@ikenxuan/qrcode";
19
18
  import { Transform } from "node:stream";
20
19
  import { pipeline } from "node:stream/promises";
20
+ import _ from "node-karin/lodash";
21
21
  import { inflateSync } from "node:zlib";
22
22
  import { embedWatermarkToPngBytes } from "@ikenxuan/watermark";
23
23
  import { snapka } from "@karinjs/plugin-puppeteer";
@@ -18863,20 +18863,50 @@ var AmbientBackground$1 = import_react.memo(({ pic }) => /* @__PURE__ */ (0, imp
18863
18863
  ]
18864
18864
  }));
18865
18865
  AmbientBackground$1.displayName = "AmbientBackground";
18866
+ var MAX_DANMAKU_ROWS = 4;
18867
+ /**
18868
+ * 封面弹幕层:模拟视频平台截图中的滚动弹幕轨道,行数由 MAX_DANMAKU_ROWS 控制。
18869
+ */
18870
+ var DanmakuOverlay = import_react.memo(({ items }) => {
18871
+ if (!items.length) return null;
18872
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
18873
+ className: "pointer-events-none absolute inset-x-0 top-0 z-20 overflow-hidden px-16",
18874
+ style: {
18875
+ height: `calc(${MAX_DANMAKU_ROWS} * 20rem)`,
18876
+ maskImage: "linear-gradient(to right, transparent 0, black 3rem, black calc(100% - 10rem), transparent 100%)",
18877
+ WebkitMaskImage: "linear-gradient(to right, transparent 0, black 3rem, black calc(100% - 10rem), transparent 100%)"
18878
+ },
18879
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
18880
+ className: "relative pt-8 overflow-visible whitespace-normal text-[0] leading-16",
18881
+ children: items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
18882
+ className: "mr-20 inline-block shrink-0 whitespace-nowrap align-top text-4xl font-black leading-16 text-white",
18883
+ style: {
18884
+ marginLeft: index === 0 ? void 0 : `${(index * 5 + item.content.length * 3) % 5 * 2}rem`,
18885
+ opacity: Math.max(.2, 1 - index * .025),
18886
+ textShadow: "0 3px 8px rgba(0,0,0,0.85), 0 0 2px rgba(0,0,0,0.95)",
18887
+ WebkitTextStroke: "1px rgba(0,0,0,0.58)"
18888
+ },
18889
+ children: item.content
18890
+ }, `${item.content}-${index}`))
18891
+ })
18892
+ });
18893
+ });
18894
+ DanmakuOverlay.displayName = "DanmakuOverlay";
18866
18895
  var BilibiliVideoInfo = import_react.memo((props) => {
18867
18896
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DefaultLayout, {
18868
18897
  ...props,
18869
18898
  className: "relative overflow-hidden",
18870
18899
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AmbientBackground$1, { pic: props.data.pic }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
18871
18900
  className: "relative z-10",
18872
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
18901
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
18902
+ className: "relative overflow-hidden",
18873
18903
  style: coverMaskStyle$1,
18874
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EnhancedImage, {
18904
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(EnhancedImage, {
18875
18905
  src: props.data.pic,
18876
18906
  alt: props.data.title,
18877
18907
  className: "object-cover w-full",
18878
18908
  placeholder: "视频封面"
18879
- })
18909
+ }), props.data.hotDanmaku && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DanmakuOverlay, { items: props.data.hotDanmaku })]
18880
18910
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
18881
18911
  className: "flex flex-col gap-10 px-16 pt-20",
18882
18912
  children: [
@@ -32546,1087 +32576,7 @@ var cleanOldDynamicCache = async (platform, days = 7) => {
32546
32576
  else return await (await getBilibiliDB()).cleanOldDynamicCache(days);
32547
32577
  };
32548
32578
  //#endregion
32549
- //#region src/platform/bilibili/web.config.ts
32550
- /**
32551
- * B站配置组件
32552
- * @param all 配置
32553
- */
32554
- var BilibiliWeb = (all) => {
32555
- return [components.accordion.create("bilibili", {
32556
- label: "B站相关",
32557
- children: [components.accordion.createItem("cfg:bilibili", {
32558
- title: "B站相关",
32559
- className: "ml-4 mr-4",
32560
- subtitle: "此处为B站相关的用户偏好设置",
32561
- children: [
32562
- components.switch.create("switch", {
32563
- label: "解析开关",
32564
- description: "B站解析开关,此开关为单独开关",
32565
- defaultSelected: all.bilibili.switch
32566
- }),
32567
- components.checkbox.group("sendContent", {
32568
- label: "解析时发送的内容",
32569
- description: "若什么都不选,可能不会返回任何解析结果",
32570
- orientation: "horizontal",
32571
- defaultValue: all.bilibili.sendContent,
32572
- isDisabled: !all.bilibili.switch,
32573
- checkbox: [
32574
- components.checkbox.create("sendContent:checkbox:1", {
32575
- label: "视频信息",
32576
- value: "info",
32577
- description: "仅解析视频时有效"
32578
- }),
32579
- components.checkbox.create("sendContent:checkbox:2", {
32580
- label: "评论列表",
32581
- value: "comment"
32582
- }),
32583
- components.checkbox.create("sendContent:checkbox:3", {
32584
- label: "视频文件",
32585
- value: "video",
32586
- description: "仅对视频稿件有效"
32587
- })
32588
- ]
32589
- }),
32590
- components.divider.create("divider-bilibili-comment", {
32591
- description: "评论详情设置",
32592
- descPosition: 20
32593
- }),
32594
- components.input.number("numcomment", {
32595
- label: "评论解析数量",
32596
- defaultValue: all.bilibili.numcomment.toString(),
32597
- rules: [{ min: 1 }],
32598
- isDisabled: !all.bilibili.sendContent.some((content) => content === "comment") || !all.bilibili.switch
32599
- }),
32600
- components.switch.create("commentImageCollection", {
32601
- label: "是否收集评论区的图片",
32602
- description: "开启后将收集评论区的图片,以合并转发的形式返回",
32603
- defaultSelected: all.bilibili.commentImageCollection,
32604
- isDisabled: !all.bilibili.sendContent.includes("comment") || !all.bilibili.switch
32605
- }),
32606
- components.switch.create("realCommentCount", {
32607
- label: "显示真实评论数量",
32608
- description: "评论图是否显示真实评论数量,关闭则显示解析到的评论数量",
32609
- defaultSelected: all.bilibili.realCommentCount,
32610
- isDisabled: !all.bilibili.sendContent.some((content) => content === "comment") || !all.bilibili.switch
32611
- }),
32612
- components.divider.create("divider-bilibili-render", {
32613
- description: "渲染与画质设置",
32614
- descPosition: 20
32615
- }),
32616
- components.radio.group("imageLayout", {
32617
- label: "解析图文动态时,遇到多张图片时的页面布局方式(动态推送图片也生效)",
32618
- description: "自动布局:少于4张时逐张上下排列;4~8张时瀑布流;9张及以上九宫格",
32619
- orientation: "horizontal",
32620
- defaultValue: all.bilibili.imageLayout,
32621
- radio: [
32622
- components.radio.create("imageLayout:radio-4", {
32623
- label: "自动布局",
32624
- value: "auto"
32625
- }),
32626
- components.radio.create("imageLayout:radio-1", {
32627
- label: "逐张上下排列",
32628
- value: "vertical"
32629
- }),
32630
- components.radio.create("imageLayout:radio-2", {
32631
- label: "瀑布流排列",
32632
- value: "waterfall"
32633
- }),
32634
- components.radio.create("imageLayout:radio-3", {
32635
- label: "九宫格排列",
32636
- value: "grid"
32637
- })
32638
- ]
32639
- }),
32640
- components.radio.group("videoQuality", {
32641
- label: "画质偏好",
32642
- description: "解析视频的分辨率偏好。",
32643
- orientation: "horizontal",
32644
- defaultValue: all.bilibili.videoQuality.toString(),
32645
- isDisabled: !all.bilibili.switch,
32646
- radio: [
32647
- components.radio.create("videoQuality:parse:radio-1", {
32648
- label: "自动选择",
32649
- value: "0"
32650
- }),
32651
- components.radio.create("videoQuality:parse:radio-2", {
32652
- label: "240P 极速",
32653
- value: "6"
32654
- }),
32655
- components.radio.create("videoQuality:parse:radio-3", {
32656
- label: "360P 流畅",
32657
- value: "16"
32658
- }),
32659
- components.radio.create("videoQuality:parse:radio-4", {
32660
- label: "480P 清晰",
32661
- value: "32",
32662
- description: "需登录(配置ck)"
32663
- }),
32664
- components.radio.create("videoQuality:parse:radio-5", {
32665
- label: "720P 高清",
32666
- value: "64",
32667
- description: "需登录(配置ck)"
32668
- }),
32669
- components.radio.create("videoQuality:parse:radio-6", {
32670
- label: "720P60 高帧率",
32671
- value: "74",
32672
- description: "需登录(配置ck)"
32673
- }),
32674
- components.radio.create("videoQuality:parse:radio-7", {
32675
- label: "1080P 高清",
32676
- value: "80",
32677
- description: "需登录(配置ck)"
32678
- }),
32679
- components.radio.create("videoQuality:parse:radio-8", {
32680
- label: "1080P+ 高码率",
32681
- value: "112",
32682
- description: "需大会员&视频支持"
32683
- }),
32684
- components.radio.create("videoQuality:parse:radio-9", {
32685
- label: "1080P60 高帧率",
32686
- value: "116",
32687
- description: "需大会员&视频支持"
32688
- }),
32689
- components.radio.create("videoQuality:parse:radio-10", {
32690
- label: "4K 超清",
32691
- value: "120",
32692
- description: "需大会员&视频支持"
32693
- }),
32694
- components.radio.create("videoQuality:parse:radio-11", {
32695
- label: "8K 超高清",
32696
- value: "127",
32697
- description: "需大会员&视频支持"
32698
- })
32699
- ]
32700
- }),
32701
- components.input.number("maxAutoVideoSize", {
32702
- label: "视频体积上限(MB)",
32703
- description: "根据该值自动选择分辨率进行下载。仅在「画质偏好」 为 \"自动选择\" 时生效",
32704
- defaultValue: all.bilibili.maxAutoVideoSize.toString(),
32705
- isDisabled: all.bilibili.videoQuality !== 0 || !all.bilibili.switch,
32706
- rules: [{
32707
- min: 1,
32708
- max: 2e4
32709
- }]
32710
- }),
32711
- components.radio.group("videoInfoMode", {
32712
- label: "视频信息返回形式",
32713
- defaultValue: all.bilibili.videoInfoMode,
32714
- isDisabled: !all.bilibili.switch,
32715
- radio: [components.radio.create("videoInfoMode:radio-2", {
32716
- label: "图片模式",
32717
- value: "image"
32718
- }), components.radio.create("videoInfoMode:radio-1", {
32719
- label: "文本模式",
32720
- value: "text"
32721
- })]
32722
- }),
32723
- components.checkbox.group("displayContent", {
32724
- label: "视频信息前返回的内容",
32725
- description: "若什么都不选,则不会返回任何视频相关信息",
32726
- orientation: "horizontal",
32727
- defaultValue: all.bilibili.displayContent,
32728
- isDisabled: !all.bilibili.switch || all.bilibili.switch && all.bilibili.videoInfoMode === "image",
32729
- checkbox: [
32730
- components.checkbox.create("displayContent:checkbox:1", {
32731
- label: "封面",
32732
- value: "cover"
32733
- }),
32734
- components.checkbox.create("displayContent:checkbox:2", {
32735
- label: "标题",
32736
- value: "title"
32737
- }),
32738
- components.checkbox.create("displayContent:checkbox:3", {
32739
- label: "作者",
32740
- value: "author"
32741
- }),
32742
- components.checkbox.create("displayContent:checkbox:4", {
32743
- label: "视频统计信息",
32744
- value: "stats"
32745
- }),
32746
- components.checkbox.create("displayContent:checkbox:5", {
32747
- label: "简介",
32748
- value: "desc"
32749
- })
32750
- ]
32751
- }),
32752
- components.divider.create("divider-bilibili-permission", {
32753
- description: "权限设置",
32754
- descPosition: 20
32755
- }),
32756
- components.radio.group("loginPerm", {
32757
- label: "谁可以触发扫码登录",
32758
- description: "修改后需重启",
32759
- orientation: "horizontal",
32760
- defaultValue: all.bilibili.loginPerm,
32761
- radio: [
32762
- components.radio.create("permission:radio-1", {
32763
- label: "所有人",
32764
- value: "all"
32765
- }),
32766
- components.radio.create("permission:radio-2", {
32767
- label: "管理员",
32768
- value: "admin"
32769
- }),
32770
- components.radio.create("permission:radio-3", {
32771
- label: "主人",
32772
- value: "master"
32773
- }),
32774
- components.radio.create("permission:radio-4", {
32775
- label: "群主",
32776
- value: "group.owner"
32777
- }),
32778
- components.radio.create("permission:radio-5", {
32779
- label: "群管理员",
32780
- value: "group.admin"
32781
- })
32782
- ]
32783
- }),
32784
- components.divider.create("divider-bilibili-danmaku", {
32785
- description: "弹幕烧录相关",
32786
- descPosition: 20
32787
- }),
32788
- components.switch.create("burnDanmaku", {
32789
- label: "弹幕烧录",
32790
- description: "将弹幕硬编码到视频画面中。开启后视频需要重新编码,耗时较长,高分辨率视频会占用较多资源",
32791
- defaultSelected: all.bilibili.burnDanmaku,
32792
- isDisabled: !all.bilibili.switch
32793
- }),
32794
- components.radio.group("danmakuArea", {
32795
- label: "弹幕区域",
32796
- description: "限制弹幕的显示范围,避免遮挡视频主体内容",
32797
- orientation: "horizontal",
32798
- defaultValue: all.bilibili.danmakuArea.toString(),
32799
- isDisabled: !all.bilibili.switch || !all.bilibili.burnDanmaku,
32800
- radio: [
32801
- components.radio.create("danmakuArea:radio-1", {
32802
- label: "1/4 屏",
32803
- value: "0.25",
32804
- description: "仅顶部区域"
32805
- }),
32806
- components.radio.create("danmakuArea:radio-2", {
32807
- label: "半屏",
32808
- value: "0.5",
32809
- description: "上半部分(推荐)"
32810
- }),
32811
- components.radio.create("danmakuArea:radio-3", {
32812
- label: "3/4 屏",
32813
- value: "0.75",
32814
- description: "大部分区域"
32815
- }),
32816
- components.radio.create("danmakuArea:radio-4", {
32817
- label: "全屏",
32818
- value: "1",
32819
- description: "铺满整个画面"
32820
- })
32821
- ]
32822
- }),
32823
- components.radio.group("danmakuFontSize", {
32824
- label: "弹幕字号",
32825
- description: "弹幕文字大小",
32826
- orientation: "horizontal",
32827
- defaultValue: all.bilibili.danmakuFontSize,
32828
- isDisabled: !all.bilibili.switch || !all.bilibili.burnDanmaku,
32829
- radio: [
32830
- components.radio.create("danmakuFontSize:radio-1", {
32831
- label: "小",
32832
- value: "small"
32833
- }),
32834
- components.radio.create("danmakuFontSize:radio-2", {
32835
- label: "中",
32836
- value: "medium"
32837
- }),
32838
- components.radio.create("danmakuFontSize:radio-3", {
32839
- label: "大",
32840
- value: "large"
32841
- })
32842
- ]
32843
- }),
32844
- components.input.number("danmakuOpacity", {
32845
- label: "弹幕透明度",
32846
- description: "0为完全透明,100为完全不透明,推荐70",
32847
- defaultValue: all.bilibili.danmakuOpacity.toString(),
32848
- isDisabled: !all.bilibili.switch || !all.bilibili.burnDanmaku,
32849
- rules: [{
32850
- min: 0,
32851
- max: 100
32852
- }]
32853
- }),
32854
- components.radio.group("verticalMode", {
32855
- label: "竖屏适配",
32856
- description: "模拟手机端竖屏观看体验,视频居中显示,上下黑边区域用于展示弹幕",
32857
- orientation: "horizontal",
32858
- defaultValue: all.bilibili.verticalMode,
32859
- isDisabled: !all.bilibili.switch || !all.bilibili.burnDanmaku,
32860
- radio: [
32861
- components.radio.create("verticalMode:radio-1", {
32862
- label: "关闭",
32863
- value: "off",
32864
- description: "保持原始比例,不做转换"
32865
- }),
32866
- components.radio.create("verticalMode:radio-2", {
32867
- label: "智能",
32868
- value: "standard",
32869
- description: "仅对 16:9、21:9 等常见宽屏比例生效"
32870
- }),
32871
- components.radio.create("verticalMode:radio-3", {
32872
- label: "强制 9:16",
32873
- value: "force",
32874
- description: "所有视频统一转为 9:16 竖屏,弹幕大小一致"
32875
- })
32876
- ]
32877
- }),
32878
- components.radio.group("videoCodec", {
32879
- label: "视频编码格式",
32880
- description: "弹幕烧录时使用的视频编码格式,会自动检测硬件加速",
32881
- orientation: "horizontal",
32882
- defaultValue: all.bilibili.videoCodec,
32883
- isDisabled: !all.bilibili.switch || !all.bilibili.burnDanmaku,
32884
- radio: [
32885
- components.radio.create("videoCodec:radio-1", {
32886
- label: "H.264",
32887
- value: "h264",
32888
- description: "兼容性最好,支持几乎所有设备"
32889
- }),
32890
- components.radio.create("videoCodec:radio-2", {
32891
- label: "H.265",
32892
- value: "h265",
32893
- description: "压缩率更高,近几年设备支持良好(推荐)"
32894
- }),
32895
- components.radio.create("videoCodec:radio-3", {
32896
- label: "AV1",
32897
- value: "av1",
32898
- description: "最新编码格式,压缩率最高,但编码较慢"
32899
- })
32900
- ]
32901
- }),
32902
- components.divider.create("divider-bilibili-1", {
32903
- description: "B站推送相关",
32904
- descPosition: 20
32905
- }),
32906
- components.switch.create("push:switch", {
32907
- label: "推送开关",
32908
- description: "推送开关,修改后需重启;使用「#设置B站推送 + UID」配置推送列表",
32909
- defaultSelected: all.bilibili.push.switch,
32910
- color: "warning"
32911
- }),
32912
- components.radio.group("push:permission", {
32913
- label: "谁可以设置推送",
32914
- description: "修改后需重启",
32915
- orientation: "horizontal",
32916
- defaultValue: all.bilibili.push.permission,
32917
- color: "warning",
32918
- isDisabled: !all.bilibili.push.switch,
32919
- radio: [
32920
- components.radio.create("push:permission:radio-1", {
32921
- label: "所有人",
32922
- value: "all"
32923
- }),
32924
- components.radio.create("push:permission:radio-2", {
32925
- label: "管理员",
32926
- value: "admin"
32927
- }),
32928
- components.radio.create("push:permission:radio-3", {
32929
- label: "主人",
32930
- value: "master"
32931
- }),
32932
- components.radio.create("push:permission:radio-4", {
32933
- label: "群主",
32934
- value: "group.owner"
32935
- }),
32936
- components.radio.create("push:permission:radio-5", {
32937
- label: "群管理员",
32938
- value: "group.admin"
32939
- })
32940
- ]
32941
- }),
32942
- components.input.string("push:cron", {
32943
- label: "定时任务表达式",
32944
- description: "定时推送的时间,格式为cron表达式(默认为每十分钟执行一次)",
32945
- defaultValue: all.bilibili.push.cron,
32946
- color: "warning",
32947
- isDisabled: !all.bilibili.push.switch
32948
- }),
32949
- components.switch.create("push:parsedynamic", {
32950
- label: "作品解析",
32951
- description: "触发推送时是否一同解析该作品",
32952
- defaultSelected: all.bilibili.push.parsedynamic,
32953
- color: "warning",
32954
- isDisabled: !all.bilibili.push.switch
32955
- }),
32956
- components.radio.group("push:pushVideoQuality", {
32957
- label: "解析视频动态时的画质偏好",
32958
- description: "「作品解析」开启时生效,仅对视频动态有效",
32959
- orientation: "horizontal",
32960
- isDisabled: !all.bilibili.push.parsedynamic || !all.bilibili.push.switch,
32961
- defaultValue: all.bilibili.push.pushVideoQuality.toString(),
32962
- color: "warning",
32963
- radio: [
32964
- components.radio.create("push:pushVideoQuality:radio-1", {
32965
- label: "自动选择",
32966
- value: "0"
32967
- }),
32968
- components.radio.create("push:pushVideoQuality:radio-2", {
32969
- label: "240P 极速",
32970
- value: "6"
32971
- }),
32972
- components.radio.create("push:pushVideoQuality:radio-3", {
32973
- label: "360P 流畅",
32974
- value: "16"
32975
- }),
32976
- components.radio.create("push:pushVideoQuality:radio-4", {
32977
- label: "480P 清晰",
32978
- value: "32",
32979
- description: "需登录(配置ck)"
32980
- }),
32981
- components.radio.create("push:pushVideoQuality:radio-5", {
32982
- label: "720P 高清",
32983
- value: "64",
32984
- description: "需登录(配置ck)"
32985
- }),
32986
- components.radio.create("push:pushVideoQuality:radio-6", {
32987
- label: "720P60 高帧率",
32988
- value: "74",
32989
- description: "需登录(配置ck)"
32990
- }),
32991
- components.radio.create("push:pushVideoQuality:radio-7", {
32992
- label: "1080P 高清",
32993
- value: "80",
32994
- description: "需登录(配置ck)"
32995
- }),
32996
- components.radio.create("push:pushVideoQuality:radio-8", {
32997
- label: "1080P+ 高码率",
32998
- value: "112",
32999
- description: "需大会员&视频支持"
33000
- }),
33001
- components.radio.create("push:pushVideoQuality:radio-9", {
33002
- label: "1080P60 高帧率",
33003
- value: "116",
33004
- description: "需大会员&视频支持"
33005
- }),
33006
- components.radio.create("push:pushVideoQuality:radio-10", {
33007
- label: "4K 超清",
33008
- value: "120",
33009
- description: "需大会员&视频支持"
33010
- }),
33011
- components.radio.create("push:pushVideoQuality:radio-11", {
33012
- label: "8K 超高清",
33013
- value: "127",
33014
- description: "需大会员&视频支持"
33015
- })
33016
- ]
33017
- }),
33018
- components.input.number("push:pushMaxAutoVideoSize", {
33019
- label: "视频动态的视频体积上限(MB)",
33020
- description: "根据该值自动选择分辨率进行下载。仅在「解析视频动态时的画质偏好」 为 \"自动选择\" 且「作品解析」开启时生效,仅对视频动态有效",
33021
- defaultValue: all.bilibili.push.pushMaxAutoVideoSize.toString(),
33022
- isDisabled: !all.bilibili.push.parsedynamic || all.bilibili.push.pushVideoQuality !== 0 || !all.bilibili.push.switch,
33023
- rules: [{
33024
- min: 1,
33025
- max: 2e4
33026
- }],
33027
- color: "warning"
33028
- })
33029
- ]
33030
- })]
33031
- })];
33032
- };
33033
- //#endregion
33034
- //#region src/platform/douyin/web.config.ts
33035
- /**
33036
- * 抖音配置组件
33037
- * @param all 配置
33038
- */
33039
- var DouyinWeb = (all) => {
33040
- return [components.accordion.create("douyin", {
33041
- label: "抖音相关",
33042
- children: [components.accordion.createItem("cfg:douyin", {
33043
- title: "抖音相关",
33044
- className: "ml-4 mr-4",
33045
- subtitle: "此处为抖音相关的用户偏好设置",
33046
- children: [
33047
- components.switch.create("switch", {
33048
- label: "解析开关",
33049
- description: "抖音解析开关,此开关为单独开关",
33050
- defaultSelected: all.douyin.switch
33051
- }),
33052
- components.checkbox.group("sendContent", {
33053
- label: "解析时发送的内容",
33054
- description: "若什么都不选,可能不会返回任何解析结果",
33055
- orientation: "horizontal",
33056
- defaultValue: all.douyin.sendContent,
33057
- isDisabled: !all.douyin.switch,
33058
- checkbox: [
33059
- components.checkbox.create("sendContent:checkbox:1", {
33060
- label: "视频信息",
33061
- value: "info",
33062
- description: "仅解析视频时有效"
33063
- }),
33064
- components.checkbox.create("sendContent:checkbox:2", {
33065
- label: "评论列表",
33066
- value: "comment"
33067
- }),
33068
- components.checkbox.create("sendContent:checkbox:3", {
33069
- label: "视频文件",
33070
- value: "video",
33071
- description: "仅对视频作品有效"
33072
- })
33073
- ]
33074
- }),
33075
- components.divider.create("divider-dy-comment", {
33076
- description: "评论详情设置",
33077
- descPosition: 20
33078
- }),
33079
- components.input.number("numcomment", {
33080
- label: "评论解析数量",
33081
- defaultValue: all.douyin.numcomment.toString(),
33082
- rules: [{ min: 1 }],
33083
- isDisabled: !all.douyin.sendContent.includes("comment") || !all.douyin.switch
33084
- }),
33085
- components.input.number("subCommentLimit", {
33086
- label: "次级评论解析数量",
33087
- description: "次级评论解析数量,当前逻辑不仅无法判断请求的来的评论的嵌套深度,而且「次级评论解析深度」会限制嵌套深度,超过深度的评论会被截断",
33088
- defaultValue: all.douyin.subCommentLimit.toString(),
33089
- rules: [{
33090
- min: 1,
33091
- max: 20
33092
- }],
33093
- isDisabled: !all.douyin.sendContent.includes("comment") || !all.douyin.switch
33094
- }),
33095
- components.switch.create("commentImageCollection", {
33096
- label: "是否收集评论区的图片",
33097
- description: "开启后将收集评论区的图片,以合并转发的形式返回",
33098
- defaultSelected: all.douyin.commentImageCollection,
33099
- isDisabled: !all.douyin.sendContent.includes("comment") || !all.douyin.switch
33100
- }),
33101
- components.divider.create("divider-dy-render", {
33102
- description: "渲染与画质设置",
33103
- descPosition: 20
33104
- }),
33105
- components.radio.group("liveImageMergeMode", {
33106
- label: "合辑 Live 图 BGM 合并方式",
33107
- orientation: "horizontal",
33108
- defaultValue: all.douyin.liveImageMergeMode.toString(),
33109
- isDisabled: !all.douyin.switch || all.app.livePhotoMode === "livephoto_only",
33110
- radio: [components.radio.create("liveImageMergeMode:radio-1", {
33111
- label: "连续",
33112
- value: "continuous",
33113
- description: "BGM 接续播放,结束后自动循环"
33114
- }), components.radio.create("liveImageMergeMode:radio-2", {
33115
- label: "独立",
33116
- value: "independent",
33117
- description: "每张图 BGM 从头开始"
33118
- })]
33119
- }),
33120
- components.radio.group("videoQuality", {
33121
- label: "画质偏好",
33122
- description: "解析视频的分辨率偏好。",
33123
- orientation: "horizontal",
33124
- defaultValue: all.douyin.videoQuality.toString(),
33125
- isDisabled: !all.douyin.switch,
33126
- radio: [
33127
- components.radio.create("videoQuality:parse:radio-1", {
33128
- label: "自动选择",
33129
- value: "adapt",
33130
- description: "根据「视频体积上限(MB)」自动选择分辨率进行下载"
33131
- }),
33132
- components.radio.create("videoQuality:parse:radio-2", {
33133
- label: "标清 540p",
33134
- value: "540p"
33135
- }),
33136
- components.radio.create("videoQuality:parse:radio-3", {
33137
- label: "高清 720p",
33138
- value: "720p"
33139
- }),
33140
- components.radio.create("videoQuality:parse:radio-4", {
33141
- label: "高清 1080p",
33142
- value: "1080p"
33143
- }),
33144
- components.radio.create("videoQuality:parse:radio-5", {
33145
- label: "超清 2k",
33146
- value: "2k"
33147
- }),
33148
- components.radio.create("videoQuality:parse:radio-6", {
33149
- label: "超清 4k",
33150
- value: "4k"
33151
- })
33152
- ]
33153
- }),
33154
- components.input.number("maxAutoVideoSize", {
33155
- label: "视频体积上限(MB)",
33156
- description: "根据该值自动选择分辨率进行下载。仅在「画质偏好」 为 \"自动选择\" 时生效",
33157
- defaultValue: all.douyin.maxAutoVideoSize.toString(),
33158
- isDisabled: all.douyin.videoQuality !== "adapt" || !all.douyin.switch,
33159
- rules: [{
33160
- min: 1,
33161
- max: 2e4
33162
- }]
33163
- }),
33164
- components.radio.group("videoInfoMode", {
33165
- label: "视频信息返回形式",
33166
- defaultValue: all.douyin.videoInfoMode,
33167
- isDisabled: !all.douyin.switch,
33168
- radio: [components.radio.create("videoInfoMode:radio-2", {
33169
- label: "图片模式",
33170
- value: "image"
33171
- }), components.radio.create("videoInfoMode:radio-1", {
33172
- label: "文本模式",
33173
- value: "text"
33174
- })]
33175
- }),
33176
- components.checkbox.group("displayContent", {
33177
- label: "视频信息的内容",
33178
- description: "若什么都不选,则不会返回任何视频相关信息",
33179
- orientation: "horizontal",
33180
- defaultValue: all.douyin.displayContent,
33181
- isDisabled: !all.douyin.switch || all.douyin.switch && all.douyin.videoInfoMode === "image",
33182
- checkbox: [
33183
- components.checkbox.create("displayContent:checkbox:1", {
33184
- label: "封面",
33185
- value: "cover"
33186
- }),
33187
- components.checkbox.create("displayContent:checkbox:2", {
33188
- label: "标题",
33189
- value: "title"
33190
- }),
33191
- components.checkbox.create("displayContent:checkbox:3", {
33192
- label: "作者",
33193
- value: "author"
33194
- }),
33195
- components.checkbox.create("displayContent:checkbox:4", {
33196
- label: "视频统计信息",
33197
- value: "stats"
33198
- })
33199
- ]
33200
- }),
33201
- components.divider.create("divider-dy-permission", {
33202
- description: "权限设置",
33203
- descPosition: 20
33204
- }),
33205
- components.radio.group("loginPerm", {
33206
- label: "谁可以触发扫码登录",
33207
- description: "修改后需重启",
33208
- orientation: "horizontal",
33209
- defaultValue: all.douyin.loginPerm,
33210
- radio: [
33211
- components.radio.create("permission:radio-1", {
33212
- label: "所有人",
33213
- value: "all"
33214
- }),
33215
- components.radio.create("permission:radio-2", {
33216
- label: "管理员",
33217
- value: "admin"
33218
- }),
33219
- components.radio.create("permission:radio-3", {
33220
- label: "主人",
33221
- value: "master"
33222
- }),
33223
- components.radio.create("permission:radio-4", {
33224
- label: "群主",
33225
- value: "group.owner"
33226
- }),
33227
- components.radio.create("permission:radio-5", {
33228
- label: "群管理员",
33229
- value: "group.admin"
33230
- })
33231
- ]
33232
- }),
33233
- components.divider.create("divider-dy-danmaku", {
33234
- description: "弹幕烧录相关",
33235
- descPosition: 20
33236
- }),
33237
- components.switch.create("burnDanmaku", {
33238
- label: "弹幕烧录",
33239
- description: "将弹幕硬编码到视频画面中。开启后视频需要重新编码,耗时较长",
33240
- defaultSelected: all.douyin.burnDanmaku,
33241
- isDisabled: !all.douyin.switch
33242
- }),
33243
- components.radio.group("danmakuArea", {
33244
- label: "弹幕显示区域",
33245
- description: "限制弹幕范围,避免遮挡视频主体",
33246
- orientation: "horizontal",
33247
- defaultValue: all.douyin.danmakuArea.toString(),
33248
- isDisabled: !all.douyin.switch || !all.douyin.burnDanmaku,
33249
- radio: [
33250
- components.radio.create("danmakuArea:radio-1", {
33251
- label: "1/4 屏",
33252
- value: "0.25"
33253
- }),
33254
- components.radio.create("danmakuArea:radio-2", {
33255
- label: "半屏",
33256
- value: "0.5"
33257
- }),
33258
- components.radio.create("danmakuArea:radio-3", {
33259
- label: "3/4 屏",
33260
- value: "0.75"
33261
- }),
33262
- components.radio.create("danmakuArea:radio-4", {
33263
- label: "全屏",
33264
- value: "1"
33265
- })
33266
- ]
33267
- }),
33268
- components.radio.group("danmakuFontSize", {
33269
- label: "弹幕字号",
33270
- description: "弹幕文字大小",
33271
- orientation: "horizontal",
33272
- defaultValue: all.douyin.danmakuFontSize,
33273
- isDisabled: !all.douyin.switch || !all.douyin.burnDanmaku,
33274
- radio: [
33275
- components.radio.create("danmakuFontSize:radio-1", {
33276
- label: "小",
33277
- value: "small"
33278
- }),
33279
- components.radio.create("danmakuFontSize:radio-2", {
33280
- label: "中",
33281
- value: "medium"
33282
- }),
33283
- components.radio.create("danmakuFontSize:radio-3", {
33284
- label: "大",
33285
- value: "large"
33286
- })
33287
- ]
33288
- }),
33289
- components.input.number("danmakuOpacity", {
33290
- label: "弹幕透明度",
33291
- description: "0为完全透明,100为完全不透明,推荐70",
33292
- defaultValue: all.douyin.danmakuOpacity.toString(),
33293
- isDisabled: !all.douyin.switch || !all.douyin.burnDanmaku,
33294
- rules: [{
33295
- min: 0,
33296
- max: 100
33297
- }]
33298
- }),
33299
- components.radio.group("verticalMode", {
33300
- label: "竖屏适配",
33301
- description: "针对横屏视频,模拟手机端竖屏观看体验,视频居中显示,上下黑边区域用于展示弹幕",
33302
- orientation: "horizontal",
33303
- defaultValue: all.douyin.verticalMode,
33304
- isDisabled: !all.douyin.switch || !all.douyin.burnDanmaku,
33305
- radio: [
33306
- components.radio.create("verticalMode:radio-1", {
33307
- label: "关闭",
33308
- value: "off",
33309
- description: "保持原始比例,不做转换"
33310
- }),
33311
- components.radio.create("verticalMode:radio-2", {
33312
- label: "智能",
33313
- value: "standard",
33314
- description: "仅对宽高比 ≥1.7 的横屏视频生效"
33315
- }),
33316
- components.radio.create("verticalMode:radio-3", {
33317
- label: "强制 9:16",
33318
- value: "force",
33319
- description: "所有视频统一转为竖屏"
33320
- })
33321
- ]
33322
- }),
33323
- components.radio.group("videoCodec", {
33324
- label: "视频编码格式",
33325
- description: "烧录弹幕后的视频编码格式",
33326
- orientation: "horizontal",
33327
- defaultValue: all.douyin.videoCodec,
33328
- isDisabled: !all.douyin.switch || !all.douyin.burnDanmaku,
33329
- radio: [
33330
- components.radio.create("videoCodec:radio-1", {
33331
- label: "H.264",
33332
- value: "h264"
33333
- }),
33334
- components.radio.create("videoCodec:radio-2", {
33335
- label: "H.265",
33336
- value: "h265"
33337
- }),
33338
- components.radio.create("videoCodec:radio-3", {
33339
- label: "AV1",
33340
- value: "av1"
33341
- })
33342
- ]
33343
- }),
33344
- components.divider.create("divider-dy-1", {
33345
- description: "抖音推送相关",
33346
- descPosition: 20
33347
- }),
33348
- components.switch.create("push:switch", {
33349
- label: "推送开关",
33350
- description: "推送开关,修改后需重启;使用「#设置抖音推送 + 抖音号」配置推送列表",
33351
- defaultSelected: all.douyin.push.switch,
33352
- color: "warning"
33353
- }),
33354
- components.radio.group("push:permission", {
33355
- label: "谁可以设置推送",
33356
- description: "修改后需重启",
33357
- orientation: "horizontal",
33358
- defaultValue: all.douyin.push.permission,
33359
- isDisabled: !all.douyin.push.switch,
33360
- color: "warning",
33361
- radio: [
33362
- components.radio.create("push:permission:radio-1", {
33363
- label: "所有人",
33364
- value: "all"
33365
- }),
33366
- components.radio.create("push:permission:radio-2", {
33367
- label: "管理员",
33368
- value: "admin"
33369
- }),
33370
- components.radio.create("push:permission:radio-3", {
33371
- label: "主人",
33372
- value: "master"
33373
- }),
33374
- components.radio.create("push:permission:radio-4", {
33375
- label: "群主",
33376
- value: "group.owner"
33377
- }),
33378
- components.radio.create("push:permission:radio-5", {
33379
- label: "群管理员",
33380
- value: "group.admin"
33381
- })
33382
- ]
33383
- }),
33384
- components.input.string("push:cron", {
33385
- label: "定时任务表达式",
33386
- description: "定时推送的时间,格式为cron表达式(默认为每十分钟执行一次)",
33387
- defaultValue: all.douyin.push.cron,
33388
- color: "warning",
33389
- isDisabled: !all.douyin.push.switch
33390
- }),
33391
- components.switch.create("push:parsedynamic", {
33392
- label: "作品解析",
33393
- description: "触发推送时是否一同解析该作品",
33394
- defaultSelected: all.douyin.push.parsedynamic,
33395
- color: "warning",
33396
- isDisabled: !all.douyin.push.switch
33397
- }),
33398
- components.radio.group("push:shareType", {
33399
- label: "推送图二维码的类型",
33400
- orientation: "horizontal",
33401
- defaultValue: all.douyin.push.shareType,
33402
- color: "warning",
33403
- isDisabled: !all.douyin.push.switch,
33404
- radio: [components.radio.create("push:shareType.radio-1", {
33405
- label: "网页链接",
33406
- description: "识别后访问抖音官网对应的作品地址",
33407
- value: "web"
33408
- }), components.radio.create("push:shareType.radio-2", {
33409
- description: "识别后访问无水印作品下载地址",
33410
- label: "下载链接",
33411
- value: "download"
33412
- })]
33413
- }),
33414
- components.radio.group("push:pushVideoQuality", {
33415
- label: "画质偏好",
33416
- description: "推送解析时解析视频的分辨率偏好。",
33417
- orientation: "horizontal",
33418
- defaultValue: all.douyin.push.pushVideoQuality.toString(),
33419
- isDisabled: !all.douyin.push.switch,
33420
- color: "warning",
33421
- radio: [
33422
- components.radio.create("push:pushVideoQuality:radio-1", {
33423
- label: "自动选择",
33424
- value: "adapt",
33425
- description: "根据「视频体积上限(MB)」自动选择分辨率进行下载"
33426
- }),
33427
- components.radio.create("push:pushVideoQuality:radio-2", {
33428
- label: "标清 540p",
33429
- value: "540p"
33430
- }),
33431
- components.radio.create("push:pushVideoQuality:radio-3", {
33432
- label: "高清 720p",
33433
- value: "720p"
33434
- }),
33435
- components.radio.create("push:pushVideoQuality:radio-4", {
33436
- label: "高清 1080p",
33437
- value: "1080p"
33438
- }),
33439
- components.radio.create("push:pushVideoQuality:radio-5", {
33440
- label: "超清 2k",
33441
- value: "2k"
33442
- }),
33443
- components.radio.create("push:pushVideoQuality:radio-6", {
33444
- label: "超清 4k",
33445
- value: "4k"
33446
- })
33447
- ]
33448
- }),
33449
- components.input.number("push:pushMaxAutoVideoSize", {
33450
- label: "视频体积上限(MB)",
33451
- color: "warning",
33452
- description: "推送解析时根据该值自动选择分辨率进行下载。仅在「画质偏好」 为 \"自动选择\" 时生效",
33453
- defaultValue: all.douyin.push.pushMaxAutoVideoSize.toString(),
33454
- isDisabled: all.douyin.push.pushVideoQuality !== "adapt" || !all.douyin.push.switch,
33455
- rules: [{
33456
- min: 1,
33457
- max: 2e4
33458
- }]
33459
- })
33460
- ]
33461
- })]
33462
- })];
33463
- };
33464
- //#endregion
33465
- //#region src/platform/kuaishou/web.config.ts
33466
- /**
33467
- * 快手配置组件
33468
- * @param all 配置
33469
- */
33470
- var KuaishouWeb = (all) => {
33471
- return [components.accordion.create("kuaishou", {
33472
- label: "快手相关",
33473
- children: [components.accordion.createItem("cfg:kuaishou", {
33474
- title: "快手相关",
33475
- className: "ml-4 mr-4",
33476
- subtitle: "此处为快手相关的用户偏好设置",
33477
- children: [
33478
- components.switch.create("switch", {
33479
- label: "解析开关",
33480
- description: "快手解析开关,此开关为单独开关",
33481
- defaultSelected: all.kuaishou.switch
33482
- }),
33483
- components.divider.create("divider-kuaishou-comment", {
33484
- description: "评论详情设置",
33485
- descPosition: 20
33486
- }),
33487
- components.switch.create("comment", {
33488
- label: "评论解析",
33489
- description: "快手评论解析,开启后可发送快手作品评论图",
33490
- defaultSelected: all.kuaishou.comment,
33491
- isDisabled: !all.kuaishou.switch
33492
- }),
33493
- components.input.number("numcomment", {
33494
- label: "评论解析数量",
33495
- defaultValue: all.kuaishou.numcomment.toString(),
33496
- rules: [{ min: 1 }],
33497
- isDisabled: !all.kuaishou.switch || !all.kuaishou.comment
33498
- })
33499
- ]
33500
- })]
33501
- })];
33502
- };
33503
- //#endregion
33504
- //#region src/platform/xiaohongshu/web.config.ts
33505
- /**
33506
- * 小红书配置组件
33507
- * @param all 配置
33508
- */
33509
- var XiaohongshuWeb = (all) => {
33510
- return [components.accordion.create("xiaohongshu", {
33511
- label: "小红书相关",
33512
- children: [components.accordion.createItem("cfg:xiaohongshu", {
33513
- title: "小红书相关",
33514
- className: "ml-4 mr-4",
33515
- subtitle: "此处为小红书相关的用户偏好设置",
33516
- children: [
33517
- components.switch.create("switch", {
33518
- label: "解析开关",
33519
- description: "小红书解析开关,此开关为单独开关",
33520
- defaultSelected: all.xiaohongshu.switch
33521
- }),
33522
- components.checkbox.group("sendContent", {
33523
- label: "解析时发送的内容",
33524
- description: "若什么都不选,可能不会返回任何解析结果",
33525
- orientation: "horizontal",
33526
- defaultValue: all.xiaohongshu.sendContent,
33527
- isDisabled: !all.xiaohongshu.switch,
33528
- checkbox: [
33529
- components.checkbox.create("sendContent:checkbox:1", {
33530
- label: "笔记信息",
33531
- value: "info"
33532
- }),
33533
- components.checkbox.create("sendContent:checkbox:2", {
33534
- label: "评论列表",
33535
- value: "comment"
33536
- }),
33537
- components.checkbox.create("sendContent:checkbox:3", {
33538
- label: "笔记图片",
33539
- value: "image"
33540
- }),
33541
- components.checkbox.create("sendContent:checkbox:4", {
33542
- label: "视频文件",
33543
- value: "video",
33544
- description: "仅对视频笔记有效"
33545
- })
33546
- ]
33547
- }),
33548
- components.input.number("numcomment", {
33549
- label: "评论解析数量",
33550
- defaultValue: all.xiaohongshu.numcomment.toString(),
33551
- rules: [{ min: 1 }],
33552
- isDisabled: !all.xiaohongshu.sendContent.some((content) => content === "comment") || !all.xiaohongshu.switch
33553
- }),
33554
- components.divider.create("divider-xiaohongshu-render", {
33555
- description: "渲染与画质设置",
33556
- descPosition: 20
33557
- }),
33558
- components.radio.group("videoQuality", {
33559
- label: "画质偏好",
33560
- description: "解析视频的分辨率偏好。",
33561
- orientation: "horizontal",
33562
- defaultValue: all.xiaohongshu.videoQuality.toString(),
33563
- isDisabled: !all.xiaohongshu.switch,
33564
- radio: [
33565
- components.radio.create("videoQuality:radio-1", {
33566
- label: "自动选择",
33567
- value: "adapt",
33568
- description: "根据「视频体积上限(MB)」自动选择分辨率进行下载"
33569
- }),
33570
- components.radio.create("videoQuality:radio-2", {
33571
- label: "标清 540p",
33572
- value: "540p"
33573
- }),
33574
- components.radio.create("videoQuality:radio-3", {
33575
- label: "高清 720p",
33576
- value: "720p"
33577
- }),
33578
- components.radio.create("videoQuality:radio-4", {
33579
- label: "高清 1080p",
33580
- value: "1080p"
33581
- }),
33582
- components.radio.create("videoQuality:radio-5", {
33583
- label: "超清 2k",
33584
- value: "2k"
33585
- }),
33586
- components.radio.create("videoQuality:radio-6", {
33587
- label: "超清 4k",
33588
- value: "4k"
33589
- })
33590
- ]
33591
- }),
33592
- components.input.number("maxAutoVideoSize", {
33593
- label: "视频体积上限(MB)",
33594
- description: "根据该值自动选择分辨率进行下载。仅在「画质偏好」 为 \"自动选择\" 时生效",
33595
- defaultValue: all.xiaohongshu.maxAutoVideoSize.toString(),
33596
- isDisabled: all.xiaohongshu.videoQuality !== "adapt" || !all.xiaohongshu.switch,
33597
- rules: [{
33598
- min: 1,
33599
- max: 2e4
33600
- }]
33601
- })
33602
- ]
33603
- })]
33604
- })];
33605
- };
33606
- //#endregion
33607
32579
  //#region src/web.config.ts
33608
- /**
33609
- * 获取本机局域网 IP 地址
33610
- * 优先返回常见局域网网段的 IP(192.168.x.x, 10.x.x.x, 172.16-31.x.x)
33611
- */
33612
- function getLocalIP$1() {
33613
- const interfaces = os.networkInterfaces();
33614
- const candidates = [];
33615
- for (const name of Object.keys(interfaces)) {
33616
- const netInterface = interfaces[name];
33617
- if (!netInterface) continue;
33618
- for (const net of netInterface) if (net.family === "IPv4" && !net.internal) candidates.push(net.address);
33619
- }
33620
- return candidates.find((ip) => {
33621
- if (ip.startsWith("192.168.")) return true;
33622
- if (ip.startsWith("10.")) return true;
33623
- if (ip.startsWith("172.")) {
33624
- const second = parseInt(ip.split(".")[1], 10);
33625
- if (second >= 16 && second <= 31) return true;
33626
- }
33627
- return false;
33628
- }) || candidates[0] || "127.0.0.1";
33629
- }
33630
32580
  var webConfig = defineConfig({
33631
32581
  info: {
33632
32582
  id: "karin-plugin-kkk",
@@ -33647,1006 +32597,12 @@ var webConfig = defineConfig({
33647
32597
  avatar: "https://github.com/sj817.png"
33648
32598
  }]
33649
32599
  },
33650
- components: async () => {
33651
- const all = await Config.All();
33652
- return [
33653
- components.accordion.create("cookies", {
33654
- label: "Cookies 相关",
33655
- children: [components.accordion.createItem("cfg:cookies", {
33656
- title: "Cookies 相关",
33657
- className: "ml-4 mr-4",
33658
- subtitle: "建议配置,否则大部分功能无法使用",
33659
- children: [
33660
- components.input.string("douyin", {
33661
- label: "抖音",
33662
- type: "text",
33663
- description: "请输入你的抖音Cookies,不输入则无法使用抖音相关功能噢",
33664
- defaultValue: all.cookies.douyin,
33665
- placeholder: "",
33666
- rules: void 0,
33667
- isRequired: false
33668
- }),
33669
- components.input.string("bilibili", {
33670
- label: "B站",
33671
- type: "text",
33672
- description: "请输入你的B站Cookies,不输入部分功能将受限噢",
33673
- defaultValue: all.cookies.bilibili,
33674
- placeholder: "",
33675
- rules: void 0,
33676
- isRequired: false
33677
- }),
33678
- components.input.string("kuaishou", {
33679
- label: "快手",
33680
- type: "text",
33681
- description: "请输入你的快手Cookies,不输入则无法使用快手相关功能噢",
33682
- defaultValue: all.cookies.kuaishou,
33683
- placeholder: "",
33684
- rules: void 0,
33685
- isRequired: false
33686
- }),
33687
- components.input.string("xiaohongshu", {
33688
- label: "小红书",
33689
- type: "text",
33690
- description: "请输入你的小红书Cookies,不输入则无法使用小红书相关功能噢",
33691
- defaultValue: all.cookies.xiaohongshu,
33692
- placeholder: "",
33693
- rules: void 0,
33694
- isRequired: false
33695
- })
33696
- ]
33697
- })]
33698
- }),
33699
- components.accordion.create("app", {
33700
- label: "插件应用相关",
33701
- children: [components.accordion.createItem("cfg:app", {
33702
- title: "插件应用相关",
33703
- className: "ml-4 mr-4",
33704
- subtitle: "此处用于管理插件的基本设置",
33705
- children: [
33706
- components.divider.create("divider-app-cache", {
33707
- description: "缓存设置",
33708
- descPosition: 20
33709
- }),
33710
- components.switch.create("removeCache", {
33711
- label: "缓存删除",
33712
- description: "下载的视频缓存自动删除,非必要不修改!",
33713
- defaultSelected: all.app.removeCache
33714
- }),
33715
- components.divider.create("divider-app-priority", {
33716
- description: "解析优先级设置",
33717
- descPosition: 20
33718
- }),
33719
- components.switch.create("videoTool", {
33720
- label: "默认解析",
33721
- description: "即识别最高优先级,修改后重启生效",
33722
- defaultSelected: all.app.videoTool
33723
- }),
33724
- components.input.number("priority", {
33725
- label: "自定义优先级",
33726
- description: "自定义优先级,「默认解析」关闭后才会生效。修改后重启生效",
33727
- defaultValue: all.app.priority.toString(),
33728
- isDisabled: all.app.videoTool,
33729
- rules: void 0
33730
- }),
33731
- components.divider.create("divider-app-render", {
33732
- description: "渲染配置",
33733
- descPosition: 20
33734
- }),
33735
- components.input.number("renderScale", {
33736
- label: "渲染精度",
33737
- description: "可选值50~200,建议100。设置高精度会提高图片的精细度,过高可能会影响渲染与发送速度",
33738
- defaultValue: all.app.renderScale.toString(),
33739
- rules: [{
33740
- min: 50,
33741
- max: 200
33742
- }]
33743
- }),
33744
- components.radio.group("Theme", {
33745
- label: "渲染图片的主题色",
33746
- orientation: "horizontal",
33747
- defaultValue: all.app.Theme.toString(),
33748
- radio: [
33749
- components.radio.create("Theme-1", {
33750
- label: "自动",
33751
- description: "06:00-18:00为浅色,18:00-06:00为深色",
33752
- value: "0"
33753
- }),
33754
- components.radio.create("Theme-2", {
33755
- label: "浅色",
33756
- value: "1"
33757
- }),
33758
- components.radio.create("Theme-3", {
33759
- label: "深色",
33760
- value: "2"
33761
- })
33762
- ]
33763
- }),
33764
- components.switch.create("RemoveWatermark", {
33765
- label: "移除版本信息",
33766
- description: "渲染的图片是否移除底部版本信息",
33767
- defaultSelected: all.app.RemoveWatermark
33768
- }),
33769
- components.input.number("RenderWaitTime", {
33770
- label: "渲染图片的等待时间",
33771
- description: os.platform() === "linux" ? "单位:秒,Linux系统下不能为0" : "单位:秒,传递 0 可禁用",
33772
- defaultValue: all.app.RenderWaitTime.toString(),
33773
- rules: [os.platform() === "linux" ? {
33774
- min: 1,
33775
- error: "Linux系统下渲染等待时间不能为0"
33776
- } : { min: 0 }]
33777
- }),
33778
- components.switch.create("multiPageRender", {
33779
- label: "分页渲染",
33780
- description: "将模板渲染成多页的图片,以降低渲染器压力,默认开启,非必要不修改!",
33781
- defaultSelected: all.app.multiPageRender
33782
- }),
33783
- components.input.number("multiPageHeight", {
33784
- label: "分页渲染时,每页的高度",
33785
- description: "经测试最佳每页高度为12000px,默认12000px",
33786
- defaultValue: all.app.multiPageHeight.toString(),
33787
- isDisabled: !all.app.multiPageRender,
33788
- rules: [{
33789
- min: 1e3,
33790
- max: 2e4,
33791
- error: "请输入一个范围在 1000 到 20000 之间的数字"
33792
- }]
33793
- }),
33794
- components.divider.create("divider-app-live-photo", {
33795
- description: "Live Photo 兼容设置",
33796
- descPosition: 20
33797
- }),
33798
- components.radio.group("livePhotoMode", {
33799
- label: "Live Photo 处理和发送方式",
33800
- description: "解析遇到实况图时的处理和发送方式。注意:生成视频性能开销大,2C2G 服务器单张约需 20 秒",
33801
- orientation: "horizontal",
33802
- defaultValue: all.app.livePhotoMode || "video_and_livephoto",
33803
- radio: [
33804
- components.radio.create("livePhotoMode-video-and-livephoto", {
33805
- label: "视频 + 实况图",
33806
- description: "生成并发送仿 iPhone Live Photo 播放效果的视频(播放三次)+ 对应系统的实况图",
33807
- value: "video_and_livephoto"
33808
- }),
33809
- components.radio.create("livePhotoMode-video-only", {
33810
- label: "仅视频",
33811
- description: "仅生成并发送仿 iPhone Live Photo 播放效果的视频(播放三次)",
33812
- value: "video_only"
33813
- }),
33814
- components.radio.create("livePhotoMode-livephoto-only", {
33815
- label: "仅实况图",
33816
- description: "仅生成并发送对应系统的实况图,性能开销小",
33817
- value: "livephoto_only"
33818
- })
33819
- ]
33820
- }),
33821
- components.radio.group("livePhotoSystem", {
33822
- label: "Live Photo 静态图兼容系统",
33823
- description: "当解析到作品/动态包含 Live Photo 时,合并转发里发送的 Live Photo 静态图按所选系统生成。推荐 OPPO,兼容性最广",
33824
- orientation: "horizontal",
33825
- defaultValue: all.app.livePhotoSystem || "oppo",
33826
- isDisabled: all.app.livePhotoMode === "video_only",
33827
- radio: [
33828
- components.radio.create("livePhotoSystem-google", {
33829
- label: "Google",
33830
- description: "Google Motion Photo 格式",
33831
- value: "google"
33832
- }),
33833
- components.radio.create("livePhotoSystem-xiaomi", {
33834
- label: "小米(HyperOS)",
33835
- description: "兼容小米(任何版本)和 Google,但无法被 OPPO 识别",
33836
- value: "xiaomi"
33837
- }),
33838
- components.radio.create("livePhotoSystem-oppo", {
33839
- label: "OPPO(ColorOS)",
33840
- description: "推荐,兼容 OPPO、小米(较新版本)和 Google",
33841
- value: "oppo"
33842
- }),
33843
- components.radio.create("livePhotoSystem-huawei-honor", {
33844
- label: "华为/荣耀(HarmonyOS/MagicOS)",
33845
- description: "理论可行但未实测(作者无对应设备)",
33846
- value: "huawei_honor"
33847
- }),
33848
- components.radio.create("livePhotoSystem-vivo", {
33849
- label: "vivo(Origin OS)",
33850
- description: "需要独立的图片和同名视频文件,暂不支持",
33851
- value: "vivo",
33852
- isDisabled: true
33853
- }),
33854
- components.radio.create("livePhotoSystem-iphone", {
33855
- label: "iPhone(iOS)",
33856
- description: "需要独立的图片和同名视频文件,暂不支持",
33857
- value: "iphone",
33858
- isDisabled: true
33859
- })
33860
- ]
33861
- }),
33862
- components.divider.create("divider-app-api", {
33863
- description: "API服务配置",
33864
- descPosition: 20
33865
- }),
33866
- components.switch.create("APIServer", {
33867
- label: "API服务",
33868
- description: "本地部署一个视频解析API服务,接口范围为本插件用到的所有",
33869
- defaultSelected: all.app.APIServer
33870
- }),
33871
- components.switch.create("APIServerMount", {
33872
- label: "挂载到 Karin",
33873
- description: "API 服务是否挂载到 Karin 上,开启后监听端口为 Karin 的 http 端口,修改后需重启。需开启「API服务」",
33874
- defaultSelected: all.app.APIServerMount,
33875
- isDisabled: !all.app.APIServer
33876
- }),
33877
- components.input.number("APIServerPort", {
33878
- label: "API服务端口",
33879
- defaultValue: all.app.APIServerPort.toString(),
33880
- isDisabled: all.app.APIServerMount,
33881
- rules: [{
33882
- min: 1024,
33883
- max: 65535,
33884
- error: "请输入一个范围在 1024 到 65535 之间的数字"
33885
- }]
33886
- }),
33887
- components.divider.create("divider-app-interaction", {
33888
- description: "交互与认证设置",
33889
- descPosition: 20
33890
- }),
33891
- components.switch.create("EmojiReply", {
33892
- label: "表情回应",
33893
- description: "在解析任务开始时添加表情回应,若适配器不支持需要关闭",
33894
- defaultSelected: all.app.EmojiReply
33895
- }),
33896
- components.switch.create("parseTip", {
33897
- label: "解析提示",
33898
- description: "发送提示信息:\"检测到xxx链接,开始解析\"",
33899
- defaultSelected: all.app.parseTip
33900
- }),
33901
- components.switch.create("fakeForward", {
33902
- label: "伪造合并转发消息",
33903
- description: "开启后合并转发将使用触发者身份展示;关闭后使用机器人身份展示",
33904
- defaultSelected: all.app.fakeForward
33905
- }),
33906
- components.checkbox.group("errorLogSendTo", {
33907
- label: "错误日志",
33908
- description: "遇到错误时谁会收到错误日志。注:推送任务只可发送给主人。「第一个主人」与「所有主人」互斥。",
33909
- orientation: "horizontal",
33910
- defaultValue: all.app.errorLogSendTo,
33911
- checkbox: [
33912
- components.checkbox.create("errorLogSendTo:checkbox:1", {
33913
- label: "第一个主人",
33914
- value: "master"
33915
- }),
33916
- components.checkbox.create("errorLogSendTo:checkbox:2", {
33917
- label: "所有主人",
33918
- value: "allMasters"
33919
- }),
33920
- components.checkbox.create("errorLogSendTo:checkbox:3", {
33921
- label: "触发者的群聊",
33922
- value: "trigger"
33923
- })
33924
- ]
33925
- }),
33926
- components.divider.create("divider-app-qrlogin", {
33927
- description: "我的小玩具配置",
33928
- descPosition: 20
33929
- }),
33930
- components.radio.group("qrLoginAddrType", {
33931
- label: "扫码登录地址类型",
33932
- description: "生成登录二维码时使用的服务器地址",
33933
- orientation: "horizontal",
33934
- defaultValue: all.app.qrLoginAddrType || "lan",
33935
- radio: [components.radio.create("qrLoginAddrType-lan", {
33936
- label: `局域网(${getLocalIP$1()})`,
33937
- description: "适用于手机和服务器在同一局域网",
33938
- value: "lan"
33939
- }), components.radio.create("qrLoginAddrType-external", {
33940
- label: "外部地址",
33941
- description: "适用于远程访问,需手动配置",
33942
- value: "external"
33943
- })]
33944
- }),
33945
- components.input.string("qrLoginExternalAddr", {
33946
- label: "外部访问地址",
33947
- type: "text",
33948
- description: "公网 IP 或域名,如:123.45.67.89 或 example.com",
33949
- defaultValue: all.app.qrLoginExternalAddr || "",
33950
- placeholder: "请输入公网 IP 或域名",
33951
- isDisabled: all.app.qrLoginAddrType !== "external",
33952
- isRequired: false
33953
- })
33954
- ]
33955
- })]
33956
- }),
33957
- ...DouyinWeb(all),
33958
- ...BilibiliWeb(all),
33959
- ...KuaishouWeb(all),
33960
- ...XiaohongshuWeb(all),
33961
- components.accordion.create("upload", {
33962
- label: "视频上传和下载相关",
33963
- children: [components.accordion.createItem("cfg:upload", {
33964
- title: "上传和下载相关",
33965
- className: "ml-4 mr-4",
33966
- subtitle: "此处为视频上传和下载相关的用户偏好设置",
33967
- children: [
33968
- components.divider.create("divider-upload-method", {
33969
- description: "发送方式配置",
33970
- descPosition: 20
33971
- }),
33972
- components.radio.group("videoSendMode", {
33973
- label: "本地视频发送方式",
33974
- orientation: "vertical",
33975
- defaultValue: all.upload.videoSendMode,
33976
- isDisabled: all.upload.usegroupfile,
33977
- radio: [components.radio.create("videoSendMode:radio-1", {
33978
- label: "File 协议(本地文件)",
33979
- description: "使用 file 协议发送本地视频,需 Karin 与协议端在同一系统",
33980
- value: "file"
33981
- }), components.radio.create("videoSendMode:radio-2", {
33982
- label: "Base64(编码传输)",
33983
- description: "将本地视频转换为 base64 发送,传输数据量增大约 30%,不在同一网络环境可能导致额外带宽成本,适合 karin 和协议端不在同一网络环境",
33984
- value: "base64"
33985
- })]
33986
- }),
33987
- components.switch.create("usegroupfile", {
33988
- label: "群文件上传",
33989
- description: "使用群文件上传,开启后会将视频文件上传到群文件中,需配置「群文件上传阈值」。与「本地视频发送方式 = Base64」互斥。",
33990
- defaultSelected: all.upload.usegroupfile,
33991
- isDisabled: all.upload.videoSendMode === "base64"
33992
- }),
33993
- components.input.number("groupfilevalue", {
33994
- label: "群文件上传阈值",
33995
- description: "当文件大小超过该值时将使用群文件上传,单位:MB,「使用群文件上传」开启后才会生效",
33996
- defaultValue: all.upload.groupfilevalue.toString(),
33997
- rules: [{ min: 1 }],
33998
- isDisabled: !all.upload.usegroupfile || all.upload.videoSendMode === "base64"
33999
- }),
34000
- components.radio.group("imageSendMode", {
34001
- label: "网络图片发送方式",
34002
- orientation: "vertical",
34003
- defaultValue: all.upload.imageSendMode,
34004
- radio: [
34005
- components.radio.create("imageSendMode:radio-1", {
34006
- label: "URL 链接(直接传递)",
34007
- description: "直接传递 HTTP 链接给上游下载,可能因上游网络问题导致下载超时",
34008
- value: "url"
34009
- }),
34010
- components.radio.create("imageSendMode:radio-2", {
34011
- label: "File 协议(本地文件)",
34012
- description: "下载到本地后使用 file 协议发送,需 Karin 与协议端在同一系统",
34013
- value: "file"
34014
- }),
34015
- components.radio.create("imageSendMode:radio-3", {
34016
- label: "Base64(编码传输)",
34017
- description: "下载后转换为 base64 发送,传输数据量增大约 30%,不在同一网络环境可能导致额外带宽成本",
34018
- value: "base64"
34019
- })
34020
- ]
34021
- }),
34022
- components.divider.create("divider-upload-limit", {
34023
- description: "上传拦截配置",
34024
- descPosition: 20
34025
- }),
34026
- components.switch.create("usefilelimit", {
34027
- label: "视频上传拦截",
34028
- description: "开启后会根据视频文件大小判断是否需要上传,需配置「视频拦截阈值」。",
34029
- defaultSelected: all.upload.usefilelimit
34030
- }),
34031
- components.input.number("filelimit", {
34032
- label: "视频拦截阈值",
34033
- description: "视频文件大于该数值则直接结束任务,不会上传,单位: MB,「视频上传拦截」开启后才会生效。",
34034
- defaultValue: all.upload.filelimit.toString(),
34035
- rules: [{ min: 1 }],
34036
- isDisabled: !all.upload.usefilelimit
34037
- }),
34038
- components.divider.create("divider-upload-compress", {
34039
- description: "视频压缩配置",
34040
- descPosition: 20
34041
- }),
34042
- components.switch.create("compress", {
34043
- label: "压缩视频",
34044
- description: "开启后会将视频文件压缩后再上传,适合上传大文件,任务过程中会吃满CPU,对低配服务器不友好。需配置「压缩触发阈值」与「压缩后的值」",
34045
- defaultSelected: all.upload.compress
34046
- }),
34047
- components.input.number("compresstrigger", {
34048
- label: "压缩触发阈值",
34049
- description: "触发视频压缩的阈值,单位:MB。当文件大小超过该值时,才会压缩视频,「压缩视频」开启后才会生效",
34050
- defaultValue: all.upload.compresstrigger.toString(),
34051
- rules: [{ min: 1 }],
34052
- isDisabled: !all.upload.compress
34053
- }),
34054
- components.input.number("compressvalue", {
34055
- label: "压缩后的值",
34056
- description: "单位:MB,若视频文件大小大于「压缩触发阈值」的值,则会进行压缩至该值(±5%),「压缩视频」开启后才会生效",
34057
- defaultValue: all.upload.compressvalue.toString(),
34058
- rules: [{ min: 1 }],
34059
- isDisabled: !all.upload.compress
34060
- }),
34061
- components.divider.create("divider-upload-throttle", {
34062
- description: "下载限速配置",
34063
- descPosition: 20
34064
- }),
34065
- components.switch.create("downloadThrottle", {
34066
- label: "下载限速",
34067
- description: "开启后会限制下载速度,避免触发服务器风控导致连接被重置(ECONNRESET)。如果下载时经常报错\"连接被重置\",建议开启",
34068
- defaultSelected: all.upload.downloadThrottle
34069
- }),
34070
- components.input.number("downloadMaxSpeed", {
34071
- label: "最大下载速度",
34072
- description: "单位:MB/s,建议设置为 5-20 之间。设置过高可能触发风控,设置过低会影响下载体验",
34073
- defaultValue: all.upload.downloadMaxSpeed.toString(),
34074
- rules: [{
34075
- min: 1,
34076
- max: 1e3,
34077
- error: "请输入一个范围在 1 到 1000 之间的数字"
34078
- }],
34079
- isDisabled: !all.upload.downloadThrottle
34080
- }),
34081
- components.switch.create("downloadAutoReduce", {
34082
- label: "断流自动降速",
34083
- description: "当检测到连接被重置时自动降低下载速度,每次断流后速度会降低到当前的 60%",
34084
- defaultSelected: all.upload.downloadAutoReduce,
34085
- isDisabled: !all.upload.downloadThrottle
34086
- }),
34087
- components.input.number("downloadMinSpeed", {
34088
- label: "最低下载速度",
34089
- description: "单位:MB/s,自动降速时不会低于此值",
34090
- defaultValue: all.upload.downloadMinSpeed.toString(),
34091
- rules: [{
34092
- min: .1,
34093
- max: 100,
34094
- error: "请输入一个范围在 0.1 到 100 之间的数字"
34095
- }],
34096
- isDisabled: !all.upload.downloadThrottle || !all.upload.downloadAutoReduce
34097
- })
34098
- ]
34099
- })]
34100
- }),
34101
- components.accordion.create("request", {
34102
- label: "解析库请求配置相关",
34103
- children: [components.accordion.createItem("cfg:request", {
34104
- title: "解析库请求配置相关",
34105
- className: "ml-4 mr-4",
34106
- subtitle: "此处用于管理解析库的网络请求配置",
34107
- children: [
34108
- components.input.number("timeout", {
34109
- label: "请求超时时间",
34110
- description: "网络请求的超时时间,单位:毫秒",
34111
- defaultValue: all.request.timeout.toString(),
34112
- rules: [{
34113
- min: 1e3,
34114
- max: 3e5,
34115
- error: "请输入一个范围在 1000 到 300000 之间的数字"
34116
- }]
34117
- }),
34118
- components.input.string("User-Agent", {
34119
- label: "User-Agent",
34120
- type: "text",
34121
- description: "请求头中的User-Agent字段,用于标识客户端类型",
34122
- defaultValue: all.request["User-Agent"],
34123
- placeholder: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
34124
- rules: void 0,
34125
- isRequired: false
34126
- }),
34127
- components.divider.create("divider-proxy", {
34128
- description: "代理配置(可选)",
34129
- descPosition: 20
34130
- }),
34131
- components.switch.create("proxy:switch", {
34132
- label: "代理开关",
34133
- description: "开启后需要配置「代理主机」「代理端口」",
34134
- defaultSelected: all.request.proxy?.switch
34135
- }),
34136
- components.input.string("proxy:host", {
34137
- label: "代理主机",
34138
- type: "text",
34139
- description: "代理服务器的主机地址,如:127.0.0.1",
34140
- defaultValue: all.request.proxy?.host || "",
34141
- placeholder: "127.0.0.1",
34142
- rules: void 0,
34143
- isDisabled: !all.request.proxy?.switch
34144
- }),
34145
- components.input.number("proxy:port", {
34146
- label: "代理端口",
34147
- description: "代理服务器的端口号",
34148
- defaultValue: all.request.proxy?.port?.toString() || "",
34149
- rules: [{
34150
- min: 1,
34151
- max: 65535,
34152
- error: "请输入一个范围在 1 到 65535 之间的数字"
34153
- }],
34154
- isDisabled: !all.request.proxy?.switch
34155
- }),
34156
- components.radio.group("proxy:protocol", {
34157
- label: "代理协议",
34158
- orientation: "horizontal",
34159
- defaultValue: all.request.proxy?.protocol || "http",
34160
- radio: [components.radio.create("proxy-protocol-1", {
34161
- label: "HTTP",
34162
- value: "http"
34163
- }), components.radio.create("proxy-protocol-2", {
34164
- label: "HTTPS",
34165
- value: "https"
34166
- })],
34167
- isDisabled: !all.request.proxy?.switch
34168
- }),
34169
- components.input.string("proxy:auth:username", {
34170
- label: "代理用户名",
34171
- type: "text",
34172
- description: "代理服务器的认证用户名(如果需要)",
34173
- defaultValue: all.request.proxy?.auth?.username || "",
34174
- placeholder: "",
34175
- rules: void 0,
34176
- isRequired: false,
34177
- isDisabled: !all.request.proxy?.switch
34178
- }),
34179
- components.input.string("proxy:auth:password", {
34180
- label: "代理密码",
34181
- type: "password",
34182
- description: "代理服务器的认证密码(如果需要)",
34183
- defaultValue: all.request.proxy?.auth?.password || "",
34184
- placeholder: "",
34185
- rules: void 0,
34186
- isRequired: false,
34187
- isDisabled: !all.request.proxy?.switch
34188
- })
34189
- ]
34190
- })]
34191
- }),
34192
- components.divider.create("divider-7", {
34193
- description: "抖音推送列表相关",
34194
- descPosition: 20
34195
- }),
34196
- components.accordionPro.create("pushlist:douyin", all.pushlist.douyin.map((item) => {
34197
- return {
34198
- ...item,
34199
- title: item.remark,
34200
- subtitle: item.short_id
34201
- };
34202
- }), {
34203
- label: "抖音推送列表",
34204
- children: components.accordion.createItem("accordion-item-douyin", {
34205
- className: "ml-4 mr-4",
34206
- children: [
34207
- components.switch.create("switch", {
34208
- label: "是否启用",
34209
- description: "是否启用该订阅项",
34210
- color: "warning"
34211
- }),
34212
- components.input.string("short_id", {
34213
- placeholder: "",
34214
- label: "抖音号",
34215
- description: "抖音号, 必填",
34216
- errorMessage: "抖音号不能为空 Ciallo~(∠・ω< )⌒☆",
34217
- color: "warning"
34218
- }),
34219
- components.input.group("group_id", {
34220
- label: "绑定推送群",
34221
- maxRows: 2,
34222
- data: [],
34223
- template: components.input.string("accordion-item-douyin:push:douyin:group_id", {
34224
- placeholder: "必填,不能出现空值",
34225
- label: "群号:机器人账号",
34226
- color: "warning",
34227
- rules: [{
34228
- regex: /.+:.+/,
34229
- error: "请使用 `群号:机器人账号` 的格式"
34230
- }]
34231
- })
34232
- }),
34233
- components.input.string("sec_uid", {
34234
- color: "default",
34235
- placeholder: "可不填,会自动获取",
34236
- label: "UID",
34237
- isRequired: false,
34238
- description: "获取方法:PC浏览器打开某个博主主页,https://www.douyin.com/user/MS4wLj..... 其中的user/后的即为UID"
34239
- }),
34240
- components.input.string("remark", {
34241
- color: "default",
34242
- placeholder: "可不填,会自动获取",
34243
- label: "昵称",
34244
- isRequired: false,
34245
- description: "博主的抖音名称"
34246
- }),
34247
- components.divider.create("push:douyin:divider-pushTypes", {
34248
- description: "推送类型配置",
34249
- descPosition: 20
34250
- }),
34251
- components.checkbox.group("pushTypes", {
34252
- label: "推送类型",
34253
- description: "选择要推送的内容类型,可多选",
34254
- orientation: "horizontal",
34255
- color: "warning",
34256
- checkbox: [
34257
- components.checkbox.create("pushTypes:checkbox:post", {
34258
- label: "作品列表",
34259
- description: "推送博主发布的作品",
34260
- value: "post"
34261
- }),
34262
- components.checkbox.create("pushTypes:checkbox:favorite", {
34263
- label: "喜欢列表",
34264
- description: "推送博主喜欢的作品",
34265
- value: "favorite"
34266
- }),
34267
- components.checkbox.create("pushTypes:checkbox:recommend", {
34268
- label: "推荐列表",
34269
- description: "推送博主的推荐作品",
34270
- value: "recommend"
34271
- }),
34272
- components.checkbox.create("pushTypes:checkbox:live", {
34273
- label: "直播",
34274
- description: "推送博主开播通知",
34275
- value: "live"
34276
- })
34277
- ]
34278
- }),
34279
- components.divider.create("push:douyin:divider-1", {
34280
- description: "过滤系统",
34281
- descPosition: 20
34282
- }),
34283
- components.radio.group("filterMode", {
34284
- label: "过滤模式",
34285
- orientation: "horizontal",
34286
- color: "warning",
34287
- radio: [components.radio.create("push:bilibili:filterMode.radio-1", {
34288
- label: "黑名单模式",
34289
- description: "命中以下内容时,不推送",
34290
- value: "blacklist"
34291
- }), components.radio.create("push:bilibili:filterMode.radio-2", {
34292
- label: "白名单模式",
34293
- description: "命中以下内容时,才推送",
34294
- value: "whitelist"
34295
- })]
34296
- }),
34297
- components.input.group("Keywords", {
34298
- label: "关键词",
34299
- maxRows: 2,
34300
- itemsPerRow: 4,
34301
- data: [],
34302
- template: components.input.string("push:bilibili:filterKeywords", {
34303
- placeholder: "严禁提交空值",
34304
- label: "",
34305
- color: "warning"
34306
- })
34307
- }),
34308
- components.input.group("Tags", {
34309
- label: "标签",
34310
- maxRows: 2,
34311
- itemsPerRow: 4,
34312
- data: [],
34313
- template: components.input.string("push:bilibili:filterTags", {
34314
- placeholder: "严禁提交空值",
34315
- label: "",
34316
- color: "warning"
34317
- })
34318
- })
34319
- ]
34320
- })
34321
- }),
34322
- components.divider.create("divider-8", {
34323
- description: "B站推送列表相关",
34324
- descPosition: 20
34325
- }),
34326
- components.accordionPro.create("pushlist:bilibili", all.pushlist.bilibili.map((item) => {
34327
- return {
34328
- ...item,
34329
- title: item.remark,
34330
- subtitle: item.host_mid
34331
- };
34332
- }), {
34333
- label: "B站推送列表",
34334
- children: components.accordion.createItem("accordion-item-bilibili", {
34335
- className: "ml-4 mr-4",
34336
- children: [
34337
- components.switch.create("switch", {
34338
- label: "是否启用",
34339
- description: "是否启用该订阅项",
34340
- color: "warning"
34341
- }),
34342
- components.input.number("host_mid", {
34343
- placeholder: "",
34344
- label: "UID",
34345
- rules: void 0,
34346
- description: "B站用户的UID,必填",
34347
- errorMessage: "UID 不能为空 Ciallo~(∠・ω< )⌒☆",
34348
- color: "warning"
34349
- }),
34350
- components.input.group("group_id", {
34351
- label: "绑定推送群",
34352
- maxRows: 2,
34353
- data: [],
34354
- template: components.input.string("accordion-item-bilibili:push:bilibili:group_id", {
34355
- placeholder: "必填,不能出现空值",
34356
- label: "",
34357
- color: "warning",
34358
- rules: [{
34359
- regex: /.+:.+/,
34360
- error: "请使用 `群号:机器人账号` 的格式"
34361
- }]
34362
- })
34363
- }),
34364
- components.input.string("remark", {
34365
- color: "default",
34366
- placeholder: "可不填,会自动获取",
34367
- label: "昵称",
34368
- isRequired: false,
34369
- description: "UP主的昵称"
34370
- }),
34371
- components.divider.create("push:bilibili:divider-pushTypes", {
34372
- description: "推送类型配置",
34373
- descPosition: 20
34374
- }),
34375
- components.checkbox.group("pushTypes", {
34376
- label: "推送类型",
34377
- description: "选择要推送的动态类型,可多选",
34378
- orientation: "horizontal",
34379
- color: "warning",
34380
- checkbox: [
34381
- components.checkbox.create("pushTypes:checkbox:video", {
34382
- label: "投稿视频",
34383
- description: "推送UP主投稿的视频",
34384
- value: "video"
34385
- }),
34386
- components.checkbox.create("pushTypes:checkbox:draw", {
34387
- label: "图文动态",
34388
- description: "推送UP主发布的图文动态",
34389
- value: "draw"
34390
- }),
34391
- components.checkbox.create("pushTypes:checkbox:word", {
34392
- label: "纯文动态",
34393
- description: "推送UP主发布的纯文字动态",
34394
- value: "word"
34395
- }),
34396
- components.checkbox.create("pushTypes:checkbox:live", {
34397
- label: "直播动态",
34398
- description: "推送UP主的直播通知",
34399
- value: "live"
34400
- }),
34401
- components.checkbox.create("pushTypes:checkbox:forward", {
34402
- label: "转发动态",
34403
- description: "推送UP主的转发动态",
34404
- value: "forward"
34405
- }),
34406
- components.checkbox.create("pushTypes:checkbox:article", {
34407
- label: "投稿专栏",
34408
- description: "推送UP主投稿的专栏文章",
34409
- value: "article"
34410
- })
34411
- ]
34412
- }),
34413
- components.divider.create("push:bilibili:divider-filter", {
34414
- description: "过滤系统",
34415
- descPosition: 20
34416
- }),
34417
- components.radio.group("filterMode", {
34418
- label: "过滤模式",
34419
- orientation: "horizontal",
34420
- color: "warning",
34421
- radio: [components.radio.create("push:bilibili:filterMode.radio-1", {
34422
- label: "黑名单模式",
34423
- description: "命中以下内容时,不推送",
34424
- value: "blacklist"
34425
- }), components.radio.create("push:bilibili:filterMode.radio-2", {
34426
- label: "白名单模式",
34427
- description: "命中以下内容时,才推送",
34428
- value: "whitelist"
34429
- })]
34430
- }),
34431
- components.input.group("Keywords", {
34432
- label: "关键词",
34433
- maxRows: 2,
34434
- itemsPerRow: 4,
34435
- data: [],
34436
- description: "关键词,多个则使用逗号隔开",
34437
- template: components.input.string("push:bilibili:filterKeywords", {
34438
- placeholder: "严禁提交空值",
34439
- label: "",
34440
- color: "warning"
34441
- })
34442
- }),
34443
- components.input.group("Tags", {
34444
- label: "标签",
34445
- maxRows: 2,
34446
- itemsPerRow: 4,
34447
- data: [],
34448
- template: components.input.string("push:bilibili:filterTags", {
34449
- placeholder: "严禁提交空值",
34450
- label: "",
34451
- color: "warning"
34452
- })
34453
- })
34454
- ]
34455
- })
34456
- })
34457
- ];
34458
- },
34459
- /** 前端点击保存之后调用的方法 */
34460
- save: async (config) => {
34461
- const formatCfg = processFrontendData(config);
34462
- const oldAllCfg = await Config.All();
34463
- const mergeCfg = _.mergeWith({}, oldAllCfg, formatCfg, customizer);
34464
- cleanFlattenedFields(mergeCfg);
34465
- let success = false;
34466
- let isChange = false;
34467
- let needReloadAmagi = false;
34468
- for (const key of Object.keys(mergeCfg)) {
34469
- const configValue = mergeCfg[key];
34470
- if (configValue && typeof configValue === "object" && Object.keys(configValue).length > 0) {
34471
- isChange = deepEqual(configValue, oldAllCfg[key]);
34472
- if (isChange) {
34473
- if (await Config.ModifyPro(key, configValue)) {
34474
- success = true;
34475
- if (key === "cookies" || key === "request") needReloadAmagi = true;
34476
- }
34477
- }
34478
- }
34479
- }
34480
- await Config.syncConfigToDatabase();
34481
- if (needReloadAmagi) reloadAmagiConfig();
34482
- return {
34483
- mergeCfg,
34484
- formatCfg,
34485
- success,
34486
- message: success ? "保存成功 Ciallo~(∠・ω< )⌒☆" : "配置无变化 Ciallo~(∠・ω< )⌒☆"
34487
- };
32600
+ page: {
32601
+ url: process.env.NODE_ENV === "development" ? "http://192.168.1.8:5176/kkk/karin-config" : "/kkk/karin-config",
32602
+ title: "kkk插件配置管理",
32603
+ description: "使用 kkk 插件自带的配置管理页面"
34488
32604
  }
34489
32605
  });
34490
- /**
34491
- * 遇到数组时用新数组覆盖原始数组(而不是合并)
34492
- * @param value 原始内容
34493
- * @param srcValue 新内容
34494
- * @returns
34495
- */
34496
- var customizer = (value, srcValue) => {
34497
- if (Array.isArray(srcValue)) return srcValue;
34498
- };
34499
- /**
34500
- * 归递判断配置是否修改
34501
- * @param a 前端传回来的配置
34502
- * @param b 用户原本的配置
34503
- * @returns 配置对象是否被修改
34504
- */
34505
- var deepEqual = (a, b) => {
34506
- if (a === b) return false;
34507
- if (typeof a === "string" && typeof b === "string") {
34508
- if (a !== b) return true;
34509
- }
34510
- if (typeof a === "number" && typeof b === "number") {
34511
- if (a !== b) return true;
34512
- }
34513
- if (typeof a === "boolean" && typeof b === "boolean") {
34514
- if (a !== b) return true;
34515
- }
34516
- if (a === null || b === null || typeof a !== typeof b) return true;
34517
- if (Array.isArray(a) && Array.isArray(b)) {
34518
- if (a.length !== b.length) return true;
34519
- for (let i = 0; i < a.length; i++) if (deepEqual(a[i], b[i])) return true;
34520
- }
34521
- let isChange = false;
34522
- if (typeof a === "object" && typeof b === "object") {
34523
- if (isChange) return true;
34524
- const keysA = Object.keys(a);
34525
- const keysB = Object.keys(b);
34526
- if (keysA.length !== keysB.length) return true;
34527
- for (const key of keysA) {
34528
- if (!keysB.includes(key)) {
34529
- isChange = true;
34530
- return true;
34531
- }
34532
- if (deepEqual(a[key], b[key])) {
34533
- isChange = true;
34534
- return true;
34535
- }
34536
- }
34537
- }
34538
- return false;
34539
- };
34540
- /**
34541
- * str 转 num
34542
- * @param value 字符串
34543
- * @returns
34544
- */
34545
- var convertToNumber = (value) => {
34546
- if (/^\d+$/.test(value)) return parseInt(value, 10);
34547
- else return value;
34548
- };
34549
- /**
34550
- * 获取数组中的第一个对象,如果数组为空则返回空对象
34551
- * @param arr 数组
34552
- * @returns 数组中的第一个对象或空对象
34553
- */
34554
- var getFirstObject = (arr) => {
34555
- return arr.length > 0 ? arr[0] : {};
34556
- };
34557
- /**
34558
- * 设置嵌套属性值
34559
- * @param obj 目标对象
34560
- * @param keys 键路径数组
34561
- * @param value 要设置的值
34562
- */
34563
- var setNestedProperty = (obj, keys, value) => {
34564
- let current = obj;
34565
- for (let i = 0; i < keys.length - 1; i++) {
34566
- const key = keys[i];
34567
- if (!current[key] || typeof current[key] !== "object") current[key] = {};
34568
- current = current[key];
34569
- }
34570
- const lastKey = keys[keys.length - 1];
34571
- current[lastKey] = value;
34572
- };
34573
- /**
34574
- * 处理前端返回的数据,将其转换为 ConfigType 格式
34575
- * @param data 前端返回的数据
34576
- * @returns 处理后符合 ConfigType 格式的数据
34577
- */
34578
- var processFrontendData = (data) => {
34579
- const result = {};
34580
- const configKeys = Object.keys(data).filter((key) => {
34581
- return !key.includes("pushlist") && key in data;
34582
- });
34583
- for (const key of configKeys) {
34584
- const value = data[key];
34585
- const firstObj = Array.isArray(value) ? getFirstObject(value) : {};
34586
- const objKeys = Object.keys(firstObj);
34587
- if (objKeys.length === 0) continue;
34588
- const configObj = {};
34589
- let hasValidData = false;
34590
- const nestedProps = objKeys.filter((prop) => prop.includes(":"));
34591
- const flatProps = objKeys.filter((prop) => !prop.includes(":"));
34592
- for (const prop of nestedProps) {
34593
- let propValue = firstObj[prop];
34594
- if (typeof propValue === "string") propValue = convertToNumber(propValue);
34595
- if (propValue !== void 0 && propValue !== null) {
34596
- setNestedProperty(configObj, prop.split(":"), propValue);
34597
- hasValidData = true;
34598
- }
34599
- }
34600
- for (const prop of flatProps) {
34601
- let propValue = firstObj[prop];
34602
- if (typeof propValue === "string") propValue = convertToNumber(propValue);
34603
- if (propValue !== void 0 && propValue !== null) {
34604
- configObj[prop] = propValue;
34605
- hasValidData = true;
34606
- }
34607
- }
34608
- if (hasValidData && Object.keys(configObj).length > 0) result[key] = configObj;
34609
- }
34610
- result.pushlist = {
34611
- douyin: data["pushlist:douyin"] || [],
34612
- bilibili: (data["pushlist:bilibili"] || []).map((item) => {
34613
- return {
34614
- ...item,
34615
- host_mid: Number(item.host_mid)
34616
- };
34617
- })
34618
- };
34619
- return result;
34620
- };
34621
- /**
34622
- * 通用的扁平化字段清理函数
34623
- * 自动检测并清理与嵌套结构冲突的扁平化字段
34624
- * @param obj 要清理的对象
34625
- */
34626
- var cleanFlattenedFields = (obj) => {
34627
- if (!obj || typeof obj !== "object") return;
34628
- for (const [, value] of Object.entries(obj)) if (typeof value === "object" && value !== null && !Array.isArray(value)) {
34629
- cleanFlattenedFields(value);
34630
- const valueObj = value;
34631
- const flattenedKeys = Object.keys(valueObj).filter((k) => k.includes("."));
34632
- for (const flatKey of flattenedKeys) if (hasNestedStructure(valueObj, flatKey.split("."))) delete valueObj[flatKey];
34633
- }
34634
- };
34635
- /**
34636
- * 检查对象中是否存在指定路径的嵌套结构
34637
- * @param obj 要检查的对象
34638
- * @param path 路径数组
34639
- * @returns 是否存在嵌套结构
34640
- */
34641
- var hasNestedStructure = (obj, path) => {
34642
- let current = obj;
34643
- for (let i = 0; i < path.length - 1; i++) {
34644
- const key = path[i];
34645
- if (!current[key] || typeof current[key] !== "object") return false;
34646
- current = current[key];
34647
- }
34648
- return path[path.length - 1] in current;
34649
- };
34650
32606
  //#endregion
34651
32607
  //#region src/export/template.ts
34652
32608
  var template_default = reactServerRender;
@@ -35288,6 +33244,30 @@ async function getBiliFrameRate(path) {
35288
33244
  } catch {}
35289
33245
  return 30;
35290
33246
  }
33247
+ /**
33248
+ * 统计用于视频信息封面展示的弹幕(相同内容聚合)
33249
+ * @param danmakuList 弹幕列表
33250
+ * @param topN 返回的条数,默认 5
33251
+ * @returns 重复弹幕优先,不足时用真实弹幕按出现顺序补齐
33252
+ */
33253
+ function getHotDanmaku(danmakuList, topN = 5) {
33254
+ const counter = /* @__PURE__ */ new Map();
33255
+ const firstSeen = /* @__PURE__ */ new Map();
33256
+ const sortedDanmaku = [...danmakuList].sort((a, b) => a.progress - b.progress);
33257
+ for (const dm of sortedDanmaku) {
33258
+ const content = dm.content?.trim();
33259
+ if (!content) continue;
33260
+ if (!firstSeen.has(content)) firstSeen.set(content, dm.progress);
33261
+ counter.set(content, (counter.get(content) ?? 0) + 1);
33262
+ }
33263
+ return [...counter.entries()].map(([content, count]) => ({
33264
+ content,
33265
+ count
33266
+ })).sort((a, b) => {
33267
+ if (b.count !== a.count) return b.count - a.count;
33268
+ return (firstSeen.get(a.content) ?? 0) - (firstSeen.get(b.content) ?? 0);
33269
+ }).slice(0, topN);
33270
+ }
35291
33271
  /** 字号配置映射 */
35292
33272
  var FONT_SIZE_MAP$1 = {
35293
33273
  small: {
@@ -36057,6 +34037,9 @@ var Bilibili = class extends Base {
36057
34037
  host_mid: infoData.data.data.owner.mid,
36058
34038
  typeMode: "strict"
36059
34039
  });
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);
36060
34043
  const img = await Render(this.e, "bilibili/videoInfo", {
36061
34044
  share_url: "https://b23.tv/" + infoData.data.data.bvid,
36062
34045
  title: infoData.data.data.title,
@@ -36065,6 +34048,7 @@ var Bilibili = class extends Base {
36065
34048
  bvid: infoData.data.data.bvid,
36066
34049
  ctime: infoData.data.data.ctime,
36067
34050
  pic: infoData.data.data.pic,
34051
+ hotDanmaku,
36068
34052
  owner: {
36069
34053
  ...infoData.data.data.owner,
36070
34054
  usernameMeta: getUsernameMetadata(userProfileData.data.data.card),
@@ -36140,20 +34124,10 @@ var Bilibili = class extends Base {
36140
34124
  else {
36141
34125
  if (Config.bilibili.videoQuality !== 0 && Config.bilibili.videoQuality < 64) this.islogin = false;
36142
34126
  let danmakuList = [];
36143
- if (this.forceBurnDanmaku || Config.bilibili.burnDanmaku) try {
34127
+ if (this.forceBurnDanmaku || Config.bilibili.burnDanmaku) {
36144
34128
  const cid = iddata.p ? infoData.data.data.pages[iddata.p - 1]?.cid ?? infoData.data.data.cid : infoData.data.data.cid;
36145
34129
  const duration = iddata.p ? infoData.data.data.pages[iddata.p - 1]?.duration ?? infoData.data.data.duration : infoData.data.data.duration;
36146
- const segmentCount = Math.ceil(duration / 360);
36147
- logger.debug(`视频时长: ${duration}秒, 需要获取 ${segmentCount} 个弹幕分段`);
36148
- const danmakuPromises = Array.from({ length: segmentCount }, (_, i) => this.amagi.bilibili.fetcher.fetchVideoDanmaku({
36149
- cid,
36150
- segment_index: i + 1,
36151
- typeMode: "strict"
36152
- }).then((res) => res.data?.data?.elems || []).catch(() => []));
36153
- danmakuList = (await Promise.all(danmakuPromises)).flat();
36154
- logger.debug(`获取到 ${danmakuList.length} 条弹幕(${segmentCount} 个分段)`);
36155
- } catch (err) {
36156
- logger.warn("获取弹幕失败,将不烧录弹幕", err);
34130
+ danmakuList = await this.fetchVideoDanmakuList(cid, duration);
36157
34131
  }
36158
34132
  await this.getvideo(Config.bilibili.videoQuality !== 0 && Config.bilibili.videoQuality < 64 ? {
36159
34133
  playUrlData: nockData.data,
@@ -36821,6 +34795,29 @@ var Bilibili = class extends Base {
36821
34795
  default: break;
36822
34796
  }
36823
34797
  }
34798
+ /**
34799
+ * 获取视频弹幕列表(按每 6 分钟一段并行拉取所有分段)
34800
+ * @param cid 视频分P的 cid
34801
+ * @param duration 视频时长(秒)
34802
+ * @returns 合并后的弹幕列表
34803
+ */
34804
+ async fetchVideoDanmakuList(cid, duration) {
34805
+ try {
34806
+ const segmentCount = Math.ceil(duration / 360);
34807
+ logger.debug(`视频时长: ${duration}秒, 需要获取 ${segmentCount} 个弹幕分段`);
34808
+ const danmakuPromises = Array.from({ length: segmentCount }, (_, i) => this.amagi.bilibili.fetcher.fetchVideoDanmaku({
34809
+ cid,
34810
+ segment_index: i + 1,
34811
+ typeMode: "strict"
34812
+ }).then((res) => res.data?.data?.elems || []).catch(() => []));
34813
+ const danmakuList = (await Promise.all(danmakuPromises)).flat();
34814
+ logger.debug(`获取到 ${danmakuList.length} 条弹幕(${segmentCount} 个分段)`);
34815
+ return danmakuList;
34816
+ } catch (err) {
34817
+ logger.warn("获取弹幕失败", err);
34818
+ return [];
34819
+ }
34820
+ }
36824
34821
  async getvideo({ infoData, playUrlData, danmakuList = [] }) {
36825
34822
  /** 获取视频 => FFmpeg合成 */
36826
34823
  logger.debug("是否登录:", this.islogin);
@@ -45498,6 +43495,18 @@ var signatureVerificationMiddleware = (req, res, next) => {
45498
43495
  /**
45499
43496
  * Bot 管理 API
45500
43497
  */
43498
+ var getOnlineBotById = (botId) => {
43499
+ return karin.getAllBotList().find((item) => item.bot.account.selfId === botId)?.bot;
43500
+ };
43501
+ var getBotAvatar = async (bot, botId) => {
43502
+ if (!bot) return "";
43503
+ try {
43504
+ return await bot.getAvatarUrl(botId) || "";
43505
+ } catch (e) {
43506
+ logger.warn(`[BotsAPI] 获取 Bot 头像失败 ${botId}:`, e);
43507
+ return "";
43508
+ }
43509
+ };
45501
43510
  /**
45502
43511
  * 获取所有在线 Bot 列表
45503
43512
  * GET /api/v1/bots
@@ -45529,6 +43538,27 @@ var getBots = async (_req, res) => {
45529
43538
  }
45530
43539
  };
45531
43540
  /**
43541
+ * 获取指定 Bot 信息
43542
+ * GET /api/v1/bots/:botId
43543
+ */
43544
+ var getBotInfo = async (req, res) => {
43545
+ try {
43546
+ const { botId } = req.params;
43547
+ if (!botId) return createServerErrorResponse(res, "缺少 botId 参数");
43548
+ const bot = getOnlineBotById(botId);
43549
+ if (!bot || bot.account.name === "console") return createServerErrorResponse(res, "Bot 不存在或不在线");
43550
+ return createSuccessResponse(res, {
43551
+ id: bot.account.selfId,
43552
+ name: bot.account.name || bot.account.selfId,
43553
+ avatar: await getBotAvatar(bot, bot.account.selfId),
43554
+ isOnline: true
43555
+ });
43556
+ } catch (error) {
43557
+ logger.error("[BotsAPI] 获取 Bot 信息失败:", error);
43558
+ return createServerErrorResponse(res, "获取 Bot 信息失败");
43559
+ }
43560
+ };
43561
+ /**
45532
43562
  * 获取指定 Bot 的群列表
45533
43563
  * GET /api/v1/bots/:botId/groups
45534
43564
  */
@@ -45559,6 +43589,42 @@ var getBotGroups = async (req, res) => {
45559
43589
  }
45560
43590
  };
45561
43591
  /**
43592
+ * 获取指定 Bot 下的群信息
43593
+ * GET /api/v1/bots/:botId/groups/:groupId
43594
+ */
43595
+ var getBotGroupInfo = async (req, res) => {
43596
+ try {
43597
+ const { botId, groupId } = req.params;
43598
+ if (!botId || !groupId) return createServerErrorResponse(res, "缺少 botId 或 groupId 参数");
43599
+ const bot = getOnlineBotById(botId);
43600
+ if (!bot) return createServerErrorResponse(res, "Bot 不存在或不在线");
43601
+ let groupName = groupId;
43602
+ let groupAvatar = "";
43603
+ let memberCount;
43604
+ try {
43605
+ const groupInfo = await bot.getGroupInfo(groupId);
43606
+ groupName = groupInfo?.groupName || groupName;
43607
+ memberCount = groupInfo?.memberCount;
43608
+ groupAvatar = await bot.getGroupAvatarUrl(groupId) || "";
43609
+ } catch (e) {
43610
+ logger.warn(`[BotsAPI] 获取群组信息失败 ${groupId}:`, e);
43611
+ }
43612
+ return createSuccessResponse(res, {
43613
+ id: groupId,
43614
+ name: groupName,
43615
+ avatar: groupAvatar,
43616
+ botId: bot.account.selfId,
43617
+ botName: bot.account.name || bot.account.selfId,
43618
+ botAvatar: await getBotAvatar(bot, bot.account.selfId),
43619
+ memberCount,
43620
+ isOnline: true
43621
+ });
43622
+ } catch (error) {
43623
+ logger.error("[BotsAPI] 获取 Bot 群信息失败:", error);
43624
+ return createServerErrorResponse(res, "获取群信息失败");
43625
+ }
43626
+ };
43627
+ /**
45562
43628
  * 批量获取群组信息
45563
43629
  * POST /api/v1/groups/batch
45564
43630
  * Body: { groups: [{ groupId: string, botId: string }] }
@@ -45681,6 +43747,7 @@ var updateConfigModule = async (req, res) => {
45681
43747
  });
45682
43748
  if (await Config.ModifyPro(module, newConfig)) {
45683
43749
  if (module === "pushlist") await Config.syncConfigToDatabase();
43750
+ if (module === "cookies" || module === "request") reloadAmagiConfig();
45684
43751
  const updatedConfig = await Config.All();
45685
43752
  res.json({
45686
43753
  success: true,
@@ -45747,13 +43814,25 @@ var updateAllConfig = async (req, res) => {
45747
43814
  message: "请求体必须是有效的配置对象",
45748
43815
  data: null
45749
43816
  });
43817
+ const oldConfig = await Config.All();
45750
43818
  const results = [];
43819
+ let needReloadAmagi = false;
45751
43820
  for (const [module, config] of Object.entries(newConfig)) try {
45752
- const success = await Config.ModifyPro(module, config);
43821
+ if (!(module in oldConfig)) {
43822
+ results.push({
43823
+ module,
43824
+ success: false,
43825
+ error: `配置模块 "${module}" 不存在`
43826
+ });
43827
+ continue;
43828
+ }
43829
+ const moduleName = module;
43830
+ const success = await Config.ModifyPro(moduleName, config);
45753
43831
  results.push({
45754
43832
  module,
45755
43833
  success
45756
43834
  });
43835
+ if (success && (moduleName === "cookies" || moduleName === "request")) needReloadAmagi = true;
45757
43836
  } catch (error) {
45758
43837
  results.push({
45759
43838
  module,
@@ -45762,6 +43841,7 @@ var updateAllConfig = async (req, res) => {
45762
43841
  });
45763
43842
  }
45764
43843
  if ("pushlist" in newConfig) await Config.syncConfigToDatabase();
43844
+ if (needReloadAmagi) reloadAmagiConfig();
45765
43845
  const allSuccess = results.every((r) => r.success);
45766
43846
  const updatedConfig = await Config.All();
45767
43847
  res.json({
@@ -45934,1826 +44014,6 @@ var resolveLink = async (req, res) => {
45934
44014
  }
45935
44015
  };
45936
44016
  //#endregion
45937
- //#region src/module/config/schema.ts
45938
- /** 创建变量引用 */
45939
- var $var = (field) => ({
45940
- type: "var",
45941
- field
45942
- });
45943
- /** 创建字面量 */
45944
- var $literal = (value) => ({
45945
- type: "literal",
45946
- value
45947
- });
45948
- /** 创建比较表达式 */
45949
- var $compare = (left, operator, right) => ({
45950
- type: "compare",
45951
- operator,
45952
- left: typeof left === "string" ? $var(left) : left,
45953
- right: typeof right === "string" ? $var(right) : typeof right === "object" ? right : $literal(right)
45954
- });
45955
- /** 创建逻辑非 */
45956
- var $not = (condition) => ({
45957
- type: "not",
45958
- condition: typeof condition === "string" ? $var(condition) : condition
45959
- });
45960
- /** 创建逻辑或 */
45961
- var $or = (...conditions) => ({
45962
- type: "or",
45963
- conditions: conditions.map((c) => typeof c === "string" ? $var(c) : c)
45964
- });
45965
- /** 创建数组包含检查 */
45966
- var $includes = (field, value) => ({
45967
- type: "includes",
45968
- field,
45969
- value
45970
- });
45971
- /** 快捷方式:字段等于某值 */
45972
- var $eq = (field, value) => $compare(field, "===", value);
45973
- /** 快捷方式:字段不等于某值 */
45974
- var $ne = (field, value) => $compare(field, "!==", value);
45975
- //#endregion
45976
- //#region src/module/config/app.schema.ts
45977
- /**
45978
- * 应用配置 Schema
45979
- */
45980
- var appConfigSchema = {
45981
- key: "app",
45982
- title: "插件应用相关",
45983
- subtitle: "此处用于管理插件的基本设置",
45984
- fields: [
45985
- {
45986
- type: "divider",
45987
- title: "缓存设置"
45988
- },
45989
- {
45990
- key: "removeCache",
45991
- type: "switch",
45992
- label: "缓存删除",
45993
- description: "下载的视频缓存自动删除,非必要不修改!"
45994
- },
45995
- {
45996
- type: "divider",
45997
- title: "解析优先级设置"
45998
- },
45999
- {
46000
- key: "videoTool",
46001
- type: "switch",
46002
- label: "默认解析",
46003
- description: "即识别最高优先级,修改后重启生效"
46004
- },
46005
- {
46006
- key: "priority",
46007
- type: "input",
46008
- inputType: "number",
46009
- label: "自定义优先级",
46010
- description: "自定义优先级,「默认解析」关闭后才会生效。修改后重启生效",
46011
- disabled: $var("videoTool")
46012
- },
46013
- {
46014
- type: "divider",
46015
- title: "渲染配置"
46016
- },
46017
- {
46018
- key: "renderScale",
46019
- type: "input",
46020
- inputType: "number",
46021
- label: "渲染精度",
46022
- description: "可选值50~200,建议100。设置高精度会提高图片的精细度,过高可能会影响渲染与发送速度",
46023
- rules: [{
46024
- min: 50,
46025
- max: 200
46026
- }]
46027
- },
46028
- {
46029
- key: "Theme",
46030
- type: "radio",
46031
- label: "渲染图片的主题色",
46032
- orientation: "horizontal",
46033
- options: [
46034
- {
46035
- label: "自动",
46036
- value: "0",
46037
- description: "06:00-18:00为浅色,18:00-06:00为深色"
46038
- },
46039
- {
46040
- label: "浅色",
46041
- value: "1"
46042
- },
46043
- {
46044
- label: "深色",
46045
- value: "2"
46046
- }
46047
- ]
46048
- },
46049
- {
46050
- key: "RemoveWatermark",
46051
- type: "switch",
46052
- label: "移除版本信息",
46053
- description: "渲染的图片是否移除底部版本信息"
46054
- },
46055
- {
46056
- key: "RenderWaitTime",
46057
- type: "input",
46058
- inputType: "number",
46059
- label: "渲染图片的等待时间",
46060
- description: os.platform() === "linux" ? "单位:秒,Linux系统下不能为0" : "单位:秒,传递 0 可禁用",
46061
- rules: [os.platform() === "linux" ? {
46062
- min: 1,
46063
- error: "Linux系统下渲染等待时间不能为0"
46064
- } : { min: 0 }]
46065
- },
46066
- {
46067
- key: "multiPageRender",
46068
- type: "switch",
46069
- label: "分页渲染",
46070
- description: "将模板渲染成多页的图片,以降低渲染器压力,默认开启,非必要不修改!"
46071
- },
46072
- {
46073
- key: "multiPageHeight",
46074
- type: "input",
46075
- inputType: "number",
46076
- label: "分页渲染时,每页的高度",
46077
- description: "经测试最佳每页高度为12000px,默认12000px",
46078
- disabled: $not("multiPageRender"),
46079
- rules: [{
46080
- min: 1e3,
46081
- max: 2e4,
46082
- error: "请输入一个范围在 1000 到 20000 之间的数字"
46083
- }]
46084
- },
46085
- {
46086
- type: "divider",
46087
- title: "Live Photo 兼容设置"
46088
- },
46089
- {
46090
- key: "livePhotoMode",
46091
- type: "radio",
46092
- label: "Live Photo 处理和发送方式",
46093
- description: "解析遇到实况图时的处理和发送方式。注意:生成视频性能开销大,2C2G 服务器单张约需 20 秒",
46094
- orientation: "horizontal",
46095
- options: [
46096
- {
46097
- label: "视频 + 实况图",
46098
- value: "video_and_livephoto",
46099
- description: "生成并发送仿 iPhone Live Photo 播放效果的视频(播放三次)+ 对应系统的实况图"
46100
- },
46101
- {
46102
- label: "仅视频",
46103
- value: "video_only",
46104
- description: "仅生成并发送仿 iPhone Live Photo 播放效果的视频(播放三次)"
46105
- },
46106
- {
46107
- label: "仅实况图",
46108
- value: "livephoto_only",
46109
- description: "仅生成并发送对应系统的实况图,性能开销小"
46110
- }
46111
- ]
46112
- },
46113
- {
46114
- key: "livePhotoSystem",
46115
- type: "radio",
46116
- label: "Live Photo 静态图兼容系统",
46117
- description: "当解析到作品/动态包含 Live Photo 时,合并转发里发送的 Live Photo 静态图按所选系统生成。推荐 OPPO,兼容性最广",
46118
- orientation: "horizontal",
46119
- disabled: $ne("livePhotoMode", "livephoto_only"),
46120
- options: [
46121
- {
46122
- label: "Google",
46123
- value: "google",
46124
- description: "Google Motion Photo 格式"
46125
- },
46126
- {
46127
- label: "小米(HyperOS)",
46128
- value: "xiaomi",
46129
- description: "兼容小米(任何版本)和 Google,但无法被 OPPO 识别"
46130
- },
46131
- {
46132
- label: "OPPO(ColorOS)",
46133
- value: "oppo",
46134
- description: "推荐,兼容 OPPO、小米(较新版本)和 Google"
46135
- },
46136
- {
46137
- label: "华为/荣耀(HarmonyOS/MagicOS)",
46138
- value: "huawei_honor",
46139
- description: "理论可行但未实测"
46140
- }
46141
- ]
46142
- },
46143
- {
46144
- type: "divider",
46145
- title: "API服务配置"
46146
- },
46147
- {
46148
- key: "APIServer",
46149
- type: "switch",
46150
- label: "API服务",
46151
- description: "本地部署一个视频解析API服务,接口范围为本插件用到的所有"
46152
- },
46153
- {
46154
- key: "APIServerMount",
46155
- type: "switch",
46156
- label: "挂载到 Karin",
46157
- description: "API 服务是否挂载到 Karin 上,开启后监听端口为 Karin 的 http 端口,修改后需重启。需开启「API服务」",
46158
- disabled: $not("APIServer")
46159
- },
46160
- {
46161
- key: "APIServerPort",
46162
- type: "input",
46163
- inputType: "number",
46164
- label: "API服务端口",
46165
- disabled: $or($not("APIServer"), $var("APIServerMount")),
46166
- rules: [{
46167
- min: 1024,
46168
- max: 65535,
46169
- error: "请输入一个范围在 1024 到 65535 之间的数字"
46170
- }]
46171
- },
46172
- {
46173
- type: "divider",
46174
- title: "交互与认证设置"
46175
- },
46176
- {
46177
- key: "EmojiReply",
46178
- type: "switch",
46179
- label: "表情回应",
46180
- description: "在解析任务开始时添加表情回应,若适配器不支持需要关闭"
46181
- },
46182
- {
46183
- key: "parseTip",
46184
- type: "switch",
46185
- label: "解析提示",
46186
- description: "发送提示信息:\"检测到xxx链接,开始解析\""
46187
- },
46188
- {
46189
- key: "fakeForward",
46190
- type: "switch",
46191
- label: "伪造合并转发消息",
46192
- description: "开启后合并转发将使用触发者身份展示;关闭后使用机器人身份展示"
46193
- },
46194
- {
46195
- key: "errorLogSendTo",
46196
- type: "checkbox",
46197
- label: "错误日志",
46198
- description: "遇到错误时谁会收到错误日志。注:推送任务只可发送给主人。「第一个主人」与「所有主人」互斥。",
46199
- orientation: "horizontal",
46200
- options: [
46201
- {
46202
- label: "第一个主人",
46203
- value: "master"
46204
- },
46205
- {
46206
- label: "所有主人",
46207
- value: "allMasters"
46208
- },
46209
- {
46210
- label: "触发者的群聊",
46211
- value: "trigger"
46212
- }
46213
- ]
46214
- },
46215
- {
46216
- type: "divider",
46217
- title: "我的小玩具配置"
46218
- },
46219
- {
46220
- key: "qrLoginAddrType",
46221
- type: "radio",
46222
- label: "扫码登录地址类型",
46223
- description: "生成登录二维码时使用的服务器地址",
46224
- orientation: "horizontal",
46225
- options: [{
46226
- label: "局域网",
46227
- value: "lan",
46228
- description: "适用于手机和服务器在同一局域网"
46229
- }, {
46230
- label: "外部地址",
46231
- value: "external",
46232
- description: "适用于远程访问,需手动配置"
46233
- }]
46234
- },
46235
- {
46236
- key: "qrLoginExternalAddr",
46237
- type: "input",
46238
- inputType: "text",
46239
- label: "外部访问地址",
46240
- description: "公网 IP 或域名,如:123.45.67.89 或 example.com",
46241
- placeholder: "请输入公网 IP 或域名",
46242
- disabled: $ne("qrLoginAddrType", "external")
46243
- }
46244
- ]
46245
- };
46246
- //#endregion
46247
- //#region src/module/config/bilibili.schema.ts
46248
- var bilibiliConfigSchema = {
46249
- key: "bilibili",
46250
- title: "B站相关",
46251
- subtitle: "此处为B站相关的用户偏好设置",
46252
- fields: [
46253
- {
46254
- key: "switch",
46255
- type: "switch",
46256
- label: "解析开关",
46257
- description: "B站解析开关,此开关为单独开关"
46258
- },
46259
- {
46260
- key: "tip",
46261
- type: "switch",
46262
- label: "解析提示",
46263
- description: "B站解析提示,发送提示信息:\"检测到B站链接,开始解析\"",
46264
- disabled: $not("switch")
46265
- },
46266
- {
46267
- key: "sendContent",
46268
- type: "checkbox",
46269
- label: "解析时发送的内容",
46270
- description: "若什么都不选,可能不会返回任何解析结果",
46271
- orientation: "horizontal",
46272
- disabled: $not("switch"),
46273
- options: [
46274
- {
46275
- label: "视频信息",
46276
- value: "info",
46277
- description: "仅解析视频时有效"
46278
- },
46279
- {
46280
- label: "评论列表",
46281
- value: "comment"
46282
- },
46283
- {
46284
- label: "视频文件",
46285
- value: "video",
46286
- description: "仅对视频稿件有效"
46287
- }
46288
- ]
46289
- },
46290
- {
46291
- type: "divider",
46292
- title: "评论详情设置"
46293
- },
46294
- {
46295
- key: "numcomment",
46296
- type: "input",
46297
- inputType: "number",
46298
- label: "评论解析数量",
46299
- disabled: $or($not("switch"), $not($includes("sendContent", "comment"))),
46300
- rules: [{ min: 1 }]
46301
- },
46302
- {
46303
- key: "realCommentCount",
46304
- type: "switch",
46305
- label: "显示真实评论数量",
46306
- description: "评论图是否显示真实评论数量,关闭则显示解析到的评论数量",
46307
- disabled: $or($not("switch"), $not($includes("sendContent", "comment")))
46308
- },
46309
- {
46310
- key: "commentImageCollection",
46311
- type: "switch",
46312
- label: "是否收集评论区的图片",
46313
- description: "开启后将收集评论区的图片,以合并转发的形式返回",
46314
- disabled: $or($not("switch"), $not($includes("sendContent", "comment")))
46315
- },
46316
- {
46317
- type: "divider",
46318
- title: "渲染与画质设置"
46319
- },
46320
- {
46321
- key: "imageLayout",
46322
- type: "radio",
46323
- label: "解析图文动态时,遇到多张图片时的页面布局方式(动态推送图片也生效)",
46324
- description: "自动布局:少于4张时逐张上下排列;4~8张时瀑布流;9张及以上九宫格",
46325
- orientation: "horizontal",
46326
- options: [
46327
- {
46328
- label: "自动布局",
46329
- value: "auto"
46330
- },
46331
- {
46332
- label: "逐张上下排列",
46333
- value: "vertical"
46334
- },
46335
- {
46336
- label: "瀑布流排列",
46337
- value: "waterfall"
46338
- },
46339
- {
46340
- label: "九宫格排列",
46341
- value: "grid"
46342
- }
46343
- ]
46344
- },
46345
- {
46346
- key: "videoQuality",
46347
- type: "radio",
46348
- label: "画质偏好",
46349
- description: "解析视频的分辨率偏好。",
46350
- orientation: "horizontal",
46351
- disabled: $or($not("switch"), $not($includes("sendContent", "video"))),
46352
- options: [
46353
- {
46354
- label: "自动选择",
46355
- value: 0
46356
- },
46357
- {
46358
- label: "240P 极速",
46359
- value: 6
46360
- },
46361
- {
46362
- label: "360P 流畅",
46363
- value: 16
46364
- },
46365
- {
46366
- label: "480P 清晰",
46367
- value: 32,
46368
- description: "需登录(配置ck)"
46369
- },
46370
- {
46371
- label: "720P 高清",
46372
- value: 64,
46373
- description: "需登录(配置ck)"
46374
- },
46375
- {
46376
- label: "720P60 高帧率",
46377
- value: 74,
46378
- description: "需登录(配置ck)"
46379
- },
46380
- {
46381
- label: "1080P 高清",
46382
- value: 80,
46383
- description: "需登录(配置ck)"
46384
- },
46385
- {
46386
- label: "1080P+ 高码率",
46387
- value: 112,
46388
- description: "需大会员&视频支持"
46389
- },
46390
- {
46391
- label: "1080P60 高帧率",
46392
- value: 116,
46393
- description: "需大会员&视频支持"
46394
- },
46395
- {
46396
- label: "4K 超清",
46397
- value: 120,
46398
- description: "需大会员&视频支持"
46399
- },
46400
- {
46401
- label: "8K 超高清",
46402
- value: 127,
46403
- description: "需大会员&视频支持"
46404
- }
46405
- ]
46406
- },
46407
- {
46408
- key: "maxAutoVideoSize",
46409
- type: "input",
46410
- inputType: "number",
46411
- label: "视频体积上限(MB)",
46412
- description: "根据该值自动选择分辨率进行下载。仅在「画质偏好」 为 \"自动选择\" 时生效",
46413
- disabled: $or($not("switch"), $not($includes("sendContent", "video")), $ne("videoQuality", 0)),
46414
- rules: [{
46415
- min: 1,
46416
- max: 2e4
46417
- }]
46418
- },
46419
- {
46420
- key: "videoInfoMode",
46421
- type: "radio",
46422
- label: "视频信息返回形式",
46423
- disabled: $not("switch"),
46424
- options: [{
46425
- label: "图片模式",
46426
- value: "image"
46427
- }, {
46428
- label: "文本模式",
46429
- value: "text"
46430
- }]
46431
- },
46432
- {
46433
- key: "displayContent",
46434
- type: "checkbox",
46435
- label: "视频信息前返回的内容",
46436
- description: "若什么都不选,则不会返回任何视频相关信息",
46437
- orientation: "horizontal",
46438
- disabled: $or($not("switch"), $eq("videoInfoMode", "image")),
46439
- options: [
46440
- {
46441
- label: "封面",
46442
- value: "cover"
46443
- },
46444
- {
46445
- label: "标题",
46446
- value: "title"
46447
- },
46448
- {
46449
- label: "作者",
46450
- value: "author"
46451
- },
46452
- {
46453
- label: "视频统计信息",
46454
- value: "stats"
46455
- },
46456
- {
46457
- label: "简介",
46458
- value: "desc"
46459
- }
46460
- ]
46461
- },
46462
- {
46463
- type: "divider",
46464
- title: "权限设置"
46465
- },
46466
- {
46467
- key: "loginPerm",
46468
- type: "radio",
46469
- label: "谁可以触发扫码登录",
46470
- description: "修改后需重启",
46471
- orientation: "horizontal",
46472
- options: [
46473
- {
46474
- label: "所有人",
46475
- value: "all"
46476
- },
46477
- {
46478
- label: "管理员",
46479
- value: "admin"
46480
- },
46481
- {
46482
- label: "主人",
46483
- value: "master"
46484
- },
46485
- {
46486
- label: "群主",
46487
- value: "group.owner"
46488
- },
46489
- {
46490
- label: "群管理员",
46491
- value: "group.admin"
46492
- }
46493
- ]
46494
- },
46495
- {
46496
- type: "divider",
46497
- title: "弹幕烧录相关"
46498
- },
46499
- {
46500
- key: "burnDanmaku",
46501
- type: "switch",
46502
- label: "弹幕烧录",
46503
- description: "将弹幕硬编码到视频画面中。开启后视频需要重新编码,耗时较长,高分辨率视频会占用较多资源",
46504
- disabled: $not("switch")
46505
- },
46506
- {
46507
- key: "danmakuArea",
46508
- type: "radio",
46509
- label: "弹幕区域",
46510
- description: "限制弹幕的显示范围,避免遮挡视频主体内容",
46511
- orientation: "horizontal",
46512
- disabled: $or($not("switch"), $not("burnDanmaku")),
46513
- options: [
46514
- {
46515
- label: "1/4 屏",
46516
- value: "0.25",
46517
- description: "仅顶部区域"
46518
- },
46519
- {
46520
- label: "半屏",
46521
- value: "0.5",
46522
- description: "上半部分(推荐)"
46523
- },
46524
- {
46525
- label: "3/4 屏",
46526
- value: "0.75",
46527
- description: "大部分区域"
46528
- },
46529
- {
46530
- label: "全屏",
46531
- value: "1",
46532
- description: "铺满整个画面"
46533
- }
46534
- ]
46535
- },
46536
- {
46537
- key: "danmakuFontSize",
46538
- type: "radio",
46539
- label: "弹幕字号",
46540
- description: "弹幕文字大小",
46541
- orientation: "horizontal",
46542
- disabled: $or($not("switch"), $not("burnDanmaku")),
46543
- options: [
46544
- {
46545
- label: "小",
46546
- value: "small"
46547
- },
46548
- {
46549
- label: "中",
46550
- value: "medium"
46551
- },
46552
- {
46553
- label: "大",
46554
- value: "large"
46555
- }
46556
- ]
46557
- },
46558
- {
46559
- key: "danmakuOpacity",
46560
- type: "input",
46561
- inputType: "number",
46562
- label: "弹幕透明度",
46563
- description: "0为完全透明,100为完全不透明,推荐70",
46564
- disabled: $or($not("switch"), $not("burnDanmaku")),
46565
- rules: [{
46566
- min: 0,
46567
- max: 100
46568
- }]
46569
- },
46570
- {
46571
- key: "verticalMode",
46572
- type: "radio",
46573
- label: "竖屏适配",
46574
- description: "模拟手机端竖屏观看体验,视频居中显示,上下黑边区域用于展示弹幕",
46575
- orientation: "horizontal",
46576
- disabled: $or($not("switch"), $not("burnDanmaku")),
46577
- options: [
46578
- {
46579
- label: "关闭",
46580
- value: "off",
46581
- description: "保持原始比例,不做转换"
46582
- },
46583
- {
46584
- label: "智能",
46585
- value: "standard",
46586
- description: "仅对 16:9、21:9 等常见宽屏比例生效"
46587
- },
46588
- {
46589
- label: "强制 9:16",
46590
- value: "force",
46591
- description: "所有视频统一转为 9:16 竖屏,弹幕大小一致"
46592
- }
46593
- ]
46594
- },
46595
- {
46596
- key: "videoCodec",
46597
- type: "radio",
46598
- label: "视频编码格式",
46599
- description: "弹幕烧录时使用的视频编码格式,会自动检测硬件加速",
46600
- orientation: "horizontal",
46601
- disabled: $or($not("switch"), $not("burnDanmaku")),
46602
- options: [
46603
- {
46604
- label: "H.264",
46605
- value: "h264",
46606
- description: "兼容性最好,支持几乎所有设备"
46607
- },
46608
- {
46609
- label: "H.265",
46610
- value: "h265",
46611
- description: "压缩率更高,近几年设备支持良好(推荐)"
46612
- },
46613
- {
46614
- label: "AV1",
46615
- value: "av1",
46616
- description: "最新编码格式,压缩率最高,但编码较慢"
46617
- }
46618
- ]
46619
- },
46620
- {
46621
- type: "divider",
46622
- title: "B站推送相关"
46623
- },
46624
- {
46625
- key: "push.switch",
46626
- type: "switch",
46627
- label: "推送开关",
46628
- description: "推送开关,修改后需重启;使用「#设置B站推送 + UID」配置推送列表",
46629
- color: "warning"
46630
- },
46631
- {
46632
- key: "push.permission",
46633
- type: "radio",
46634
- label: "谁可以设置推送",
46635
- description: "修改后需重启",
46636
- orientation: "horizontal",
46637
- color: "warning",
46638
- disabled: $not("push.switch"),
46639
- options: [
46640
- {
46641
- label: "所有人",
46642
- value: "all"
46643
- },
46644
- {
46645
- label: "管理员",
46646
- value: "admin"
46647
- },
46648
- {
46649
- label: "主人",
46650
- value: "master"
46651
- },
46652
- {
46653
- label: "群主",
46654
- value: "group.owner"
46655
- },
46656
- {
46657
- label: "群管理员",
46658
- value: "group.admin"
46659
- }
46660
- ]
46661
- },
46662
- {
46663
- key: "push.cron",
46664
- type: "input",
46665
- inputType: "text",
46666
- label: "定时任务表达式",
46667
- description: "定时推送的时间,格式为cron表达式(默认为每十分钟执行一次)",
46668
- color: "warning",
46669
- disabled: $not("push.switch")
46670
- },
46671
- {
46672
- key: "push.parsedynamic",
46673
- type: "switch",
46674
- label: "作品解析",
46675
- description: "触发推送时是否一同解析该作品",
46676
- color: "warning",
46677
- disabled: $not("push.switch")
46678
- },
46679
- {
46680
- key: "push.pushVideoQuality",
46681
- type: "radio",
46682
- label: "解析视频动态时的画质偏好",
46683
- description: "「作品解析」开启时生效,仅对视频动态有效",
46684
- orientation: "horizontal",
46685
- disabled: $or($not("push.switch"), $not("push.parsedynamic")),
46686
- color: "warning",
46687
- options: [
46688
- {
46689
- label: "自动选择",
46690
- value: 0
46691
- },
46692
- {
46693
- label: "240P 极速",
46694
- value: 6
46695
- },
46696
- {
46697
- label: "360P 流畅",
46698
- value: 16
46699
- },
46700
- {
46701
- label: "480P 清晰",
46702
- value: 32,
46703
- description: "需登录(配置ck)"
46704
- },
46705
- {
46706
- label: "720P 高清",
46707
- value: 64,
46708
- description: "需登录(配置ck)"
46709
- },
46710
- {
46711
- label: "720P60 高帧率",
46712
- value: 74,
46713
- description: "需登录(配置ck)"
46714
- },
46715
- {
46716
- label: "1080P 高清",
46717
- value: 80,
46718
- description: "需登录(配置ck)"
46719
- },
46720
- {
46721
- label: "1080P+ 高码率",
46722
- value: 112,
46723
- description: "需大会员&视频支持"
46724
- },
46725
- {
46726
- label: "1080P60 高帧率",
46727
- value: 116,
46728
- description: "需大会员&视频支持"
46729
- },
46730
- {
46731
- label: "4K 超清",
46732
- value: 120,
46733
- description: "需大会员&视频支持"
46734
- },
46735
- {
46736
- label: "8K 超高清",
46737
- value: 127,
46738
- description: "需大会员&视频支持"
46739
- }
46740
- ]
46741
- },
46742
- {
46743
- key: "push.pushMaxAutoVideoSize",
46744
- type: "input",
46745
- inputType: "number",
46746
- label: "视频动态的视频体积上限(MB)",
46747
- description: "根据该值自动选择分辨率进行下载。仅在「解析视频动态时的画质偏好」 为 \"自动选择\" 且「作品解析」开启时生效,仅对视频动态有效",
46748
- disabled: $or($not("push.switch"), $not("push.parsedynamic"), $ne("push.pushVideoQuality", 0)),
46749
- color: "warning",
46750
- rules: [{
46751
- min: 1,
46752
- max: 2e4
46753
- }]
46754
- }
46755
- ]
46756
- };
46757
- //#endregion
46758
- //#region src/module/config/cookies.schema.ts
46759
- var cookiesConfigSchema = {
46760
- key: "cookies",
46761
- title: "Cookies 相关",
46762
- subtitle: "建议配置,否则大部分功能无法使用",
46763
- fields: [
46764
- {
46765
- key: "douyin",
46766
- type: "input",
46767
- inputType: "text",
46768
- label: "抖音",
46769
- description: "请输入你的抖音Cookies,不输入则无法使用抖音相关功能噢"
46770
- },
46771
- {
46772
- key: "bilibili",
46773
- type: "input",
46774
- inputType: "text",
46775
- label: "B站",
46776
- description: "请输入你的B站Cookies,不输入则无法使用B站相关功能噢"
46777
- },
46778
- {
46779
- key: "kuaishou",
46780
- type: "input",
46781
- inputType: "text",
46782
- label: "快手",
46783
- description: "请输入你的快手Cookies,不输入则无法使用快手相关功能噢"
46784
- },
46785
- {
46786
- key: "xiaohongshu",
46787
- type: "input",
46788
- inputType: "text",
46789
- label: "小红书",
46790
- description: "请输入你的小红书Cookies,不输入则无法使用小红书相关功能噢"
46791
- }
46792
- ]
46793
- };
46794
- //#endregion
46795
- //#region src/module/config/douyin.schema.ts
46796
- var douyinConfigSchema = {
46797
- key: "douyin",
46798
- title: "抖音相关",
46799
- subtitle: "此处为抖音相关的用户偏好设置",
46800
- fields: [
46801
- {
46802
- key: "switch",
46803
- type: "switch",
46804
- label: "解析开关",
46805
- description: "抖音解析开关,此开关为单独开关"
46806
- },
46807
- {
46808
- key: "tip",
46809
- type: "switch",
46810
- label: "解析提示",
46811
- description: "抖音解析提示,发送提示信息:\"检测到抖音链接,开始解析\"",
46812
- disabled: $not("switch")
46813
- },
46814
- {
46815
- key: "sendContent",
46816
- type: "checkbox",
46817
- label: "解析时发送的内容",
46818
- description: "若什么都不选,可能不会返回任何解析结果",
46819
- orientation: "horizontal",
46820
- disabled: $not("switch"),
46821
- options: [
46822
- {
46823
- label: "视频信息",
46824
- value: "info",
46825
- description: "仅解析视频时有效"
46826
- },
46827
- {
46828
- label: "评论列表",
46829
- value: "comment"
46830
- },
46831
- {
46832
- label: "视频文件",
46833
- value: "video",
46834
- description: "仅对视频作品有效"
46835
- }
46836
- ]
46837
- },
46838
- {
46839
- type: "divider",
46840
- title: "评论详情设置"
46841
- },
46842
- {
46843
- key: "numcomment",
46844
- type: "input",
46845
- inputType: "number",
46846
- label: "评论解析数量",
46847
- disabled: $or($not("switch"), $not($includes("sendContent", "comment"))),
46848
- rules: [{ min: 1 }]
46849
- },
46850
- {
46851
- key: "subCommentLimit",
46852
- type: "input",
46853
- inputType: "number",
46854
- label: "次级评论解析数量",
46855
- description: "次级评论解析数量,当前逻辑不仅无法判断请求的来的评论的嵌套深度,而且「次级评论解析深度」会限制嵌套深度,超过深度的评论会被截断",
46856
- disabled: $or($not("switch"), $not($includes("sendContent", "comment"))),
46857
- rules: [{
46858
- min: 1,
46859
- max: 20
46860
- }]
46861
- },
46862
- {
46863
- key: "commentImageCollection",
46864
- type: "switch",
46865
- label: "是否收集评论区的图片",
46866
- description: "开启后将收集评论区的图片,以合并转发的形式返回",
46867
- disabled: $or($not("switch"), $not($includes("sendContent", "comment")))
46868
- },
46869
- {
46870
- type: "divider",
46871
- title: "渲染与画质设置"
46872
- },
46873
- {
46874
- key: "liveImageMergeMode",
46875
- type: "radio",
46876
- label: "合辑 Live 图 BGM 合并方式",
46877
- orientation: "horizontal",
46878
- disabled: $not("switch"),
46879
- options: [{
46880
- label: "连续",
46881
- value: "continuous",
46882
- description: "BGM 接续播放,结束后自动循环"
46883
- }, {
46884
- label: "独立",
46885
- value: "independent",
46886
- description: "每张图 BGM 从头开始"
46887
- }]
46888
- },
46889
- {
46890
- key: "videoQuality",
46891
- type: "radio",
46892
- label: "画质偏好",
46893
- description: "解析视频的分辨率偏好。",
46894
- orientation: "horizontal",
46895
- disabled: $or($not("switch"), $not($includes("sendContent", "video"))),
46896
- options: [
46897
- {
46898
- label: "自动选择",
46899
- value: "adapt",
46900
- description: "根据「视频体积上限(MB)」自动选择分辨率进行下载"
46901
- },
46902
- {
46903
- label: "标清 540p",
46904
- value: "540p"
46905
- },
46906
- {
46907
- label: "高清 720p",
46908
- value: "720p"
46909
- },
46910
- {
46911
- label: "高清 1080p",
46912
- value: "1080p"
46913
- },
46914
- {
46915
- label: "超清 2k",
46916
- value: "2k"
46917
- },
46918
- {
46919
- label: "超清 4k",
46920
- value: "4k"
46921
- }
46922
- ]
46923
- },
46924
- {
46925
- key: "maxAutoVideoSize",
46926
- type: "input",
46927
- inputType: "number",
46928
- label: "视频体积上限(MB)",
46929
- description: "根据该值自动选择分辨率进行下载。仅在「画质偏好」 为 \"自动选择\" 时生效",
46930
- disabled: $or($not("switch"), $not($includes("sendContent", "video")), $ne("videoQuality", "adapt")),
46931
- rules: [{
46932
- min: 1,
46933
- max: 2e4
46934
- }]
46935
- },
46936
- {
46937
- key: "videoInfoMode",
46938
- type: "radio",
46939
- label: "视频信息返回形式",
46940
- disabled: $not("switch"),
46941
- options: [{
46942
- label: "图片模式",
46943
- value: "image"
46944
- }, {
46945
- label: "文本模式",
46946
- value: "text"
46947
- }]
46948
- },
46949
- {
46950
- key: "displayContent",
46951
- type: "checkbox",
46952
- label: "视频信息的内容",
46953
- description: "若什么都不选,则不会返回任何视频相关信息",
46954
- orientation: "horizontal",
46955
- disabled: $or($not("switch"), $ne("videoInfoMode", "text")),
46956
- options: [
46957
- {
46958
- label: "封面",
46959
- value: "cover"
46960
- },
46961
- {
46962
- label: "标题",
46963
- value: "title"
46964
- },
46965
- {
46966
- label: "作者",
46967
- value: "author"
46968
- },
46969
- {
46970
- label: "视频统计信息",
46971
- value: "stats"
46972
- }
46973
- ]
46974
- },
46975
- {
46976
- type: "divider",
46977
- title: "权限设置"
46978
- },
46979
- {
46980
- key: "loginPerm",
46981
- type: "radio",
46982
- label: "谁可以触发扫码登录",
46983
- description: "修改后需重启",
46984
- orientation: "horizontal",
46985
- options: [
46986
- {
46987
- label: "所有人",
46988
- value: "all"
46989
- },
46990
- {
46991
- label: "管理员",
46992
- value: "admin"
46993
- },
46994
- {
46995
- label: "主人",
46996
- value: "master"
46997
- },
46998
- {
46999
- label: "群主",
47000
- value: "group.owner"
47001
- },
47002
- {
47003
- label: "群管理员",
47004
- value: "group.admin"
47005
- }
47006
- ]
47007
- },
47008
- {
47009
- type: "divider",
47010
- title: "弹幕烧录相关"
47011
- },
47012
- {
47013
- key: "burnDanmaku",
47014
- type: "switch",
47015
- label: "弹幕烧录",
47016
- description: "将弹幕硬编码到视频画面中。开启后视频需要重新编码,耗时较长",
47017
- disabled: $not("switch")
47018
- },
47019
- {
47020
- key: "danmakuArea",
47021
- type: "radio",
47022
- label: "弹幕显示区域",
47023
- description: "限制弹幕范围,避免遮挡视频主体",
47024
- orientation: "horizontal",
47025
- disabled: $or($not("switch"), $not("burnDanmaku")),
47026
- options: [
47027
- {
47028
- label: "1/4 屏",
47029
- value: "0.25"
47030
- },
47031
- {
47032
- label: "半屏",
47033
- value: "0.5"
47034
- },
47035
- {
47036
- label: "3/4 屏",
47037
- value: "0.75"
47038
- },
47039
- {
47040
- label: "全屏",
47041
- value: "1"
47042
- }
47043
- ]
47044
- },
47045
- {
47046
- key: "danmakuFontSize",
47047
- type: "radio",
47048
- label: "弹幕字号",
47049
- description: "弹幕文字大小",
47050
- orientation: "horizontal",
47051
- disabled: $or($not("switch"), $not("burnDanmaku")),
47052
- options: [
47053
- {
47054
- label: "小",
47055
- value: "small"
47056
- },
47057
- {
47058
- label: "中",
47059
- value: "medium"
47060
- },
47061
- {
47062
- label: "大",
47063
- value: "large"
47064
- }
47065
- ]
47066
- },
47067
- {
47068
- key: "danmakuOpacity",
47069
- type: "input",
47070
- inputType: "number",
47071
- label: "弹幕透明度",
47072
- description: "0为完全透明,100为完全不透明,推荐70",
47073
- disabled: $or($not("switch"), $not("burnDanmaku")),
47074
- rules: [{
47075
- min: 0,
47076
- max: 100
47077
- }]
47078
- },
47079
- {
47080
- key: "verticalMode",
47081
- type: "radio",
47082
- label: "竖屏适配",
47083
- description: "针对横屏视频,模拟手机端竖屏观看体验,视频居中显示,上下黑边区域用于展示弹幕",
47084
- orientation: "horizontal",
47085
- disabled: $or($not("switch"), $not("burnDanmaku")),
47086
- options: [
47087
- {
47088
- label: "关闭",
47089
- value: "off",
47090
- description: "保持原始比例,不做转换"
47091
- },
47092
- {
47093
- label: "智能",
47094
- value: "standard",
47095
- description: "仅对宽高比 ≥1.7 的横屏视频生效"
47096
- },
47097
- {
47098
- label: "强制 9:16",
47099
- value: "force",
47100
- description: "所有视频统一转为竖屏"
47101
- }
47102
- ]
47103
- },
47104
- {
47105
- key: "videoCodec",
47106
- type: "radio",
47107
- label: "视频编码格式",
47108
- description: "烧录弹幕后的视频编码格式",
47109
- orientation: "horizontal",
47110
- disabled: $or($not("switch"), $not("burnDanmaku")),
47111
- options: [
47112
- {
47113
- label: "H.264",
47114
- value: "h264"
47115
- },
47116
- {
47117
- label: "H.265",
47118
- value: "h265"
47119
- },
47120
- {
47121
- label: "AV1",
47122
- value: "av1"
47123
- }
47124
- ]
47125
- },
47126
- {
47127
- type: "divider",
47128
- title: "抖音推送相关"
47129
- },
47130
- {
47131
- key: "push.switch",
47132
- type: "switch",
47133
- label: "推送开关",
47134
- description: "推送开关,修改后需重启;使用「#设置抖音推送 + 抖音号」配置推送列表",
47135
- color: "warning"
47136
- },
47137
- {
47138
- key: "push.permission",
47139
- type: "radio",
47140
- label: "谁可以设置推送",
47141
- description: "修改后需重启",
47142
- orientation: "horizontal",
47143
- disabled: $not("push.switch"),
47144
- color: "warning",
47145
- options: [
47146
- {
47147
- label: "所有人",
47148
- value: "all"
47149
- },
47150
- {
47151
- label: "管理员",
47152
- value: "admin"
47153
- },
47154
- {
47155
- label: "主人",
47156
- value: "master"
47157
- },
47158
- {
47159
- label: "群主",
47160
- value: "group.owner"
47161
- },
47162
- {
47163
- label: "群管理员",
47164
- value: "group.admin"
47165
- }
47166
- ]
47167
- },
47168
- {
47169
- key: "push.cron",
47170
- type: "input",
47171
- inputType: "text",
47172
- label: "定时任务表达式",
47173
- description: "定时推送的时间,格式为cron表达式(默认为每十分钟执行一次)",
47174
- color: "warning",
47175
- disabled: $not("push.switch")
47176
- },
47177
- {
47178
- key: "push.parsedynamic",
47179
- type: "switch",
47180
- label: "作品解析",
47181
- description: "触发推送时是否一同解析该作品",
47182
- color: "warning",
47183
- disabled: $not("push.switch")
47184
- },
47185
- {
47186
- key: "push.shareType",
47187
- type: "radio",
47188
- label: "推送图二维码的类型",
47189
- orientation: "horizontal",
47190
- color: "warning",
47191
- disabled: $not("push.switch"),
47192
- options: [{
47193
- label: "网页链接",
47194
- value: "web",
47195
- description: "识别后访问抖音官网对应的作品地址"
47196
- }, {
47197
- label: "下载链接",
47198
- value: "download",
47199
- description: "识别后访问无水印作品下载地址"
47200
- }]
47201
- },
47202
- {
47203
- key: "push.pushVideoQuality",
47204
- type: "radio",
47205
- label: "画质偏好",
47206
- description: "推送解析时解析视频的分辨率偏好。",
47207
- orientation: "horizontal",
47208
- disabled: $not("push.switch"),
47209
- color: "warning",
47210
- options: [
47211
- {
47212
- label: "自动选择",
47213
- value: "adapt",
47214
- description: "根据「视频体积上限(MB)」自动选择分辨率进行下载"
47215
- },
47216
- {
47217
- label: "标清 540p",
47218
- value: "540p"
47219
- },
47220
- {
47221
- label: "高清 720p",
47222
- value: "720p"
47223
- },
47224
- {
47225
- label: "高清 1080p",
47226
- value: "1080p"
47227
- },
47228
- {
47229
- label: "超清 2k",
47230
- value: "2k"
47231
- },
47232
- {
47233
- label: "超清 4k",
47234
- value: "4k"
47235
- }
47236
- ]
47237
- },
47238
- {
47239
- key: "push.pushMaxAutoVideoSize",
47240
- type: "input",
47241
- inputType: "number",
47242
- label: "视频体积上限(MB)",
47243
- description: "推送解析时根据该值自动选择分辨率进行下载。仅在「画质偏好」 为 \"自动选择\" 时生效",
47244
- disabled: $or($not("push.switch"), $ne("push.pushVideoQuality", "adapt")),
47245
- color: "warning",
47246
- rules: [{
47247
- min: 1,
47248
- max: 2e4
47249
- }]
47250
- }
47251
- ]
47252
- };
47253
- //#endregion
47254
- //#region src/module/config/kuaishou.schema.ts
47255
- var kuaishouConfigSchema = {
47256
- key: "kuaishou",
47257
- title: "快手相关",
47258
- subtitle: "此处为快手相关的用户偏好设置",
47259
- fields: [
47260
- {
47261
- key: "switch",
47262
- type: "switch",
47263
- label: "解析开关",
47264
- description: "快手解析开关,此开关为单独开关"
47265
- },
47266
- {
47267
- key: "tip",
47268
- type: "switch",
47269
- label: "解析提示",
47270
- description: "快手解析提示,发送提示信息:\"检测到快手链接,开始解析\"",
47271
- disabled: $not("switch")
47272
- },
47273
- {
47274
- type: "divider",
47275
- title: "评论详情设置"
47276
- },
47277
- {
47278
- key: "comment",
47279
- type: "switch",
47280
- label: "评论解析",
47281
- description: "快手评论解析,开启后可发送快手作品评论图",
47282
- disabled: $not("switch")
47283
- },
47284
- {
47285
- key: "numcomment",
47286
- type: "input",
47287
- inputType: "number",
47288
- label: "评论解析数量",
47289
- disabled: $or($not("switch"), $not("comment")),
47290
- rules: [{ min: 1 }]
47291
- }
47292
- ]
47293
- };
47294
- //#endregion
47295
- //#region src/module/config/request.schema.ts
47296
- var requestConfigSchema = {
47297
- key: "request",
47298
- title: "解析库请求配置相关",
47299
- subtitle: "此处用于管理解析库的网络请求配置",
47300
- fields: [
47301
- {
47302
- key: "timeout",
47303
- type: "input",
47304
- inputType: "number",
47305
- label: "请求超时时间",
47306
- description: "网络请求的超时时间,单位:毫秒",
47307
- rules: [{
47308
- min: 1e3,
47309
- max: 3e5,
47310
- error: "请输入一个范围在 1000 到 300000 之间的数字"
47311
- }]
47312
- },
47313
- {
47314
- key: "User-Agent",
47315
- type: "input",
47316
- inputType: "text",
47317
- label: "User-Agent",
47318
- description: "请求头中的User-Agent字段,用于标识客户端类型",
47319
- placeholder: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
47320
- },
47321
- {
47322
- type: "divider",
47323
- title: "代理配置(可选)"
47324
- },
47325
- {
47326
- key: "proxy.switch",
47327
- type: "switch",
47328
- label: "代理开关",
47329
- description: "开启后需要配置「代理主机」「代理端口」"
47330
- },
47331
- {
47332
- key: "proxy.host",
47333
- type: "input",
47334
- inputType: "text",
47335
- label: "代理主机",
47336
- description: "代理服务器的主机地址,如:127.0.0.1",
47337
- placeholder: "127.0.0.1",
47338
- disabled: $not("proxy.switch")
47339
- },
47340
- {
47341
- key: "proxy.port",
47342
- type: "input",
47343
- inputType: "number",
47344
- label: "代理端口",
47345
- description: "代理服务器的端口号",
47346
- disabled: $not("proxy.switch"),
47347
- rules: [{
47348
- min: 1,
47349
- max: 65535,
47350
- error: "请输入一个范围在 1 到 65535 之间的数字"
47351
- }]
47352
- },
47353
- {
47354
- key: "proxy.protocol",
47355
- type: "radio",
47356
- label: "代理协议",
47357
- orientation: "horizontal",
47358
- disabled: $not("proxy.switch"),
47359
- options: [{
47360
- label: "HTTP",
47361
- value: "http"
47362
- }, {
47363
- label: "HTTPS",
47364
- value: "https"
47365
- }]
47366
- },
47367
- {
47368
- key: "proxy.auth.username",
47369
- type: "input",
47370
- inputType: "text",
47371
- label: "代理用户名",
47372
- description: "代理服务器的认证用户名(如果需要)",
47373
- disabled: $not("proxy.switch")
47374
- },
47375
- {
47376
- key: "proxy.auth.password",
47377
- type: "input",
47378
- inputType: "password",
47379
- label: "代理密码",
47380
- description: "代理服务器的认证密码(如果需要)",
47381
- disabled: $not("proxy.switch")
47382
- }
47383
- ]
47384
- };
47385
- //#endregion
47386
- //#region src/module/config/upload.schema.ts
47387
- var uploadConfigSchema = {
47388
- key: "upload",
47389
- title: "上传和下载相关",
47390
- subtitle: "此处为视频上传和下载相关的用户偏好设置",
47391
- fields: [
47392
- {
47393
- type: "divider",
47394
- title: "发送方式配置"
47395
- },
47396
- {
47397
- key: "videoSendMode",
47398
- type: "radio",
47399
- label: "本地视频发送方式",
47400
- description: "选择发送本地视频的方式:\n• File - 使用 file 协议发送(需 Karin 与协议端同系统)\n• Base64 - 转 base64 发送(传输数据量增大 1/3,不在同一网络环境可能导致额外带宽成本,适合 karin 和协议端不在同一网络环境)",
47401
- disabled: $var("usegroupfile"),
47402
- options: [{
47403
- label: "File 协议(本地文件)",
47404
- value: "file"
47405
- }, {
47406
- label: "Base64(编码传输)",
47407
- value: "base64"
47408
- }]
47409
- },
47410
- {
47411
- key: "usegroupfile",
47412
- type: "switch",
47413
- label: "群文件上传",
47414
- description: "使用群文件上传,开启后会将视频文件上传到群文件中,需配置「群文件上传阈值」。与「本地视频发送方式 = Base64」互斥。",
47415
- disabled: $eq("videoSendMode", "base64")
47416
- },
47417
- {
47418
- key: "groupfilevalue",
47419
- type: "input",
47420
- inputType: "number",
47421
- label: "群文件上传阈值",
47422
- description: "当文件大小超过该值时将使用群文件上传,单位:MB,「使用群文件上传」开启后才会生效",
47423
- disabled: $or($not("usegroupfile"), $eq("videoSendMode", "base64")),
47424
- rules: [{ min: 1 }]
47425
- },
47426
- {
47427
- key: "imageSendMode",
47428
- type: "radio",
47429
- label: "网络图片发送方式",
47430
- description: "选择发送网络图片的方式:\n• URL - 直接传递链接给上游(可能因上游网络问题超时)\n• File - 下载后用 file 协议发送(需 Karin 与协议端同系统)\n• Base64 - 转 base64 发送(传输数据量增大 1/3,不在同一网络环境可能导致额外带宽成本)",
47431
- options: [
47432
- {
47433
- label: "URL 链接(直接传递)",
47434
- value: "url"
47435
- },
47436
- {
47437
- label: "File 协议(本地文件)",
47438
- value: "file"
47439
- },
47440
- {
47441
- label: "Base64(编码传输)",
47442
- value: "base64"
47443
- }
47444
- ]
47445
- },
47446
- {
47447
- type: "divider",
47448
- title: "上传拦截配置"
47449
- },
47450
- {
47451
- key: "usefilelimit",
47452
- type: "switch",
47453
- label: "视频上传拦截",
47454
- description: "开启后会根据视频文件大小判断是否需要上传,需配置「视频拦截阈值」。"
47455
- },
47456
- {
47457
- key: "filelimit",
47458
- type: "input",
47459
- inputType: "number",
47460
- label: "视频拦截阈值",
47461
- description: "视频文件大于该数值则直接结束任务,不会上传,单位: MB,「视频上传拦截」开启后才会生效。",
47462
- disabled: $not("usefilelimit"),
47463
- rules: [{ min: 1 }]
47464
- },
47465
- {
47466
- type: "divider",
47467
- title: "视频压缩配置"
47468
- },
47469
- {
47470
- key: "compress",
47471
- type: "switch",
47472
- label: "压缩视频",
47473
- description: "开启后会将视频文件压缩后再上传,适合上传大文件,任务过程中会吃满CPU,对低配服务器不友好。需配置「压缩触发阈值」与「压缩后的值」"
47474
- },
47475
- {
47476
- key: "compresstrigger",
47477
- type: "input",
47478
- inputType: "number",
47479
- label: "压缩触发阈值",
47480
- description: "触发视频压缩的阈值,单位:MB。当文件大小超过该值时,才会压缩视频,「压缩视频」开启后才会生效",
47481
- disabled: $not("compress"),
47482
- rules: [{ min: 1 }]
47483
- },
47484
- {
47485
- key: "compressvalue",
47486
- type: "input",
47487
- inputType: "number",
47488
- label: "压缩后的值",
47489
- description: "单位:MB,若视频文件大小大于「压缩触发阈值」的值,则会进行压缩至该值(±5%),「压缩视频」开启后才会生效",
47490
- disabled: $not("compress"),
47491
- rules: [{ min: 1 }]
47492
- },
47493
- {
47494
- type: "divider",
47495
- title: "下载限速配置"
47496
- },
47497
- {
47498
- key: "downloadThrottle",
47499
- type: "switch",
47500
- label: "下载限速",
47501
- description: "开启后会限制下载速度,避免触发服务器风控导致连接被重置(ECONNRESET)。如果下载时经常报错\"连接被重置\",建议开启"
47502
- },
47503
- {
47504
- key: "downloadMaxSpeed",
47505
- type: "input",
47506
- inputType: "number",
47507
- label: "最大下载速度",
47508
- description: "单位:MB/s,建议设置为 5-20 之间。设置过高可能触发风控,设置过低会影响下载体验",
47509
- disabled: $not("downloadThrottle"),
47510
- rules: [{
47511
- min: 1,
47512
- max: 1e3,
47513
- error: "请输入一个范围在 1 到 1000 之间的数字"
47514
- }]
47515
- },
47516
- {
47517
- key: "downloadAutoReduce",
47518
- type: "switch",
47519
- label: "断流自动降速",
47520
- description: "当检测到连接被重置时自动降低下载速度,每次断流后速度会降低到当前的 60%",
47521
- disabled: $not("downloadThrottle")
47522
- },
47523
- {
47524
- key: "downloadMinSpeed",
47525
- type: "input",
47526
- inputType: "number",
47527
- label: "最低下载速度",
47528
- description: "单位:MB/s,自动降速时不会低于此值",
47529
- disabled: $or($not("downloadThrottle"), $not("downloadAutoReduce")),
47530
- rules: [{
47531
- min: .1,
47532
- max: 100,
47533
- error: "请输入一个范围在 0.1 到 100 之间的数字"
47534
- }]
47535
- }
47536
- ]
47537
- };
47538
- //#endregion
47539
- //#region src/module/config/xiaohongshu.schema.ts
47540
- var xiaohongshuConfigSchema = {
47541
- key: "xiaohongshu",
47542
- title: "小红书相关",
47543
- subtitle: "此处为小红书相关的用户偏好设置",
47544
- fields: [
47545
- {
47546
- key: "switch",
47547
- type: "switch",
47548
- label: "解析开关",
47549
- description: "小红书解析开关,此开关为单独开关"
47550
- },
47551
- {
47552
- key: "tip",
47553
- type: "switch",
47554
- label: "解析提示",
47555
- description: "小红书解析提示,发送提示信息:\"检测到小红书链接,开始解析\"",
47556
- disabled: $not("switch")
47557
- },
47558
- {
47559
- key: "sendContent",
47560
- type: "checkbox",
47561
- label: "解析时发送的内容",
47562
- description: "若什么都不选,可能不会返回任何解析结果",
47563
- orientation: "horizontal",
47564
- disabled: $not("switch"),
47565
- options: [
47566
- {
47567
- label: "笔记信息",
47568
- value: "info"
47569
- },
47570
- {
47571
- label: "评论列表",
47572
- value: "comment"
47573
- },
47574
- {
47575
- label: "笔记图片",
47576
- value: "image"
47577
- },
47578
- {
47579
- label: "视频文件",
47580
- value: "video",
47581
- description: "仅对视频笔记有效"
47582
- }
47583
- ]
47584
- },
47585
- {
47586
- key: "numcomment",
47587
- type: "input",
47588
- inputType: "number",
47589
- label: "评论解析数量",
47590
- disabled: $or($not("switch"), $not($includes("sendContent", "comment"))),
47591
- rules: [{ min: 1 }]
47592
- },
47593
- {
47594
- type: "divider",
47595
- title: "渲染与画质设置"
47596
- },
47597
- {
47598
- key: "videoQuality",
47599
- type: "radio",
47600
- label: "画质偏好",
47601
- description: "解析视频的分辨率偏好。",
47602
- orientation: "horizontal",
47603
- disabled: $or($not("switch"), $not($includes("sendContent", "video"))),
47604
- options: [
47605
- {
47606
- label: "自动选择",
47607
- value: "adapt",
47608
- description: "根据「视频体积上限(MB)」自动选择分辨率进行下载"
47609
- },
47610
- {
47611
- label: "标清 540p",
47612
- value: "540p"
47613
- },
47614
- {
47615
- label: "高清 720p",
47616
- value: "720p"
47617
- },
47618
- {
47619
- label: "高清 1080p",
47620
- value: "1080p"
47621
- },
47622
- {
47623
- label: "超清 2k",
47624
- value: "2k"
47625
- },
47626
- {
47627
- label: "超清 4k",
47628
- value: "4k"
47629
- }
47630
- ]
47631
- },
47632
- {
47633
- key: "maxAutoVideoSize",
47634
- type: "input",
47635
- inputType: "number",
47636
- label: "视频体积上限(MB)",
47637
- description: "根据该值自动选择分辨率进行下载。仅在「画质偏好」 为 \"自动选择\" 时生效",
47638
- disabled: $or($not("switch"), $not($includes("sendContent", "video")), $ne("videoQuality", "adapt")),
47639
- rules: [{
47640
- min: 1,
47641
- max: 2e4
47642
- }]
47643
- }
47644
- ]
47645
- };
47646
- //#endregion
47647
- //#region src/module/config/index.ts
47648
- /** 所有配置区块 Schema */
47649
- var allSectionSchemas = {
47650
- cookies: cookiesConfigSchema,
47651
- app: appConfigSchema,
47652
- douyin: douyinConfigSchema,
47653
- bilibili: bilibiliConfigSchema,
47654
- kuaishou: kuaishouConfigSchema,
47655
- xiaohongshu: xiaohongshuConfigSchema,
47656
- upload: uploadConfigSchema,
47657
- request: requestConfigSchema
47658
- };
47659
- /** 获取完整配置 Schema(用于 API 返回) */
47660
- function getConfigSchema() {
47661
- return { modules: [
47662
- {
47663
- key: "cookies",
47664
- label: "Cookies 相关",
47665
- sections: [cookiesConfigSchema]
47666
- },
47667
- {
47668
- key: "app",
47669
- label: "插件应用相关",
47670
- sections: [appConfigSchema]
47671
- },
47672
- {
47673
- key: "douyin",
47674
- label: "抖音相关",
47675
- sections: [douyinConfigSchema]
47676
- },
47677
- {
47678
- key: "bilibili",
47679
- label: "B站相关",
47680
- sections: [bilibiliConfigSchema]
47681
- },
47682
- {
47683
- key: "kuaishou",
47684
- label: "快手相关",
47685
- sections: [kuaishouConfigSchema]
47686
- },
47687
- {
47688
- key: "xiaohongshu",
47689
- label: "小红书相关",
47690
- sections: [xiaohongshuConfigSchema]
47691
- },
47692
- {
47693
- key: "upload",
47694
- label: "视频文件上传相关",
47695
- sections: [uploadConfigSchema]
47696
- },
47697
- {
47698
- key: "request",
47699
- label: "解析库请求配置相关",
47700
- sections: [requestConfigSchema]
47701
- }
47702
- ] };
47703
- }
47704
- /** 获取指定模块的 Schema */
47705
- function getModuleSchema(moduleKey) {
47706
- return allSectionSchemas[moduleKey];
47707
- }
47708
- //#endregion
47709
- //#region src/module/server/api/schema.ts
47710
- /**
47711
- * 获取完整配置 Schema
47712
- * GET /api/kkk/v1/schema
47713
- */
47714
- var getFullSchema = async (_req, res) => {
47715
- try {
47716
- const schema = getConfigSchema();
47717
- res.json({
47718
- success: true,
47719
- message: "获取配置 Schema 成功",
47720
- data: schema
47721
- });
47722
- } catch (error) {
47723
- res.status(500).json({
47724
- success: false,
47725
- message: `获取配置 Schema 失败: ${error.message}`,
47726
- data: null
47727
- });
47728
- }
47729
- };
47730
- /**
47731
- * 获取指定模块的 Schema
47732
- * GET /api/kkk/v1/schema/:module
47733
- */
47734
- var getModuleSchemaApi = async (req, res) => {
47735
- try {
47736
- const { module } = req.params;
47737
- const schema = getModuleSchema(Array.isArray(module) ? module[0] : module);
47738
- if (!schema) return res.status(404).json({
47739
- success: false,
47740
- message: `配置模块 "${module}" 的 Schema 不存在`,
47741
- data: null
47742
- });
47743
- res.json({
47744
- success: true,
47745
- message: "获取配置 Schema 成功",
47746
- data: schema
47747
- });
47748
- } catch (error) {
47749
- res.status(500).json({
47750
- success: false,
47751
- message: `获取配置 Schema 失败: ${error.message}`,
47752
- data: null
47753
- });
47754
- }
47755
- };
47756
- //#endregion
47757
44017
  //#region src/module/server/api/index.ts
47758
44018
  /**
47759
44019
  * API 路由聚合
@@ -47761,7 +44021,9 @@ var getModuleSchemaApi = async (req, res) => {
47761
44021
  var apiRouter = express.Router();
47762
44022
  var authMiddlewares = [authMiddleware, signatureVerificationMiddleware];
47763
44023
  apiRouter.get("/bots", ...authMiddlewares, getBots);
44024
+ apiRouter.get("/bots/:botId", ...authMiddlewares, getBotInfo);
47764
44025
  apiRouter.get("/bots/:botId/groups", ...authMiddlewares, getBotGroups);
44026
+ apiRouter.get("/bots/:botId/groups/:groupId", ...authMiddlewares, getBotGroupInfo);
47765
44027
  apiRouter.get("/groups", ...authMiddlewares, getGroups);
47766
44028
  apiRouter.post("/groups/batch", ...authMiddlewares, getGroupsBatch);
47767
44029
  apiRouter.post("/link/resolve", ...authMiddlewares, resolveLink);
@@ -47772,8 +44034,6 @@ apiRouter.get("/config/:module", ...authMiddlewares, getConfigModule);
47772
44034
  apiRouter.put("/config/:module", ...authMiddlewares, updateConfigModule);
47773
44035
  apiRouter.post("/config/:module", ...authMiddlewares, updateConfigModule);
47774
44036
  apiRouter.patch("/config/:module", ...authMiddlewares, patchConfigItem);
47775
- apiRouter.get("/schema", ...authMiddlewares, getFullSchema);
47776
- apiRouter.get("/schema/:module", ...authMiddlewares, getModuleSchemaApi);
47777
44037
  apiRouter.use("/platforms/douyin", ...authMiddlewares, router);
47778
44038
  apiRouter.use("/platforms/bilibili", ...authMiddlewares, router$1);
47779
44039
  //#endregion
@@ -47966,6 +44226,19 @@ var proxyOptions = {
47966
44226
  target: "https://developer.huawei.com",
47967
44227
  changeOrigin: true
47968
44228
  };
44229
+ var webDistPath = path.join(Root.pluginPath, "lib", "web");
44230
+ var webIndexPath = path.join(webDistPath, "index.html");
44231
+ var sendWebIndex = (res) => {
44232
+ try {
44233
+ const html = fs.readFileSync(webIndexPath, "utf-8");
44234
+ res.setHeader("Cache-Control", "no-cache");
44235
+ res.type("html").send(html);
44236
+ } 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);
44240
+ }
44241
+ };
47969
44242
  server.use(import_lib.default());
47970
44243
  server.use("/", createProxyMiddleware(proxyOptions));
47971
44244
  if (process.env.NODE_ENV !== "test") checkPort(3780).then((isOpen) => {
@@ -47996,11 +44269,27 @@ app$1.get("/stream/:filename", videoStreamRouter);
47996
44269
  app$1.get("/video/:filename", getVideoRouter);
47997
44270
  app$1.get("/video/:filename/events", videoPreviewEventsRouter);
47998
44271
  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
+ });
47999
44287
  /** 将子路由挂载到主路由上 */
44288
+ app.use("/kkk", staticRouter);
48000
44289
  app.use("/api/kkk", app$1);
48001
44290
  //#endregion
48002
44291
  //#region src/setup.ts
48003
- var requireVersion = "1.15.5";
44292
+ var requireVersion = "1.16.1";
48004
44293
  if (process.env.NODE_ENV !== "development" && isSemverGreater(requireVersion, Root.karinVersion)) {
48005
44294
  const msg = `[karin-plugin-kkk] 插件构建时的 karin 版本 (${requireVersion}) 高于当前运行版本 (${Root.karinVersion}),可能会出现兼容性问题!`;
48006
44295
  logger.warn(msg);