fix: dplayer can not playing

This commit is contained in:
2026-03-23 18:08:59 +08:00
parent 08ae7414d0
commit 970aae1c5f
8 changed files with 1117 additions and 1120 deletions

View File

@@ -13,6 +13,10 @@ app.use((req, res, next) => {
next();
});
// 本地静态文件代理:/local -> C:\Users\xyf17\Merlin\data
const localDataPath = 'C:\\Users\\xyf17\\Merlin\\data';
app.use('/local', express.static(localDataPath));
// 通用代理路由
app.get('/proxy', async (req, res) => {
console.log('Received Range header:', req.headers.range);
@@ -28,6 +32,10 @@ app.get('/proxy', async (req, res) => {
const headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
};
// 避免上游压缩导致 Range/MSE 解析异常mp4 一般不会压缩,但这里保守处理)
headers['Accept-Encoding'] = 'identity';
if (req.headers.range) {
headers['Range'] = req.headers.range; // 转发 Range 请求头,支持视频分段加载
}
@@ -43,6 +51,9 @@ app.get('/proxy', async (req, res) => {
});
}
// 关键Range 请求上游通常返回 206必须把状态码透传给浏览器
res.status(response.status);
// 设置响应头
response.headers.forEach((value, key) => {
// 跳过一些可能导致问题的头信息

View File

@@ -65,3 +65,29 @@ export const joinPlayroom = async (r_id: number) => {
throw new Error(response.data.message);
}
}
export const getPlayroomDetails = async (r_id:number) =>{
const response = await axios.get('/api/playroom/detail/'+r_id,{
headers:{
'Authorization': "Bearer " + userinfo.token
}
})
if(response.data.code === "200"){
return response.data.data;
} else {
throw new Error(response.data.message);
}
}
export const getPlayroomMembers = async (r_id: number) => {
const response = await axios.get('/api/playroom/member/'+r_id,{
headers:{
'Authorization': "Bearer " + userinfo.token
}
})
if(response.data.code === "200"){
return response.data.data;
} else {
throw new Error(response.data.message);
}
}

View File

@@ -2,6 +2,8 @@
#app {
max-width: 1280px;
width: 100%;
height: 100%;
/* margin: 0 auto; */
/* padding: 2rem; */
font-weight: normal;

View File

@@ -1,6 +1,6 @@
import { ref } from "vue";
import { defineStore } from "pinia";
import { connectWebSocket, disconnectWebSocket, sendMessage, setIsManualClose } from '@/websocket/roomSocket'
import { connectWebSocket, disconnectWebSocket, sendMessage, setIsManualClose } from "@/websocket/roomSocket";
interface PlayroomState {
id: number
@@ -11,10 +11,18 @@ interface PlayroomState {
role: number;
}
interface member {
id: number;
u_id: string;
u_name: string;
u_avatar: string;
}
export const PlayroomStore = defineStore("PlayroomStore",
() =>{
const currentPlayroom = ref<PlayroomState>();
const members = ref<member[]>([]);
const currentUrl = ref<string>("");
const setCurrentPlayroom = (playroom: PlayroomState) => {
@@ -34,16 +42,27 @@ export const PlayroomStore = defineStore("PlayroomStore",
}
const clearPlayroom = () => {
currentPlayroom.value = undefined;
currentPlayroom.value = null;
currentUrl.value = "";
members.value = [];
}
const addmember = (member: member) => {
members.value.push(member);
}
const getmembers = (page: number, pageSize: number) => {
return members.value.slice((page - 1) * pageSize, page * pageSize);
}
return {
getCurrentPlayroom,
getCurrentId,
currentPlayroom,
currentUrl,
setCurrentPlayroom,
setCurrentUrl,
clearPlayroom,
addmember,
getmembers,
}
})
@@ -58,7 +77,7 @@ export const videoSocketStore = defineStore("videoSocketStore",{
connect(id: number) {
this.id = id;
if (this.isConnected === true) return
connectWebSocket();
connectWebSocket(id);
this.isConnected = true;
},
disconnect() {

View File

@@ -55,7 +55,7 @@ const goToRoom = (r) => {
roominfo.setCurrentPlayroom(r);
// 跳转到房间页面
const baseUrl = window.location.origin;
const targetUrl = `${baseUrl}/room`; // 替换为你的目标路由
const targetUrl = `${baseUrl}/room?r_id=${r.r_id}`; // 替换为你的目标路由
window.open(targetUrl, "room");
};

View File

@@ -36,46 +36,9 @@
</el-row>
</el-col>
<el-col :span="16">
<el-col :span="20">
<video-player :autoplay="false" :videoUrl="currentURL" />
</el-col>
<el-col :span="4">
<el-row style="height: 50%">
<!-- 语音大厅 -->
<el-col :span="24" class="voice-room">
<!-- <audio-player :audioUrl="audioUrl" /> -->
<el-row>
<!-- 加入的语言用户 -->
<el-text size="large">语言聊天室:</el-text>
<el-button type="success" round style="margin-left: 10px;" @click="joinVoiceRoom"
v-if="!invoice">加入</el-button>
<el-button type="info" round style="margin-left: 10px;" @click="leaveVoiceRoom"
v-if="invoice">离开</el-button>
<el-scrollbar style="height: 150px;width: 100%;">
<div class="invoice">
<div v-for="(item) in membersInVoice" class="user-profile-invoice">
<img :src="item.u_avatar" alt="User Avatar" />
</div>
</div>
</el-scrollbar>
</el-row>
</el-col>
</el-row>
<el-row style="height: 50%">
<el-col :span="24">
<!-- 弹幕显示区域 -->
<el-scroller class="comments">
<div v-for="comment in roomMessages" :key="comment.id" class="comment">
{{ comment.u_name }}:
{{ comment.content }}
</div>
</el-scroller>
<!-- 发送弹幕输入框 -->
<el-input v-model="newComment" placeholder="发送弹幕" style="height: 20%;margin-left: 3px;"
@keyup.enter="sendComment" />
</el-col>
</el-row>
</el-col>
</el-row>
</div>
</div>
@@ -162,6 +125,8 @@ import { ElMessage } from 'element-plus';
import { userInfoStore } from '@/store/user';
import videoPlayer from '@/components/videoPlayer.vue';
import { PlayroomStore } from '@/store/playroom';
import { getPlayroomDetails } from '@/api/playroom';
import { connectWebSocket } from '@/websocket/roomSocket';
//import audioPlayer from '@/components/audioPlayer.vue'; // 假设你有一个音频播放组件
@@ -171,7 +136,9 @@ const userinfo = userInfoStore();
const dialogVisibleCode = ref(false)
const dialogVisibleVideo = ref(false)
const invoice = ref(false)
const invoice = ref(false);
const drawer = ref(false);
const role = ref(null);
const invitingCode = ref('666666')
@@ -259,7 +226,7 @@ const membersInVoice = ref([
])
const videoUrl = ref('https://www.5dm.link/api/dd.php?vid=ccccxhndnys1&cid=ccccxhndnys1&xid=0&pid=55293&tid=1742788904&t=616d5131b6ade51a0e20814466b13515&ext=.mp4'); // 替换为你的视频流地址
const videoUrl = ref(''); // 替换为你的视频流地址
const audioUrl = ref('https://example.com/audio/stream'); // 替换为你的音频流地址
const danmakuOptions = ref({
@@ -289,8 +256,13 @@ const sendComment = () => {
}
};
const getRoomInfo = () => {
const getRoomInfo = async (r_id) => {
try {
const response = await getPlayroomDetails(r_id)
curruentRoomInfo.setCurrentPlayroom(response)
} catch (error) {
ElMessage.error('获取房间信息失败:' + error)
}
}
const handleAvatarChange = (event) => {
@@ -303,32 +275,7 @@ const handleAvatarChange = (event) => {
//上传到服务器
const uploadAvatar = () => {
if (!avatar.value) {
ElMessage('请选择头像')
return
}
const formdata = new FormData();
formdata.append('file', avatar.value)
formdata.append('r_id', curruentRoomInfo.r_id)
axios({
headers: {
'Content-Type': 'multipart/form-data',
'Authorization': userinfo.token
},
method: 'post',
url: '/api/avatar/upload',
data: formdata
}).then((response) => {
if (response.data.code !== 200) {
ElMessage.error(response.data.msg || '上传失败')
return
}
getUserInfo()
ElMessage.success('上传成功')
}).catch((error) => {
console.error('上传失败:', error);
ElMessage.error('上传失败')
})
}
const getInvitingCode = () => {
@@ -362,27 +309,10 @@ const goback = () => {
}
onMounted(() => {
getRoomInfo()
//测试代码
curruentRoomInfo.r_id = '123456';
curruentRoomInfo.r_name = '弹幕聊天室';
curruentRoomInfo.setCurrentUrl('https://www.5dm.link/api/dd.php?vid=ccccxhndnys1&cid=ccccxhndnys1&xid=0&pid=55293&tid=1742788904&t=616d5131b6ade51a0e20814466b13515&ext=.mp4')
curruentRoomInfo.r_avatar = 'https://merlin.xin/avatars/avatar';
role.value = 0;
avatarPreview.value = curruentRoomInfo.r_avatar;
watchEffect(() => {
if (curruentRoomInfo.r_id !== '' && curruentRoomInfo.r_id !== null) { // 如果 r_id 不为空
console.log('Room ID is available:', curruentRoomInfo.r_id);
//页面需要加载的逻辑
//头像预览容器赋值
avatarPreview.value = curruentRoomInfo.r_avatar;
}
})
onMounted(async () => {
const r_id = window.location.search.split('=')[1];
await getRoomInfo(r_id)
connectWebSocket(r_id)
})
@@ -399,11 +329,12 @@ onMounted(() => {
.container {
position: relative;
top: 50px;
display: flex;
flex-direction: column;
height: 80vh;
width: 80vw;
height: 80%;
width: 100%;
padding: 50px;
max-height: 100vh;
}
/* .maincontent {} */

View File

@@ -1,59 +1,55 @@
import { ref } from "vue";
import { userInfoStore } from "@/store/user";
import { ElMessage } from "element-plus";
import { PlayroomStore } from "@/store/playroom";
// userinfo 实例
const userinfo = userInfoStore();
// 延迟获取 playroom 实例,避免循环依赖
const getPlayroom = () => {
return PlayroomStore();
};
const roomid = ref<number>(0);
// WebSocket 实例
const socket = ref(null);
const socket = ref<WebSocket | null>(null);
const isManualClose = ref(false);
const isManualClose = ref<boolean>(false);
const reconnectScheduled = ref(false);
const reconnectScheduled = ref<boolean>(false);
const retryCount = ref(0);
const retryCount = ref<number>(0);
export const getRetryCount = () => {
const getRetryCount = () => {
return retryCount.value;
};
export const addRetryCount = () => {
const addRetryCount = () => {
retryCount.value = retryCount.value + 1;
};
export const resetRetryCount = () => {
const resetRetryCount = () => {
retryCount.value = 0;
};
export const setReconnectScheduled = (value) => {
const setReconnectScheduled = (value: boolean) => {
reconnectScheduled.value = value;
};
export const getReconnectScheduled = () => {
const getReconnectScheduled = () => {
return reconnectScheduled.value;
};
export const setIsManualClose = (value) => {
export const setIsManualClose = (value: boolean) => {
isManualClose.value = value;
};
export const getIsManualClose = () => {
const getIsManualClose = () => {
return isManualClose.value;
};
// 连接WebSocket
export const connectWebSocket = () => {
const playroom = getPlayroom();
export const connectWebSocket = (r_id: number) => {
roomid.value = r_id;
const protocol = window.location.protocol === "https:" ? "wss://" : "ws://";
const host = window.location.host;
const socketUrl = `${protocol}${host}/ws/video?r_id=${playroom.getCurrentId()}`;
const socketUrl = `${protocol}${host}/ws/playroom?r_id=${r_id}`;
// const socketUrl = `ws://localhost:8080/online?u_id=${userinfo.user.u_id}&u_name=${userinfo.user.u_name}`;
if (socket.value && socket.value.readyState !== WebSocket.CLOSED) {
@@ -68,7 +64,7 @@ export const connectWebSocket = () => {
console.log(retrytime);
socket.value = new WebSocket(socketUrl, "token-"+ userinfo.token);
socket.value.onopen = (event) => {
socket.value.onopen = (event: any) => {
console.log("WebSocket for video 连接已建立", event);
setReconnectScheduled(false);
setIsManualClose(false);
@@ -82,8 +78,18 @@ export const connectWebSocket = () => {
const MessageData = JSON.parse(event.data);
const cmd = MessageData.cmd;
switch(cmd){
case "PING":
console.log("收到PING消息");
const msg = {
cmd: "PONG",
from: MessageData.to,
// 可扩展字段
time: new Date().toLocaleString()
}
sendMessage(msg);
break;
case "VIDEO_SYNC":
console.log("视频同步消息", MessageData);
// console.log("视频同步消息", MessageData);
break;
}
}catch(error){
@@ -117,21 +123,28 @@ export const connectWebSocket = () => {
// 断开WebSocket连接
export const disconnectWebSocket = () => {
if (socket.value && socket.value.readyState === WebSocket.OPEN) {
roomid.value = 0;
socket.value.close();
}
};
// 重连机制
export const reConnectWebSocket = () => {
connectWebSocket();
connectWebSocket(roomid.value);
};
// 发送消息
export const sendMessage = (message) => {
if (socket.value && socket.value.readyState === WebSocket.OPEN) {
socket.value.send(message);
} else {
console.warn("WebSocket for video 未连接,无法发送消息");
export const sendMessage = (message: any) => {
try{
const jsonmessage = JSON.stringify(message);
if (socket.value && socket.value.readyState === WebSocket.OPEN) {
socket.value.send(jsonmessage);
} else {
console.warn("WebSocket for video 未连接,无法发送消息");
}
}
catch(error){
console.error("Failed to stringify message:", error);
}
};

View File

@@ -29,11 +29,6 @@ export default defineConfig({
changeOrigin: true,
ws: true,
},
// '/ws': {
// target: 'ws://localhost:8080',
// changeOrigin: true,
// ws: true,
// }
},
host: '0.0.0.0',
port: 5173,