From 7a8c0b2d29888530206acbfe4f9b7cc684510bd5 Mon Sep 17 00:00:00 2001 From: mtvpls Date: Wed, 21 Jan 2026 11:44:52 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BC=B9=E5=B9=95=E4=B8=8A?= =?UTF-8?q?=E9=99=90=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/play/page.tsx | 71 ++++++++++++++++++++++++++++- src/components/DanmakuPanel.tsx | 1 + src/components/UserMenu.tsx | 81 +++++++++++++++++++++++++++++++++ src/lib/danmaku/types.ts | 2 + 4 files changed, 154 insertions(+), 1 deletion(-) diff --git a/src/app/play/page.tsx b/src/app/play/page.tsx index 4f2635e..eac5e03 100644 --- a/src/app/play/page.tsx +++ b/src/app/play/page.tsx @@ -367,6 +367,7 @@ function PlayPageClient() { >([]); const [danmakuLoading, setDanmakuLoading] = useState(false); const [danmakuCount, setDanmakuCount] = useState(0); + const [danmakuOriginalCount, setDanmakuOriginalCount] = useState(0); const danmakuPluginRef = useRef(null); const danmakuSettingsRef = useRef(danmakuSettings); @@ -705,6 +706,22 @@ function PlayPageClient() { } } + // 应用弹幕数量限制 + const maxCount = typeof window !== 'undefined' ? parseInt(localStorage.getItem('danmakuMaxCount') || '0', 10) : 0; + if (maxCount > 0 && danmakuData.length > maxCount) { + const originalCount = danmakuData.length; + const step = danmakuData.length / maxCount; + const limitedData = []; + for (let i = 0; i < maxCount; i++) { + limitedData.push(danmakuData[Math.floor(i * step)]); + } + danmakuData = limitedData; + setDanmakuOriginalCount(originalCount); + console.log(`弹幕数量限制: 原始 ${originalCount} 条,限制到 ${danmakuData.length} 条`); + } else { + setDanmakuOriginalCount(0); + } + // 加载弹幕到插件 const currentSettings = danmakuSettingsRef.current; danmakuPluginRef.current.config({ @@ -3602,6 +3619,22 @@ function PlayPageClient() { } } + // 应用弹幕数量限制 + const maxCount = typeof window !== 'undefined' ? parseInt(localStorage.getItem('danmakuMaxCount') || '0', 10) : 0; + if (maxCount > 0 && danmakuData.length > maxCount) { + const originalCount = danmakuData.length; + const step = danmakuData.length / maxCount; + const limitedData = []; + for (let i = 0; i < maxCount; i++) { + limitedData.push(danmakuData[Math.floor(i * step)]); + } + danmakuData = limitedData; + setDanmakuOriginalCount(originalCount); + console.log(`弹幕数量限制: 原始 ${originalCount} 条,限制到 ${danmakuData.length} 条`); + } else { + setDanmakuOriginalCount(0); + } + // 加载弹幕到插件,同时应用当前的弹幕设置 const currentSettings = danmakuSettingsRef.current; danmakuPluginRef.current.config({ @@ -3634,6 +3667,7 @@ function PlayPageClient() { episodeTitle: metadata.episodeTitle || '', searchKeyword: metadata.searchKeyword, danmakuCount: danmakuData.length, + danmakuOriginalCount: danmakuOriginalCount > 0 ? danmakuOriginalCount : undefined, }); } @@ -3801,6 +3835,22 @@ function PlayPageClient() { }); } + // 应用弹幕数量限制 + const maxCount = typeof window !== 'undefined' ? parseInt(localStorage.getItem('danmakuMaxCount') || '0', 10) : 0; + if (maxCount > 0 && danmakuData.length > maxCount) { + const originalCount = danmakuData.length; + const step = danmakuData.length / maxCount; + const limitedData = []; + for (let i = 0; i < maxCount; i++) { + limitedData.push(danmakuData[Math.floor(i * step)]); + } + danmakuData = limitedData; + setDanmakuOriginalCount(originalCount); + console.log(`弹幕数量限制: 原始 ${originalCount} 条,限制到 ${danmakuData.length} 条`); + } else { + setDanmakuOriginalCount(0); + } + // 加载弹幕到播放器(使用 reset 方法清空,不触发显示/隐藏事件) if (danmakuPluginRef.current) { danmakuPluginRef.current.reset(); @@ -4050,6 +4100,22 @@ function PlayPageClient() { } } + // 应用弹幕数量限制 + const maxCount = typeof window !== 'undefined' ? parseInt(localStorage.getItem('danmakuMaxCount') || '0', 10) : 0; + if (maxCount > 0 && danmakuData.length > maxCount) { + const originalCount = danmakuData.length; + const step = danmakuData.length / maxCount; + const limitedData = []; + for (let i = 0; i < maxCount; i++) { + limitedData.push(danmakuData[Math.floor(i * step)]); + } + danmakuData = limitedData; + setDanmakuOriginalCount(originalCount); + console.log(`弹幕数量限制: 原始 ${originalCount} 条,限制到 ${danmakuData.length} 条`); + } else { + setDanmakuOriginalCount(0); + } + // 加载弹幕到插件 const currentSettings = danmakuSettingsRef.current; danmakuPluginRef.current.config({ @@ -7107,7 +7173,10 @@ function PlayPageClient() { /> - 已加载 {danmakuCount} 条弹幕 + {danmakuOriginalCount > 0 + ? `已加载 ${danmakuCount} 条弹幕(原始 ${danmakuOriginalCount} 条)` + : `已加载 ${danmakuCount} 条弹幕` + } ) : ( diff --git a/src/components/DanmakuPanel.tsx b/src/components/DanmakuPanel.tsx index 66304d0..b2c332b 100644 --- a/src/components/DanmakuPanel.tsx +++ b/src/components/DanmakuPanel.tsx @@ -241,6 +241,7 @@ export default function DanmakuPanel({ {currentSelection.danmakuCount !== undefined && (

