mirror of
https://github.com/MoonTechLab/LunaTV.git
synced 2026-02-21 00:44:41 +08:00
feat: fetch douban video info with year
This commit is contained in:
@@ -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 = {
|
||||
|
||||
@@ -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: '',
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -294,6 +294,7 @@ function DoubanPageClient() {
|
||||
poster={item.poster}
|
||||
douban_id={item.id}
|
||||
rate={item.rate}
|
||||
year={item.year}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
@@ -288,6 +293,7 @@ function HomeClient() {
|
||||
poster={show.poster}
|
||||
douban_id={show.id}
|
||||
rate={show.rate}
|
||||
year={show.year}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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<DoubanResult> {
|
||||
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<DoubanResult> {
|
||||
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 {
|
||||
|
||||
@@ -87,6 +87,7 @@ export interface DoubanItem {
|
||||
title: string;
|
||||
poster: string;
|
||||
rate: string;
|
||||
year: string;
|
||||
}
|
||||
|
||||
export interface DoubanResult {
|
||||
|
||||
Reference in New Issue
Block a user