From e071d6fff237d06b57affbc6652337ff7cfe85df Mon Sep 17 00:00:00 2001 From: mtvpls Date: Sat, 27 Dec 2025 01:28:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=9B=B4=E5=A4=9A=E6=8E=A8?= =?UTF-8?q?=E8=8D=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/admin/page.tsx | 8 +- src/app/api/douban-recommendations/route.ts | 104 +++++++++++++++ src/app/api/image-proxy/route.ts | 2 +- src/app/play/page.tsx | 37 ++++++ src/components/DoubanRecommendations.tsx | 132 ++++++++++++++++++++ src/components/VideoCard.tsx | 2 +- src/lib/utils.ts | 5 + 7 files changed, 284 insertions(+), 6 deletions(-) create mode 100644 src/app/api/douban-recommendations/route.ts create mode 100644 src/components/DoubanRecommendations.tsx diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index 3a473a4..610d6b0 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -6181,11 +6181,11 @@ const SiteConfigComponent = ({ 评论配置 - {/* 开启评论 */} + {/* 开启评论与相似推荐 */}

- 开启后将显示豆瓣评论。评论为逆向抓取,请自行承担责任。 + 开启后将显示豆瓣评论与相似推荐。评论为逆向抓取,请自行承担责任。

@@ -6253,7 +6253,7 @@ const SiteConfigComponent = ({

- 开启评论功能 + 开启评论与相似推荐功能

+ {/* 豆瓣推荐区域 */} + {videoDoubanId !== 0 && enableComments && ( +
+
+ {/* 标题 */} +
+

+ + + + 更多推荐 +

+
+ + {/* 推荐内容 */} +
+ +
+
+
+ )} + {/* 豆瓣评论区域 */} {videoDoubanId !== 0 && enableComments && (
diff --git a/src/components/DoubanRecommendations.tsx b/src/components/DoubanRecommendations.tsx new file mode 100644 index 0000000..2301463 --- /dev/null +++ b/src/components/DoubanRecommendations.tsx @@ -0,0 +1,132 @@ +'use client'; + +import { useEffect, useState, useCallback } from 'react'; +import { useEnableComments } from '@/hooks/useEnableComments'; +import VideoCard from '@/components/VideoCard'; +import ScrollableRow from '@/components/ScrollableRow'; + +interface DoubanRecommendation { + doubanId: string; + title: string; + poster: string; + rating: string; +} + +interface DoubanRecommendationsProps { + doubanId: number; +} + +export default function DoubanRecommendations({ doubanId }: DoubanRecommendationsProps) { + const [recommendations, setRecommendations] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const enableComments = useEnableComments(); + + const fetchRecommendations = useCallback(async () => { + try { + console.log('正在获取推荐'); + setLoading(true); + setError(null); + + // 检查localStorage缓存 + const cacheKey = `douban_recommendations_${doubanId}`; + const cached = localStorage.getItem(cacheKey); + + if (cached) { + try { + const { data, timestamp } = JSON.parse(cached); + const cacheAge = Date.now() - timestamp; + const cacheMaxAge = 7 * 24 * 60 * 60 * 1000; // 7天 + + if (cacheAge < cacheMaxAge) { + console.log('使用缓存的推荐数据'); + setRecommendations(data); + setLoading(false); + return; + } + } catch (e) { + console.error('解析缓存失败:', e); + } + } + + const response = await fetch( + `/api/douban-recommendations?id=${doubanId}` + ); + + if (!response.ok) { + throw new Error('获取推荐失败'); + } + + const result = await response.json(); + console.log('获取到推荐:', result.recommendations); + + const recommendationsData = result.recommendations || []; + setRecommendations(recommendationsData); + + // 保存到localStorage + try { + localStorage.setItem(cacheKey, JSON.stringify({ + data: recommendationsData, + timestamp: Date.now() + })); + } catch (e) { + console.error('保存缓存失败:', e); + } + } catch (err) { + console.error('获取推荐失败:', err); + setError(err instanceof Error ? err.message : '获取推荐失败'); + } finally { + setLoading(false); + } + }, [doubanId]); + + useEffect(() => { + if (enableComments && doubanId) { + fetchRecommendations(); + } + }, [enableComments, doubanId, fetchRecommendations]); + + if (!enableComments) { + return null; + } + + if (loading) { + return ( +
+
+
+ ); + } + + if (error) { + return ( +
+ {error} +
+ ); + } + + if (recommendations.length === 0) { + return null; + } + + return ( + + {recommendations.map((rec) => ( +
+ +
+ ))} +
+ ); +} diff --git a/src/components/VideoCard.tsx b/src/components/VideoCard.tsx index 59b69a7..12aa34b 100644 --- a/src/components/VideoCard.tsx +++ b/src/components/VideoCard.tsx @@ -398,7 +398,7 @@ const VideoCard = forwardRef(function VideoCard showPlayButton: !isUpcoming, // 即将上映不显示播放按钮 showHeart: false, showCheckCircle: false, - showDoubanLink: true, + showDoubanLink: false, showRating: !!rate, showYear: false, }, diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 3c287b5..94d7c18 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -32,6 +32,11 @@ function getDoubanImageProxyConfig(): { export function processImageUrl(originalUrl: string): string { if (!originalUrl) return originalUrl; + // 如果已经是代理URL,直接返回 + if (originalUrl.startsWith('/api/image-proxy')) { + return originalUrl; + } + // 仅处理豆瓣图片代理 if (!originalUrl.includes('doubanio.com')) { return originalUrl;