diff --git a/src/app/api/search/resources/route.ts b/src/app/api/search/resources/route.ts index 9a92aa4..1070cf9 100644 --- a/src/app/api/search/resources/route.ts +++ b/src/app/api/search/resources/route.ts @@ -1,6 +1,6 @@ import { NextRequest, NextResponse } from 'next/server'; -import { getAvailableApiSites, getCacheTime } from '@/lib/config'; +import { getAvailableApiSites } from '@/lib/config'; export const runtime = 'edge'; diff --git a/src/app/search/page.tsx b/src/app/search/page.tsx index e77ee2a..0f2fb61 100644 --- a/src/app/search/page.tsx +++ b/src/app/search/page.tsx @@ -3,7 +3,7 @@ import { ChevronUp, Search, X } from 'lucide-react'; import { useRouter, useSearchParams } from 'next/navigation'; -import { Suspense, useEffect, useMemo, useState } from 'react'; +import { Suspense, useEffect, useMemo, useRef, useState } from 'react'; import { addSearchHistory, @@ -28,6 +28,7 @@ function SearchPageClient() { const router = useRouter(); const searchParams = useSearchParams(); + const currentQueryRef = useRef(''); const [searchQuery, setSearchQuery] = useState(''); const [isLoading, setIsLoading] = useState(false); const [showResults, setShowResults] = useState(false); @@ -276,7 +277,9 @@ function SearchPageClient() { useEffect(() => { // 当搜索参数变化时更新搜索状态 - const query = searchParams.get('q'); + const query = searchParams.get('q') || ''; + currentQueryRef.current = query.trim(); + if (query) { setSearchQuery(query); fetchSearchResults(query); @@ -291,10 +294,13 @@ function SearchPageClient() { }, [searchParams]); const fetchSearchResults = async (query: string) => { + // 在函数开始时缓存查询参数 + const cachedQuery = query.trim(); + try { setIsLoading(true); const response = await fetch( - `/api/search?q=${encodeURIComponent(query.trim())}` + `/api/search?q=${encodeURIComponent(cachedQuery)}` ); const data = await response.json(); let results = data.results; @@ -307,11 +313,18 @@ function SearchPageClient() { return !yellowWords.some((word: string) => typeName.includes(word)); }); } + + // 在 setSearchResults 之前检查当前页面的 query 与缓存的查询是否一致 + if (currentQueryRef.current !== cachedQuery) { + // 查询已经改变,不需要设置结果,直接返回 + return; + } + setSearchResults( results.sort((a: SearchResult, b: SearchResult) => { // 优先排序:标题与搜索词完全一致的排在前面 - const aExactMatch = a.title === query.trim(); - const bExactMatch = b.title === query.trim(); + const aExactMatch = a.title === cachedQuery; + const bExactMatch = b.title === cachedQuery; if (aExactMatch && !bExactMatch) return -1; if (!aExactMatch && bExactMatch) return 1; diff --git a/src/components/VideoCard.tsx b/src/components/VideoCard.tsx index 34ba20c..ab24cad 100644 --- a/src/components/VideoCard.tsx +++ b/src/components/VideoCard.tsx @@ -230,6 +230,7 @@ export default function VideoCard({ showCheckCircle: true, showDoubanLink: false, showRating: false, + showYear: false, }, favorite: { showSourceName: true, @@ -239,6 +240,7 @@ export default function VideoCard({ showCheckCircle: false, showDoubanLink: false, showRating: false, + showYear: false, }, search: { showSourceName: true, @@ -246,8 +248,9 @@ export default function VideoCard({ showPlayButton: true, showHeart: false, showCheckCircle: false, - showDoubanLink: !!actualDoubanId, + showDoubanLink: false, showRating: false, + showYear: true, }, douban: { showSourceName: false, @@ -257,6 +260,7 @@ export default function VideoCard({ showCheckCircle: false, showDoubanLink: true, showRating: !!rate, + showYear: false, }, }; return configs[from] || configs.search; @@ -329,6 +333,16 @@ export default function VideoCard({ )} + {/* 年份徽章 */} + {config.showYear && actualYear && actualYear !== 'unknown' && actualYear.trim() !== '' && ( +
+ {actualYear} +
+ )} + {/* 徽章 */} {config.showRating && rate && (
@@ -362,6 +376,73 @@ export default function VideoCard({
)} + + {/* 聚合播放源指示器 */} + {isAggregate && items && items.length > 0 && (() => { + const uniqueSources = Array.from(new Set(items.map(item => item.source_name))); + const sourceCount = uniqueSources.length; + + return ( +
+
+
+ {sourceCount} +
+ + {/* 播放源详情悬浮框 */} + {(() => { + // 优先显示的播放源(常见的主流平台) + const prioritySources = ['爱奇艺', '腾讯视频', '优酷', '芒果TV', '哔哩哔哩', 'Netflix', 'Disney+']; + + // 按优先级排序播放源 + const sortedSources = uniqueSources.sort((a, b) => { + const aIndex = prioritySources.indexOf(a); + const bIndex = prioritySources.indexOf(b); + if (aIndex !== -1 && bIndex !== -1) return aIndex - bIndex; + if (aIndex !== -1) return -1; + if (bIndex !== -1) return 1; + return a.localeCompare(b); + }); + + const maxDisplayCount = 6; // 最多显示6个 + const displaySources = sortedSources.slice(0, maxDisplayCount); + const hasMore = sortedSources.length > maxDisplayCount; + const remainingCount = sortedSources.length - maxDisplayCount; + + return ( +
+
+ {/* 单列布局 */} +
+ {displaySources.map((sourceName, index) => ( +
+
+ + {sourceName} + +
+ ))} +
+ + {/* 显示更多提示 */} + {hasMore && ( +
+
+ +{remainingCount} 播放源 +
+
+ )} + + {/* 小箭头 */} +
+
+
+ ); + })()} +
+
+ ); + })()} {/* 进度条 */}