feat: dplayer

This commit is contained in:
merlin
2025-12-29 15:40:42 +08:00
parent 27c4a247d3
commit b40691a4a3
10 changed files with 394 additions and 168 deletions

View File

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