直链播放

This commit is contained in:
mtvpls
2026-01-26 17:40:09 +08:00
parent 73232fd172
commit f4a4768c16
4 changed files with 478 additions and 293 deletions
+6 -4
View File
@@ -1,6 +1,6 @@
/* eslint-disable @next/next/no-img-element */
import { Settings } from 'lucide-react';
import { Link as LinkIcon, Settings } from 'lucide-react';
import { useRouter } from 'next/navigation';
import React, {
useCallback,
@@ -811,8 +811,10 @@ const EpisodeSelector: React.FC<EpisodeSelectorProps> = ({
}`.trim()}
>
{/* 封面 */}
<div className='flex-shrink-0 w-12 h-20 bg-gray-300 dark:bg-gray-600 rounded overflow-hidden'>
{source.poster && (
<div className='flex-shrink-0 w-12 h-20 bg-gray-300 dark:bg-gray-600 rounded overflow-hidden flex items-center justify-center'>
{source.source === 'directplay' ? (
<LinkIcon className='w-6 h-6 text-blue-500' />
) : source.poster ? (
<img
src={processImageUrl(source.poster)}
alt={source.title}
@@ -822,7 +824,7 @@ const EpisodeSelector: React.FC<EpisodeSelectorProps> = ({
target.style.display = 'none';
}}
/>
)}
) : null}
</div>
{/* 信息区域 */}
+42 -36
View File
@@ -167,6 +167,7 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
const actualYear = year;
const actualQuery = query || '';
const actualSearchType = type;
const isDirectPlaySource = actualSource === 'directplay';
const displayYear = useMemo(() => {
if (!actualYear) return '';
const normalized = actualYear.trim();
@@ -719,42 +720,47 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
}}
>
{/* 骨架屏 */}
{!isLoading && <ImagePlaceholder aspectRatio={orientation === 'horizontal' ? 'aspect-[3/2]' : 'aspect-[2/3]'} />}
{/* 图片 */}
<Image
src={processImageUrl(actualPoster)}
alt={actualTitle}
fill
className={origin === 'live' ? 'object-contain' : orientation === 'horizontal' ? 'object-cover object-center' : 'object-cover'}
referrerPolicy='no-referrer'
loading='lazy'
onLoadingComplete={() => setIsLoading(true)}
onError={(e) => {
// 图片加载失败时的重试机制
const img = e.target as HTMLImageElement;
if (!img.dataset.retried) {
img.dataset.retried = 'true';
setTimeout(() => {
img.src = processImageUrl(actualPoster);
}, 2000);
}
}}
style={{
// 禁用图片的默认长按效果
WebkitUserSelect: 'none',
userSelect: 'none',
WebkitTouchCallout: 'none',
pointerEvents: 'none', // 图片不响应任何指针事件
} as React.CSSProperties}
onContextMenu={(e) => {
e.preventDefault();
return false;
}}
onDragStart={(e) => {
e.preventDefault();
return false;
}}
/>
{!isLoading && !isDirectPlaySource && <ImagePlaceholder aspectRatio={orientation === 'horizontal' ? 'aspect-[3/2]' : 'aspect-[2/3]'} />}
{isDirectPlaySource ? (
<div className='absolute inset-0 flex items-center justify-center bg-gray-200/80 dark:bg-gray-700/80'>
<Link className='w-8 h-8 text-blue-500' />
</div>
) : (
<Image
src={processImageUrl(actualPoster)}
alt={actualTitle}
fill
className={origin === 'live' ? 'object-contain' : orientation === 'horizontal' ? 'object-cover object-center' : 'object-cover'}
referrerPolicy='no-referrer'
loading='lazy'
onLoadingComplete={() => setIsLoading(true)}
onError={(e) => {
// 图片加载失败时的重试机制
const img = e.target as HTMLImageElement;
if (!img.dataset.retried) {
img.dataset.retried = 'true';
setTimeout(() => {
img.src = processImageUrl(actualPoster);
}, 2000);
}
}}
style={{
// 禁用图片的默认长按效果
WebkitUserSelect: 'none',
userSelect: 'none',
WebkitTouchCallout: 'none',
pointerEvents: 'none', // 图片不响应任何指针事件
} as React.CSSProperties}
onContextMenu={(e) => {
e.preventDefault();
return false;
}}
onDragStart={(e) => {
e.preventDefault();
return false;
}}
/>
)}
{/* 悬浮遮罩 */}
<div