diff --git a/src/app/api/tmdb/upcoming/route.ts b/src/app/api/tmdb/upcoming/route.ts new file mode 100644 index 0000000..aa9c743 --- /dev/null +++ b/src/app/api/tmdb/upcoming/route.ts @@ -0,0 +1,66 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { getTMDBUpcomingContent } from '@/lib/tmdb.client'; +import { getConfig } from '@/lib/config'; + +// 内存缓存对象 +interface CacheItem { + data: any; + timestamp: number; +} + +let cache: CacheItem | null = null; +const CACHE_DURATION = 60 * 60 * 1000; // 1小时(毫秒) + +export async function GET(request: NextRequest) { + try { + // 检查缓存是否存在且未过期 + const now = Date.now(); + if (cache && now - cache.timestamp < CACHE_DURATION) { + return NextResponse.json({ + code: 200, + data: cache.data, + cached: true, + cacheAge: Math.floor((now - cache.timestamp) / 1000), // 缓存年龄(秒) + }); + } + + // 缓存不存在或已过期,获取新数据 + const config = await getConfig(); + const tmdbApiKey = config.SiteConfig?.TMDBApiKey; + + if (!tmdbApiKey) { + return NextResponse.json( + { code: 400, message: 'TMDB API Key 未配置' }, + { status: 400 } + ); + } + + // 调用TMDB API获取数据 + const result = await getTMDBUpcomingContent(tmdbApiKey); + + if (result.code !== 200) { + return NextResponse.json( + { code: result.code, message: '获取TMDB数据失败' }, + { status: result.code === 401 ? 401 : 500 } + ); + } + + // 更新缓存 + cache = { + data: result.list, + timestamp: now, + }; + + return NextResponse.json({ + code: 200, + data: result.list, + cached: false, + }); + } catch (error) { + console.error('获取TMDB即将上映数据失败:', error); + return NextResponse.json( + { code: 500, message: '服务器内部错误' }, + { status: 500 } + ); + } +} diff --git a/src/app/page.tsx b/src/app/page.tsx index 608a92c..16d9641 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -18,11 +18,7 @@ import { subscribeToDataUpdates, } from '@/lib/db.client'; import { getDoubanCategories } from '@/lib/douban.client'; -import { - getTMDBUpcomingContent, - getTMDBImageUrl, - TMDBItem, -} from '@/lib/tmdb.client'; +import { getTMDBImageUrl, TMDBItem } from '@/lib/tmdb.client'; import { DoubanItem } from '@/lib/types'; import CapsuleSwitch from '@/components/CapsuleSwitch'; @@ -42,7 +38,7 @@ function HomeClient() { BangumiCalendarData[] >([]); const [loading, setLoading] = useState(true); - const { announcement, tmdbApiKey } = useSite(); + const { announcement } = useSite(); const [showAnnouncement, setShowAnnouncement] = useState(false); @@ -106,18 +102,23 @@ function HomeClient() { setBangumiCalendarData(bangumiCalendarData); - // 如果配置了 TMDB API Key,则获取即将上映/播出内容 - if (tmdbApiKey) { - const tmdbData = await getTMDBUpcomingContent(tmdbApiKey); - if (tmdbData.code === 200) { - // 按上映/播出日期升序排序(最近的排在前面) - const sortedContent = [...tmdbData.list].sort((a, b) => { - const dateA = new Date(a.release_date || '9999-12-31').getTime(); - const dateB = new Date(b.release_date || '9999-12-31').getTime(); - return dateA - dateB; - }); - setUpcomingContent(sortedContent); + // 获取即将上映/播出内容(使用后端API缓存) + try { + const response = await fetch('/api/tmdb/upcoming'); + if (response.ok) { + const result = await response.json(); + if (result.code === 200 && result.data) { + // 按上映/播出日期升序排序(最近的排在前面) + const sortedContent = [...result.data].sort((a, b) => { + const dateA = new Date(a.release_date || '9999-12-31').getTime(); + const dateB = new Date(b.release_date || '9999-12-31').getTime(); + return dateA - dateB; + }); + setUpcomingContent(sortedContent); + } } + } catch (error) { + console.error('获取TMDB即将上映数据失败:', error); } } catch (error) { console.error('获取推荐数据失败:', error); @@ -127,7 +128,7 @@ function HomeClient() { }; fetchRecommendData(); - }, [tmdbApiKey]); + }, []); // 处理收藏数据更新的函数 const updateFavoriteItems = useCallback( @@ -469,7 +470,7 @@ function HomeClient() { {/* 即将上映/播出 (TMDB) */} - {tmdbApiKey && upcomingContent.length > 0 && ( + {upcomingContent.length > 0 && (

diff --git a/src/lib/db.client.ts b/src/lib/db.client.ts index b0f1296..6054174 100644 --- a/src/lib/db.client.ts +++ b/src/lib/db.client.ts @@ -15,7 +15,7 @@ */ import { getAuthInfoFromBrowserCookie } from './auth'; -import { SkipConfig, DanmakuFilterConfig } from './types'; +import { SkipConfig, DanmakuFilterConfig, EpisodeFilterConfig } from './types'; // 全局错误触发函数 function triggerGlobalError(message: string) {