diff --git a/package.json b/package.json
index 4527951..7040508 100644
--- a/package.json
+++ b/package.json
@@ -39,6 +39,7 @@
"framer-motion": "^12.18.1",
"he": "^1.2.0",
"hls.js": "^1.6.10",
+ "https-proxy-agent": "^7.0.6",
"lucide-react": "^0.438.0",
"media-icons": "^1.1.5",
"mux.js": "^6.3.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7de938d..14e4b6f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -65,6 +65,9 @@ importers:
hls.js:
specifier: ^1.6.10
version: 1.6.10
+ https-proxy-agent:
+ specifier: ^7.0.6
+ version: 7.0.6
lucide-react:
specifier: ^0.438.0
version: 0.438.0(react@18.3.1)
@@ -1889,6 +1892,10 @@ packages:
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
engines: {node: '>= 6.0.0'}
+ agent-base@7.1.4:
+ resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
+ engines: {node: '>= 14'}
+
aggregate-error@3.1.0:
resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
engines: {node: '>=8'}
@@ -3211,6 +3218,10 @@ packages:
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
engines: {node: '>= 6'}
+ https-proxy-agent@7.0.6:
+ resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
+ engines: {node: '>= 14'}
+
human-signals@2.1.0:
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
engines: {node: '>=10.17.0'}
@@ -7539,6 +7550,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ agent-base@7.1.4: {}
+
aggregate-error@3.1.0:
dependencies:
clean-stack: 2.2.0
@@ -9088,6 +9101,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ https-proxy-agent@7.0.6:
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.1(supports-color@9.4.0)
+ transitivePeerDependencies:
+ - supports-color
+
human-signals@2.1.0: {}
husky@7.0.4: {}
diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx
index 39f6112..6744fa2 100644
--- a/src/app/admin/page.tsx
+++ b/src/app/admin/page.tsx
@@ -298,6 +298,7 @@ interface SiteConfig {
DanmakuApiBase: string;
DanmakuApiToken: string;
TMDBApiKey?: string;
+ TMDBProxy?: string;
EnableComments: boolean;
EnableRegistration?: boolean;
RegistrationRequireTurnstile?: boolean;
@@ -4601,6 +4602,7 @@ const SiteConfigComponent = ({
DanmakuApiBase: 'http://localhost:9321',
DanmakuApiToken: '87654321',
TMDBApiKey: '',
+ TMDBProxy: '',
EnableComments: false,
EnableRegistration: false,
RegistrationRequireTurnstile: false,
@@ -4684,6 +4686,7 @@ const SiteConfigComponent = ({
config.SiteConfig.DanmakuApiBase || 'http://localhost:9321',
DanmakuApiToken: config.SiteConfig.DanmakuApiToken || '87654321',
TMDBApiKey: config.SiteConfig.TMDBApiKey || '',
+ TMDBProxy: config.SiteConfig.TMDBProxy || '',
EnableComments: config.SiteConfig.EnableComments || false,
});
}
@@ -5242,6 +5245,28 @@ const SiteConfigComponent = ({
+
+ {/* TMDB Proxy */}
+
+
+
+ setSiteSettings((prev) => ({
+ ...prev,
+ TMDBProxy: e.target.value,
+ }))
+ }
+ 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'
+ />
+
+ 配置代理服务器地址,用于访问 TMDB API(可选)
+
+
{/* 评论功能配置 */}
diff --git a/src/app/api/admin/site/route.ts b/src/app/api/admin/site/route.ts
index 51cc10c..00d78e9 100644
--- a/src/app/api/admin/site/route.ts
+++ b/src/app/api/admin/site/route.ts
@@ -42,6 +42,7 @@ export async function POST(request: NextRequest) {
DanmakuApiBase,
DanmakuApiToken,
TMDBApiKey,
+ TMDBProxy,
EnableComments,
CustomAdFilterCode,
CustomAdFilterVersion,
@@ -74,6 +75,7 @@ export async function POST(request: NextRequest) {
DanmakuApiBase: string;
DanmakuApiToken: string;
TMDBApiKey?: string;
+ TMDBProxy?: string;
EnableComments: boolean;
CustomAdFilterCode?: string;
CustomAdFilterVersion?: number;
@@ -109,6 +111,7 @@ export async function POST(request: NextRequest) {
typeof DanmakuApiBase !== 'string' ||
typeof DanmakuApiToken !== 'string' ||
(TMDBApiKey !== undefined && typeof TMDBApiKey !== 'string') ||
+ (TMDBProxy !== undefined && typeof TMDBProxy !== 'string') ||
typeof EnableComments !== 'boolean' ||
(CustomAdFilterCode !== undefined && typeof CustomAdFilterCode !== 'string') ||
(CustomAdFilterVersion !== undefined && typeof CustomAdFilterVersion !== 'number') ||
@@ -159,6 +162,7 @@ export async function POST(request: NextRequest) {
DanmakuApiBase,
DanmakuApiToken,
TMDBApiKey,
+ TMDBProxy,
EnableComments,
CustomAdFilterCode,
CustomAdFilterVersion,
diff --git a/src/app/api/tmdb/upcoming/route.ts b/src/app/api/tmdb/upcoming/route.ts
index aa9c743..5486d20 100644
--- a/src/app/api/tmdb/upcoming/route.ts
+++ b/src/app/api/tmdb/upcoming/route.ts
@@ -27,6 +27,7 @@ export async function GET(request: NextRequest) {
// 缓存不存在或已过期,获取新数据
const config = await getConfig();
const tmdbApiKey = config.SiteConfig?.TMDBApiKey;
+ const tmdbProxy = config.SiteConfig?.TMDBProxy;
if (!tmdbApiKey) {
return NextResponse.json(
@@ -36,7 +37,7 @@ export async function GET(request: NextRequest) {
}
// 调用TMDB API获取数据
- const result = await getTMDBUpcomingContent(tmdbApiKey);
+ const result = await getTMDBUpcomingContent(tmdbApiKey, tmdbProxy);
if (result.code !== 200) {
return NextResponse.json(
diff --git a/src/lib/admin.types.ts b/src/lib/admin.types.ts
index f5b1aea..609939f 100644
--- a/src/lib/admin.types.ts
+++ b/src/lib/admin.types.ts
@@ -21,6 +21,7 @@ export interface AdminConfig {
DanmakuApiToken: string;
// TMDB配置
TMDBApiKey?: string;
+ TMDBProxy?: string;
// 评论功能开关
EnableComments: boolean;
// 自定义去广告代码
diff --git a/src/lib/config.ts b/src/lib/config.ts
index ba7b782..91016cf 100644
--- a/src/lib/config.ts
+++ b/src/lib/config.ts
@@ -223,6 +223,7 @@ async function getInitConfig(configFile: string, subConfig: {
DanmakuApiToken: process.env.DANMAKU_API_TOKEN || '87654321',
// TMDB配置
TMDBApiKey: '',
+ TMDBProxy: '',
// 评论功能开关
EnableComments: false,
},
diff --git a/src/lib/tmdb.client.ts b/src/lib/tmdb.client.ts
index a72cc9e..062ac81 100644
--- a/src/lib/tmdb.client.ts
+++ b/src/lib/tmdb.client.ts
@@ -1,5 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any,no-console */
+import { HttpsProxyAgent } from 'https-proxy-agent';
+
export interface TMDBMovie {
id: number;
title: string;
@@ -48,21 +50,24 @@ interface TMDBTVAiringTodayResponse {
* @param apiKey - TMDB API Key
* @param page - 页码
* @param region - 地区代码,默认 CN (中国)
+ * @param proxy - 代理服务器地址
* @returns 即将上映的电影列表
*/
export async function getTMDBUpcomingMovies(
apiKey: string,
page: number = 1,
- region: string = 'CN'
+ region: string = 'CN',
+ proxy?: string
): Promise<{ code: number; list: TMDBMovie[] }> {
try {
if (!apiKey) {
return { code: 400, list: [] };
}
- const response = await fetch(
- `https://api.themoviedb.org/3/movie/upcoming?api_key=${apiKey}&language=zh-CN&page=${page}®ion=${region}`
- );
+ const url = `https://api.themoviedb.org/3/movie/upcoming?api_key=${apiKey}&language=zh-CN&page=${page}®ion=${region}`;
+ const fetchOptions: RequestInit = proxy ? { agent: new HttpsProxyAgent(proxy) as any } : {};
+
+ const response = await fetch(url, fetchOptions);
if (!response.ok) {
console.error('TMDB API 请求失败:', response.status, response.statusText);
@@ -85,11 +90,13 @@ export async function getTMDBUpcomingMovies(
* 获取正在播出的电视剧
* @param apiKey - TMDB API Key
* @param page - 页码
+ * @param proxy - 代理服务器地址
* @returns 正在播出的电视剧列表
*/
export async function getTMDBUpcomingTVShows(
apiKey: string,
- page: number = 1
+ page: number = 1,
+ proxy?: string
): Promise<{ code: number; list: TMDBTVShow[] }> {
try {
if (!apiKey) {
@@ -97,9 +104,10 @@ export async function getTMDBUpcomingTVShows(
}
// 使用 on_the_air 接口获取正在播出的电视剧
- const response = await fetch(
- `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=${apiKey}&language=zh-CN&page=${page}`;
+ const fetchOptions: RequestInit = proxy ? { agent: new HttpsProxyAgent(proxy) as any } : {};
+
+ const response = await fetch(url, fetchOptions);
if (!response.ok) {
console.error('TMDB TV API 请求失败:', response.status, response.statusText);
@@ -121,10 +129,12 @@ export async function getTMDBUpcomingTVShows(
/**
* 获取即将上映/播出的内容(电影+电视剧)
* @param apiKey - TMDB API Key
+ * @param proxy - 代理服务器地址
* @returns 统一格式的即将上映/播出列表
*/
export async function getTMDBUpcomingContent(
- apiKey: string
+ apiKey: string,
+ proxy?: string
): Promise<{ code: number; list: TMDBItem[] }> {
try {
if (!apiKey) {
@@ -133,8 +143,8 @@ export async function getTMDBUpcomingContent(
// 并行获取电影和电视剧数据
const [moviesResult, tvShowsResult] = await Promise.all([
- getTMDBUpcomingMovies(apiKey),
- getTMDBUpcomingTVShows(apiKey),
+ getTMDBUpcomingMovies(apiKey, 1, 'CN', proxy),
+ getTMDBUpcomingTVShows(apiKey, 1, proxy),
]);
// 检查是否有错误