mirror of
https://github.com/MoonTechLab/LunaTV.git
synced 2026-06-11 19:43:13 +08:00
feat: add play page douban link
This commit is contained in:
@@ -1 +1 @@
|
|||||||
20250806191001
|
20250806193618
|
||||||
@@ -427,7 +427,7 @@ function DoubanPageClient() {
|
|||||||
from='douban'
|
from='douban'
|
||||||
title={item.title}
|
title={item.title}
|
||||||
poster={item.poster}
|
poster={item.poster}
|
||||||
douban_id={item.id}
|
douban_id={Number(item.id)}
|
||||||
rate={item.rate}
|
rate={item.rate}
|
||||||
year={item.year}
|
year={item.year}
|
||||||
type={type === 'movie' ? 'movie' : ''} // 电影类型严格控制,tv 不控
|
type={type === 'movie' ? 'movie' : ''} // 电影类型严格控制,tv 不控
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ function HomeClient() {
|
|||||||
from='douban'
|
from='douban'
|
||||||
title={movie.title}
|
title={movie.title}
|
||||||
poster={movie.poster}
|
poster={movie.poster}
|
||||||
douban_id={movie.id}
|
douban_id={Number(movie.id)}
|
||||||
rate={movie.rate}
|
rate={movie.rate}
|
||||||
year={movie.year}
|
year={movie.year}
|
||||||
type='movie'
|
type='movie'
|
||||||
@@ -299,7 +299,7 @@ function HomeClient() {
|
|||||||
from='douban'
|
from='douban'
|
||||||
title={show.title}
|
title={show.title}
|
||||||
poster={show.poster}
|
poster={show.poster}
|
||||||
douban_id={show.id}
|
douban_id={Number(show.id)}
|
||||||
rate={show.rate}
|
rate={show.rate}
|
||||||
year={show.year}
|
year={show.year}
|
||||||
/>
|
/>
|
||||||
@@ -346,7 +346,7 @@ function HomeClient() {
|
|||||||
from='douban'
|
from='douban'
|
||||||
title={show.title}
|
title={show.title}
|
||||||
poster={show.poster}
|
poster={show.poster}
|
||||||
douban_id={show.id}
|
douban_id={Number(show.id)}
|
||||||
rate={show.rate}
|
rate={show.rate}
|
||||||
year={show.year}
|
year={show.year}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ function PlayPageClient() {
|
|||||||
const [videoTitle, setVideoTitle] = useState(searchParams.get('title') || '');
|
const [videoTitle, setVideoTitle] = useState(searchParams.get('title') || '');
|
||||||
const [videoYear, setVideoYear] = useState(searchParams.get('year') || '');
|
const [videoYear, setVideoYear] = useState(searchParams.get('year') || '');
|
||||||
const [videoCover, setVideoCover] = useState('');
|
const [videoCover, setVideoCover] = useState('');
|
||||||
|
const [videoDoubanId, setVideoDoubanId] = useState(0);
|
||||||
// 当前源和ID
|
// 当前源和ID
|
||||||
const [currentSource, setCurrentSource] = useState(
|
const [currentSource, setCurrentSource] = useState(
|
||||||
searchParams.get('source') || ''
|
searchParams.get('source') || ''
|
||||||
@@ -715,6 +716,7 @@ function PlayPageClient() {
|
|||||||
setVideoYear(detailData.year);
|
setVideoYear(detailData.year);
|
||||||
setVideoTitle(detailData.title || videoTitleRef.current);
|
setVideoTitle(detailData.title || videoTitleRef.current);
|
||||||
setVideoCover(detailData.poster);
|
setVideoCover(detailData.poster);
|
||||||
|
setVideoDoubanId(detailData.douban_id || 0);
|
||||||
setDetail(detailData);
|
setDetail(detailData);
|
||||||
if (currentEpisodeIndex >= detailData.episodes.length) {
|
if (currentEpisodeIndex >= detailData.episodes.length) {
|
||||||
setCurrentEpisodeIndex(0);
|
setCurrentEpisodeIndex(0);
|
||||||
@@ -868,6 +870,7 @@ function PlayPageClient() {
|
|||||||
setVideoTitle(newDetail.title || newTitle);
|
setVideoTitle(newDetail.title || newTitle);
|
||||||
setVideoYear(newDetail.year);
|
setVideoYear(newDetail.year);
|
||||||
setVideoCover(newDetail.poster);
|
setVideoCover(newDetail.poster);
|
||||||
|
setVideoDoubanId(newDetail.douban_id || 0);
|
||||||
setCurrentSource(newSource);
|
setCurrentSource(newSource);
|
||||||
setCurrentId(newId);
|
setCurrentId(newId);
|
||||||
setDetail(newDetail);
|
setDetail(newDetail);
|
||||||
@@ -1932,13 +1935,41 @@ function PlayPageClient() {
|
|||||||
{/* 封面展示 */}
|
{/* 封面展示 */}
|
||||||
<div className='hidden md:block md:col-span-1 md:order-first'>
|
<div className='hidden md:block md:col-span-1 md:order-first'>
|
||||||
<div className='pl-0 py-4 pr-6'>
|
<div className='pl-0 py-4 pr-6'>
|
||||||
<div className='bg-gray-300 dark:bg-gray-700 aspect-[2/3] flex items-center justify-center rounded-xl overflow-hidden'>
|
<div className='relative bg-gray-300 dark:bg-gray-700 aspect-[2/3] flex items-center justify-center rounded-xl overflow-hidden'>
|
||||||
{videoCover ? (
|
{videoCover ? (
|
||||||
<img
|
<>
|
||||||
src={processImageUrl(videoCover)}
|
<img
|
||||||
alt={videoTitle}
|
src={processImageUrl(videoCover)}
|
||||||
className='w-full h-full object-cover'
|
alt={videoTitle}
|
||||||
/>
|
className='w-full h-full object-cover'
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 豆瓣链接按钮 */}
|
||||||
|
{videoDoubanId !== 0 && (
|
||||||
|
<a
|
||||||
|
href={`https://movie.douban.com/subject/${videoDoubanId.toString()}`}
|
||||||
|
target='_blank'
|
||||||
|
rel='noopener noreferrer'
|
||||||
|
className='absolute top-3 left-3'
|
||||||
|
>
|
||||||
|
<div className='bg-green-500 text-white text-xs font-bold w-8 h-8 rounded-full flex items-center justify-center shadow-md hover:bg-green-600 hover:scale-[1.1] transition-all duration-300 ease-out'>
|
||||||
|
<svg
|
||||||
|
width='16'
|
||||||
|
height='16'
|
||||||
|
viewBox='0 0 24 24'
|
||||||
|
fill='none'
|
||||||
|
stroke='currentColor'
|
||||||
|
strokeWidth='2'
|
||||||
|
strokeLinecap='round'
|
||||||
|
strokeLinejoin='round'
|
||||||
|
>
|
||||||
|
<path d='M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71'></path>
|
||||||
|
<path d='M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71'></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<span className='text-gray-600 dark:text-gray-400'>
|
<span className='text-gray-600 dark:text-gray-400'>
|
||||||
封面图片
|
封面图片
|
||||||
|
|||||||
@@ -322,12 +322,12 @@ function SearchPageClient() {
|
|||||||
>
|
>
|
||||||
<VideoCard
|
<VideoCard
|
||||||
id={item.id}
|
id={item.id}
|
||||||
title={item.title + ' ' + item.type_name}
|
title={item.title}
|
||||||
poster={item.poster}
|
poster={item.poster}
|
||||||
episodes={item.episodes.length}
|
episodes={item.episodes.length}
|
||||||
source={item.source}
|
source={item.source}
|
||||||
source_name={item.source_name}
|
source_name={item.source_name}
|
||||||
douban_id={item.douban_id?.toString()}
|
douban_id={item.douban_id}
|
||||||
query={
|
query={
|
||||||
searchQuery.trim() !== item.title
|
searchQuery.trim() !== item.title
|
||||||
? searchQuery.trim()
|
? searchQuery.trim()
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ interface VideoCardProps {
|
|||||||
year?: string;
|
year?: string;
|
||||||
from: 'playrecord' | 'favorite' | 'search' | 'douban';
|
from: 'playrecord' | 'favorite' | 'search' | 'douban';
|
||||||
currentEpisode?: number;
|
currentEpisode?: number;
|
||||||
douban_id?: string;
|
douban_id?: number;
|
||||||
onDelete?: () => void;
|
onDelete?: () => void;
|
||||||
rate?: string;
|
rate?: string;
|
||||||
items?: SearchResult[];
|
items?: SearchResult[];
|
||||||
@@ -63,7 +63,7 @@ export default function VideoCard({
|
|||||||
|
|
||||||
const aggregateData = useMemo(() => {
|
const aggregateData = useMemo(() => {
|
||||||
if (!isAggregate || !items) return null;
|
if (!isAggregate || !items) return null;
|
||||||
const countMap = new Map<string | number, number>();
|
const countMap = new Map<number, number>();
|
||||||
const episodeCountMap = new Map<number, number>();
|
const episodeCountMap = new Map<number, number>();
|
||||||
items.forEach((item) => {
|
items.forEach((item) => {
|
||||||
if (item.douban_id && item.douban_id !== 0) {
|
if (item.douban_id && item.douban_id !== 0) {
|
||||||
@@ -75,11 +75,9 @@ export default function VideoCard({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const getMostFrequent = <T extends string | number>(
|
const getMostFrequent = (map: Map<number, number>) => {
|
||||||
map: Map<T, number>
|
|
||||||
) => {
|
|
||||||
let maxCount = 0;
|
let maxCount = 0;
|
||||||
let result: T | undefined;
|
let result: number | undefined;
|
||||||
map.forEach((cnt, key) => {
|
map.forEach((cnt, key) => {
|
||||||
if (cnt > maxCount) {
|
if (cnt > maxCount) {
|
||||||
maxCount = cnt;
|
maxCount = cnt;
|
||||||
@@ -100,9 +98,7 @@ export default function VideoCard({
|
|||||||
const actualPoster = aggregateData?.first.poster ?? poster;
|
const actualPoster = aggregateData?.first.poster ?? poster;
|
||||||
const actualSource = aggregateData?.first.source ?? source;
|
const actualSource = aggregateData?.first.source ?? source;
|
||||||
const actualId = aggregateData?.first.id ?? id;
|
const actualId = aggregateData?.first.id ?? id;
|
||||||
const actualDoubanId = String(
|
const actualDoubanId = aggregateData?.mostFrequentDoubanId ?? douban_id;
|
||||||
aggregateData?.mostFrequentDoubanId ?? douban_id
|
|
||||||
);
|
|
||||||
const actualEpisodes = aggregateData?.mostFrequentEpisodes ?? episodes;
|
const actualEpisodes = aggregateData?.mostFrequentEpisodes ?? episodes;
|
||||||
const actualYear = aggregateData?.first.year ?? year;
|
const actualYear = aggregateData?.first.year ?? year;
|
||||||
const actualQuery = query || '';
|
const actualQuery = query || '';
|
||||||
@@ -340,9 +336,9 @@ export default function VideoCard({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 豆瓣链接 */}
|
{/* 豆瓣链接 */}
|
||||||
{config.showDoubanLink && actualDoubanId && (
|
{config.showDoubanLink && actualDoubanId && actualDoubanId !== 0 && (
|
||||||
<a
|
<a
|
||||||
href={`https://movie.douban.com/subject/${actualDoubanId}`}
|
href={`https://movie.douban.com/subject/${actualDoubanId.toString()}`}
|
||||||
target='_blank'
|
target='_blank'
|
||||||
rel='noopener noreferrer'
|
rel='noopener noreferrer'
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
const CURRENT_VERSION = '20250806191001';
|
const CURRENT_VERSION = '20250806193618';
|
||||||
|
|
||||||
// 版本检查结果枚举
|
// 版本检查结果枚举
|
||||||
export enum UpdateStatus {
|
export enum UpdateStatus {
|
||||||
|
|||||||
Reference in New Issue
Block a user