From 1c8a4f7bd2eb4826ee847788e3eb955471bc3b71 Mon Sep 17 00:00:00 2001 From: mtvpls Date: Fri, 9 Jan 2026 10:14:22 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=B9=E5=B9=95=E7=BC=93=E5=AD=98=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E5=85=83=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/play/page.tsx | 60 ++++++++++++++++++++++++++------- src/components/DanmakuPanel.tsx | 5 +++ src/lib/danmaku/api.ts | 26 ++++++++++---- src/lib/danmaku/cache.ts | 48 ++++++++++++++++++++++++-- src/lib/danmaku/types.ts | 1 + 5 files changed, 119 insertions(+), 21 deletions(-) diff --git a/src/app/play/page.tsx b/src/app/play/page.tsx index b7ce3c5..a35cfda 100644 --- a/src/app/play/page.tsx +++ b/src/app/play/page.tsx @@ -606,9 +606,21 @@ function PlayPageClient() { // 先尝试从 IndexedDB 缓存加载 try { - const cachedComments = await getDanmakuFromCache(title, episodeIndex); - if (cachedComments && cachedComments.length > 0) { - console.log(`[弹幕] 使用缓存: title="${title}", episodeIndex=${episodeIndex}, 数量=${cachedComments.length}`); + const cachedData = await getDanmakuFromCache(title, episodeIndex); + if (cachedData && cachedData.comments.length > 0) { + console.log(`[弹幕] 使用缓存: title="${title}", episodeIndex=${episodeIndex}, 数量=${cachedData.comments.length}`); + + // 如果缓存中有元信息,更新当前选择状态 + if (cachedData.metadata) { + setCurrentDanmakuSelection({ + animeId: cachedData.metadata.animeId || 0, + episodeId: cachedData.metadata.episodeId || 0, + animeTitle: cachedData.metadata.animeTitle || '', + episodeTitle: cachedData.metadata.episodeTitle || '', + searchKeyword: cachedData.metadata.searchKeyword, + danmakuCount: cachedData.metadata.danmakuCount || cachedData.comments.length, + }); + } // 如果弹幕插件还未初始化,等待初始化 if (!danmakuPluginRef.current) { @@ -620,7 +632,7 @@ function PlayPageClient() { setDanmakuLoading(true); // 转换弹幕格式 - let danmakuData = convertDanmakuFormat(cachedComments); + let danmakuData = convertDanmakuFormat(cachedData.comments); // 手动应用过滤规则 const filterConfig = danmakuFilterConfigRef.current; @@ -3007,7 +3019,13 @@ function PlayPageClient() { }; // 加载弹幕到播放器 - const loadDanmaku = async (episodeId: number) => { + const loadDanmaku = async (episodeId: number, metadata?: { + animeId?: number; + animeTitle?: string; + episodeTitle?: string; + searchKeyword?: string; + danmakuCount?: number; + }) => { if (!danmakuPluginRef.current) { console.warn('弹幕插件未初始化'); return; @@ -3036,7 +3054,7 @@ function PlayPageClient() { console.log(`[弹幕加载] episodeId=${episodeId}, title="${title}", episodeIndex=${episodeIndex}`); - const comments = await getDanmakuById(episodeId, title, episodeIndex); + const comments = await getDanmakuById(episodeId, title, episodeIndex, metadata); if (comments.length === 0) { console.warn('未获取到弹幕数据'); @@ -3214,8 +3232,14 @@ function PlayPageClient() { } } - // 加载弹幕 - await loadDanmaku(selection.episodeId); + // 加载弹幕,传递元信息 + await loadDanmaku(selection.episodeId, { + animeId: selection.animeId, + animeTitle: selection.animeTitle, + episodeTitle: selection.episodeTitle, + searchKeyword: selection.searchKeyword, + danmakuCount: selection.danmakuCount, + }); }; // 处理用户选择弹幕源 @@ -3330,9 +3354,21 @@ function PlayPageClient() { // 先尝试从 IndexedDB 缓存加载 try { - const cachedComments = await getDanmakuFromCache(title, currentEpisodeIndex); - if (cachedComments && cachedComments.length > 0) { - console.log(`[弹幕] 使用缓存: title="${title}", episodeIndex=${currentEpisodeIndex}, 数量=${cachedComments.length}`); + const cachedData = await getDanmakuFromCache(title, currentEpisodeIndex); + if (cachedData && cachedData.comments.length > 0) { + console.log(`[弹幕] 使用缓存: title="${title}", episodeIndex=${currentEpisodeIndex}, 数量=${cachedData.comments.length}`); + + // 如果缓存中有元信息,更新当前选择状态 + if (cachedData.metadata) { + setCurrentDanmakuSelection({ + animeId: cachedData.metadata.animeId || 0, + episodeId: cachedData.metadata.episodeId || 0, + animeTitle: cachedData.metadata.animeTitle || '', + episodeTitle: cachedData.metadata.episodeTitle || '', + searchKeyword: cachedData.metadata.searchKeyword, + danmakuCount: cachedData.metadata.danmakuCount || cachedData.comments.length, + }); + } // 直接加载缓存的弹幕,不需要调用 API if (!danmakuPluginRef.current) { @@ -3343,7 +3379,7 @@ function PlayPageClient() { setDanmakuLoading(true); // 转换弹幕格式 - let danmakuData = convertDanmakuFormat(cachedComments); + let danmakuData = convertDanmakuFormat(cachedData.comments); // 手动应用过滤规则 const filterConfig = danmakuFilterConfigRef.current; diff --git a/src/components/DanmakuPanel.tsx b/src/components/DanmakuPanel.tsx index 3b6f8fe..66304d0 100644 --- a/src/components/DanmakuPanel.tsx +++ b/src/components/DanmakuPanel.tsx @@ -238,6 +238,11 @@ export default function DanmakuPanel({

{currentSelection.episodeTitle}

+ {currentSelection.danmakuCount !== undefined && ( +

+ 弹幕数量: {currentSelection.danmakuCount} +

+ )} )} diff --git a/src/lib/danmaku/api.ts b/src/lib/danmaku/api.ts index 1cc5cdc..6d1a8ee 100644 --- a/src/lib/danmaku/api.ts +++ b/src/lib/danmaku/api.ts @@ -143,15 +143,22 @@ export async function getEpisodes( export async function getDanmakuById( episodeId: number, title?: string, - episodeIndex?: number + episodeIndex?: number, + metadata?: { + animeId?: number; + animeTitle?: string; + episodeTitle?: string; + searchKeyword?: string; + danmakuCount?: number; + } ): Promise { try { // 1. 如果提供了 title 和 episodeIndex,先尝试从缓存读取 if (title && episodeIndex !== undefined) { - const cachedComments = await getDanmakuFromCache(title, episodeIndex); - if (cachedComments) { - console.log(`[弹幕缓存] 使用缓存: title=${title}, episodeIndex=${episodeIndex}, 数量=${cachedComments.length}`); - return cachedComments; + const cachedData = await getDanmakuFromCache(title, episodeIndex); + if (cachedData) { + console.log(`[弹幕缓存] 使用缓存: title=${title}, episodeIndex=${episodeIndex}, 数量=${cachedData.comments.length}`); + return cachedData.comments; } console.log(`[弹幕缓存] 缓存未命中,从 API 获取: title=${title}, episodeIndex=${episodeIndex}`); } else { @@ -173,7 +180,14 @@ export async function getDanmakuById( if (comments.length > 0 && title && title.trim() !== '' && episodeIndex !== undefined && episodeIndex >= 0) { try { console.log(`[弹幕缓存] 尝试保存缓存: title="${title}", episodeIndex=${episodeIndex}, 数量=${comments.length}`); - await saveDanmakuToCache(title, episodeIndex, comments); + await saveDanmakuToCache(title, episodeIndex, comments, { + animeId: metadata?.animeId, + episodeId: episodeId, + animeTitle: metadata?.animeTitle, + episodeTitle: metadata?.episodeTitle, + searchKeyword: metadata?.searchKeyword, + danmakuCount: metadata?.danmakuCount ?? comments.length, + }); console.log(`[弹幕缓存] 已缓存: title=${title}, episodeIndex=${episodeIndex}, 数量=${comments.length}`); } catch (cacheError) { console.error('[弹幕缓存] 保存缓存失败:', cacheError); diff --git a/src/lib/danmaku/cache.ts b/src/lib/danmaku/cache.ts index 3100e9b..928ab51 100644 --- a/src/lib/danmaku/cache.ts +++ b/src/lib/danmaku/cache.ts @@ -14,6 +14,13 @@ export interface DanmakuCacheData { timestamp: number; // 缓存时间戳 title?: string; // 可选:视频标题 episodeIndex?: number; // 可选:集数索引 + // 弹幕元信息 + animeId?: number; // 动漫ID + episodeId?: number; // 剧集ID + animeTitle?: string; // 动漫标题 + episodeTitle?: string; // 剧集标题 + searchKeyword?: string; // 搜索关键词 + danmakuCount?: number; // 弹幕数量 } // 生成缓存键(title + episodeIndex) @@ -73,7 +80,15 @@ async function openDB(): Promise { export async function saveDanmakuToCache( title: string, episodeIndex: number, - comments: DanmakuComment[] + comments: DanmakuComment[], + metadata?: { + animeId?: number; + episodeId?: number; + animeTitle?: string; + episodeTitle?: string; + searchKeyword?: string; + danmakuCount?: number; + } ): Promise { // 验证参数 if (!title || title.trim() === '') { @@ -104,6 +119,13 @@ export async function saveDanmakuToCache( timestamp: Date.now(), title, episodeIndex, + // 保存弹幕元信息 + animeId: metadata?.animeId, + episodeId: metadata?.episodeId, + animeTitle: metadata?.animeTitle, + episodeTitle: metadata?.episodeTitle, + searchKeyword: metadata?.searchKeyword, + danmakuCount: metadata?.danmakuCount ?? comments.length, // 使用提供的数量或comments长度 }; // 添加调试日志 @@ -141,7 +163,17 @@ export async function saveDanmakuToCache( export async function getDanmakuFromCache( title: string, episodeIndex: number -): Promise { +): Promise<{ + comments: DanmakuComment[]; + metadata?: { + animeId?: number; + episodeId?: number; + animeTitle?: string; + episodeTitle?: string; + searchKeyword?: string; + danmakuCount?: number; + }; +} | null> { // 如果缓存时间设置为 0,不使用缓存 const expireTime = getDanmakuCacheExpireTime(); if (expireTime === 0) { @@ -186,7 +218,17 @@ export async function getDanmakuFromCache( console.log( `从缓存获取弹幕: title=${title}, episodeIndex=${episodeIndex}, 数量=${result.comments.length}, 年龄=${ageMinutes}分钟` ); - resolve(result.comments); + resolve({ + comments: result.comments, + metadata: { + animeId: result.animeId, + episodeId: result.episodeId, + animeTitle: result.animeTitle, + episodeTitle: result.episodeTitle, + searchKeyword: result.searchKeyword, + danmakuCount: result.danmakuCount ?? result.comments.length, + }, + }); }; request.onerror = () => { diff --git a/src/lib/danmaku/types.ts b/src/lib/danmaku/types.ts index 143dd94..d032a53 100644 --- a/src/lib/danmaku/types.ts +++ b/src/lib/danmaku/types.ts @@ -114,4 +114,5 @@ export interface DanmakuSelection { animeTitle: string; episodeTitle: string; searchKeyword?: string; // 用户搜索时使用的关键词 + danmakuCount?: number; // 弹幕数量 }