diff --git a/src/app/api/douban/categories/route.ts b/src/app/api/douban/categories/route.ts index fb62227..cf2ac87 100644 --- a/src/app/api/douban/categories/route.ts +++ b/src/app/api/douban/categories/route.ts @@ -8,6 +8,7 @@ interface DoubanCategoryApiResponse { items: Array<{ id: string; title: string; + card_subtitle: string; pic: { large: string; normal: string; @@ -106,6 +107,7 @@ export async function GET(request: Request) { title: item.title, poster: item.pic?.normal || item.pic?.large || '', rate: item.rating?.value ? item.rating.value.toFixed(1) : '', + year: item.card_subtitle?.match(/(\d{4})/)?.[1] || '', })); const response: DoubanResult = { diff --git a/src/app/api/douban/route.ts b/src/app/api/douban/route.ts index ead1c55..bbe4a05 100644 --- a/src/app/api/douban/route.ts +++ b/src/app/api/douban/route.ts @@ -100,6 +100,7 @@ export async function GET(request: Request) { title: item.title, poster: item.cover, rate: item.rate, + year: '', })); const response: DoubanResult = { @@ -171,6 +172,7 @@ function handleTop250(pageStart: number) { title: title, poster: processedCover, rate: rate, + year: '', }); } diff --git a/src/app/douban/page.tsx b/src/app/douban/page.tsx index 8660bd2..be8d3c8 100644 --- a/src/app/douban/page.tsx +++ b/src/app/douban/page.tsx @@ -294,6 +294,7 @@ function DoubanPageClient() { poster={item.poster} douban_id={item.id} rate={item.rate} + year={item.year} /> ))} diff --git a/src/app/page.tsx b/src/app/page.tsx index b3d64b5..ebc2be0 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -13,7 +13,7 @@ import { getAllPlayRecords, subscribeToDataUpdates, } from '@/lib/db.client'; -import { getDoubanRecommends } from '@/lib/douban.client'; +import { getDoubanCategories } from '@/lib/douban.client'; import { DoubanItem } from '@/lib/types'; import CapsuleSwitch from '@/components/CapsuleSwitch'; @@ -65,8 +65,12 @@ function HomeClient() { // 并行获取热门电影和热门剧集 const [moviesData, tvShowsData] = await Promise.all([ - getDoubanRecommends({ type: 'movie', tag: '热门' }), - getDoubanRecommends({ type: 'tv', tag: '热门' }), + getDoubanCategories({ + kind: 'movie', + category: '热门', + type: '全部', + }), + getDoubanCategories({ kind: 'tv', category: 'tv', type: 'tv' }), ]); if (moviesData.code === 200) { @@ -242,6 +246,7 @@ function HomeClient() { poster={movie.poster} douban_id={movie.id} rate={movie.rate} + year={movie.year} /> ))} @@ -288,6 +293,7 @@ function HomeClient() { poster={show.poster} douban_id={show.id} rate={show.rate} + year={show.year} /> ))} diff --git a/src/components/VideoCard.tsx b/src/components/VideoCard.tsx index 30c9a26..7c7877b 100644 --- a/src/components/VideoCard.tsx +++ b/src/components/VideoCard.tsx @@ -195,7 +195,11 @@ export default function VideoCard({ const handleClick = useCallback(() => { if (from === 'douban') { - router.push(`/play?title=${encodeURIComponent(actualTitle.trim())}`); + router.push( + `/play?title=${encodeURIComponent(actualTitle.trim())}${ + actualYear ? `&year=${actualYear}` : '' + }` + ); } else if (actualSource && actualId) { router.push( `/play?source=${actualSource}&id=${actualId}&title=${encodeURIComponent( diff --git a/src/lib/douban.client.ts b/src/lib/douban.client.ts index 082127e..a74a6dc 100644 --- a/src/lib/douban.client.ts +++ b/src/lib/douban.client.ts @@ -1,21 +1,5 @@ import { DoubanItem, DoubanResult } from './types'; -interface DoubanApiResponse { - subjects: Array<{ - id: string; - title: string; - cover: string; - rate: string; - }>; -} - -interface DoubanRecommendsParams { - type: 'tv' | 'movie'; - tag: string; - pageSize?: number; - pageStart?: number; -} - interface DoubanCategoriesParams { kind: 'tv' | 'movie'; category: string; @@ -29,6 +13,7 @@ interface DoubanCategoryApiResponse { items: Array<{ id: string; title: string; + card_subtitle: string; pic: { large: string; normal: string; @@ -39,56 +24,6 @@ interface DoubanCategoryApiResponse { }>; } -/** - * 浏览器端豆瓣数据获取函数 - */ -export async function fetchDoubanRecommends( - params: DoubanRecommendsParams -): Promise { - const { type, tag, pageSize = 16, pageStart = 0 } = params; - - // 验证参数 - if (!['tv', 'movie'].includes(type)) { - throw new Error('type 参数必须是 tv 或 movie'); - } - - if (pageSize < 1 || pageSize > 100) { - throw new Error('pageSize 必须在 1-100 之间'); - } - - if (pageStart < 0) { - throw new Error('pageStart 不能小于 0'); - } - - const target = `https://movie.douban.com/j/search_subjects?type=${type}&tag=${tag}&sort=recommend&page_limit=${pageSize}&page_start=${pageStart}`; - - try { - const response = await fetchWithTimeout(target); - - if (!response.ok) { - throw new Error(`HTTP error! Status: ${response.status}`); - } - - const doubanData: DoubanApiResponse = await response.json(); - - // 转换数据格式 - const list: DoubanItem[] = doubanData.subjects.map((item) => ({ - id: item.id, - title: item.title, - poster: item.cover, - rate: item.rate, - })); - - return { - code: 200, - message: '获取成功', - list: list, - }; - } catch (error) { - throw new Error(`获取豆瓣数据失败: ${(error as Error).message}`); - } -} - /** * 带超时的 fetch 请求 */ @@ -142,30 +77,6 @@ export function shouldUseDoubanClient(): boolean { return getDoubanProxyUrl() !== null; } -/** - * 统一的豆瓣数据获取函数,根据代理设置选择使用服务端 API 或客户端代理获取 - */ -export async function getDoubanRecommends( - params: DoubanRecommendsParams -): Promise { - if (shouldUseDoubanClient()) { - // 使用客户端代理获取(当设置了代理 URL 时) - return fetchDoubanRecommends(params); - } else { - // 使用服务端 API(当没有设置代理 URL 时) - const { type, tag, pageSize = 16, pageStart = 0 } = params; - const response = await fetch( - `/api/douban?type=${type}&tag=${tag}&pageSize=${pageSize}&pageStart=${pageStart}` - ); - - if (!response.ok) { - throw new Error('获取豆瓣数据失败'); - } - - return response.json(); - } -} - /** * 浏览器端豆瓣分类数据获取函数 */ @@ -208,6 +119,7 @@ export async function fetchDoubanCategories( title: item.title, poster: item.pic?.normal || item.pic?.large || '', rate: item.rating?.value ? item.rating.value.toFixed(1) : '', + year: item.card_subtitle?.match(/(\d{4})/)?.[1] || '', })); return { diff --git a/src/lib/types.ts b/src/lib/types.ts index 94e4362..a7e1f48 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -87,6 +87,7 @@ export interface DoubanItem { title: string; poster: string; rate: string; + year: string; } export interface DoubanResult {