fix: dplayer can not playing
This commit is contained in:
11
proxy.js
11
proxy.js
@@ -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) => {
|
||||
// 跳过一些可能导致问题的头信息
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/* margin: 0 auto; */
|
||||
/* padding: 2rem; */
|
||||
font-weight: normal;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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");
|
||||
};
|
||||
|
||||
|
||||
@@ -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 {} */
|
||||
|
||||
@@ -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,22 +123,29 @@ 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) => {
|
||||
export const sendMessage = (message: any) => {
|
||||
try{
|
||||
const jsonmessage = JSON.stringify(message);
|
||||
if (socket.value && socket.value.readyState === WebSocket.OPEN) {
|
||||
socket.value.send(message);
|
||||
socket.value.send(jsonmessage);
|
||||
} else {
|
||||
console.warn("WebSocket for video 未连接,无法发送消息");
|
||||
}
|
||||
}
|
||||
catch(error){
|
||||
console.error("Failed to stringify message:", error);
|
||||
}
|
||||
};
|
||||
|
||||
//没有错误的重连,只是浏览器在后台断开了连接
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user