弹幕数量: {currentSelection.danmakuCount} + {currentSelection.danmakuOriginalCount && ` (原始 ${currentSelection.danmakuOriginalCount} 条)`}

)} diff --git a/src/components/UserMenu.tsx b/src/components/UserMenu.tsx index 29230f8..1d4c1e1 100644 --- a/src/components/UserMenu.tsx +++ b/src/components/UserMenu.tsx @@ -114,6 +114,7 @@ export const UserMenu: React.FC = () => { const [nextEpisodePreCache, setNextEpisodePreCache] = useState(true); const [nextEpisodeDanmakuPreload, setNextEpisodeDanmakuPreload] = useState(true); const [disableAutoLoadDanmaku, setDisableAutoLoadDanmaku] = useState(false); + const [danmakuMaxCount, setDanmakuMaxCount] = useState(0); const [searchTraditionalToSimplified, setSearchTraditionalToSimplified] = useState(false); // 邮件通知设置 @@ -409,6 +410,19 @@ export const UserMenu: React.FC = () => { setDisableAutoLoadDanmaku(savedDisableAutoLoadDanmaku === 'true'); } + const savedDanmakuMaxCount = localStorage.getItem('danmakuMaxCount'); + if (savedDanmakuMaxCount !== null) { + const value = parseInt(savedDanmakuMaxCount, 10); + // 兼容旧版本的0-4映射值 + if (value <= 4) { + const mappedValue = value === 0 ? 0 : value === 1 ? 1000 : value === 2 ? 3000 : value === 3 ? 5000 : 10000; + setDanmakuMaxCount(mappedValue); + localStorage.setItem('danmakuMaxCount', String(mappedValue)); + } else { + setDanmakuMaxCount(value); + } + } + // 加载首页模块配置 const savedHomeModules = localStorage.getItem('homeModules'); if (savedHomeModules !== null) { @@ -772,6 +786,13 @@ export const UserMenu: React.FC = () => { } }; + const handleDanmakuMaxCountChange = (value: number) => { + setDanmakuMaxCount(value); + if (typeof window !== 'undefined') { + localStorage.setItem('danmakuMaxCount', String(value)); + } + }; + const handleSearchTraditionalToSimplifiedToggle = (value: boolean) => { setSearchTraditionalToSimplified(value); if (typeof window !== 'undefined') { @@ -891,6 +912,7 @@ export const UserMenu: React.FC = () => { localStorage.setItem('nextEpisodePreCache', 'true'); localStorage.setItem('nextEpisodeDanmakuPreload', 'true'); localStorage.setItem('disableAutoLoadDanmaku', 'false'); + localStorage.setItem('danmakuMaxCount', '0'); localStorage.setItem('homeModules', JSON.stringify(defaultHomeModules)); localStorage.setItem('searchTraditionalToSimplified', 'false'); window.dispatchEvent(new CustomEvent('homeModulesUpdated')); @@ -1879,6 +1901,65 @@ export const UserMenu: React.FC = () => { + {/* 弹幕加载上限 */} +
+
+ + 弹幕加载上限 + + + {danmakuMaxCount === 0 ? '无上限' : `${danmakuMaxCount} 条`} + +
+
+ handleDanmakuMaxCountChange(parseInt(e.target.value))} + className='flex-1 h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700' + style={{ + background: `linear-gradient(to right, #10b981 0%, #10b981 ${(danmakuMaxCount / 10000) * 100}%, #e5e7eb ${(danmakuMaxCount / 10000) * 100}%, #e5e7eb 100%)` + }} + /> +
+
+ + + + +
+

+ 限制加载的弹幕数量,减少性能消耗 +

+
+ {/* 清除弹幕缓存 */}
diff --git a/src/lib/danmaku/types.ts b/src/lib/danmaku/types.ts index d032a53..642c2b7 100644 --- a/src/lib/danmaku/types.ts +++ b/src/lib/danmaku/types.ts @@ -57,6 +57,7 @@ export interface DanmakuSettings { filterRules: string[]; // 过滤规则(正则表达式) unlimited: boolean; // 无限弹幕 synchronousPlayback: boolean; // 同步播放 + maxCount?: number; // 弹幕加载上限 } // 自动匹配请求 @@ -115,4 +116,5 @@ export interface DanmakuSelection { episodeTitle: string; searchKeyword?: string; // 用户搜索时使用的关键词 danmakuCount?: number; // 弹幕数量 + danmakuOriginalCount?: number; // 原始弹幕数量 }