From af436915b99f684da25e91e0666ec125c48dfc21 Mon Sep 17 00:00:00 2001 From: zhiyu1998 <542716863@qq.com> Date: Fri, 22 Nov 2024 12:02:51 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8feat:=20=E6=B7=BB=E5=8A=A0=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E9=A1=B5=E9=9D=A2=EF=BC=9Abili=E3=80=81tiktok?= =?UTF-8?q?=E3=80=81YouTube=E3=80=81ncm=E3=80=81=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?=E9=80=9A=E7=94=A8=E6=9D=82=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + server/app/r/api/config/route.js | 52 +++++ server/components/content.jsx | 6 + server/components/contents/bili.jsx | 294 ++++++++++++++++++++++++- server/components/contents/generic.jsx | 289 ++++++++++++++++++++++++ server/components/contents/ncm.jsx | 242 ++++++++++++++++++++ server/components/contents/tiktok.jsx | 150 ++++++++++++- server/components/contents/youtube.jsx | 164 ++++++++++++++ server/components/home/bot-config.jsx | 53 +---- server/constants/sidebar.js | 86 ++++++-- server/tailwind.config.js | 2 +- server/utils/yamlHelper.js | 29 +++ 12 files changed, 1305 insertions(+), 63 deletions(-) create mode 100644 server/app/r/api/config/route.js create mode 100644 server/components/contents/generic.jsx create mode 100644 server/components/contents/ncm.jsx create mode 100644 server/components/contents/youtube.jsx create mode 100644 server/utils/yamlHelper.js diff --git a/package.json b/package.json index a75ac9f..254174c 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "dependencies": { "axios": "^1.3.4", "form-data": "^4.0.1", + "js-yaml": "^4.1.0", "next": "^14.2.16", "node-id3": "^0.2.6", "p-queue": "^8.0.1", diff --git a/server/app/r/api/config/route.js b/server/app/r/api/config/route.js new file mode 100644 index 0000000..bfe1a14 --- /dev/null +++ b/server/app/r/api/config/route.js @@ -0,0 +1,52 @@ +import fs from 'fs'; +import yaml from 'js-yaml'; +import path from 'path'; + +export async function GET(req, res) { + const configPath = path.join(process.cwd(), "../", 'config', 'tools.yaml'); + try { + const yamlContent = await fs.promises.readFile(configPath, 'utf8'); + const config = yaml.load(yamlContent); + return new Response(JSON.stringify(config), { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }); + } catch (error) { + console.error('读取配置文件失败:', error); + return new Response(JSON.stringify({ error: '读取配置文件失败' }), { + status: 500, + headers: { 'Content-Type': 'application/json' }, + }); + } +} + +export async function POST(req, res) { + const configPath = path.join(process.cwd(), "../", 'config', 'tools.yaml'); + try { + const updates = await req.json(); + + const yamlContent = await fs.promises.readFile(configPath, 'utf8'); + const currentConfig = yaml.load(yamlContent); + + // 只更新指定的字段 + const newConfig = { ...currentConfig, ...updates }; + + // 转换回YAML并保存 + const newYamlContent = yaml.dump(newConfig, { + indent: 2, + lineWidth: -1, + quotingType: '"' + }); + await fs.promises.writeFile(configPath, newYamlContent, 'utf8'); + return new Response(JSON.stringify({ success: true }), { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }); + } catch (error) { + console.error('更新配置文件失败:', error); + return new Response(JSON.stringify({ error: '更新配置文件失败' }), { + status: 500, + headers: { 'Content-Type': 'application/json' }, + }); + } +} diff --git a/server/components/content.jsx b/server/components/content.jsx index 39ae729..8b18e1a 100644 --- a/server/components/content.jsx +++ b/server/components/content.jsx @@ -1,15 +1,21 @@ import Bili from "./contents/bili.jsx"; +import Generic from "./contents/generic.jsx"; import Home from "./contents/home.jsx"; +import Ncm from "./contents/ncm.jsx"; import Tiktok from "./contents/tiktok.jsx"; import Weekly from "./contents/weekly.jsx"; +import Youtube from "./contents/youtube.jsx"; export function Content({ activeItem }) { // 使用对象映射内容,以便于后期扩展和维护 const contentMap = { "总控制台": , + "通用及杂项": , "哔哩哔哩控制台": , "抖音控制台": , + "网易云控制台": , + "油管控制台": , "周刊预览": }; diff --git a/server/components/contents/bili.jsx b/server/components/contents/bili.jsx index e98ba6e..2184832 100644 --- a/server/components/contents/bili.jsx +++ b/server/components/contents/bili.jsx @@ -1,7 +1,297 @@ -export default function Bili() { +import { useState, useEffect } from 'react'; +import { readYamlConfig, updateYamlConfig } from '../../utils/yamlHelper'; +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 [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 + }); + } + }; + + 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 + }); + + if (success) { + // 使用 daisyUI 的 toast 提示 + document.getElementById('toast-success').classList.remove('hidden'); + setTimeout(() => { + document.getElementById('toast-success').classList.add('hidden'); + }, 3000); + } + } catch (error) { + console.error('保存配置失败:', error); + } finally { + setLoading(false); + } + }; + + // 重置配置 + 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 + }); + } + }; return ( -
Bili
+
+ {/* 成功提示 */} +
+
+ 配置保存成功! +
+
+ +
+

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" + /> +
+
+ + setConfig({ ...config, biliIntroLenLimit: parseInt(e.target.value) })} + className="input input-bordered" + /> +
+
+ + {/* 开关配置部分 */} +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + {/* 下拉选择配置部分 */} +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + {/* 保存按钮 */} +
+ + +
+
+
) } diff --git a/server/components/contents/generic.jsx b/server/components/contents/generic.jsx new file mode 100644 index 0000000..bf08997 --- /dev/null +++ b/server/components/contents/generic.jsx @@ -0,0 +1,289 @@ +import { useState, useEffect } from 'react'; +import { readYamlConfig, updateYamlConfig } from '../../utils/yamlHelper'; + +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 * * ?', + deeplApiUrls: '' + }); + + 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 * * ?', + deeplApiUrls: yamlConfig.deeplApiUrls || '' + }); + } + }; + + 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, + deeplApiUrls: config.deeplApiUrls + }); + + if (success) { + document.getElementById('generic-toast-success').classList.remove('hidden'); + setTimeout(() => { + document.getElementById('generic-toast-success').classList.add('hidden'); + }, 3000); + } + } catch (error) { + console.error('保存配置失败:', error); + } finally { + setLoading(false); + } + }; + + // 重置配置 + 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 * * ?', + deeplApiUrls: yamlConfig.deeplApiUrls || '' + }); + } + }; + + return ( +
+ {/* 成功提示 */} +
+
+ 配置保存成功! +
+
+ +
+

