直链播放

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
+85 -2
View File
@@ -2,8 +2,9 @@
'use client';
import { Bot, ChevronRight, ListVideo } from 'lucide-react';
import { Bot, ChevronRight, Link as LinkIcon, ListVideo } from 'lucide-react';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { Suspense, useEffect, useState } from 'react';
import {
@@ -13,7 +14,7 @@ import {
import { getDoubanCategories } from '@/lib/douban.client';
import { getTMDBImageUrl, TMDBItem } from '@/lib/tmdb.client';
import { DoubanItem } from '@/lib/types';
import { processImageUrl } from '@/lib/utils';
import { base58Encode, processImageUrl } from '@/lib/utils';
import AIChatPanel from '@/components/AIChatPanel';
import BannerCarousel from '@/components/BannerCarousel';
@@ -45,6 +46,7 @@ function HomeClient() {
>([]);
const [loading, setLoading] = useState(true);
const { announcement } = useSite();
const router = useRouter();
// 首页模块配置状态
const [homeModules, setHomeModules] = useState<HomeModule[]>([
@@ -64,6 +66,23 @@ function HomeClient() {
const [aiEnabled, setAiEnabled] = useState(false);
const [aiDefaultMessageNoVideo, setAiDefaultMessageNoVideo] = useState('你好!我是MoonTVPlus的AI影视助手。想看什么电影或剧集?需要推荐吗?');
const [sourceSearchEnabled, setSourceSearchEnabled] = useState(true);
const [showDirectPlayDialog, setShowDirectPlayDialog] = useState(false);
const [directPlayUrl, setDirectPlayUrl] = useState('');
const handleDirectPlay = () => {
setDirectPlayUrl('');
setShowDirectPlayDialog(true);
};
const submitDirectPlay = () => {
const trimmed = directPlayUrl.trim();
if (!trimmed) return;
const encoded = base58Encode(trimmed);
if (!encoded) return;
setShowDirectPlayDialog(false);
setDirectPlayUrl('');
router.push(`/play?source=directplay&id=${encodeURIComponent(encoded)}`);
};
const loadHomeLayoutSettings = () => {
if (typeof window === 'undefined') return;
@@ -566,6 +585,14 @@ function HomeClient() {
<>
{/* 源站寻片和AI问片入口 */}
<div className={`flex items-center justify-end gap-2 mb-4 ${homeBannerEnabled ? '' : 'mt-[30px]'}`}>
<button
onClick={handleDirectPlay}
className='p-1.5 rounded-lg text-blue-500 hover:text-blue-600 transition-colors'
title='直链播放'
>
<LinkIcon size={18} />
</button>
{/* 源站寻片入口 */}
{sourceSearchEnabled && (
<Link href='/source-search'>
@@ -635,6 +662,62 @@ function HomeClient() {
</div>
</div>
)}
{showDirectPlayDialog && (
<div
className='fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4'
onClick={() => setShowDirectPlayDialog(false)}
>
<div
className='bg-white dark:bg-gray-900 rounded-lg shadow-xl w-full max-w-lg'
onClick={(event) => event.stopPropagation()}
>
<div className='flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700'>
<h3 className='text-lg font-semibold text-gray-900 dark:text-gray-100'>
</h3>
<button
onClick={() => setShowDirectPlayDialog(false)}
className='p-2 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors'
aria-label='关闭'
>
<span className='text-gray-600 dark:text-gray-400'>×</span>
</button>
</div>
<div className='p-4 space-y-4'>
<div className='text-sm text-gray-600 dark:text-gray-300'>
</div>
<input
value={directPlayUrl}
onChange={(event) => setDirectPlayUrl(event.target.value)}
onKeyDown={(event) => {
if (event.key === 'Enter') {
submitDirectPlay();
}
}}
placeholder='https://example.com/video.m3u8'
className='w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500'
/>
<div className='flex justify-end gap-2'>
<button
onClick={() => setShowDirectPlayDialog(false)}
className='px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-700 text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors'
>
</button>
<button
onClick={submitDirectPlay}
disabled={!directPlayUrl.trim()}
className='px-4 py-2 rounded-lg bg-blue-600 hover:bg-blue-700 text-white transition-colors disabled:opacity-50 disabled:cursor-not-allowed'
>
</button>
</div>
</div>
</div>
</div>
)}
</PageLayout>
);
}