feat: disable flv live

This commit is contained in:
shinya
2025-08-26 14:31:46 +08:00
parent ac504a3f2e
commit 918943e173
4 changed files with 86 additions and 68 deletions

View File

@@ -35,6 +35,9 @@ export async function GET(request: NextRequest) {
}
const contentType = response.headers.get('Content-Type');
if (response.body) {
response.body.cancel();
}
if (contentType?.includes('video/mp4')) {
return NextResponse.json({ success: true, type: 'mp4' }, { status: 200 });
}

View File

@@ -16,7 +16,6 @@ import PageLayout from '@/components/PageLayout';
declare global {
interface HTMLVideoElement {
hls?: any;
flv?: any;
}
}
@@ -68,6 +67,7 @@ function LivePageClient() {
// 播放器相关
const [videoUrl, setVideoUrl] = useState('');
const [isVideoLoading, setIsVideoLoading] = useState(false);
const [unsupportedType, setUnsupportedType] = useState<string | null>(null);
// 切换直播源状态
const [isSwitchingSource, setIsSwitchingSource] = useState(false);
@@ -350,6 +350,12 @@ function LivePageClient() {
// 设置切换状态,锁住频道切换器
setIsSwitchingSource(true);
// 首先销毁当前播放器
cleanupPlayer();
// 重置不支持的类型状态
setUnsupportedType(null);
// 清空节目单信息
setEpgData(null);
@@ -371,6 +377,12 @@ function LivePageClient() {
// 如果正在切换直播源,则禁用频道切换
if (isSwitchingSource) return;
// 首先销毁当前播放器
cleanupPlayer();
// 重置不支持的类型状态
setUnsupportedType(null);
setCurrentChannel(channel);
setVideoUrl(channel.url);
@@ -404,6 +416,9 @@ function LivePageClient() {
// 清理播放器资源的统一函数
const cleanupPlayer = () => {
// 重置不支持的类型状态
setUnsupportedType(null);
if (artPlayerRef.current) {
try {
// 先暂停播放
@@ -419,10 +434,22 @@ function LivePageClient() {
artPlayerRef.current.video.hls = null;
}
// 销毁 FLV 实例
// 销毁 FLV 实例 - 增强清理逻辑
if (artPlayerRef.current.video && artPlayerRef.current.video.flv) {
artPlayerRef.current.video.flv.destroy();
artPlayerRef.current.video.flv = null;
try {
// 先停止加载
if (artPlayerRef.current.video.flv.unload) {
artPlayerRef.current.video.flv.unload();
}
// 销毁播放器
artPlayerRef.current.video.flv.destroy();
// 确保引用被清空
artPlayerRef.current.video.flv = null;
} catch (flvError) {
console.warn('FLV实例销毁时出错:', flvError);
// 强制清空引用
artPlayerRef.current.video.flv = null;
}
}
// 移除所有事件监听器
@@ -607,32 +634,6 @@ function LivePageClient() {
});
}
async function flvLoader(video: HTMLVideoElement, url: string) {
try {
const flvjs = await import('flv.js');
const flv = flvjs.default as any;
if (!flv.isSupported()) {
console.error('Flv.js 未支持');
return;
}
if (video.flv) {
video.flv.destroy();
}
const flvPlayer = flv.createPlayer({
type: 'flv',
url: url,
});
flvPlayer.attachMediaElement(video);
flvPlayer.load();
video.flv = flvPlayer;
} catch (error) {
console.error('加载 Flv.js 失败:', error);
}
}
// 播放器初始化
useEffect(() => {
const preload = async () => {
@@ -666,18 +667,25 @@ function LivePageClient() {
type = precheckResult.type;
}
const customType = type === 'flv' ? {
flv: flvLoader,
} : type === 'mp4' ? {} : {
m3u8: m3u8Loader,
};
// 如果不是 m3u8 类型,设置不支持的类型并返回
if (type !== 'm3u8') {
setUnsupportedType(type);
setIsVideoLoading(false);
return;
}
// 重置不支持的类型
setUnsupportedType(null);
const customType = { m3u8: m3u8Loader };
const targetUrl = `/api/proxy/m3u8?url=${encodeURIComponent(videoUrl)}&moontv-source=${currentSourceRef.current?.key || ''}`;
try {
// 创建新的播放器实例
Artplayer.USE_RAF = true;
artPlayerRef.current = new Artplayer({
container: artRef.current,
url: `/api/proxy/m3u8?url=${encodeURIComponent(videoUrl)}&moontv-source=${currentSourceRef.current?.key || ''}`,
url: targetUrl,
poster: currentChannel.logo,
volume: 0.7,
isLive: true, // 设置为直播模式
@@ -746,10 +754,9 @@ function LivePageClient() {
});
if (artPlayerRef.current?.video) {
const finalUrl = `/api/proxy/m3u8?url=${encodeURIComponent(videoUrl)}`;
ensureVideoSource(
artPlayerRef.current.video as HTMLVideoElement,
finalUrl
targetUrl
);
}
@@ -1029,6 +1036,36 @@ function LivePageClient() {
className='bg-black w-full h-full rounded-xl overflow-hidden shadow-lg border border-white/0 dark:border-white/30'
></div>
{/* 不支持的直播类型提示 */}
{unsupportedType && (
<div className='absolute inset-0 bg-black/90 backdrop-blur-sm rounded-xl flex items-center justify-center z-[600] transition-all duration-300'>
<div className='text-center max-w-md mx-auto px-6'>
<div className='relative mb-8'>
<div className='relative mx-auto w-24 h-24 bg-gradient-to-r from-orange-500 to-red-600 rounded-2xl shadow-2xl flex items-center justify-center transform hover:scale-105 transition-transform duration-300'>
<div className='text-white text-4xl'></div>
<div className='absolute -inset-2 bg-gradient-to-r from-orange-500 to-red-600 rounded-2xl opacity-20 animate-pulse'></div>
</div>
</div>
<div className='space-y-4'>
<h3 className='text-xl font-semibold text-white'>
</h3>
<div className='bg-orange-500/20 border border-orange-500/30 rounded-lg p-4'>
<p className='text-orange-300 font-medium'>
<span className='text-white font-bold'>{unsupportedType.toUpperCase()}</span>
</p>
<p className='text-orange-200 text-sm mt-2'>
M3U8
</p>
</div>
<p className='text-sm text-gray-300'>
</p>
</div>
</div>
</div>
)}
{/* 视频加载蒙层 */}
{isVideoLoading && (
<div className='absolute inset-0 bg-black/85 backdrop-blur-sm rounded-xl flex items-center justify-center z-[500] transition-all duration-300'>