feat: disable flv live

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

View File

@@ -32,7 +32,6 @@
"bs58": "^6.0.0",
"clsx": "^2.0.0",
"crypto-js": "^4.2.0",
"flv.js": "^1.6.2",
"framer-motion": "^12.18.1",
"he": "^1.2.0",
"hls.js": "^1.6.10",

37
pnpm-lock.yaml generated
View File

@@ -47,9 +47,6 @@ importers:
crypto-js:
specifier: ^4.2.0
version: 4.2.0
flv.js:
specifier: ^1.6.2
version: 1.6.2
framer-motion:
specifier: ^12.18.1
version: 12.18.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -2630,9 +2627,6 @@ packages:
es6-object-assign@1.1.0:
resolution: {integrity: sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==}
es6-promise@4.2.8:
resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==}
escalade@3.2.0:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
engines: {node: '>=6'}
@@ -2887,9 +2881,6 @@ packages:
flatted@3.3.3:
resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
flv.js@1.6.2:
resolution: {integrity: sha512-xre4gUbX1MPtgQRKj2pxJENp/RnaHaxYvy3YToVVCrSmAWUu85b9mug6pTXF6zakUjNP2lFWZ1rkSX7gxhB/2A==}
for-each@0.3.5:
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
engines: {node: '>= 0.4'}
@@ -5133,9 +5124,6 @@ packages:
webpack-cli:
optional: true
webworkify-webpack@2.1.5:
resolution: {integrity: sha512-2akF8FIyUvbiBBdD+RoHpoTbHMQF2HwjcxfDvgztAX5YwbZNyrtfUMgvfgFVsgDhDPVTlkbb5vyasqDHfIDPQw==}
whatwg-encoding@1.0.5:
resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==}
@@ -8210,8 +8198,6 @@ snapshots:
es6-object-assign@1.1.0: {}
es6-promise@4.2.8: {}
escalade@3.2.0: {}
escape-string-regexp@2.0.0: {}
@@ -8234,8 +8220,8 @@ snapshots:
'@typescript-eslint/parser': 5.62.0(eslint@8.57.1)(typescript@4.9.5)
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@8.57.1)
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1)
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1))(eslint@8.57.1)
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1)
eslint-plugin-react: 7.37.5(eslint@8.57.1)
eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.1)
@@ -8258,7 +8244,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@8.57.1):
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1))(eslint@8.57.1):
dependencies:
'@nolyfill/is-core-module': 1.0.39
debug: 4.4.1(supports-color@9.4.0)
@@ -8269,22 +8255,22 @@ snapshots:
tinyglobby: 0.2.14
unrs-resolver: 1.9.0
optionalDependencies:
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1)
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.12.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1):
eslint-module-utils@2.12.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 5.62.0(eslint@8.57.1)(typescript@4.9.5)
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@8.57.1)
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1))(eslint@8.57.1)
transitivePeerDependencies:
- supports-color
eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1):
eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.9
@@ -8295,7 +8281,7 @@ snapshots:
doctrine: 2.1.0
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1)
eslint-module-utils: 2.12.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -8553,11 +8539,6 @@ snapshots:
flatted@3.3.3: {}
flv.js@1.6.2:
dependencies:
es6-promise: 4.2.8
webworkify-webpack: 2.1.5
for-each@0.3.5:
dependencies:
is-callable: 1.2.7
@@ -11146,8 +11127,6 @@ snapshots:
- esbuild
- uglify-js
webworkify-webpack@2.1.5: {}
whatwg-encoding@1.0.5:
dependencies:
iconv-lite: 0.4.24

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'>