通用配置

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

基础配置

+ + {/* 路径和大小限制配置 */} +
+
+ + setConfig({ ...config, defaultPath: e.target.value })} + placeholder="请输入视频保存路径..." + className="input input-bordered" + /> +
+
+ + setConfig({ ...config, videoSizeLimit: parseInt(e.target.value) })} + className="input input-bordered" + /> +
+
+ + {/* 代理配置 */} +
+
+ + setConfig({ ...config, proxyAddr: e.target.value })} + placeholder="请输入代理地址..." + className="input input-bordered" + /> +
+
+ + setConfig({ ...config, proxyPort: e.target.value })} + placeholder="请输入代理端口..." + className="input input-bordered" + /> +
+
+ + {/* 其他基础配置 */} +
+
+ + setConfig({ ...config, identifyPrefix: e.target.value })} + placeholder="请输入识别前缀..." + className="input input-bordered" + /> +
+
+ + setConfig({ ...config, streamDuration: parseInt(e.target.value) })} + className="input input-bordered" + /> +
+
+ + {/* 并发和定时配置 */} +
+
+ + setConfig({ ...config, queueConcurrency: parseInt(e.target.value) })} + className="input input-bordered" + /> +
+
+ + setConfig({ ...config, videoDownloadConcurrency: parseInt(e.target.value) })} + className="input input-bordered" + /> +
+
+ + {/* DeepL API配置 */} +
+ +