diff --git a/apps/webUI.js b/apps/webUI.js index 06c23d7..060bcf6 100644 --- a/apps/webUI.js +++ b/apps/webUI.js @@ -29,9 +29,7 @@ export class WebUI extends plugin { this.isOpenWebUI = this.toolsConfig.isOpenWebUI; } - async rWebSwitch(e) { - config.updateField("tools", "isOpenWebUI", !this.isOpenWebUI); - const realIsOpenWebUI = config.getConfig("tools").isOpenWebUI; + async initData(e, realIsOpenWebUI) { if (realIsOpenWebUI) { Promise.all([getBotStatus(e), getBotVersionInfo(e), getBotLoginInfo(e)]).then(values => { const status = values[0].data; @@ -44,13 +42,20 @@ export class WebUI extends plugin { }) }) } + } + + async rWebSwitch(e) { + config.updateField("tools", "isOpenWebUI", !this.isOpenWebUI); + const realIsOpenWebUI = config.getConfig("tools").isOpenWebUI; + // 初始化数据 + await this.initData(e, realIsOpenWebUI); // 这里有点延迟,需要写反 - e.reply(`R插件 WebUI:${ realIsOpenWebUI ? "开启\n🚀 请重启以启动 WebUI" : "关闭" }`); + e.reply(`R插件可视化面板:${ realIsOpenWebUI ? "✅已开启" : "❌已关闭" },重启后生效`); return true; } async rWebStatus(e) { - e.reply(`R插件 WebUI:${ this.toolsConfig.isOpenWebUI ? "开启" : "关闭" }`); + e.reply(`R插件可视化面板:${ this.toolsConfig.isOpenWebUI ? "✅开启" : "❌关闭" }`); return true; } } diff --git a/server/components/common/ConfigItem.jsx b/server/components/common/ConfigItem.jsx new file mode 100644 index 0000000..33b36bf --- /dev/null +++ b/server/components/common/ConfigItem.jsx @@ -0,0 +1,47 @@ +import React from 'react'; + +export const ConfigToggle = ({ label, checked, onChange }) => ( +
+ +
+); + +export const ConfigInput = ({ label, value, onChange, type = "text", placeholder = "" }) => ( +
+ + onChange(type === "number" ? parseInt(e.target.value) : e.target.value)} + placeholder={placeholder} + className="input input-bordered" + /> +
+); + +export const ConfigSelect = ({ label, value, onChange, options }) => ( +
+ + +
+); \ No newline at end of file diff --git a/server/components/contents/bili.jsx b/server/components/contents/bili.jsx index eda9dd8..44627de 100644 --- a/server/components/contents/bili.jsx +++ b/server/components/contents/bili.jsx @@ -2,71 +2,69 @@ import { useState, useEffect } from 'react'; import { BILI_CDN_SELECT_LIST, BILI_DOWNLOAD_METHOD, BILI_RESOLUTION_LIST } from "../../../constants/constant.js"; import { readYamlConfig, updateYamlConfig } from '../../utils/yamlHelper'; import Toast from "../toast.jsx"; +import { ConfigToggle, ConfigInput, ConfigSelect } from '../common/ConfigItem'; + +// 定义配置项 +const BILI_CONFIG = { + toggles: [ + { key: 'biliDisplayCover', label: '显示封面' }, + { key: 'biliDisplayInfo', label: '显示视频信息' }, + { key: 'biliDisplayIntro', label: '显示简介' }, + { key: 'biliDisplayOnline', label: '显示在线人数' }, + { key: 'biliDisplaySummary', label: '显示总结' }, + { key: 'biliUseBBDown', label: '使用BBDown' }, + ], + inputs: [ + { key: 'biliSessData', label: 'SESSDATA', type: 'text', placeholder: '请输入Bilibili SESSDATA' }, + { key: 'biliDuration', label: '视频时长限制(秒)', type: 'number' }, + { key: 'biliIntroLenLimit', label: '简介长度限制', type: 'number' }, + ], + selects: [ + { key: 'biliCDN', label: 'CDN选择', options: BILI_CDN_SELECT_LIST }, + { key: 'biliDownloadMethod', label: '下载方式', options: BILI_DOWNLOAD_METHOD }, + { key: 'biliResolution', label: '视频画质', options: BILI_RESOLUTION_LIST }, + ] +}; + +// 默认配置 +const DEFAULT_CONFIG = { + biliSessData: '', + biliDuration: 480, + biliIntroLenLimit: 50, + biliDisplayCover: true, + biliDisplayInfo: true, + biliDisplayIntro: true, + biliDisplayOnline: true, + biliDisplaySummary: false, + biliUseBBDown: false, + biliCDN: 0, + biliDownloadMethod: 0, + biliResolution: 5 +}; export default function Bili() { - const [config, setConfig] = useState({ - biliSessData: '', - biliDuration: 480, - biliIntroLenLimit: 50, - biliDisplayCover: true, - biliDisplayInfo: true, - biliDisplayIntro: true, - biliDisplayOnline: true, - biliDisplaySummary: false, - biliUseBBDown: false, - biliCDN: 0, - biliDownloadMethod: 0, - biliResolution: 5 - }); - + const [config, setConfig] = useState(DEFAULT_CONFIG); const [loading, setLoading] = useState(false); - // 读取配置 useEffect(() => { const loadConfig = async () => { const yamlConfig = await readYamlConfig(); if (yamlConfig) { - setConfig({ - biliSessData: yamlConfig.biliSessData || '', - biliDuration: yamlConfig.biliDuration || 480, - biliIntroLenLimit: yamlConfig.biliIntroLenLimit || 50, - biliDisplayCover: yamlConfig.biliDisplayCover ?? true, - biliDisplayInfo: yamlConfig.biliDisplayInfo ?? true, - biliDisplayIntro: yamlConfig.biliDisplayIntro ?? true, - biliDisplayOnline: yamlConfig.biliDisplayOnline ?? true, - biliDisplaySummary: yamlConfig.biliDisplaySummary ?? false, - biliUseBBDown: yamlConfig.biliUseBBDown ?? false, - biliCDN: yamlConfig.biliCDN || 0, - biliDownloadMethod: yamlConfig.biliDownloadMethod || 0, - biliResolution: yamlConfig.biliResolution || 5 + const newConfig = {}; + Object.keys(DEFAULT_CONFIG).forEach(key => { + newConfig[key] = yamlConfig[key] ?? DEFAULT_CONFIG[key]; }); + setConfig(newConfig); } }; - loadConfig(); }, []); - // 保存配置 const handleSave = async () => { setLoading(true); try { - const success = await updateYamlConfig({ - biliSessData: config.biliSessData, - biliDuration: config.biliDuration, - biliIntroLenLimit: config.biliIntroLenLimit, - biliDisplayCover: config.biliDisplayCover, - biliDisplayInfo: config.biliDisplayInfo, - biliDisplayIntro: config.biliDisplayIntro, - biliDisplayOnline: config.biliDisplayOnline, - biliDisplaySummary: config.biliDisplaySummary, - biliUseBBDown: config.biliUseBBDown, - biliCDN: config.biliCDN, - biliDownloadMethod: config.biliDownloadMethod, - biliResolution: config.biliResolution - }); - + const success = await updateYamlConfig(config); if (success) { - // 使用 daisyUI 的 toast 提示 document.getElementById('toast-success').classList.remove('hidden'); setTimeout(() => { document.getElementById('toast-success').classList.add('hidden'); @@ -79,212 +77,67 @@ export default function Bili() { } }; - // 重置配置 - const handleReset = async () => { - const yamlConfig = await readYamlConfig(); - if (yamlConfig) { - setConfig({ - biliSessData: yamlConfig.biliSessData || '', - biliDuration: yamlConfig.biliDuration || 480, - biliIntroLenLimit: yamlConfig.biliIntroLenLimit || 50, - biliDisplayCover: yamlConfig.biliDisplayCover ?? true, - biliDisplayInfo: yamlConfig.biliDisplayInfo ?? true, - biliDisplayIntro: yamlConfig.biliDisplayIntro ?? true, - biliDisplayOnline: yamlConfig.biliDisplayOnline ?? true, - biliDisplaySummary: yamlConfig.biliDisplaySummary ?? false, - biliUseBBDown: yamlConfig.biliUseBBDown ?? false, - biliCDN: yamlConfig.biliCDN || 0, - biliDownloadMethod: yamlConfig.biliDownloadMethod || 0, - biliResolution: yamlConfig.biliResolution || 5 - }); - } + const handleConfigChange = (key, value) => { + setConfig(prev => ({ ...prev, [key]: value })); }; return (
- {/* 成功提示 */}

