轮播图数据源增加豆瓣

This commit is contained in:
mtvpls
2026-01-05 22:14:07 +08:00
parent f1a295cd75
commit 7a4a8262e1
7 changed files with 326 additions and 38 deletions

View File

@@ -5,6 +5,7 @@ import Image from 'next/image';
import { useRouter } from 'next/navigation';
import { getTMDBImageUrl, getGenreNames, type TMDBItem } from '@/lib/tmdb.client';
import { ChevronLeft, ChevronRight, Play } from 'lucide-react';
import { processImageUrl, processVideoUrl } from '@/lib/utils';
interface BannerCarouselProps {
autoPlayInterval?: number; // 自动播放间隔(毫秒)
@@ -14,6 +15,8 @@ interface BannerCarouselProps {
interface BannerItem extends TMDBItem {
subtitle?: string; // TX数据源的子标题
tags?: string[]; // TX数据源的标签
trailer_url?: string | null; // 豆瓣预告片直链
genres?: string[]; // 豆瓣数据源的类型标签
}
export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarouselProps) {
@@ -25,6 +28,7 @@ export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarous
const [skipNextAutoPlay, setSkipNextAutoPlay] = useState(false); // 跳过下一次自动播放
const [isYouTubeAccessible, setIsYouTubeAccessible] = useState(false); // YouTube连通性默认false检查后再决定
const [enableTrailers, setEnableTrailers] = useState(false); // 是否启用预告片(默认关闭)
const [dataSource, setDataSource] = useState<string>(''); // 当前数据源
const touchStartX = useRef(0);
const touchEndX = useRef(0);
const isManualChange = useRef(false); // 标记是否为手动切换
@@ -45,14 +49,29 @@ export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarous
// 获取图片URL处理TX完整URL和TMDB路径
const getImageUrl = (path: string | null) => {
if (!path) return '';
// 如果是完整URLTX数据源),直接返回
// 如果是完整URLTX数据源或豆瓣),需要判断是否需要代理
if (path.startsWith('http://') || path.startsWith('https://')) {
// 豆瓣图片需要通过代理
if (path.includes('doubanio.com')) {
return processImageUrl(path);
}
// TX等其他完整URL直接返回
return path;
}
// 否则使用TMDB的URL拼接
return getTMDBImageUrl(path, 'original');
};
// 获取视频URL处理豆瓣视频代理
const getVideoUrl = (url: string | null) => {
if (!url) return null;
// 豆瓣视频需要通过域名替换代理
if (url.includes('doubanio.com')) {
return processVideoUrl(url);
}
return url;
};
// 读取本地设置
useEffect(() => {
const setting = localStorage.getItem('enableTrailers');
@@ -61,10 +80,10 @@ export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarous
}
}, []);
// 检测YouTube连通性 - 仅在启用预告片时检测
// 检测YouTube连通性 - 仅在启用预告片且数据源为TMDB时检测
useEffect(() => {
// 如果未启用预告片,不进行检测
if (!enableTrailers) {
// 如果未启用预告片或数据源不是TMDB,不进行检测
if (!enableTrailers || dataSource !== 'TMDB') {
setIsYouTubeAccessible(false);
return;
}
@@ -91,14 +110,14 @@ export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarous
};
checkYouTubeAccess();
}, [enableTrailers]);
}, [enableTrailers, dataSource]);
// 获取热门内容
useEffect(() => {
const fetchTrending = async () => {
try {
// 先尝试从所有可能的数据源缓存中读取
const sources = ['TMDB', 'TX'];
const sources = ['TMDB', 'TX', 'Douban'];
let cachedData = null;
let validSource = null;
@@ -126,6 +145,7 @@ export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarous
// 如果有有效的缓存直接使用不请求API
if (cachedData) {
setItems(cachedData);
setDataSource(validSource || ''); // 设置数据源
setIsLoading(false);
return;
}
@@ -139,6 +159,7 @@ export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarous
const cacheKey = getLocalStorageKey(dataSource);
setItems(result.list);
setDataSource(dataSource); // 设置数据源
// 保存到 localStorage使用数据源特定的key
try {
@@ -285,7 +306,20 @@ export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarous
index === currentIndex ? 'opacity-100' : 'opacity-0'
}`}
>
{item.video_key && isYouTubeAccessible && enableTrailers ? (
{item.trailer_url && enableTrailers ? (
/* 显示豆瓣直链视频 */
<div className="absolute inset-0 overflow-hidden">
<video
src={getVideoUrl(item.trailer_url) || undefined}
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 min-w-full min-h-full w-auto h-auto object-cover"
autoPlay
muted
loop
playsInline
preload="metadata"
/>
</div>
) : item.video_key && isYouTubeAccessible && enableTrailers ? (
/* 显示YouTube视频 */
<div className="absolute inset-0 overflow-hidden">
<iframe
@@ -333,13 +367,20 @@ export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarous
{currentItem.vote_average.toFixed(1)}
</span>
)}
{/* 显示TX数据源的标签 */}
{/* 显示标签优先TX的tags其次豆瓣的genres最后TMDB的genre_ids */}
{currentItem.tags && currentItem.tags.length > 0 ? (
currentItem.tags.slice(0, 3).map((tag, index) => (
<span key={index} className="px-2 py-1 bg-white/20 backdrop-blur-sm rounded text-sm">
{tag}
</span>
))
) : currentItem.genres && Array.isArray(currentItem.genres) && currentItem.genres.length > 0 ? (
/* 显示豆瓣数据源的标签 */
currentItem.genres.slice(0, 3).map((genre, index) => (
<span key={index} className="px-2 py-1 bg-white/20 backdrop-blur-sm rounded text-sm">
{genre}
</span>
))
) : (
/* 显示TMDB数据源的类型标签 */
getGenreNames(currentItem.genre_ids, 3).map(genre => (