diff --git a/src/app/api/danmaku/comment/route.ts b/src/app/api/danmaku/comment/route.ts index 480a907..0f8d05e 100644 --- a/src/app/api/danmaku/comment/route.ts +++ b/src/app/api/danmaku/comment/route.ts @@ -68,27 +68,46 @@ export async function GET(request: NextRequest) { apiUrl = `${baseUrl}/api/v2/comment?url=${encodeURIComponent(url!)}&format=xml`; } - const response = await fetch(apiUrl, { - method: 'GET', - headers: { - 'Accept': 'application/xml, text/xml', - }, - }); + // 添加超时控制 + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 10000); // 10秒超时 - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); + try { + const response = await fetch(apiUrl, { + method: 'GET', + headers: { + 'Accept': 'application/xml, text/xml', + }, + signal: controller.signal, + keepalive: true, + }); + + clearTimeout(timeoutId); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + // 获取 XML 文本 + const xmlText = await response.text(); + + // 解析 XML 为 JSON + const comments = parseXmlDanmaku(xmlText); + + return NextResponse.json({ + count: comments.length, + comments, + }); + } catch (fetchError) { + clearTimeout(timeoutId); + + // 如果是超时错误,返回更友好的错误信息 + if (fetchError instanceof Error && fetchError.name === 'AbortError') { + throw new Error('弹幕服务器请求超时,请稍后重试'); + } + + throw fetchError; } - - // 获取 XML 文本 - const xmlText = await response.text(); - - // 解析 XML 为 JSON - const comments = parseXmlDanmaku(xmlText); - - return NextResponse.json({ - count: comments.length, - comments, - }); } catch (error) { console.error('获取弹幕代理错误:', error); return NextResponse.json( diff --git a/src/app/api/danmaku/episodes/route.ts b/src/app/api/danmaku/episodes/route.ts index 488be41..112ee85 100644 --- a/src/app/api/danmaku/episodes/route.ts +++ b/src/app/api/danmaku/episodes/route.ts @@ -38,20 +38,40 @@ export async function GET(request: NextRequest) { const apiUrl = `${baseUrl}/api/v2/bangumi/${animeId}`; - const response = await fetch(apiUrl, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); + // 添加超时控制和重试机制 + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 10000); // 10秒超时 - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); + try { + const response = await fetch(apiUrl, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + signal: controller.signal, + // 添加 keepalive 避免连接被重置 + keepalive: true, + }); + + clearTimeout(timeoutId); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + + return NextResponse.json(data); + } catch (fetchError) { + clearTimeout(timeoutId); + + // 如果是超时错误,返回更友好的错误信息 + if (fetchError instanceof Error && fetchError.name === 'AbortError') { + throw new Error('弹幕服务器请求超时,请稍后重试'); + } + + throw fetchError; } - - const data = await response.json(); - - return NextResponse.json(data); } catch (error) { console.error('获取剧集列表代理错误:', error); return NextResponse.json( diff --git a/src/app/api/danmaku/match/route.ts b/src/app/api/danmaku/match/route.ts index d678524..194a66c 100644 --- a/src/app/api/danmaku/match/route.ts +++ b/src/app/api/danmaku/match/route.ts @@ -35,21 +35,40 @@ export async function POST(request: NextRequest) { const apiUrl = `${baseUrl}/api/v2/match`; - const response = await fetch(apiUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ fileName }), - }); + // 添加超时控制 + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 10000); // 10秒超时 - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); + try { + const response = await fetch(apiUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ fileName }), + signal: controller.signal, + keepalive: true, + }); + + clearTimeout(timeoutId); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + + return NextResponse.json(data); + } catch (fetchError) { + clearTimeout(timeoutId); + + // 如果是超时错误,返回更友好的错误信息 + if (fetchError instanceof Error && fetchError.name === 'AbortError') { + throw new Error('弹幕服务器请求超时,请稍后重试'); + } + + throw fetchError; } - - const data = await response.json(); - - return NextResponse.json(data); } catch (error) { console.error('自动匹配代理错误:', error); return NextResponse.json( diff --git a/src/app/api/danmaku/search/route.ts b/src/app/api/danmaku/search/route.ts index 4924fef..670381a 100644 --- a/src/app/api/danmaku/search/route.ts +++ b/src/app/api/danmaku/search/route.ts @@ -34,20 +34,39 @@ export async function GET(request: NextRequest) { const apiUrl = `${baseUrl}/api/v2/search/anime?keyword=${encodeURIComponent(keyword)}`; - const response = await fetch(apiUrl, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); + // 添加超时控制 + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 10000); // 10秒超时 - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); + try { + const response = await fetch(apiUrl, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + signal: controller.signal, + keepalive: true, + }); + + clearTimeout(timeoutId); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + + return NextResponse.json(data); + } catch (fetchError) { + clearTimeout(timeoutId); + + // 如果是超时错误,返回更友好的错误信息 + if (fetchError instanceof Error && fetchError.name === 'AbortError') { + throw new Error('弹幕服务器请求超时,请稍后重试'); + } + + throw fetchError; } - - const data = await response.json(); - - return NextResponse.json(data); } catch (error) { console.error('弹幕搜索代理错误:', error); return NextResponse.json( diff --git a/src/app/api/debug/watch-room-config/route.ts b/src/app/api/debug/watch-room-config/route.ts index f60e73b..d21f201 100644 --- a/src/app/api/debug/watch-room-config/route.ts +++ b/src/app/api/debug/watch-room-config/route.ts @@ -18,15 +18,23 @@ export async function GET(request: NextRequest) { hasUpstashUrl: !!process.env.UPSTASH_REDIS_REST_URL, hasUpstashToken: !!process.env.UPSTASH_REDIS_REST_TOKEN, hasKvrocksUrl: !!process.env.KVROCKS_URL, + watchRoomEnabled: process.env.WATCH_ROOM_ENABLED, + watchRoomServerType: process.env.WATCH_ROOM_SERVER_TYPE, + hasWatchRoomExternalUrl: !!process.env.WATCH_ROOM_EXTERNAL_SERVER_URL, + hasWatchRoomExternalAuth: !!process.env.WATCH_ROOM_EXTERNAL_SERVER_AUTH, + }, + watchRoomConfig: { + enabled: process.env.WATCH_ROOM_ENABLED === 'true', + serverType: process.env.WATCH_ROOM_SERVER_TYPE || 'internal', + externalServerUrl: process.env.WATCH_ROOM_EXTERNAL_SERVER_URL, + externalServerAuth: process.env.WATCH_ROOM_EXTERNAL_SERVER_AUTH ? '***' : undefined, }, - watchRoomConfig: null as any, configReadError: null as string | null, }; - // 尝试读取配置 + // 尝试读取配置(验证数据库连接) try { - const config = await getConfig(); - debugInfo.watchRoomConfig = config.WatchRoomConfig || null; + await getConfig(); } catch (error) { debugInfo.configReadError = (error as Error).message; } diff --git a/src/app/play/page.tsx b/src/app/play/page.tsx index 363ed2e..a1cb0b6 100644 --- a/src/app/play/page.tsx +++ b/src/app/play/page.tsx @@ -1976,7 +1976,7 @@ function PlayPageClient() { // 检查是否有记忆 const memory = loadDanmakuMemory(title); if (memory) { - console.log('[弹幕] 找到记忆 - 视频:', title, '→ 弹幕源:', memory.animeTitle); + console.log('[弹幕] 找到缓存 - 视频:', title, '→ 弹幕源:', memory.animeTitle); // 获取该动漫的所有剧集列表 try { @@ -2011,13 +2011,14 @@ function PlayPageClient() { memory.searchKeyword // 保留原有的搜索关键词 ); + console.log('[弹幕] 使用缓存成功,跳过搜索'); await loadDanmaku(episode.episodeId); - return; + return; // 成功使用缓存,直接返回 } } - // 如果使用记忆加载失败,清除该记忆并继续自动搜索 - console.warn('[弹幕] 使用缓存加载失败,清除缓存并从头搜索'); + // 如果使用记忆加载失败(没有找到对应的剧集),清除该记忆并继续自动搜索 + console.warn('[弹幕] 缓存中没有找到对应剧集,清除缓存并重新搜索'); if (artPlayerRef.current) { artPlayerRef.current.notice.show = '缓存的弹幕源失效,正在重新搜索...'; } @@ -2029,10 +2030,10 @@ function PlayPageClient() { const memories = JSON.parse(memoriesJson); delete memories[title]; localStorage.setItem('danmaku_memories', JSON.stringify(memories)); - console.log('[弹幕] 已清除失效的缓存记忆'); + console.log('[弹幕] 已清除失效的缓存'); } } catch (e) { - console.error('[弹幕] 清除缓存记忆失败:', e); + console.error('[弹幕] 清除缓存失败:', e); } } } catch (error) { @@ -2048,14 +2049,14 @@ function PlayPageClient() { const memories = JSON.parse(memoriesJson); delete memories[title]; localStorage.setItem('danmaku_memories', JSON.stringify(memories)); - console.log('[弹幕] 已清除失效的缓存记忆'); + console.log('[弹幕] 已清除失效的缓存'); } } catch (e) { - console.error('[弹幕] 清除缓存记忆失败:', e); + console.error('[弹幕] 清除缓存失败:', e); } } } - // 继续执行后面的自动搜索逻辑,不要 return + // 如果缓存加载失败,继续执行后面的自动搜索逻辑 } // 自动搜索弹幕 @@ -3534,8 +3535,8 @@ function PlayPageClient() { - {/* 第三方应用打开按钮 */} - {videoUrl && ( + {/* 第三方应用打开按钮 - 观影室同步状态下隐藏 */} + {videoUrl && !playSync.isInRoom && (
👥 观影室模式
-- {playSync.isOwner ? '您是房主,可以控制播放' : '房主控制中,无法切换集数和播放源'} -
-