feat: dplayer
This commit is contained in:
@@ -8,6 +8,8 @@ import DPlayer from 'dplayer';
|
||||
import Hls from 'hls.js';
|
||||
|
||||
const videoRef = ref(null);
|
||||
const player = ref(null);
|
||||
const hlsInstance = ref(null);
|
||||
|
||||
const props = defineProps({
|
||||
autoplay: { type: Boolean, default: false },
|
||||
@@ -15,75 +17,208 @@ const props = defineProps({
|
||||
danmaku: { type: Object, default: () => ({}) }
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
const playerOptions = {
|
||||
// 判断是否为 HLS 格式
|
||||
const isHlsUrl = (url) => {
|
||||
return url.includes('.m3u8') || url.includes('hls') || url.includes('application/x-mpegURL');
|
||||
};
|
||||
|
||||
// 清理 HLS 实例
|
||||
const cleanupHls = () => {
|
||||
if (hlsInstance.value) {
|
||||
try {
|
||||
hlsInstance.value.destroy();
|
||||
} catch (e) {
|
||||
console.warn('清理 HLS 实例时出错:', e);
|
||||
}
|
||||
hlsInstance.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
// 创建播放器配置
|
||||
const createPlayerOptions = (url) => {
|
||||
const proxyUrl = `/proxy?url=${encodeURIComponent(url)}`;
|
||||
const isHls = isHlsUrl(url);
|
||||
|
||||
console.log('视频 URL:', url);
|
||||
console.log('是否为 HLS 格式:', isHls);
|
||||
console.log('代理 URL:', proxyUrl);
|
||||
|
||||
const options = {
|
||||
container: videoRef.value,
|
||||
autoplay: props.autoplay,
|
||||
video: {
|
||||
url: `/proxy?url=${encodeURIComponent(props.videoUrl)}`,
|
||||
video: {},
|
||||
danmaku: props.danmaku
|
||||
};
|
||||
|
||||
if (isHls && Hls.isSupported()) {
|
||||
// HLS 格式使用 customHls
|
||||
options.video = {
|
||||
url: proxyUrl,
|
||||
type: 'customHls',
|
||||
customType: {
|
||||
customHls: function (video, player) {
|
||||
if (Hls.isSupported()) {
|
||||
const hls = new Hls();
|
||||
hls.loadSource(video.src);
|
||||
hls.attachMedia(video);
|
||||
hls.config.maxBufferLength = 60; // 设置最大缓冲时间为60秒
|
||||
hls.on(Hls.Events.MEDIA_ATTACHED, () => {
|
||||
video.play();
|
||||
});
|
||||
}
|
||||
cleanupHls(); // 清理旧的实例
|
||||
|
||||
const hls = new Hls({
|
||||
maxBufferLength: 20,
|
||||
maxMaxBufferLength: 60,
|
||||
enableWorker: true,
|
||||
lowLatencyMode: false
|
||||
});
|
||||
|
||||
hlsInstance.value = hls;
|
||||
|
||||
hls.loadSource(video.src);
|
||||
hls.attachMedia(video);
|
||||
|
||||
// 错误处理
|
||||
hls.on(Hls.Events.ERROR, (event, data) => {
|
||||
console.error('HLS 错误:', data);
|
||||
if (data.fatal) {
|
||||
switch (data.type) {
|
||||
case Hls.ErrorTypes.NETWORK_ERROR:
|
||||
console.error('网络错误,尝试恢复...');
|
||||
hls.startLoad();
|
||||
break;
|
||||
case Hls.ErrorTypes.MEDIA_ERROR:
|
||||
console.error('媒体错误,尝试恢复...');
|
||||
hls.recoverMediaError();
|
||||
break;
|
||||
default:
|
||||
console.error('致命错误,无法恢复');
|
||||
hls.destroy();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||
console.log('HLS 清单解析完成');
|
||||
if (props.autoplay) {
|
||||
video.play().catch(err => {
|
||||
console.warn('自动播放失败:', err);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
danmaku: props.danmaku
|
||||
};
|
||||
} else {
|
||||
// 普通视频格式(MP4等)
|
||||
options.video = {
|
||||
url: proxyUrl,
|
||||
type: 'auto' // 让 DPlayer 自动检测类型
|
||||
};
|
||||
}
|
||||
|
||||
return options;
|
||||
};
|
||||
|
||||
};
|
||||
onMounted(() => {
|
||||
try {
|
||||
const playerOptions = createPlayerOptions(props.videoUrl);
|
||||
player.value = new DPlayer(playerOptions);
|
||||
|
||||
console.log(`/proxy?url=${encodeURIComponent(props.videoUrl)}`)
|
||||
const player = new DPlayer(playerOptions);
|
||||
player.value.on('play', () => {
|
||||
console.log('播放:', props.videoUrl);
|
||||
});
|
||||
|
||||
player.value.on('pause', () => {
|
||||
console.log('暂停');
|
||||
});
|
||||
|
||||
player.value.on('ended', () => {
|
||||
console.log('播放结束');
|
||||
});
|
||||
|
||||
player.value.on('error', (error) => {
|
||||
console.error('播放器错误:', error);
|
||||
});
|
||||
|
||||
player.value.on('loadstart', () => {
|
||||
console.log('开始加载视频');
|
||||
});
|
||||
|
||||
player.value.on('canplay', () => {
|
||||
console.log('视频可以播放');
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('初始化播放器失败:', error);
|
||||
}
|
||||
});
|
||||
|
||||
player.on('play', () => {
|
||||
console.log("播放...")
|
||||
})
|
||||
player.on('pause', () => {
|
||||
console.log("暂停...")
|
||||
})
|
||||
player.on('ended', () => {
|
||||
console.log("结束...")
|
||||
})
|
||||
player.on('error', () => {
|
||||
console.log("出错...")
|
||||
})
|
||||
|
||||
watch(() => props.videoUrl, (newUrl) => {
|
||||
if (player) {
|
||||
console.log("切换视频...")
|
||||
player.switchVideo({
|
||||
url: `/proxy?url=${encodeURIComponent(newUrl)}`,
|
||||
type: 'customHls',
|
||||
customType: {
|
||||
customHls: function (video, player) {
|
||||
if (Hls.isSupported()) {
|
||||
const hls = new Hls();
|
||||
watch(() => props.videoUrl, (newUrl) => {
|
||||
if (player.value && newUrl) {
|
||||
console.log('切换视频到:', newUrl);
|
||||
|
||||
try {
|
||||
cleanupHls(); // 清理旧的 HLS 实例
|
||||
|
||||
const isHls = isHlsUrl(newUrl);
|
||||
const proxyUrl = `/proxy?url=${encodeURIComponent(newUrl)}`;
|
||||
|
||||
if (isHls && Hls.isSupported()) {
|
||||
// HLS 格式切换
|
||||
player.value.switchVideo({
|
||||
url: proxyUrl,
|
||||
type: 'customHls',
|
||||
customType: {
|
||||
customHls: function (video, player) {
|
||||
cleanupHls();
|
||||
|
||||
const hls = new Hls({
|
||||
maxBufferLength: 20,
|
||||
maxMaxBufferLength: 60,
|
||||
enableWorker: true
|
||||
});
|
||||
|
||||
hlsInstance.value = hls;
|
||||
hls.loadSource(video.src);
|
||||
hls.attachMedia(video);
|
||||
hls.on(Hls.Events.MEDIA_ATTACHED, () => {
|
||||
video.play();
|
||||
|
||||
hls.on(Hls.Events.ERROR, (event, data) => {
|
||||
console.error('HLS 错误:', data);
|
||||
if (data.fatal) {
|
||||
switch (data.type) {
|
||||
case Hls.ErrorTypes.NETWORK_ERROR:
|
||||
hls.startLoad();
|
||||
break;
|
||||
case Hls.ErrorTypes.MEDIA_ERROR:
|
||||
hls.recoverMediaError();
|
||||
break;
|
||||
default:
|
||||
hls.destroy();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
} else {
|
||||
// 普通视频格式切换
|
||||
player.value.switchVideo({
|
||||
url: proxyUrl,
|
||||
type: 'auto'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('切换视频失败:', error);
|
||||
}
|
||||
}
|
||||
)
|
||||
});
|
||||
|
||||
// 销毁时清理播放器
|
||||
onBeforeUnmount(() => {
|
||||
player.destroy();
|
||||
});
|
||||
// 销毁时清理播放器和 HLS 实例
|
||||
onBeforeUnmount(() => {
|
||||
cleanupHls();
|
||||
if (player.value) {
|
||||
try {
|
||||
player.value.destroy();
|
||||
} catch (e) {
|
||||
console.warn('销毁播放器时出错:', e);
|
||||
}
|
||||
player.value = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user