feat: support save live channel to favorite
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { X } from 'lucide-react';
|
||||
import { Radio, X } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
@@ -22,6 +22,7 @@ interface MobileActionSheetProps {
|
||||
sourceName?: string; // 播放源名称
|
||||
currentEpisode?: number; // 当前集数
|
||||
totalEpisodes?: number; // 总集数
|
||||
origin?: 'vod' | 'live';
|
||||
}
|
||||
|
||||
const MobileActionSheet: React.FC<MobileActionSheetProps> = ({
|
||||
@@ -35,6 +36,7 @@ const MobileActionSheet: React.FC<MobileActionSheetProps> = ({
|
||||
sourceName,
|
||||
currentEpisode,
|
||||
totalEpisodes,
|
||||
origin = 'vod',
|
||||
}) => {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const [isAnimating, setIsAnimating] = useState(false);
|
||||
@@ -217,7 +219,7 @@ const MobileActionSheet: React.FC<MobileActionSheetProps> = ({
|
||||
src={poster}
|
||||
alt={title}
|
||||
fill
|
||||
className="object-cover"
|
||||
className={origin === 'live' ? 'object-contain' : 'object-cover'}
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
@@ -229,6 +231,9 @@ const MobileActionSheet: React.FC<MobileActionSheetProps> = ({
|
||||
</h3>
|
||||
{sourceName && (
|
||||
<span className="flex-shrink-0 text-xs px-2 py-1 border border-gray-300 dark:border-gray-600 rounded text-gray-600 dark:text-gray-400 bg-gray-50 dark:bg-gray-800">
|
||||
{origin === 'live' && (
|
||||
<Radio size={12} className="inline-block text-gray-500 dark:text-gray-400 mr-1.5" />
|
||||
)}
|
||||
{sourceName}
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any,react-hooks/exhaustive-deps,@typescript-eslint/no-empty-function */
|
||||
|
||||
import { ExternalLink, Heart, Link, PlayCircleIcon, Trash2 } from 'lucide-react';
|
||||
import { ExternalLink, Heart, Link, PlayCircleIcon, Radio, Trash2 } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import React, {
|
||||
@@ -46,6 +46,7 @@ export interface VideoCardProps {
|
||||
type?: string;
|
||||
isBangumi?: boolean;
|
||||
isAggregate?: boolean;
|
||||
origin?: 'vod' | 'live';
|
||||
}
|
||||
|
||||
export type VideoCardHandle = {
|
||||
@@ -74,6 +75,7 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
|
||||
type = '',
|
||||
isBangumi = false,
|
||||
isAggregate = false,
|
||||
origin = 'vod',
|
||||
}: VideoCardProps,
|
||||
ref
|
||||
) {
|
||||
@@ -221,7 +223,11 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
|
||||
);
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
if (from === 'douban' || (isAggregate && !actualSource && !actualId)) {
|
||||
if (origin === 'live' && actualSource && actualId) {
|
||||
// 直播内容跳转到直播页面
|
||||
const url = `/live?source=${actualSource.replace('live_', '')}&id=${actualId.replace('live_', '')}`;
|
||||
router.push(url);
|
||||
} else if (from === 'douban' || (isAggregate && !actualSource && !actualId)) {
|
||||
const url = `/play?title=${encodeURIComponent(actualTitle.trim())}${actualYear ? `&year=${actualYear}` : ''
|
||||
}${actualSearchType ? `&stype=${actualSearchType}` : ''}${isAggregate ? '&prefer=true' : ''}${actualQuery ? `&stitle=${encodeURIComponent(actualQuery.trim())}` : ''}`;
|
||||
router.push(url);
|
||||
@@ -234,6 +240,7 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
|
||||
router.push(url);
|
||||
}
|
||||
}, [
|
||||
origin,
|
||||
from,
|
||||
actualSource,
|
||||
actualId,
|
||||
@@ -247,9 +254,12 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
|
||||
|
||||
// 新标签页播放处理函数
|
||||
const handlePlayInNewTab = useCallback(() => {
|
||||
if (from === 'douban' || (isAggregate && !actualSource && !actualId)) {
|
||||
const url = `/play?title=${encodeURIComponent(actualTitle.trim())}${actualYear ? `&year=${actualYear}` : ''
|
||||
}${actualSearchType ? `&stype=${actualSearchType}` : ''}${isAggregate ? '&prefer=true' : ''}${actualQuery ? `&stitle=${encodeURIComponent(actualQuery.trim())}` : ''}`;
|
||||
if (origin === 'live' && actualSource && actualId) {
|
||||
// 直播内容跳转到直播页面
|
||||
const url = `/live?source=${actualSource.replace('live_', '')}&id=${actualId.replace('live_', '')}`;
|
||||
window.open(url, '_blank');
|
||||
} else if (from === 'douban' || (isAggregate && !actualSource && !actualId)) {
|
||||
const url = `/play?title=${encodeURIComponent(actualTitle.trim())}${actualYear ? `&year=${actualYear}` : ''}${actualSearchType ? `&stype=${actualSearchType}` : ''}${isAggregate ? '&prefer=true' : ''}${actualQuery ? `&stitle=${encodeURIComponent(actualQuery.trim())}` : ''}`;
|
||||
window.open(url, '_blank');
|
||||
} else if (actualSource && actualId) {
|
||||
const url = `/play?source=${actualSource}&id=${actualId}&title=${encodeURIComponent(
|
||||
@@ -260,6 +270,7 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
}, [
|
||||
origin,
|
||||
from,
|
||||
actualSource,
|
||||
actualId,
|
||||
@@ -356,7 +367,7 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
|
||||
if (config.showPlayButton) {
|
||||
actions.push({
|
||||
id: 'play',
|
||||
label: '播放',
|
||||
label: origin === 'live' ? '观看直播' : '播放',
|
||||
icon: <PlayCircleIcon size={20} />,
|
||||
onClick: handleClick,
|
||||
color: 'primary' as const,
|
||||
@@ -365,7 +376,7 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
|
||||
// 新标签页播放
|
||||
actions.push({
|
||||
id: 'play-new-tab',
|
||||
label: '新标签页播放',
|
||||
label: origin === 'live' ? '新标签页观看' : '新标签页播放',
|
||||
icon: <ExternalLink size={20} />,
|
||||
onClick: handlePlayInNewTab,
|
||||
color: 'default' as const,
|
||||
@@ -521,7 +532,7 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
|
||||
>
|
||||
{/* 海报容器 */}
|
||||
<div
|
||||
className='relative aspect-[2/3] overflow-hidden rounded-lg'
|
||||
className={`relative aspect-[2/3] overflow-hidden rounded-lg ${origin === 'live' ? 'ring-1 ring-gray-300/80 dark:ring-gray-600/80' : ''}`}
|
||||
style={{
|
||||
WebkitUserSelect: 'none',
|
||||
userSelect: 'none',
|
||||
@@ -539,7 +550,7 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
|
||||
src={processImageUrl(actualPoster)}
|
||||
alt={actualTitle}
|
||||
fill
|
||||
className='object-cover'
|
||||
className={origin === 'live' ? 'object-contain' : 'object-cover'}
|
||||
referrerPolicy='no-referrer'
|
||||
loading='lazy'
|
||||
onLoadingComplete={() => setIsLoading(true)}
|
||||
@@ -1004,6 +1015,9 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
|
||||
return false;
|
||||
}}
|
||||
>
|
||||
{origin === 'live' && (
|
||||
<Radio size={12} className="inline-block text-gray-500 dark:text-gray-400 mr-1.5" />
|
||||
)}
|
||||
{source_name}
|
||||
</span>
|
||||
</span>
|
||||
@@ -1023,6 +1037,7 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
|
||||
sourceName={source_name}
|
||||
currentEpisode={currentEpisode}
|
||||
totalEpisodes={actualEpisodes}
|
||||
origin={origin}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user