增加play页背景图渲染
This commit is contained in:
180
src/app/api/tmdb-details/route.ts
Normal file
180
src/app/api/tmdb-details/route.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import {
|
||||
searchTMDBMulti,
|
||||
getTMDBMovieDetails,
|
||||
getTMDBTVDetails,
|
||||
getTMDBImageUrl,
|
||||
} from '@/lib/tmdb.client';
|
||||
import { getConfig } from '@/lib/config';
|
||||
|
||||
// 服务器端缓存(内存)
|
||||
const searchCache = new Map<
|
||||
string,
|
||||
{ data: { tmdbId: number; mediaType: 'movie' | 'tv' }; timestamp: number }
|
||||
>();
|
||||
const CACHE_TTL = 24 * 60 * 60 * 1000; // 1天
|
||||
|
||||
// 移除季度信息的辅助函数
|
||||
function removeSeasonInfo(title: string): string {
|
||||
return title
|
||||
.replace(/第[一二三四五六七八九十\d]+[((]\d+[))][季部]/g, '')
|
||||
.replace(/第[一二三四五六七八九十\d]+[季部]/g, '')
|
||||
.replace(/[((]\d+[))]/g, '')
|
||||
.replace(/\s+season\s+\d+/gi, '')
|
||||
.replace(/\s+S\d+/gi, '')
|
||||
.trim();
|
||||
}
|
||||
|
||||
// 精确匹配标题
|
||||
function findExactMatch(results: any[], originalTitle: string): any | null {
|
||||
if (!results || results.length === 0) return null;
|
||||
if (results.length === 1) return results[0];
|
||||
|
||||
const cleanTitle = originalTitle.toLowerCase().trim();
|
||||
|
||||
// 尝试精确匹配
|
||||
for (const result of results) {
|
||||
const resultTitle = (result.title || result.name || '').toLowerCase().trim();
|
||||
if (resultTitle === cleanTitle) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有精确匹配,返回第一个
|
||||
return results[0];
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const searchParams = request.nextUrl.searchParams;
|
||||
const title = searchParams.get('title');
|
||||
const cachedId = searchParams.get('cachedId');
|
||||
|
||||
if (!title && !cachedId) {
|
||||
return NextResponse.json(
|
||||
{ error: '缺少 title 或 cachedId 参数' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// 获取配置
|
||||
const config = await getConfig();
|
||||
const tmdbApiKey = config.SiteConfig.TMDBApiKey;
|
||||
const tmdbProxy = config.SiteConfig.TMDBProxy;
|
||||
|
||||
if (!tmdbApiKey) {
|
||||
return NextResponse.json(
|
||||
{ error: '未配置 TMDB API Key' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
|
||||
let tmdbId: number;
|
||||
let mediaType: 'movie' | 'tv';
|
||||
|
||||
// 如果提供了cachedId,直接使用
|
||||
if (cachedId) {
|
||||
const [type, id] = cachedId.split(':');
|
||||
mediaType = type as 'movie' | 'tv';
|
||||
tmdbId = parseInt(id, 10);
|
||||
} else {
|
||||
// 否则需要搜索获取ID
|
||||
const cleanedTitle = removeSeasonInfo(title!);
|
||||
const cacheKey = `search_${cleanedTitle}`;
|
||||
|
||||
// 检查服务器缓存
|
||||
const cached = searchCache.get(cacheKey);
|
||||
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
||||
console.log('使用服务器缓存的搜索结果');
|
||||
tmdbId = cached.data.tmdbId;
|
||||
mediaType = cached.data.mediaType;
|
||||
} else {
|
||||
// 搜索TMDB
|
||||
console.log('搜索TMDB:', cleanedTitle);
|
||||
const searchResult = await searchTMDBMulti(
|
||||
tmdbApiKey,
|
||||
cleanedTitle,
|
||||
tmdbProxy
|
||||
);
|
||||
|
||||
if (searchResult.code !== 200 || !searchResult.results.length) {
|
||||
return NextResponse.json(
|
||||
{ error: '未找到匹配的内容' },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
// 精确匹配
|
||||
const match = findExactMatch(searchResult.results, cleanedTitle);
|
||||
if (!match) {
|
||||
return NextResponse.json(
|
||||
{ error: '未找到匹配的内容' },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
tmdbId = match.id;
|
||||
mediaType = match.media_type;
|
||||
|
||||
// 保存到服务器缓存
|
||||
searchCache.set(cacheKey, {
|
||||
data: { tmdbId, mediaType },
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
// 清理过期缓存
|
||||
Array.from(searchCache.entries()).forEach(([key, value]) => {
|
||||
if (Date.now() - value.timestamp > CACHE_TTL) {
|
||||
searchCache.delete(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 获取详情
|
||||
let detailsResult;
|
||||
if (mediaType === 'movie') {
|
||||
detailsResult = await getTMDBMovieDetails(tmdbApiKey, tmdbId, tmdbProxy);
|
||||
} else {
|
||||
detailsResult = await getTMDBTVDetails(tmdbApiKey, tmdbId, tmdbProxy);
|
||||
}
|
||||
|
||||
if (detailsResult.code !== 200 || !detailsResult.details) {
|
||||
return NextResponse.json(
|
||||
{ error: '获取详情失败' },
|
||||
{ status: detailsResult.code }
|
||||
);
|
||||
}
|
||||
|
||||
const details = detailsResult.details;
|
||||
|
||||
// 构建返回数据
|
||||
const responseData = {
|
||||
tmdbId: `${mediaType}:${tmdbId}`, // 用于缓存
|
||||
mediaType,
|
||||
title: details.title || details.name,
|
||||
backdrop: details.backdrop_path
|
||||
? getTMDBImageUrl(details.backdrop_path, 'w1280')
|
||||
: null,
|
||||
poster: details.poster_path
|
||||
? getTMDBImageUrl(details.poster_path, 'w500')
|
||||
: null,
|
||||
overview: details.overview || '',
|
||||
rating: details.vote_average ? details.vote_average.toFixed(1) : '',
|
||||
releaseDate: details.release_date || details.first_air_date || '',
|
||||
};
|
||||
|
||||
return NextResponse.json(responseData, {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Cache-Control': 'public, max-age=86400', // 浏览器缓存1天
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取 TMDB 详情失败:', error);
|
||||
return NextResponse.json(
|
||||
{ error: '获取详情失败' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user