tmdb兼容cf

This commit is contained in:
mtvpls
2026-01-23 22:51:12 +08:00
parent 79b0201c64
commit e0b3ecd580
2 changed files with 89 additions and 150 deletions

View File

@@ -9,6 +9,43 @@ const DEFAULT_TMDB_BASE_URL = 'https://api.themoviedb.org';
// TMDB API Key 轮询管理
let currentKeyIndex = 0;
/**
* 检测是否在 Cloudflare 环境中运行
*/
function isCloudflareEnvironment(): boolean {
return process.env.CF_PAGES === '1' || process.env.BUILD_TARGET === 'cloudflare';
}
/**
* 统一的 fetch 函数,根据环境选择使用 node-fetch 或原生 fetch
*/
async function universalFetch(url: string, proxy?: string): Promise<Response> {
const isCloudflare = isCloudflareEnvironment();
if (isCloudflare) {
// Cloudflare 环境:使用原生 fetch忽略 proxy 参数
const response = await fetch(url, {
signal: AbortSignal.timeout(15000),
});
return response as unknown as Response;
} else {
// Node.js 环境:使用 node-fetch支持 proxy
const fetchOptions: any = proxy
? {
agent: new HttpsProxyAgent(proxy, {
timeout: 30000,
keepAlive: false,
}),
signal: AbortSignal.timeout(30000),
}
: {
signal: AbortSignal.timeout(15000),
};
return nodeFetch(url, fetchOptions) as unknown as Response;
}
}
/**
* 解析并获取下一个可用的 TMDB API Key
* @param apiKeys - API Key 字符串支持逗号分隔的多个key
@@ -98,20 +135,9 @@ export async function getTMDBUpcomingMovies(
const baseUrl = reverseProxyBaseUrl || DEFAULT_TMDB_BASE_URL;
const url = `${baseUrl}/3/movie/upcoming?api_key=${actualKey}&language=zh-CN&page=${page}&region=${region}`;
const fetchOptions: any = proxy
? {
agent: new HttpsProxyAgent(proxy, {
timeout: 30000,
keepAlive: false,
}),
signal: AbortSignal.timeout(30000),
}
: {
signal: AbortSignal.timeout(15000),
};
// 使用 node-fetch 而不是原生 fetch
const response = await nodeFetch(url, fetchOptions);
// 使用统一的 fetch 函数
const response = await universalFetch(url, proxy);
if (!response.ok) {
console.error('TMDB API 请求失败:', response.status, response.statusText);
@@ -153,20 +179,9 @@ export async function getTMDBUpcomingTVShows(
// 使用 on_the_air 接口获取正在播出的电视剧
const baseUrl = reverseProxyBaseUrl || DEFAULT_TMDB_BASE_URL;
const url = `${baseUrl}/3/tv/on_the_air?api_key=${actualKey}&language=zh-CN&page=${page}`;
const fetchOptions: any = proxy
? {
agent: new HttpsProxyAgent(proxy, {
timeout: 30000,
keepAlive: false,
}),
signal: AbortSignal.timeout(30000),
}
: {
signal: AbortSignal.timeout(15000),
};
// 使用 node-fetch 而不是原生 fetch
const response = await nodeFetch(url, fetchOptions);
// 使用统一的 fetch 函数
const response = await universalFetch(url, proxy);
if (!response.ok) {
console.error('TMDB TV API 请求失败:', response.status, response.statusText);
@@ -291,19 +306,8 @@ export async function getTMDBVideos(
const baseUrl = reverseProxyBaseUrl || DEFAULT_TMDB_BASE_URL;
const url = `${baseUrl}/3/${mediaType}/${mediaId}/videos?api_key=${actualKey}`;
const fetchOptions: any = proxy
? {
agent: new HttpsProxyAgent(proxy, {
timeout: 30000,
keepAlive: false,
}),
signal: AbortSignal.timeout(30000),
}
: {
signal: AbortSignal.timeout(15000),
};
const response = await nodeFetch(url, fetchOptions);
const response = await universalFetch(url, proxy);
if (!response.ok) {
return null;
@@ -345,19 +349,8 @@ export async function getTMDBTrendingContent(
// 获取本周热门内容(电影+电视剧)
const baseUrl = reverseProxyBaseUrl || DEFAULT_TMDB_BASE_URL;
const url = `${baseUrl}/3/trending/all/week?api_key=${actualKey}&language=zh-CN`;
const fetchOptions: any = proxy
? {
agent: new HttpsProxyAgent(proxy, {
timeout: 30000,
keepAlive: false,
}),
signal: AbortSignal.timeout(30000),
}
: {
signal: AbortSignal.timeout(15000),
};
const response = await nodeFetch(url, fetchOptions);
const response = await universalFetch(url, proxy);
if (!response.ok) {
console.error('TMDB Trending API 请求失败:', response.status, response.statusText);
@@ -479,19 +472,8 @@ export async function searchTMDBMulti(
const baseUrl = reverseProxyBaseUrl || DEFAULT_TMDB_BASE_URL;
const url = `${baseUrl}/3/search/multi?api_key=${actualKey}&language=zh-CN&query=${encodeURIComponent(query)}&page=1`;
const fetchOptions: any = proxy
? {
agent: new HttpsProxyAgent(proxy, {
timeout: 30000,
keepAlive: false,
}),
signal: AbortSignal.timeout(30000),
}
: {
signal: AbortSignal.timeout(15000),
};
const response = await nodeFetch(url, fetchOptions);
const response = await universalFetch(url, proxy);
if (!response.ok) {
console.error('TMDB Search API 请求失败:', response.status, response.statusText);
@@ -532,19 +514,8 @@ export async function getTMDBMovieRecommendations(
const baseUrl = reverseProxyBaseUrl || DEFAULT_TMDB_BASE_URL;
const url = `${baseUrl}/3/movie/${movieId}/recommendations?api_key=${actualKey}&language=zh-CN&page=1`;
const fetchOptions: any = proxy
? {
agent: new HttpsProxyAgent(proxy, {
timeout: 30000,
keepAlive: false,
}),
signal: AbortSignal.timeout(30000),
}
: {
signal: AbortSignal.timeout(15000),
};
const response = await nodeFetch(url, fetchOptions);
const response = await universalFetch(url, proxy);
if (!response.ok) {
console.error('TMDB Movie Recommendations API 请求失败:', response.status, response.statusText);
@@ -585,19 +556,8 @@ export async function getTMDBTVRecommendations(
const baseUrl = reverseProxyBaseUrl || DEFAULT_TMDB_BASE_URL;
const url = `${baseUrl}/3/tv/${tvId}/recommendations?api_key=${actualKey}&language=zh-CN&page=1`;
const fetchOptions: any = proxy
? {
agent: new HttpsProxyAgent(proxy, {
timeout: 30000,
keepAlive: false,
}),
signal: AbortSignal.timeout(30000),
}
: {
signal: AbortSignal.timeout(15000),
};
const response = await nodeFetch(url, fetchOptions);
const response = await universalFetch(url, proxy);
if (!response.ok) {
console.error('TMDB TV Recommendations API 请求失败:', response.status, response.statusText);
@@ -638,19 +598,8 @@ export async function getTMDBMovieDetails(
const baseUrl = reverseProxyBaseUrl || DEFAULT_TMDB_BASE_URL;
const url = `${baseUrl}/3/movie/${movieId}?api_key=${actualKey}&language=zh-CN`;
const fetchOptions: any = proxy
? {
agent: new HttpsProxyAgent(proxy, {
timeout: 30000,
keepAlive: false,
}),
signal: AbortSignal.timeout(30000),
}
: {
signal: AbortSignal.timeout(15000),
};
const response = await nodeFetch(url, fetchOptions);
const response = await universalFetch(url, proxy);
if (!response.ok) {
console.error('TMDB API 请求失败:', response.status, response.statusText);
@@ -691,19 +640,8 @@ export async function getTMDBTVDetails(
const baseUrl = reverseProxyBaseUrl || DEFAULT_TMDB_BASE_URL;
const url = `${baseUrl}/3/tv/${tvId}?api_key=${actualKey}&language=zh-CN`;
const fetchOptions: any = proxy
? {
agent: new HttpsProxyAgent(proxy, {
timeout: 30000,
keepAlive: false,
}),
signal: AbortSignal.timeout(30000),
}
: {
signal: AbortSignal.timeout(15000),
};
const response = await nodeFetch(url, fetchOptions);
const response = await universalFetch(url, proxy);
if (!response.ok) {
console.error('TMDB API 请求失败:', response.status, response.statusText);

View File

@@ -8,6 +8,43 @@ import { getNextApiKey } from './tmdb.client';
// TMDB API 默认 Base URL不包含 /3/,由程序拼接)
const DEFAULT_TMDB_BASE_URL = 'https://api.themoviedb.org';
/**
* 检测是否在 Cloudflare 环境中运行
*/
function isCloudflareEnvironment(): boolean {
return process.env.CF_PAGES === '1' || process.env.BUILD_TARGET === 'cloudflare';
}
/**
* 统一的 fetch 函数,根据环境选择使用 node-fetch 或原生 fetch
*/
async function universalFetch(url: string, proxy?: string): Promise<Response> {
const isCloudflare = isCloudflareEnvironment();
if (isCloudflare) {
// Cloudflare 环境:使用原生 fetch忽略 proxy 参数
const response = await fetch(url, {
signal: AbortSignal.timeout(15000),
});
return response as unknown as Response;
} else {
// Node.js 环境:使用 node-fetch支持 proxy
const fetchOptions: any = proxy
? {
agent: new HttpsProxyAgent(proxy, {
timeout: 30000,
keepAlive: false,
}),
signal: AbortSignal.timeout(30000),
}
: {
signal: AbortSignal.timeout(15000),
};
return nodeFetch(url, fetchOptions) as unknown as Response;
}
}
export interface TMDBSearchResult {
id: number;
title?: string; // 电影
@@ -52,20 +89,8 @@ export async function searchTMDB(
url += `&year=${year}`;
}
const fetchOptions: any = proxy
? {
agent: new HttpsProxyAgent(proxy, {
timeout: 30000,
keepAlive: false,
}),
signal: AbortSignal.timeout(30000),
}
: {
signal: AbortSignal.timeout(15000),
};
// 使用 node-fetch 而不是原生 fetch因为原生 fetch 不支持 agent 选项
const response = await nodeFetch(url, fetchOptions);
// 使用统一的 fetch 函数
const response = await universalFetch(url, proxy);
if (!response.ok) {
console.error('TMDB 搜索失败:', response.status, response.statusText);
@@ -138,19 +163,7 @@ export async function getTVSeasons(
const baseUrl = reverseProxyBaseUrl || DEFAULT_TMDB_BASE_URL;
const url = `${baseUrl}/3/tv/${tvId}?api_key=${actualKey}&language=zh-CN`;
const fetchOptions: any = proxy
? {
agent: new HttpsProxyAgent(proxy, {
timeout: 30000,
keepAlive: false,
}),
signal: AbortSignal.timeout(30000),
}
: {
signal: AbortSignal.timeout(15000),
};
const response = await nodeFetch(url, fetchOptions);
const response = await universalFetch(url, proxy);
if (!response.ok) {
console.error('TMDB 获取电视剧详情失败:', response.status, response.statusText);
@@ -191,19 +204,7 @@ export async function getTVSeasonDetails(
const baseUrl = reverseProxyBaseUrl || DEFAULT_TMDB_BASE_URL;
const url = `${baseUrl}/3/tv/${tvId}/season/${seasonNumber}?api_key=${actualKey}&language=zh-CN`;
const fetchOptions: any = proxy
? {
agent: new HttpsProxyAgent(proxy, {
timeout: 30000,
keepAlive: false,
}),
signal: AbortSignal.timeout(30000),
}
: {
signal: AbortSignal.timeout(15000),
};
const response = await nodeFetch(url, fetchOptions);
const response = await universalFetch(url, proxy);
if (!response.ok) {
console.error('TMDB 获取季度详情失败:', response.status, response.statusText);