'use client'; import { useEffect, useRef, useState } from 'react'; import PageLayout from '@/components/PageLayout'; import { Radio } from 'lucide-react'; import Head from 'next/head'; let Artplayer: any = null; let Hls: any = null; let flvjs: any = null; export default function WebLivePage() { const artRef = useRef(null); const artPlayerRef = useRef(null); const [loading, setLoading] = useState(true); const [loadingStage, setLoadingStage] = useState<'loading' | 'fetching' | 'ready'>('loading'); const [loadingMessage, setLoadingMessage] = useState('正在加载直播源...'); const [sources, setSources] = useState([]); const [currentSource, setCurrentSource] = useState(null); const [videoUrl, setVideoUrl] = useState(''); const [activeTab, setActiveTab] = useState<'rooms' | 'platforms'>('rooms'); const [isChannelListCollapsed, setIsChannelListCollapsed] = useState(false); const [isVideoLoading, setIsVideoLoading] = useState(false); const [errorMessage, setErrorMessage] = useState(null); useEffect(() => { const meta = document.createElement('meta'); meta.name = 'referrer'; meta.content = 'no-referrer'; document.head.appendChild(meta); if (typeof window !== 'undefined') { import('artplayer').then(mod => { Artplayer = mod.default; }); import('hls.js').then(mod => { Hls = mod.default; }); import('flv.js').then(mod => { flvjs = mod.default; }); } fetchSources(); return () => { document.head.removeChild(meta); }; }, []); const fetchSources = async () => { try { setLoading(true); setLoadingStage('loading'); setLoadingMessage('正在加载直播源...'); const res = await fetch('/api/web-live/sources'); if (res.ok) { setLoadingStage('fetching'); const data = await res.json(); setSources(data); setLoadingStage('ready'); await new Promise(resolve => setTimeout(resolve, 500)); } } catch (err) { console.error('获取直播源失败:', err); } finally { setLoading(false); } }; function m3u8Loader(video: HTMLVideoElement, url: string) { if (!Hls) return; const hls = new Hls({ debug: false, enableWorker: true, lowLatencyMode: true }); hls.loadSource(url); hls.attachMedia(video); (video as any).hls = hls; } function flvLoader(video: HTMLVideoElement, url: string) { if (!flvjs) return; const flvPlayer = flvjs.createPlayer({ type: 'flv', url, isLive: true }); flvPlayer.attachMediaElement(video); flvPlayer.on(flvjs.Events.ERROR, (errorType: string, errorDetail: string) => { console.error('FLV.js error:', errorType, errorDetail); setErrorMessage(`播放失败: ${errorType} - ${errorDetail}`); setVideoUrl(''); }); flvPlayer.load(); (video as any).flvPlayer = flvPlayer; } useEffect(() => { if (!Artplayer || !Hls || !flvjs || !videoUrl || !artRef.current) return; if (artPlayerRef.current) { artPlayerRef.current.destroy(); } artPlayerRef.current = new Artplayer({ container: artRef.current, url: videoUrl, isLive: true, autoplay: true, fullscreen: true, customType: { m3u8: m3u8Loader, flv: flvLoader }, icons: { loading: '' } }); return () => { if (artPlayerRef.current) { artPlayerRef.current.destroy(); artPlayerRef.current = null; } }; }, [videoUrl]); const handleSourceClick = async (source: any) => { setCurrentSource(source); setIsVideoLoading(true); setErrorMessage(null); try { const res = await fetch(`/api/web-live/stream?platform=${source.platform}&roomId=${source.roomId}`); if (res.ok) { const data = await res.json(); setVideoUrl(data.url); } else { const data = await res.json(); setErrorMessage(data.error || '获取直播流失败'); } } catch (err) { console.error('获取直播流失败:', err); setErrorMessage(err instanceof Error ? err.message : '获取直播流失败'); } finally { setIsVideoLoading(false); } }; const getRoomUrl = (source: any) => { if (source.platform === 'huya') { return `https://huya.com/${source.roomId}`; } return ''; }; const platforms = Array.from(new Set(sources.map(s => s.platform))); if (loading) { return (
{/* 动画直播图标 */}
📺
{/* 旋转光环 */}
{/* 浮动粒子效果 */}
{/* 进度指示器 */}
{/* 进度条 */}
{/* 加载消息 */}

{loadingMessage}

); } return (

{currentSource?.name || '网络直播'}

{errorMessage && (
⚠️

获取直播流失败

{errorMessage}

请尝试其他房间

)} {isVideoLoading && (
📺

🔄 加载中...

)}
{/* 外部播放器按钮 */} {currentSource && (
{/* 网页播放 */} {/* PotPlayer */} {/* VLC */} {/* MPV */} {/* MX Player */} {/* nPlayer */} {/* IINA */}
)}
setActiveTab('rooms')} className={`flex-1 py-3 px-6 text-center cursor-pointer transition-all duration-200 font-medium ${activeTab === 'rooms' ? 'text-green-600 dark:text-green-400' : 'text-gray-700 hover:text-green-600 bg-black/5 dark:bg-white/5 dark:text-gray-300 dark:hover:text-green-400 hover:bg-black/3 dark:hover:bg-white/3'}`} > 房间
setActiveTab('platforms')} className={`flex-1 py-3 px-6 text-center cursor-pointer transition-all duration-200 font-medium ${activeTab === 'platforms' ? 'text-green-600 dark:text-green-400' : 'text-gray-700 hover:text-green-600 bg-black/5 dark:bg-white/5 dark:text-gray-300 dark:hover:text-green-400 hover:bg-black/3 dark:hover:bg-white/3'}`} > 平台
{activeTab === 'rooms' && (
{sources.length > 0 ? ( sources.map((source) => { const isActive = source.key === currentSource?.key; return ( ); }) ) : (

暂无可用房间

)}
)} {activeTab === 'platforms' && (
{platforms.length > 0 ? ( platforms.map((platform) => (
{platform === 'huya' ? ( 虎牙 ) : ( )}
{platform === 'huya' ? '虎牙' : platform}
{sources.filter(s => s.platform === platform).length} 个房间
)) ) : (

暂无可用平台

)}
)}
); }