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 = ({
评论配置
- {/* 开启评论 */}
+ {/* 开启评论与相似推荐 */}
- 开启评论功能
+ 开启评论与相似推荐功能
+ {/* 豆瓣推荐区域 */}
+ {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;