Bilibili 配置

- {/* 基础配置部分 */}

基础配置

- {/* SESSDATA配置 */} -
- - setConfig({ ...config, biliSessData: e.target.value })} - placeholder="请输入Bilibili SESSDATA" - className="input input-bordered w-full" - /> -
- - {/* 数值配置部分 */} + {/* 输入框配置 */}
-
- - setConfig({ ...config, biliDuration: parseInt(e.target.value) })} - className="input input-bordered" + {BILI_CONFIG.inputs.map(item => ( + handleConfigChange(item.key, value)} + placeholder={item.placeholder} /> -
-
- - setConfig({ ...config, biliIntroLenLimit: parseInt(e.target.value) })} - className="input input-bordered" - /> -
+ ))}
- {/* 开关配置部分 */} + {/* 开关配置 */}
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
+ {BILI_CONFIG.toggles.map(item => ( + handleConfigChange(item.key, value)} + /> + ))}
- {/* 下拉选择配置部分 */} + {/* 选择框配置 */}
-
- - -
-
- - -
-
- - -
+ {BILI_CONFIG.selects.map(item => ( + handleConfigChange(item.key, value)} + options={item.options} + /> + ))}
- {/* 保存按钮 */} + {/* 操作按钮 */}
- ) + ); } diff --git a/server/components/contents/generic.jsx b/server/components/contents/generic.jsx index e7a2799..321f5ad 100644 --- a/server/components/contents/generic.jsx +++ b/server/components/contents/generic.jsx @@ -1,69 +1,170 @@ import { useState, useEffect } from 'react'; import { readYamlConfig, updateYamlConfig } from '../../utils/yamlHelper'; import Toast from "../toast.jsx"; +import { ConfigToggle, ConfigInput, ConfigSelect } from '../common/ConfigItem'; +import { AI_MODEL_LIST } from "../../../constants/constant.js"; + +// 定义配置项 +const GENERIC_CONFIG = { + basicInputs: [ + { + key: 'defaultPath', + label: '视频保存路径', + type: 'text', + placeholder: '请输入视频保存路径...', + defaultValue: './data/rcmp4/' + }, + { + key: 'videoSizeLimit', + label: '视频大小限制(MB)', + type: 'number', + hint: '超过限制转为群文件', + defaultValue: 70 + } + ], + proxyInputs: [ + { + key: 'proxyAddr', + label: '魔法地址', + type: 'text', + placeholder: '请输入代理地址...', + defaultValue: '127.0.0.1' + }, + { + key: 'proxyPort', + label: '魔法端口', + type: 'text', + placeholder: '请输入代理端口...', + defaultValue: '7890' + } + ], + streamInputs: [ + { + key: 'identifyPrefix', + label: '识别前缀', + type: 'text', + placeholder: '请输入识别前缀...', + defaultValue: '' + }, + { + key: 'streamDuration', + label: '视频最大时长(秒)', + type: 'number', + defaultValue: 10 + } + ], + concurrencyInputs: [ + { + key: 'queueConcurrency', + label: '队列并发数', + type: 'number', + hint: '仅影响B站下载', + defaultValue: 1 + }, + { + key: 'videoDownloadConcurrency', + label: '视频下载并发数', + type: 'number', + defaultValue: 1 + } + ], + textareas: [ + { + key: 'deeplApiUrls', + label: 'DeepL API地址', + placeholder: '请输入DeepL API地址,多个地址用逗号分隔...', + defaultValue: '' + } + ], + toggles: [ + { + key: 'streamCompatibility', + label: '兼容模式', + hint: 'NCQQ不用开启,其他ICQQ、LLO需要开启', + defaultValue: false + } + ], + otherInputs: [ + { + key: 'xiaohongshuCookie', + label: '小红书Cookie', + type: 'text', + placeholder: '请输入小红书的Cookie...', + defaultValue: '' + }, + { + key: 'autoclearTrashtime', + label: '自动清理时间', + type: 'text', + placeholder: '请输入Cron表达式...', + hint: 'Cron表达式', + defaultValue: '0 0 8 * * ?' + } + ], + aiInputs: [ + { + key: 'aiBaseURL', + label: 'AI接口地址', + type: 'text', + placeholder: '请输入AI接口地址...', + defaultValue: '', + hint: '用于识图的接口,kimi默认接口为:https://api.moonshot.cn,其他服务商自己填写' + }, + { + key: 'aiApiKey', + label: 'API Key', + type: 'text', + placeholder: '请输入API Key...', + defaultValue: '', + hint: '用于识图的api key,kimi接口申请:https://platform.moonshot.cn/console/api-keys' + } + ], + aiSelects: [ + { + key: 'aiModel', + label: 'AI模型', + options: [ + { value: 'moonshot-v1-8k', label: 'Moonshot V1 8K' }, + { value: 'moonshot-v1-32k', label: 'Moonshot V1 32K' }, + { value: 'moonshot-v1-128k', label: 'Moonshot V1 128K' }, + // 可以根据需要添加更多模型选项 + ], + defaultValue: 'moonshot-v1-8k', + hint: '模型,使用kimi不用填写,其他要填写' + } + ] +}; + +// 生成默认配置 +const DEFAULT_CONFIG = Object.values(GENERIC_CONFIG).reduce((acc, group) => { + group.forEach(item => { + acc[item.key] = item.defaultValue; + }); + return acc; +}, {}); export default function Generic() { - const [config, setConfig] = useState({ - defaultPath: './data/rcmp4/', - videoSizeLimit: 70, - proxyAddr: '127.0.0.1', - proxyPort: '7890', - identifyPrefix: '', - streamDuration: 10, - streamCompatibility: false, - queueConcurrency: 1, - videoDownloadConcurrency: 1, - autoclearTrashtime: '0 0 8 * * ?', - xiaohongshuCookie: '', - deeplApiUrls: '' - }); - + const [config, setConfig] = useState(DEFAULT_CONFIG); const [loading, setLoading] = useState(false); - // 读取配置 useEffect(() => { const loadConfig = async () => { const yamlConfig = await readYamlConfig(); if (yamlConfig) { - setConfig({ - defaultPath: yamlConfig.defaultPath || './data/rcmp4/', - videoSizeLimit: yamlConfig.videoSizeLimit || 70, - proxyAddr: yamlConfig.proxyAddr || '127.0.0.1', - proxyPort: yamlConfig.proxyPort || '7890', - identifyPrefix: yamlConfig.identifyPrefix || '', - streamDuration: yamlConfig.streamDuration || 10, - streamCompatibility: yamlConfig.streamCompatibility ?? false, - queueConcurrency: yamlConfig.queueConcurrency || 1, - videoDownloadConcurrency: yamlConfig.videoDownloadConcurrency || 1, - autoclearTrashtime: yamlConfig.autoclearTrashtime || '0 0 8 * * ?', - xiaohongshuCookie: yamlConfig.xiaohongshuCookie || '', - deeplApiUrls: yamlConfig.deeplApiUrls || '' + const newConfig = {}; + Object.keys(DEFAULT_CONFIG).forEach(key => { + newConfig[key] = yamlConfig[key] ?? DEFAULT_CONFIG[key]; }); + setConfig(newConfig); } }; - loadConfig(); }, []); - // 保存配置 const handleSave = async () => { setLoading(true); try { - const success = await updateYamlConfig({ - defaultPath: config.defaultPath, - videoSizeLimit: config.videoSizeLimit, - proxyAddr: config.proxyAddr, - proxyPort: config.proxyPort, - identifyPrefix: config.identifyPrefix, - streamDuration: config.streamDuration, - streamCompatibility: config.streamCompatibility, - queueConcurrency: config.queueConcurrency, - videoDownloadConcurrency: config.videoDownloadConcurrency, - autoclearTrashtime: config.autoclearTrashtime, - xiaohongshuCookie: config.xiaohongshuCookie, - deeplApiUrls: config.deeplApiUrls - }); - + const success = await updateYamlConfig(config); if (success) { document.getElementById('generic-toast-success').classList.remove('hidden'); setTimeout(() => { @@ -77,237 +178,132 @@ export default function Generic() { } }; - // 重置配置 - const handleReset = async () => { - const yamlConfig = await readYamlConfig(); - if (yamlConfig) { - setConfig({ - defaultPath: yamlConfig.defaultPath || './data/rcmp4/', - videoSizeLimit: yamlConfig.videoSizeLimit || 70, - proxyAddr: yamlConfig.proxyAddr || '127.0.0.1', - proxyPort: yamlConfig.proxyPort || '7890', - identifyPrefix: yamlConfig.identifyPrefix || '', - streamDuration: yamlConfig.streamDuration || 10, - streamCompatibility: yamlConfig.streamCompatibility ?? false, - queueConcurrency: yamlConfig.queueConcurrency || 1, - videoDownloadConcurrency: yamlConfig.videoDownloadConcurrency || 1, - autoclearTrashtime: yamlConfig.autoclearTrashtime || '0 0 8 * * ?', - xiaohongshuCookie: yamlConfig.xiaohongshuCookie || '', - deeplApiUrls: yamlConfig.deeplApiUrls || '', - }); - } + const handleConfigChange = (key, value) => { + setConfig(prev => ({ ...prev, [key]: value })); }; + // 渲染输入框组 + const renderInputGroup = (inputs, title) => ( +
+ {inputs.map(item => ( +
+ handleConfigChange(item.key, value)} + placeholder={item.placeholder} + /> + {item.hint && ( + + {item.hint} + + )} +
+ ))} +
+ ); + return (
- {/* 成功提示 */}

通用配置

- {/* 基础配置部分 */}

基础配置

- {/* 路径和大小限制配置 */ } + {/* 基础配置 */} + {renderInputGroup(GENERIC_CONFIG.basicInputs)} + + {/* 代理配置 */} +

代理设置

+ {renderInputGroup(GENERIC_CONFIG.proxyInputs)} + + {/* 流媒体配置 */} +

流媒体设置

+ {renderInputGroup(GENERIC_CONFIG.streamInputs)} + + {/* 并发配置 */} +

并发设置

+ {renderInputGroup(GENERIC_CONFIG.concurrencyInputs)} + + {/* DeepL API配置 */} +

API设置

+ {GENERIC_CONFIG.textareas.map(item => ( +
+ +