tmdb增加轮询

This commit is contained in:
mtvpls
2025-12-31 01:11:47 +08:00
parent 2a7d68bf84
commit 69c45352cc
6 changed files with 85 additions and 28 deletions

View File

@@ -6382,7 +6382,7 @@ const SiteConfigComponent = ({
</label>
<input
type='text'
placeholder='请输入 TMDB API Key'
placeholder='请输入 TMDB API Key多个key用英文逗号分隔'
value={siteSettings.TMDBApiKey}
onChange={(e) =>
setSiteSettings((prev) => ({
@@ -6393,7 +6393,7 @@ const SiteConfigComponent = ({
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-green-500 focus:border-transparent'
/>
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
TMDB API Key 访{' '}
TMDB API Key Key API Key 访{' '}
<a
href='https://www.themoviedb.org/settings/api'
target='_blank'
@@ -8928,6 +8928,24 @@ function AdminPageClient() {
)}
</div>
{/* TMDB 未配置提示 */}
{config && !config.SiteConfig.TMDBApiKey && (
<div className='bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4 mb-4'>
<div className='flex items-start gap-3'>
<div className='flex-shrink-0 mt-0.5'>
<svg className='w-5 h-5 text-blue-600 dark:text-blue-400' fill='currentColor' viewBox='0 0 20 20'>
<path fillRule='evenodd' d='M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z' clipRule='evenodd' />
</svg>
</div>
<div className='flex-1'>
<p className='text-sm font-medium text-blue-800 dark:text-blue-300'>
TMDB API Key
</p>
</div>
</div>
</div>
)}
{/* 配置文件标签 - 仅站长可见 */}
{role === 'owner' && (
<CollapsibleTab

View File

@@ -4,6 +4,7 @@ import { NextRequest, NextResponse } from 'next/server';
import { getAuthInfoFromCookie } from '@/lib/auth';
import { getConfig } from '@/lib/config';
import { getNextApiKey } from '@/lib/tmdb.client';
import { HttpsProxyAgent } from 'https-proxy-agent';
import nodeFetch from 'node-fetch';
@@ -31,7 +32,8 @@ export async function GET(request: NextRequest) {
const tmdbApiKey = config.SiteConfig.TMDBApiKey;
const tmdbProxy = config.SiteConfig.TMDBProxy;
if (!tmdbApiKey) {
const actualKey = getNextApiKey(tmdbApiKey || '');
if (!actualKey) {
return NextResponse.json(
{ error: 'TMDB API Key 未配置' },
{ status: 400 }
@@ -39,7 +41,7 @@ export async function GET(request: NextRequest) {
}
// 使用 multi search 同时搜索电影和电视剧
const url = `https://api.themoviedb.org/3/search/multi?api_key=${tmdbApiKey}&language=zh-CN&query=${encodeURIComponent(query)}&page=1`;
const url = `https://api.themoviedb.org/3/search/multi?api_key=${actualKey}&language=zh-CN&query=${encodeURIComponent(query)}&page=1`;
const fetchOptions: any = tmdbProxy
? {

View File

@@ -6,6 +6,7 @@
import { fetchDoubanData as fetchDoubanAPI } from '@/lib/douban';
import { searchTMDB, getTVSeasons } from '@/lib/tmdb.search';
import { getNextApiKey } from '@/lib/tmdb.client';
export interface VideoContext {
title?: string;
@@ -251,7 +252,8 @@ async function fetchTMDBData(
tmdbProxy?: string
): Promise<any> {
try {
if (!tmdbApiKey) {
const actualKey = getNextApiKey(tmdbApiKey || '');
if (!actualKey) {
console.log('⚠️ TMDB API Key 未配置跳过TMDB数据获取');
return null;
}
@@ -263,7 +265,7 @@ async function fetchTMDBData(
// 使用 TMDB API 获取详情
// TMDB API: https://api.themoviedb.org/3/{type}/{id}
const url = `https://api.themoviedb.org/3/${params.type}/${params.id}?api_key=${tmdbApiKey}&language=zh-CN&append_to_response=keywords,similar`;
const url = `https://api.themoviedb.org/3/${params.type}/${params.id}?api_key=${actualKey}&language=zh-CN&append_to_response=keywords,similar`;
console.log('📡 获取TMDB详情:', params.type, params.id);

View File

@@ -183,6 +183,7 @@ export class DbManager {
enabledApis?: string[];
created_at: number;
playrecord_migrated?: boolean;
favorite_migrated?: boolean;
} | null> {
if (typeof (this.storage as any).getUserInfoV2 === 'function') {
return (this.storage as any).getUserInfoV2(userName);

View File

@@ -3,6 +3,28 @@
import { HttpsProxyAgent } from 'https-proxy-agent';
import nodeFetch from 'node-fetch';
// TMDB API Key 轮询管理
let currentKeyIndex = 0;
/**
* 解析并获取下一个可用的 TMDB API Key
* @param apiKeys - API Key 字符串支持逗号分隔的多个key
* @returns 当前应使用的 API Key
*/
export function getNextApiKey(apiKeys: string): string {
if (!apiKeys) return '';
const keys = apiKeys.split(',').map(k => k.trim()).filter(k => k);
if (keys.length === 0) return '';
if (keys.length === 1) return keys[0];
// 轮询获取下一个key
const key = keys[currentKeyIndex % keys.length];
currentKeyIndex = (currentKeyIndex + 1) % keys.length;
return key;
}
export interface TMDBMovie {
id: number;
title: string;
@@ -63,11 +85,12 @@ export async function getTMDBUpcomingMovies(
proxy?: string
): Promise<{ code: number; list: TMDBMovie[] }> {
try {
if (!apiKey) {
const actualKey = getNextApiKey(apiKey);
if (!actualKey) {
return { code: 400, list: [] };
}
const url = `https://api.themoviedb.org/3/movie/upcoming?api_key=${apiKey}&language=zh-CN&page=${page}&region=${region}`;
const url = `https://api.themoviedb.org/3/movie/upcoming?api_key=${actualKey}&language=zh-CN&page=${page}&region=${region}`;
const fetchOptions: any = proxy
? {
agent: new HttpsProxyAgent(proxy, {
@@ -113,12 +136,13 @@ export async function getTMDBUpcomingTVShows(
proxy?: string
): Promise<{ code: number; list: TMDBTVShow[] }> {
try {
if (!apiKey) {
const actualKey = getNextApiKey(apiKey);
if (!actualKey) {
return { code: 400, list: [] };
}
// 使用 on_the_air 接口获取正在播出的电视剧
const url = `https://api.themoviedb.org/3/tv/on_the_air?api_key=${apiKey}&language=zh-CN&page=${page}`;
const url = `https://api.themoviedb.org/3/tv/on_the_air?api_key=${actualKey}&language=zh-CN&page=${page}`;
const fetchOptions: any = proxy
? {
agent: new HttpsProxyAgent(proxy, {
@@ -242,12 +266,13 @@ export async function getTMDBTrendingContent(
proxy?: string
): Promise<{ code: number; list: TMDBItem[] }> {
try {
if (!apiKey) {
const actualKey = getNextApiKey(apiKey);
if (!actualKey) {
return { code: 400, list: [] };
}
// 获取本周热门内容(电影+电视剧)
const url = `https://api.themoviedb.org/3/trending/all/week?api_key=${apiKey}&language=zh-CN`;
const url = `https://api.themoviedb.org/3/trending/all/week?api_key=${actualKey}&language=zh-CN`;
const fetchOptions: any = proxy
? {
agent: new HttpsProxyAgent(proxy, {
@@ -370,11 +395,12 @@ export async function searchTMDBMulti(
proxy?: string
): Promise<{ code: number; results: any[] }> {
try {
if (!apiKey || !query) {
const actualKey = getNextApiKey(apiKey);
if (!actualKey || !query) {
return { code: 400, results: [] };
}
const url = `https://api.themoviedb.org/3/search/multi?api_key=${apiKey}&language=zh-CN&query=${encodeURIComponent(query)}&page=1`;
const url = `https://api.themoviedb.org/3/search/multi?api_key=${actualKey}&language=zh-CN&query=${encodeURIComponent(query)}&page=1`;
const fetchOptions: any = proxy
? {
agent: new HttpsProxyAgent(proxy, {
@@ -419,11 +445,12 @@ export async function getTMDBMovieRecommendations(
proxy?: string
): Promise<{ code: number; results: TMDBMovie[] }> {
try {
if (!apiKey || !movieId) {
const actualKey = getNextApiKey(apiKey);
if (!actualKey || !movieId) {
return { code: 400, results: [] };
}
const url = `https://api.themoviedb.org/3/movie/${movieId}/recommendations?api_key=${apiKey}&language=zh-CN&page=1`;
const url = `https://api.themoviedb.org/3/movie/${movieId}/recommendations?api_key=${actualKey}&language=zh-CN&page=1`;
const fetchOptions: any = proxy
? {
agent: new HttpsProxyAgent(proxy, {
@@ -468,11 +495,12 @@ export async function getTMDBTVRecommendations(
proxy?: string
): Promise<{ code: number; results: TMDBTVShow[] }> {
try {
if (!apiKey || !tvId) {
const actualKey = getNextApiKey(apiKey);
if (!actualKey || !tvId) {
return { code: 400, results: [] };
}
const url = `https://api.themoviedb.org/3/tv/${tvId}/recommendations?api_key=${apiKey}&language=zh-CN&page=1`;
const url = `https://api.themoviedb.org/3/tv/${tvId}/recommendations?api_key=${actualKey}&language=zh-CN&page=1`;
const fetchOptions: any = proxy
? {
agent: new HttpsProxyAgent(proxy, {
@@ -517,11 +545,12 @@ export async function getTMDBMovieDetails(
proxy?: string
): Promise<{ code: number; details: any }> {
try {
if (!apiKey) {
const actualKey = getNextApiKey(apiKey);
if (!actualKey) {
return { code: 400, details: null };
}
const url = `https://api.themoviedb.org/3/movie/${movieId}?api_key=${apiKey}&language=zh-CN`;
const url = `https://api.themoviedb.org/3/movie/${movieId}?api_key=${actualKey}&language=zh-CN`;
const fetchOptions: any = proxy
? {
agent: new HttpsProxyAgent(proxy, {
@@ -566,11 +595,12 @@ export async function getTMDBTVDetails(
proxy?: string
): Promise<{ code: number; details: any }> {
try {
if (!apiKey) {
const actualKey = getNextApiKey(apiKey);
if (!actualKey) {
return { code: 400, details: null };
}
const url = `https://api.themoviedb.org/3/tv/${tvId}?api_key=${apiKey}&language=zh-CN`;
const url = `https://api.themoviedb.org/3/tv/${tvId}?api_key=${actualKey}&language=zh-CN`;
const fetchOptions: any = proxy
? {
agent: new HttpsProxyAgent(proxy, {

View File

@@ -2,6 +2,7 @@
import { HttpsProxyAgent } from 'https-proxy-agent';
import nodeFetch from 'node-fetch';
import { getNextApiKey } from './tmdb.client';
export interface TMDBSearchResult {
id: number;
@@ -32,12 +33,13 @@ export async function searchTMDB(
year?: number
): Promise<{ code: number; result: TMDBSearchResult | null }> {
try {
if (!apiKey) {
const actualKey = getNextApiKey(apiKey);
if (!actualKey) {
return { code: 400, result: null };
}
// 使用 multi search 同时搜索电影和电视剧
let url = `https://api.themoviedb.org/3/search/multi?api_key=${apiKey}&language=zh-CN&query=${encodeURIComponent(query)}&page=1`;
let url = `https://api.themoviedb.org/3/search/multi?api_key=${actualKey}&language=zh-CN&query=${encodeURIComponent(query)}&page=1`;
// 如果提供了年份,添加到搜索参数中
if (year) {
@@ -121,11 +123,12 @@ export async function getTVSeasons(
proxy?: string
): Promise<{ code: number; seasons: TMDBSeasonInfo[] | null }> {
try {
if (!apiKey) {
const actualKey = getNextApiKey(apiKey);
if (!actualKey) {
return { code: 400, seasons: null };
}
const url = `https://api.themoviedb.org/3/tv/${tvId}?api_key=${apiKey}&language=zh-CN`;
const url = `https://api.themoviedb.org/3/tv/${tvId}?api_key=${actualKey}&language=zh-CN`;
const fetchOptions: any = proxy
? {
@@ -171,11 +174,12 @@ export async function getTVSeasonDetails(
proxy?: string
): Promise<{ code: number; season: TMDBSeasonInfo | null }> {
try {
if (!apiKey) {
const actualKey = getNextApiKey(apiKey);
if (!actualKey) {
return { code: 400, season: null };
}
const url = `https://api.themoviedb.org/3/tv/${tvId}/season/${seasonNumber}?api_key=${apiKey}&language=zh-CN`;
const url = `https://api.themoviedb.org/3/tv/${tvId}/season/${seasonNumber}?api_key=${actualKey}&language=zh-CN`;
const fetchOptions: any = proxy
? {