diff --git a/components/search/VideoCard.tsx b/components/search/VideoCard.tsx index 915a8f9..d658b5a 100644 --- a/components/search/VideoCard.tsx +++ b/components/search/VideoCard.tsx @@ -10,7 +10,8 @@ import { LatencyBadge } from '@/components/ui/LatencyBadge'; import { FavoriteButton } from '@/components/favorites/FavoriteButton'; import { Video } from '@/lib/types'; -import { parseVideoTitle, extractQualityLabel } from '@/lib/utils/video'; +import { parseVideoTitle } from '@/lib/utils/video'; +import type { ResolutionInfo } from '@/lib/hooks/useResolutionProbe'; interface VideoCardProps { video: Video; @@ -20,6 +21,8 @@ interface VideoCardProps { onCardClick: (e: React.MouseEvent, cardId: string, videoUrl: string) => void; isPremium?: boolean; latencies?: Record; + resolution?: ResolutionInfo | null; + isProbing?: boolean; } export const VideoCard = memo(({ @@ -29,7 +32,9 @@ export const VideoCard = memo(({ isActive, onCardClick, isPremium = false, - latencies = {} + latencies = {}, + resolution, + isProbing = false, }) => { const displayLatency = latencies[video.source] ?? video.latency; return ( @@ -157,10 +162,7 @@ export const VideoCard = memo(({ {/* Info */}
{(() => { - const { cleanTitle, quality } = parseVideoTitle(video.vod_name); - // Visual priority: Quality from title tag, then vod_remarks - const displayQuality = quality || video.vod_remarks; - const qualityBadge = extractQualityLabel(video.vod_remarks, quality); + const { cleanTitle } = parseVideoTitle(video.vod_name); return ( <> @@ -168,23 +170,16 @@ export const VideoCard = memo(({ {cleanTitle}
- {qualityBadge && ( - - {qualityBadge.label} + {resolution ? ( + + {resolution.label} - )} - {displayQuality && ( -

- {displayQuality} -

- )} + ) : isProbing ? ( + + ... + + ) : null}
- {/* Hide remarks if it was used as quality to avoid duplication */} - {video.vod_remarks && video.vod_remarks !== displayQuality && ( -

- {video.vod_remarks} -

- )} {video.vod_lang && (

{video.vod_lang} diff --git a/components/search/VideoGrid.tsx b/components/search/VideoGrid.tsx index ca99092..963ecea 100644 --- a/components/search/VideoGrid.tsx +++ b/components/search/VideoGrid.tsx @@ -74,6 +74,15 @@ export const VideoGrid = memo(function VideoGrid({ return null; } + // Build stable list of videos to probe for resolution + const videosToProbe = useMemo(() => { + if (displayMode === 'grouped') { + // For grouped mode, will probe after grouping + return []; + } + return videos.map(v => ({ id: String(v.vod_id), source: v.source })); + }, [videos, displayMode]); + // Group videos by name when in grouped mode const groupedVideos = useMemo(() => { if (displayMode !== 'grouped') return []; @@ -166,6 +175,16 @@ export const VideoGrid = memo(function VideoGrid({ })); }, [groupedVideos, displayMode]); + // Build probe list for grouped mode (probe representative of each group) + const groupedProbeList = useMemo(() => { + if (displayMode !== 'grouped') return []; + return groupedVideos.map(g => ({ id: String(g.representative.vod_id), source: g.representative.source })); + }, [groupedVideos, displayMode]); + + // Probe resolutions + const probeList = displayMode === 'grouped' ? groupedProbeList : videosToProbe; + const { resolutions, isProbing } = useResolutionProbe(probeList); + const totalItems = displayMode === 'grouped' ? groupItems.length : videoItems.length; return ( @@ -189,6 +208,8 @@ export const VideoGrid = memo(function VideoGrid({ onCardClick={handleCardClick} isPremium={isPremium} latencies={latencies} + resolution={resolutions[`${group.representative.source}:${group.representative.vod_id}`]} + isProbing={isProbing && !resolutions[`${group.representative.source}:${group.representative.vod_id}`]} /> ); }) @@ -206,6 +227,8 @@ export const VideoGrid = memo(function VideoGrid({ onCardClick={handleCardClick} isPremium={isPremium} latencies={latencies} + resolution={resolutions[`${video.source}:${video.vod_id}`]} + isProbing={isProbing && !resolutions[`${video.source}:${video.vod_id}`]} /> ); }) diff --git a/components/search/VideoGroupCard.tsx b/components/search/VideoGroupCard.tsx index ce83b45..4d6a9f5 100644 --- a/components/search/VideoGroupCard.tsx +++ b/components/search/VideoGroupCard.tsx @@ -16,6 +16,7 @@ import { FavoriteButton } from '@/components/favorites/FavoriteButton'; import { Video } from '@/lib/types'; import { parseVideoTitle } from '@/lib/utils/video'; import { storeGroupedSources } from '@/lib/utils/grouped-sources-cache'; +import type { ResolutionInfo } from '@/lib/hooks/useResolutionProbe'; export interface GroupedVideo { /** Representative video (lowest latency) */ @@ -33,6 +34,8 @@ interface VideoGroupCardProps { onCardClick: (e: React.MouseEvent, cardId: string, videoUrl: string) => void; isPremium?: boolean; latencies?: Record; + resolution?: ResolutionInfo | null; + isProbing?: boolean; } export const VideoGroupCard = memo(({ @@ -41,7 +44,9 @@ export const VideoGroupCard = memo(({ isActive, onCardClick, isPremium = false, - latencies = {} + latencies = {}, + resolution, + isProbing = false, }) => { const { representative, videos, name } = group; @@ -201,19 +206,24 @@ export const VideoGroupCard = memo(({ {/* Info */}

{(() => { - const { cleanTitle, quality } = parseVideoTitle(name); - const displayQuality = quality || representative.vod_remarks; + const { cleanTitle } = parseVideoTitle(name); return ( <>

{cleanTitle}

- {displayQuality && ( -

- {displayQuality} -

- )} +
+ {resolution ? ( + + {resolution.label} + + ) : isProbing ? ( + + ... + + ) : null} +
{representative.vod_lang && (

{representative.vod_lang}