From 97d479786b293d7a9272f933d77bf56a9b82f28c Mon Sep 17 00:00:00 2001 From: mtvpls Date: Fri, 3 Apr 2026 10:34:52 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BC=B9=E5=B9=95=E5=86=85?= =?UTF-8?q?=E7=BD=AE=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/admin/page.tsx | 138 +++++++++++++++++--------- src/app/api/admin/site/route.ts | 6 ++ src/app/api/danmaku/comment/route.ts | 9 +- src/app/api/danmaku/episodes/route.ts | 9 +- src/app/api/danmaku/match/route.ts | 9 +- src/app/api/danmaku/search/route.ts | 9 +- src/lib/admin.types.ts | 1 + src/lib/config.ts | 21 +++- src/lib/danmaku/config.ts | 19 ++++ 9 files changed, 145 insertions(+), 76 deletions(-) create mode 100644 src/lib/danmaku/config.ts diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index 4721044..b9700ce 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -341,6 +341,7 @@ interface SiteConfig { DoubanImageProxy: string; DisableYellowFilter: boolean; FluidSearch: boolean; + DanmakuSourceType?: 'builtin' | 'custom'; DanmakuApiBase: string; DanmakuApiToken: string; TMDBApiKey?: string; @@ -7923,7 +7924,8 @@ const SiteConfigComponent = ({ DoubanImageProxy: '', DisableYellowFilter: false, FluidSearch: true, - DanmakuApiBase: 'http://localhost:9321', + DanmakuSourceType: 'builtin', + DanmakuApiBase: 'https://mtvpls-danmu.netlify.app/87654321', DanmakuApiToken: '87654321', TMDBApiKey: '', TMDBProxy: '', @@ -8018,6 +8020,7 @@ const SiteConfigComponent = ({ DoubanImageProxy: config.SiteConfig.DoubanImageProxy || '', DisableYellowFilter: config.SiteConfig.DisableYellowFilter || false, FluidSearch: config.SiteConfig.FluidSearch || true, + DanmakuSourceType: config.SiteConfig.DanmakuSourceType || 'custom', DanmakuApiBase: config.SiteConfig.DanmakuApiBase || 'http://localhost:9321', DanmakuApiToken: config.SiteConfig.DanmakuApiToken || '87654321', @@ -8567,57 +8570,102 @@ const SiteConfigComponent = ({ 弹幕配置
- {/* 弹幕 API 地址 */} -
- - +
+ +
- {/* 弹幕 API Token */} -
- - - setSiteSettings((prev) => ({ - ...prev, - DanmakuApiToken: e.target.value, - })) - } - className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-green-500 focus:border-transparent' - /> -

- 弹幕服务器的访问令牌,默认为 87654321 + {siteSettings.DanmakuSourceType !== 'custom' && ( +

+ ⚠️ 内置弹幕源为多人共享服务,稳定性可能受使用高峰影响,建议自行部署后使用自定义源。

-
+ )} + + {siteSettings.DanmakuSourceType === 'custom' && ( + <> + {/* 弹幕 API 地址 */} +
+ + + setSiteSettings((prev) => ({ + ...prev, + DanmakuApiBase: e.target.value, + })) + } + className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-green-500 focus:border-transparent' + /> +

+ 自定义弹幕服务器的 API 地址。API部署参考 + + danmu_api + +

+
+ + {/* 弹幕 API Token */} +
+ + + setSiteSettings((prev) => ({ + ...prev, + DanmakuApiToken: e.target.value, + })) + } + className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-green-500 focus:border-transparent' + /> +

+ 自定义弹幕服务器的访问令牌,默认为 87654321 +

+
+ + )}
diff --git a/src/app/api/admin/site/route.ts b/src/app/api/admin/site/route.ts index 75ce279..b51a4f5 100644 --- a/src/app/api/admin/site/route.ts +++ b/src/app/api/admin/site/route.ts @@ -39,6 +39,7 @@ export async function POST(request: NextRequest) { DoubanImageProxy, DisableYellowFilter, FluidSearch, + DanmakuSourceType, DanmakuApiBase, DanmakuApiToken, TMDBApiKey, @@ -86,6 +87,7 @@ export async function POST(request: NextRequest) { DoubanImageProxy: string; DisableYellowFilter: boolean; FluidSearch: boolean; + DanmakuSourceType?: 'builtin' | 'custom'; DanmakuApiBase: string; DanmakuApiToken: string; TMDBApiKey?: string; @@ -136,6 +138,9 @@ export async function POST(request: NextRequest) { typeof DoubanImageProxy !== 'string' || typeof DisableYellowFilter !== 'boolean' || typeof FluidSearch !== 'boolean' || + (DanmakuSourceType !== undefined && + DanmakuSourceType !== 'builtin' && + DanmakuSourceType !== 'custom') || typeof DanmakuApiBase !== 'string' || typeof DanmakuApiToken !== 'string' || (TMDBApiKey !== undefined && typeof TMDBApiKey !== 'string') || @@ -195,6 +200,7 @@ export async function POST(request: NextRequest) { DoubanImageProxy, DisableYellowFilter, FluidSearch, + DanmakuSourceType, DanmakuApiBase, DanmakuApiToken, TMDBApiKey, diff --git a/src/app/api/danmaku/comment/route.ts b/src/app/api/danmaku/comment/route.ts index c56059a..4c754f3 100644 --- a/src/app/api/danmaku/comment/route.ts +++ b/src/app/api/danmaku/comment/route.ts @@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { getConfig } from '@/lib/config'; +import { getDanmakuApiBaseUrl } from '@/lib/danmaku/config'; export const runtime = 'nodejs'; @@ -50,13 +51,7 @@ export async function GET(request: NextRequest) { // 从数据库读取弹幕配置 const config = await getConfig(); - const { DanmakuApiBase, DanmakuApiToken } = config.SiteConfig; - - // 构建 API URL - const baseUrl = - DanmakuApiToken === '87654321' - ? DanmakuApiBase - : `${DanmakuApiBase}/${DanmakuApiToken}`; + const baseUrl = getDanmakuApiBaseUrl(config.SiteConfig); let apiUrl: string; diff --git a/src/app/api/danmaku/episodes/route.ts b/src/app/api/danmaku/episodes/route.ts index b28e064..181e93b 100644 --- a/src/app/api/danmaku/episodes/route.ts +++ b/src/app/api/danmaku/episodes/route.ts @@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { getConfig } from '@/lib/config'; +import { getDanmakuApiBaseUrl } from '@/lib/danmaku/config'; export const runtime = 'nodejs'; @@ -28,13 +29,7 @@ export async function GET(request: NextRequest) { // 从数据库读取弹幕配置 const config = await getConfig(); - const { DanmakuApiBase, DanmakuApiToken } = config.SiteConfig; - - // 构建 API URL - const baseUrl = - DanmakuApiToken === '87654321' - ? DanmakuApiBase - : `${DanmakuApiBase}/${DanmakuApiToken}`; + const baseUrl = getDanmakuApiBaseUrl(config.SiteConfig); const apiUrl = `${baseUrl}/api/v2/bangumi/${animeId}`; diff --git a/src/app/api/danmaku/match/route.ts b/src/app/api/danmaku/match/route.ts index 972bf00..03b16b5 100644 --- a/src/app/api/danmaku/match/route.ts +++ b/src/app/api/danmaku/match/route.ts @@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { getConfig } from '@/lib/config'; +import { getDanmakuApiBaseUrl } from '@/lib/danmaku/config'; export const runtime = 'nodejs'; @@ -25,13 +26,7 @@ export async function POST(request: NextRequest) { // 从数据库读取弹幕配置 const config = await getConfig(); - const { DanmakuApiBase, DanmakuApiToken } = config.SiteConfig; - - // 构建 API URL - const baseUrl = - DanmakuApiToken === '87654321' - ? DanmakuApiBase - : `${DanmakuApiBase}/${DanmakuApiToken}`; + const baseUrl = getDanmakuApiBaseUrl(config.SiteConfig); const apiUrl = `${baseUrl}/api/v2/match`; diff --git a/src/app/api/danmaku/search/route.ts b/src/app/api/danmaku/search/route.ts index ecda7ef..5c025d5 100644 --- a/src/app/api/danmaku/search/route.ts +++ b/src/app/api/danmaku/search/route.ts @@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { getConfig } from '@/lib/config'; +import { getDanmakuApiBaseUrl } from '@/lib/danmaku/config'; export const runtime = 'nodejs'; @@ -24,13 +25,7 @@ export async function GET(request: NextRequest) { // 从数据库读取弹幕配置 const config = await getConfig(); - const { DanmakuApiBase, DanmakuApiToken } = config.SiteConfig; - - // 构建 API URL - const baseUrl = - DanmakuApiToken === '87654321' - ? DanmakuApiBase - : `${DanmakuApiBase}/${DanmakuApiToken}`; + const baseUrl = getDanmakuApiBaseUrl(config.SiteConfig); const apiUrl = `${baseUrl}/api/v2/search/anime?keyword=${encodeURIComponent(keyword)}`; diff --git a/src/lib/admin.types.ts b/src/lib/admin.types.ts index 117f797..33b8332 100644 --- a/src/lib/admin.types.ts +++ b/src/lib/admin.types.ts @@ -17,6 +17,7 @@ export interface AdminConfig { DisableYellowFilter: boolean; FluidSearch: boolean; // 弹幕配置 + DanmakuSourceType?: 'builtin' | 'custom'; DanmakuApiBase: string; DanmakuApiToken: string; // TMDB配置 diff --git a/src/lib/config.ts b/src/lib/config.ts index 25090aa..c35c9c2 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -4,6 +4,8 @@ import { db } from '@/lib/db'; import { AdminConfig } from './admin.types'; +const BUILTIN_DANMAKU_API_BASE = 'https://mtvpls-danmu.netlify.app/87654321'; + export interface ApiSite { key: string; api: string; @@ -223,6 +225,9 @@ async function getInitConfig(configFile: string, subConfig: { } catch (e) { cfgFile = {} as ConfigFileStruct; } + const hasCustomDanmakuEnv = Boolean( + process.env.DANMAKU_API_BASE || process.env.DANMAKU_API_TOKEN + ); const adminConfig: AdminConfig = { ConfigFile: configSource, ConfigSubscribtion: subConfig, @@ -245,7 +250,10 @@ async function getInitConfig(configFile: string, subConfig: { FluidSearch: process.env.NEXT_PUBLIC_FLUID_SEARCH !== 'false', // 弹幕配置 - DanmakuApiBase: process.env.DANMAKU_API_BASE || 'http://localhost:9321', + DanmakuSourceType: hasCustomDanmakuEnv ? 'custom' : 'builtin', + DanmakuApiBase: + process.env.DANMAKU_API_BASE || + (hasCustomDanmakuEnv ? 'http://localhost:9321' : BUILTIN_DANMAKU_API_BASE), DanmakuApiToken: process.env.DANMAKU_API_TOKEN || '87654321', // TMDB配置 TMDBApiKey: process.env.TMDB_API_KEY || '', @@ -439,7 +447,8 @@ export function configSelfCheck(adminConfig: AdminConfig): AdminConfig { DoubanImageProxy: '', DisableYellowFilter: false, FluidSearch: true, - DanmakuApiBase: 'http://localhost:9321', + DanmakuSourceType: 'builtin', + DanmakuApiBase: BUILTIN_DANMAKU_API_BASE, DanmakuApiToken: '87654321', PansouApiUrl: '', PansouUsername: '', @@ -461,8 +470,14 @@ export function configSelfCheck(adminConfig: AdminConfig): AdminConfig { }; } // 确保弹幕配置存在 + if (adminConfig.SiteConfig.DanmakuSourceType === undefined) { + adminConfig.SiteConfig.DanmakuSourceType = 'custom'; + } if (!adminConfig.SiteConfig.DanmakuApiBase) { - adminConfig.SiteConfig.DanmakuApiBase = 'http://localhost:9321'; + adminConfig.SiteConfig.DanmakuApiBase = + adminConfig.SiteConfig.DanmakuSourceType === 'builtin' + ? BUILTIN_DANMAKU_API_BASE + : 'http://localhost:9321'; } if (!adminConfig.SiteConfig.DanmakuApiToken) { adminConfig.SiteConfig.DanmakuApiToken = '87654321'; diff --git a/src/lib/danmaku/config.ts b/src/lib/danmaku/config.ts new file mode 100644 index 0000000..0e9dec4 --- /dev/null +++ b/src/lib/danmaku/config.ts @@ -0,0 +1,19 @@ +import type { AdminConfig } from '@/lib/admin.types'; + +export const BUILTIN_DANMAKU_API_BASE = 'https://mtvpls-danmu.netlify.app/87654321'; +export const BUILTIN_DANMAKU_API_TOKEN = '87654321'; + +function trimTrailingSlash(value: string) { + return value.replace(/\/+$/, ''); +} + +export function getDanmakuApiBaseUrl(siteConfig: AdminConfig['SiteConfig']) { + if (siteConfig.DanmakuSourceType === 'builtin') { + return BUILTIN_DANMAKU_API_BASE; + } + + const base = trimTrailingSlash(siteConfig.DanmakuApiBase || 'http://localhost:9321'); + const token = (siteConfig.DanmakuApiToken || BUILTIN_DANMAKU_API_TOKEN).trim(); + + return token === BUILTIN_DANMAKU_API_TOKEN ? base : `${base}/${token}`; +}