添加即将上映
This commit is contained in:
@@ -296,6 +296,7 @@ interface SiteConfig {
|
||||
FluidSearch: boolean;
|
||||
DanmakuApiBase: string;
|
||||
DanmakuApiToken: string;
|
||||
TMDBApiKey?: string;
|
||||
EnableComments: boolean;
|
||||
EnableRegistration?: boolean;
|
||||
RegistrationRequireTurnstile?: boolean;
|
||||
@@ -4592,6 +4593,7 @@ const SiteConfigComponent = ({
|
||||
FluidSearch: true,
|
||||
DanmakuApiBase: 'http://localhost:9321',
|
||||
DanmakuApiToken: '87654321',
|
||||
TMDBApiKey: '',
|
||||
EnableComments: false,
|
||||
EnableRegistration: false,
|
||||
RegistrationRequireTurnstile: false,
|
||||
@@ -4674,6 +4676,7 @@ const SiteConfigComponent = ({
|
||||
DanmakuApiBase:
|
||||
config.SiteConfig.DanmakuApiBase || 'http://localhost:9321',
|
||||
DanmakuApiToken: config.SiteConfig.DanmakuApiToken || '87654321',
|
||||
TMDBApiKey: config.SiteConfig.TMDBApiKey || '',
|
||||
EnableComments: config.SiteConfig.EnableComments || false,
|
||||
EnableRegistration: config.SiteConfig.EnableRegistration || false,
|
||||
RegistrationRequireTurnstile: config.SiteConfig.RegistrationRequireTurnstile || false,
|
||||
@@ -5235,6 +5238,43 @@ const SiteConfigComponent = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* TMDB 配置 */}
|
||||
<div className='space-y-4 pt-4 border-t border-gray-200 dark:border-gray-700'>
|
||||
<h3 className='text-sm font-semibold text-gray-900 dark:text-gray-100'>
|
||||
TMDB 配置
|
||||
</h3>
|
||||
|
||||
{/* TMDB API Key */}
|
||||
<div>
|
||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||
TMDB API Key
|
||||
</label>
|
||||
<input
|
||||
type='text'
|
||||
placeholder='请输入 TMDB API Key'
|
||||
value={siteSettings.TMDBApiKey}
|
||||
onChange={(e) =>
|
||||
setSiteSettings((prev) => ({
|
||||
...prev,
|
||||
TMDBApiKey: 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'
|
||||
/>
|
||||
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
|
||||
配置后首页将显示 TMDB 即将上映电影。获取 API Key 请访问{' '}
|
||||
<a
|
||||
href='https://www.themoviedb.org/settings/api'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300'
|
||||
>
|
||||
TMDB API 设置页面
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 评论功能配置 */}
|
||||
<div className='space-y-4 pt-4 border-t border-gray-200 dark:border-gray-700'>
|
||||
<h3 className='text-sm font-semibold text-gray-900 dark:text-gray-100'>
|
||||
|
||||
@@ -41,6 +41,7 @@ export async function POST(request: NextRequest) {
|
||||
FluidSearch,
|
||||
DanmakuApiBase,
|
||||
DanmakuApiToken,
|
||||
TMDBApiKey,
|
||||
EnableComments,
|
||||
CustomAdFilterCode,
|
||||
CustomAdFilterVersion,
|
||||
@@ -72,6 +73,7 @@ export async function POST(request: NextRequest) {
|
||||
FluidSearch: boolean;
|
||||
DanmakuApiBase: string;
|
||||
DanmakuApiToken: string;
|
||||
TMDBApiKey?: string;
|
||||
EnableComments: boolean;
|
||||
CustomAdFilterCode?: string;
|
||||
CustomAdFilterVersion?: number;
|
||||
@@ -106,6 +108,7 @@ export async function POST(request: NextRequest) {
|
||||
typeof FluidSearch !== 'boolean' ||
|
||||
typeof DanmakuApiBase !== 'string' ||
|
||||
typeof DanmakuApiToken !== 'string' ||
|
||||
(TMDBApiKey !== undefined && typeof TMDBApiKey !== 'string') ||
|
||||
typeof EnableComments !== 'boolean' ||
|
||||
(CustomAdFilterCode !== undefined && typeof CustomAdFilterCode !== 'string') ||
|
||||
(CustomAdFilterVersion !== undefined && typeof CustomAdFilterVersion !== 'number') ||
|
||||
@@ -155,6 +158,7 @@ export async function POST(request: NextRequest) {
|
||||
FluidSearch,
|
||||
DanmakuApiBase,
|
||||
DanmakuApiToken,
|
||||
TMDBApiKey,
|
||||
EnableComments,
|
||||
CustomAdFilterCode,
|
||||
CustomAdFilterVersion,
|
||||
|
||||
+3
-1
@@ -61,6 +61,7 @@ export default async function RootLayout({
|
||||
process.env.NEXT_PUBLIC_DISABLE_YELLOW_FILTER === 'true';
|
||||
let fluidSearch = process.env.NEXT_PUBLIC_FLUID_SEARCH !== 'false';
|
||||
let enableComments = false;
|
||||
let tmdbApiKey = '';
|
||||
let customCategories = [] as {
|
||||
name: string;
|
||||
type: 'movie' | 'tv';
|
||||
@@ -85,6 +86,7 @@ export default async function RootLayout({
|
||||
}));
|
||||
fluidSearch = config.SiteConfig.FluidSearch;
|
||||
enableComments = config.SiteConfig.EnableComments;
|
||||
tmdbApiKey = config.SiteConfig.TMDBApiKey || '';
|
||||
}
|
||||
|
||||
// 将运行时配置注入到全局 window 对象,供客户端在运行时读取
|
||||
@@ -128,7 +130,7 @@ export default async function RootLayout({
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<SiteProvider siteName={siteName} announcement={announcement}>
|
||||
<SiteProvider siteName={siteName} announcement={announcement} tmdbApiKey={tmdbApiKey}>
|
||||
<WatchRoomProvider>
|
||||
<DownloadProvider>
|
||||
<DanmakuCacheCleanup />
|
||||
|
||||
+57
-3
@@ -18,6 +18,11 @@ import {
|
||||
subscribeToDataUpdates,
|
||||
} from '@/lib/db.client';
|
||||
import { getDoubanCategories } from '@/lib/douban.client';
|
||||
import {
|
||||
getTMDBUpcomingContent,
|
||||
getTMDBImageUrl,
|
||||
TMDBItem,
|
||||
} from '@/lib/tmdb.client';
|
||||
import { DoubanItem } from '@/lib/types';
|
||||
|
||||
import CapsuleSwitch from '@/components/CapsuleSwitch';
|
||||
@@ -32,11 +37,12 @@ function HomeClient() {
|
||||
const [hotMovies, setHotMovies] = useState<DoubanItem[]>([]);
|
||||
const [hotTvShows, setHotTvShows] = useState<DoubanItem[]>([]);
|
||||
const [hotVarietyShows, setHotVarietyShows] = useState<DoubanItem[]>([]);
|
||||
const [upcomingContent, setUpcomingContent] = useState<TMDBItem[]>([]);
|
||||
const [bangumiCalendarData, setBangumiCalendarData] = useState<
|
||||
BangumiCalendarData[]
|
||||
>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { announcement } = useSite();
|
||||
const { announcement, tmdbApiKey } = useSite();
|
||||
|
||||
const [showAnnouncement, setShowAnnouncement] = useState(false);
|
||||
|
||||
@@ -73,7 +79,7 @@ function HomeClient() {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
// 并行获取热门电影、热门剧集和热门综艺
|
||||
// 并行获取热门电影、热门剧集、热门综艺和番剧日历
|
||||
const [moviesData, tvShowsData, varietyShowsData, bangumiCalendarData] =
|
||||
await Promise.all([
|
||||
getDoubanCategories({
|
||||
@@ -99,6 +105,20 @@ function HomeClient() {
|
||||
}
|
||||
|
||||
setBangumiCalendarData(bangumiCalendarData);
|
||||
|
||||
// 如果配置了 TMDB API Key,则获取即将上映/播出内容
|
||||
if (tmdbApiKey) {
|
||||
const tmdbData = await getTMDBUpcomingContent(tmdbApiKey);
|
||||
if (tmdbData.code === 200) {
|
||||
// 按上映/播出日期升序排序(最近的排在前面)
|
||||
const sortedContent = [...tmdbData.list].sort((a, b) => {
|
||||
const dateA = new Date(a.release_date || '9999-12-31').getTime();
|
||||
const dateB = new Date(b.release_date || '9999-12-31').getTime();
|
||||
return dateA - dateB;
|
||||
});
|
||||
setUpcomingContent(sortedContent);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取推荐数据失败:', error);
|
||||
} finally {
|
||||
@@ -107,7 +127,7 @@ function HomeClient() {
|
||||
};
|
||||
|
||||
fetchRecommendData();
|
||||
}, []);
|
||||
}, [tmdbApiKey]);
|
||||
|
||||
// 处理收藏数据更新的函数
|
||||
const updateFavoriteItems = useCallback(
|
||||
@@ -447,6 +467,40 @@ function HomeClient() {
|
||||
))}
|
||||
</ScrollableRow>
|
||||
</section>
|
||||
|
||||
{/* 即将上映/播出 (TMDB) */}
|
||||
{tmdbApiKey && upcomingContent.length > 0 && (
|
||||
<section className='mb-8'>
|
||||
<div className='mb-4 flex items-center justify-between'>
|
||||
<h2 className='text-xl font-bold text-gray-800 dark:text-gray-200'>
|
||||
即将上映
|
||||
</h2>
|
||||
</div>
|
||||
<ScrollableRow>
|
||||
{upcomingContent.map((item) => (
|
||||
<div
|
||||
key={`${item.media_type}-${item.id}`}
|
||||
className='min-w-[96px] w-24 sm:min-w-[180px] sm:w-44'
|
||||
>
|
||||
<VideoCard
|
||||
title={item.title}
|
||||
poster={getTMDBImageUrl(item.poster_path)}
|
||||
year={item.release_date?.split('-')?.[0] || ''}
|
||||
rate={
|
||||
item.vote_average && item.vote_average > 0
|
||||
? item.vote_average.toFixed(1)
|
||||
: ''
|
||||
}
|
||||
type={item.media_type === 'tv' ? 'tv' : 'movie'}
|
||||
from='douban'
|
||||
releaseDate={item.release_date}
|
||||
isUpcoming={true}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</ScrollableRow>
|
||||
</section>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user