fix: dplayer can not playing
This commit is contained in:
237
proxy.js
237
proxy.js
@@ -1,113 +1,124 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import fetch from 'node-fetch';
|
import fetch from 'node-fetch';
|
||||||
import { URL } from 'url';
|
import { URL } from 'url';
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const port = 3000;
|
const port = 3000;
|
||||||
|
|
||||||
// 允许跨域请求
|
// 允许跨域请求
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
res.header('Access-Control-Allow-Origin', '*');
|
res.header('Access-Control-Allow-Origin', '*');
|
||||||
res.header('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS');
|
res.header('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS');
|
||||||
res.header('Access-Control-Allow-Headers', 'Range');
|
res.header('Access-Control-Allow-Headers', 'Range');
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 通用代理路由
|
// 本地静态文件代理:/local -> C:\Users\xyf17\Merlin\data
|
||||||
app.get('/proxy', async (req, res) => {
|
const localDataPath = 'C:\\Users\\xyf17\\Merlin\\data';
|
||||||
console.log('Received Range header:', req.headers.range);
|
app.use('/local', express.static(localDataPath));
|
||||||
// 获取目标 URL
|
|
||||||
const targetUrl = req.query.url;
|
// 通用代理路由
|
||||||
console.log('Fetching data from:', targetUrl);
|
app.get('/proxy', async (req, res) => {
|
||||||
if (!targetUrl) {
|
console.log('Received Range header:', req.headers.range);
|
||||||
return res.status(400).json({ error: 'URL parameter is required' });
|
// 获取目标 URL
|
||||||
}
|
const targetUrl = req.query.url;
|
||||||
|
console.log('Fetching data from:', targetUrl);
|
||||||
try {
|
if (!targetUrl) {
|
||||||
// 设置请求头
|
return res.status(400).json({ error: 'URL parameter is required' });
|
||||||
const headers = {
|
}
|
||||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
|
||||||
};
|
try {
|
||||||
if (req.headers.range) {
|
// 设置请求头
|
||||||
headers['Range'] = req.headers.range; // 转发 Range 请求头,支持视频分段加载
|
const headers = {
|
||||||
}
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
||||||
|
};
|
||||||
// 向目标 URL 发起请求
|
|
||||||
const response = await fetch(targetUrl, { headers });
|
// 避免上游压缩导致 Range/MSE 解析异常(mp4 一般不会压缩,但这里保守处理)
|
||||||
|
headers['Accept-Encoding'] = 'identity';
|
||||||
// 检查响应状态
|
|
||||||
if (!response.ok) {
|
if (req.headers.range) {
|
||||||
return res.status(response.status).json({
|
headers['Range'] = req.headers.range; // 转发 Range 请求头,支持视频分段加载
|
||||||
error: `Failed to fetch: ${response.statusText}`,
|
}
|
||||||
status: response.status
|
|
||||||
});
|
// 向目标 URL 发起请求
|
||||||
}
|
const response = await fetch(targetUrl, { headers });
|
||||||
|
|
||||||
// 设置响应头
|
// 检查响应状态
|
||||||
response.headers.forEach((value, key) => {
|
if (!response.ok) {
|
||||||
// 跳过一些可能导致问题的头信息
|
return res.status(response.status).json({
|
||||||
if (key.toLowerCase() !== 'content-encoding' &&
|
error: `Failed to fetch: ${response.statusText}`,
|
||||||
key.toLowerCase() !== 'transfer-encoding' &&
|
status: response.status
|
||||||
key.toLowerCase() !== 'connection') {
|
});
|
||||||
res.setHeader(key, value);
|
}
|
||||||
}
|
|
||||||
});
|
// 关键:Range 请求上游通常返回 206;必须把状态码透传给浏览器
|
||||||
|
res.status(response.status);
|
||||||
// 特殊处理 Transfer-Encoding 和 Content-Length
|
|
||||||
if (response.headers.get('transfer-encoding') === 'chunked') {
|
// 设置响应头
|
||||||
res.removeHeader('Content-Length');
|
response.headers.forEach((value, key) => {
|
||||||
}
|
// 跳过一些可能导致问题的头信息
|
||||||
|
if (key.toLowerCase() !== 'content-encoding' &&
|
||||||
// 将响应体直接流式传输给客户端
|
key.toLowerCase() !== 'transfer-encoding' &&
|
||||||
if (response.body) {
|
key.toLowerCase() !== 'connection') {
|
||||||
response.body.pipe(res, { end: true });
|
res.setHeader(key, value);
|
||||||
|
}
|
||||||
// 错误处理
|
});
|
||||||
response.body.on('error', (err) => {
|
|
||||||
console.error('Error during data transfer:', err);
|
// 特殊处理 Transfer-Encoding 和 Content-Length
|
||||||
if (!res.headersSent) {
|
if (response.headers.get('transfer-encoding') === 'chunked') {
|
||||||
res.status(500).json({ error: 'Error during data transfer' });
|
res.removeHeader('Content-Length');
|
||||||
}
|
}
|
||||||
});
|
|
||||||
} else {
|
// 将响应体直接流式传输给客户端
|
||||||
res.status(500).json({ error: 'No response body' });
|
if (response.body) {
|
||||||
}
|
response.body.pipe(res, { end: true });
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching data:', error);
|
// 错误处理
|
||||||
|
response.body.on('error', (err) => {
|
||||||
// 根据错误类型返回更具体的错误信息
|
console.error('Error during data transfer:', err);
|
||||||
let errorMessage = 'Error fetching data';
|
if (!res.headersSent) {
|
||||||
let statusCode = 500;
|
res.status(500).json({ error: 'Error during data transfer' });
|
||||||
let hostname = '';
|
}
|
||||||
|
});
|
||||||
try {
|
} else {
|
||||||
hostname = new URL(targetUrl).hostname;
|
res.status(500).json({ error: 'No response body' });
|
||||||
} catch (e) {
|
}
|
||||||
hostname = 'unknown';
|
} catch (error) {
|
||||||
}
|
console.error('Error fetching data:', error);
|
||||||
|
|
||||||
if (error.code === 'ENOTFOUND') {
|
// 根据错误类型返回更具体的错误信息
|
||||||
errorMessage = `DNS解析失败:无法解析域名 "${hostname}"。请检查URL是否正确,或网络连接是否正常。`;
|
let errorMessage = 'Error fetching data';
|
||||||
statusCode = 502;
|
let statusCode = 500;
|
||||||
} else if (error.code === 'ECONNREFUSED') {
|
let hostname = '';
|
||||||
errorMessage = `连接被拒绝:无法连接到目标服务器 "${hostname}"。`;
|
|
||||||
statusCode = 502;
|
try {
|
||||||
} else if (error.code === 'ETIMEDOUT') {
|
hostname = new URL(targetUrl).hostname;
|
||||||
errorMessage = `请求超时:目标服务器 "${hostname}" 响应时间过长。`;
|
} catch (e) {
|
||||||
statusCode = 504;
|
hostname = 'unknown';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!res.headersSent) {
|
if (error.code === 'ENOTFOUND') {
|
||||||
res.status(statusCode).json({
|
errorMessage = `DNS解析失败:无法解析域名 "${hostname}"。请检查URL是否正确,或网络连接是否正常。`;
|
||||||
error: errorMessage,
|
statusCode = 502;
|
||||||
code: error.code,
|
} else if (error.code === 'ECONNREFUSED') {
|
||||||
targetUrl: targetUrl
|
errorMessage = `连接被拒绝:无法连接到目标服务器 "${hostname}"。`;
|
||||||
});
|
statusCode = 502;
|
||||||
}
|
} else if (error.code === 'ETIMEDOUT') {
|
||||||
}
|
errorMessage = `请求超时:目标服务器 "${hostname}" 响应时间过长。`;
|
||||||
});
|
statusCode = 504;
|
||||||
|
}
|
||||||
app.listen(port, () => {
|
|
||||||
console.log(`Proxy server running at http://localhost:${port}`);
|
if (!res.headersSent) {
|
||||||
});
|
res.status(statusCode).json({
|
||||||
|
error: errorMessage,
|
||||||
|
code: error.code,
|
||||||
|
targetUrl: targetUrl
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(port, () => {
|
||||||
|
console.log(`Proxy server running at http://localhost:${port}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,67 +1,93 @@
|
|||||||
import { userInfoStore } from "@/store/user";
|
import { userInfoStore } from "@/store/user";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
const userinfo = userInfoStore();
|
const userinfo = userInfoStore();
|
||||||
|
|
||||||
|
|
||||||
export const searchPlayRoom = async (inputValue: string) => {
|
export const searchPlayRoom = async (inputValue: string) => {
|
||||||
const response = await axios.post('/api/playroom/search',{
|
const response = await axios.post('/api/playroom/search',{
|
||||||
r_name: inputValue
|
r_name: inputValue
|
||||||
},{
|
},{
|
||||||
headers:{
|
headers:{
|
||||||
'Authorization': "Bearer " + userinfo.token
|
'Authorization': "Bearer " + userinfo.token
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if(response.data.code === "200"){
|
if(response.data.code === "200"){
|
||||||
return response.data.data.records
|
return response.data.data.records
|
||||||
} else {
|
} else {
|
||||||
console.error(response.data.msg)
|
console.error(response.data.msg)
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createPlayroom = async (room_name: string) => {
|
export const createPlayroom = async (room_name: string) => {
|
||||||
const response = await axios.post('/api/playroom/create',{
|
const response = await axios.post('/api/playroom/create',{
|
||||||
r_name: room_name
|
r_name: room_name
|
||||||
},{
|
},{
|
||||||
headers:{
|
headers:{
|
||||||
'Authorization': "Bearer " + userinfo.token
|
'Authorization': "Bearer " + userinfo.token
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if(response.data.code === "200"){
|
if(response.data.code === "200"){
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.data.message);
|
throw new Error(response.data.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getPlayrooms = async ()=> {
|
export const getPlayrooms = async ()=> {
|
||||||
const response = await axios.get('/api/playroom/get',{
|
const response = await axios.get('/api/playroom/get',{
|
||||||
headers:{
|
headers:{
|
||||||
'Authorization' : `Bearer ${userinfo.token}`
|
'Authorization' : `Bearer ${userinfo.token}`
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if(response.data.code === "200"){
|
if(response.data.code === "200"){
|
||||||
return response.data.data;
|
return response.data.data;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.data.message);
|
throw new Error(response.data.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const joinPlayroom = async (r_id: number) => {
|
export const joinPlayroom = async (r_id: number) => {
|
||||||
const response = await axios.post('/api/inviting/playroom',{
|
const response = await axios.post('/api/inviting/playroom',{
|
||||||
inviter: userinfo.user.id,
|
inviter: userinfo.user.id,
|
||||||
target: userinfo.user.id,
|
target: userinfo.user.id,
|
||||||
status: 0,
|
status: 0,
|
||||||
room: r_id,
|
room: r_id,
|
||||||
},{
|
},{
|
||||||
headers:{
|
headers:{
|
||||||
'Authorization': "Bearer " + userinfo.token
|
'Authorization': "Bearer " + userinfo.token
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if(response.data.code === "200"){
|
if(response.data.code === "200"){
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.data.message);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,35 +1,37 @@
|
|||||||
@import './base.css';
|
@import './base.css';
|
||||||
|
|
||||||
#app {
|
#app {
|
||||||
max-width: 1280px;
|
max-width: 1280px;
|
||||||
/* margin: 0 auto; */
|
width: 100%;
|
||||||
/* padding: 2rem; */
|
height: 100%;
|
||||||
font-weight: normal;
|
/* margin: 0 auto; */
|
||||||
}
|
/* padding: 2rem; */
|
||||||
|
font-weight: normal;
|
||||||
a,
|
}
|
||||||
.green {
|
|
||||||
text-decoration: none;
|
a,
|
||||||
color: hsla(160, 100%, 37%, 1);
|
.green {
|
||||||
transition: 0.4s;
|
text-decoration: none;
|
||||||
padding: 3px;
|
color: hsla(160, 100%, 37%, 1);
|
||||||
}
|
transition: 0.4s;
|
||||||
|
padding: 3px;
|
||||||
@media (hover: hover) {
|
}
|
||||||
a:hover {
|
|
||||||
/* background-color: hsla(160, 100%, 37%, 0.2); */
|
@media (hover: hover) {
|
||||||
}
|
a:hover {
|
||||||
}
|
/* background-color: hsla(160, 100%, 37%, 0.2); */
|
||||||
|
}
|
||||||
@media (min-width: 1024px) {
|
}
|
||||||
body {
|
|
||||||
display: flex;
|
@media (min-width: 1024px) {
|
||||||
place-items: center;
|
body {
|
||||||
}
|
display: flex;
|
||||||
|
place-items: center;
|
||||||
#app {
|
}
|
||||||
display: grid;
|
|
||||||
/* grid-template-columns: 1fr 1fr;
|
#app {
|
||||||
padding: 0 2rem; */
|
display: grid;
|
||||||
}
|
/* grid-template-columns: 1fr 1fr;
|
||||||
}
|
padding: 0 2rem; */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,73 +1,92 @@
|
|||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { connectWebSocket, disconnectWebSocket, sendMessage, setIsManualClose } from '@/websocket/roomSocket'
|
import { connectWebSocket, disconnectWebSocket, sendMessage, setIsManualClose } from "@/websocket/roomSocket";
|
||||||
|
|
||||||
interface PlayroomState {
|
interface PlayroomState {
|
||||||
id: number
|
id: number
|
||||||
r_id: number;
|
r_id: number;
|
||||||
r_name: string;
|
r_name: string;
|
||||||
r_introduction: string;
|
r_introduction: string;
|
||||||
r_avatar: string;
|
r_avatar: string;
|
||||||
role: number;
|
role: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface member {
|
||||||
export const PlayroomStore = defineStore("PlayroomStore",
|
id: number;
|
||||||
() =>{
|
u_id: string;
|
||||||
const currentPlayroom = ref<PlayroomState>();
|
u_name: string;
|
||||||
const currentUrl = ref<string>("");
|
u_avatar: string;
|
||||||
|
}
|
||||||
const setCurrentPlayroom = (playroom: PlayroomState) => {
|
|
||||||
currentPlayroom.value = playroom;
|
|
||||||
}
|
export const PlayroomStore = defineStore("PlayroomStore",
|
||||||
|
() =>{
|
||||||
const getCurrentPlayroom = () =>{
|
const currentPlayroom = ref<PlayroomState>();
|
||||||
return currentPlayroom.value;
|
const members = ref<member[]>([]);
|
||||||
}
|
const currentUrl = ref<string>("");
|
||||||
|
|
||||||
const getCurrentId = () =>{
|
const setCurrentPlayroom = (playroom: PlayroomState) => {
|
||||||
return currentPlayroom.value?.r_id;
|
currentPlayroom.value = playroom;
|
||||||
}
|
}
|
||||||
|
|
||||||
const setCurrentUrl = (url: string) => {
|
const getCurrentPlayroom = () =>{
|
||||||
currentUrl.value = url;
|
return currentPlayroom.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearPlayroom = () => {
|
const getCurrentId = () =>{
|
||||||
currentPlayroom.value = undefined;
|
return currentPlayroom.value?.r_id;
|
||||||
currentUrl.value = "";
|
}
|
||||||
}
|
|
||||||
|
const setCurrentUrl = (url: string) => {
|
||||||
return {
|
currentUrl.value = url;
|
||||||
getCurrentPlayroom,
|
}
|
||||||
getCurrentId,
|
|
||||||
setCurrentPlayroom,
|
const clearPlayroom = () => {
|
||||||
setCurrentUrl,
|
currentPlayroom.value = null;
|
||||||
clearPlayroom,
|
currentUrl.value = "";
|
||||||
}
|
members.value = [];
|
||||||
})
|
}
|
||||||
|
|
||||||
export const videoSocketStore = defineStore("videoSocketStore",{
|
const addmember = (member: member) => {
|
||||||
state: () => ({
|
members.value.push(member);
|
||||||
isConnected: false,
|
}
|
||||||
hasGotMessage: false,
|
|
||||||
id: 0
|
const getmembers = (page: number, pageSize: number) => {
|
||||||
}),
|
return members.value.slice((page - 1) * pageSize, page * pageSize);
|
||||||
|
}
|
||||||
actions: {
|
|
||||||
connect(id: number) {
|
|
||||||
this.id = id;
|
return {
|
||||||
if (this.isConnected === true) return
|
currentPlayroom,
|
||||||
connectWebSocket();
|
currentUrl,
|
||||||
this.isConnected = true;
|
setCurrentPlayroom,
|
||||||
},
|
clearPlayroom,
|
||||||
disconnect() {
|
addmember,
|
||||||
setIsManualClose(true);
|
getmembers,
|
||||||
disconnectWebSocket();
|
}
|
||||||
this.isConnected = false;
|
})
|
||||||
},
|
|
||||||
send(message: string) {
|
export const videoSocketStore = defineStore("videoSocketStore",{
|
||||||
sendMessage(JSON.stringify(message));
|
state: () => ({
|
||||||
}
|
isConnected: false,
|
||||||
}
|
hasGotMessage: false,
|
||||||
})
|
id: 0
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
connect(id: number) {
|
||||||
|
this.id = id;
|
||||||
|
if (this.isConnected === true) return
|
||||||
|
connectWebSocket(id);
|
||||||
|
this.isConnected = true;
|
||||||
|
},
|
||||||
|
disconnect() {
|
||||||
|
setIsManualClose(true);
|
||||||
|
disconnectWebSocket();
|
||||||
|
this.isConnected = false;
|
||||||
|
},
|
||||||
|
send(message: string) {
|
||||||
|
sendMessage(JSON.stringify(message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|||||||
@@ -1,119 +1,119 @@
|
|||||||
<template>
|
<template>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<el-button @Click="createWindow = true">创建房间</el-button>
|
<el-button @Click="createWindow = true">创建房间</el-button>
|
||||||
|
|
||||||
<el-table :data="rooms">
|
<el-table :data="rooms">
|
||||||
<el-table-column label="" prop="r_avatar" width="100">
|
<el-table-column label="" prop="r_avatar" width="100">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<!-- 使用 el-avatar 组件显示头像 -->
|
<!-- 使用 el-avatar 组件显示头像 -->
|
||||||
<el-avatar :src="rooms[scope.$index].r_avatar" size="large" />
|
<el-avatar :src="rooms[scope.$index].r_avatar" size="large" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="房间名" prop="r_name"></el-table-column>
|
<el-table-column label="房间名" prop="r_name"></el-table-column>
|
||||||
<el-table-column label="房间ID" prop="r_id"></el-table-column>
|
<el-table-column label="房间ID" prop="r_id"></el-table-column>
|
||||||
<el-table-column label="角色" prop="role"></el-table-column>
|
<el-table-column label="角色" prop="role"></el-table-column>
|
||||||
<el-table-column label="简介" prop="r_introduction"></el-table-column>
|
<el-table-column label="简介" prop="r_introduction"></el-table-column>
|
||||||
<el-table-column label="">
|
<el-table-column label="">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button @click="goToRoom(scope.row)">进入房间</el-button>
|
<el-button @click="goToRoom(scope.row)">进入房间</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<el-dialog v-model="createWindow" title="创建房间" style="width: 400px;height: 300px;">
|
<el-dialog v-model="createWindow" title="创建房间" style="width: 400px;height: 300px;">
|
||||||
<el-form ref="form" :model="formData" label-width="80px" @submit.prevent="createroom">
|
<el-form ref="form" :model="formData" label-width="80px" @submit.prevent="createroom">
|
||||||
<el-form-item label="房间名" prop="roomName">
|
<el-form-item label="房间名" prop="roomName">
|
||||||
<el-input v-model="formData.roomName" placeholder="请输入房间名" clearable></el-input>
|
<el-input v-model="formData.roomName" placeholder="请输入房间名" clearable></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-button type="primary" native-type="submit">创建</el-button>
|
<el-button type="primary" native-type="submit">创建</el-button>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, reactive, ref } from 'vue'
|
import { onMounted, reactive, ref } from 'vue'
|
||||||
import { PlayroomStore } from '@/store/playroom';
|
import { PlayroomStore } from '@/store/playroom';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { getPlayrooms, createPlayroom } from '@/api/playroom';
|
import { getPlayrooms, createPlayroom } from '@/api/playroom';
|
||||||
|
|
||||||
const roominfo = PlayroomStore()
|
const roominfo = PlayroomStore()
|
||||||
const createWindow = ref(false)
|
const createWindow = ref(false)
|
||||||
const formData = reactive({
|
const formData = reactive({
|
||||||
roomName: ''
|
roomName: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
const rooms = ref([])
|
const rooms = ref([])
|
||||||
|
|
||||||
const goToRoom = (r) => {
|
const goToRoom = (r) => {
|
||||||
roominfo.setCurrentPlayroom(r);
|
roominfo.setCurrentPlayroom(r);
|
||||||
// 跳转到房间页面
|
// 跳转到房间页面
|
||||||
const baseUrl = window.location.origin;
|
const baseUrl = window.location.origin;
|
||||||
const targetUrl = `${baseUrl}/room`; // 替换为你的目标路由
|
const targetUrl = `${baseUrl}/room?r_id=${r.r_id}`; // 替换为你的目标路由
|
||||||
window.open(targetUrl, "room");
|
window.open(targetUrl, "room");
|
||||||
};
|
};
|
||||||
|
|
||||||
const createroom = async () => {
|
const createroom = async () => {
|
||||||
if (!formData.roomName || formData.roomName.trim() === '') {
|
if (!formData.roomName || formData.roomName.trim() === '') {
|
||||||
ElMessage.error('房间名不能为空')
|
ElMessage.error('房间名不能为空')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if(await createPlayroom(formData.roomName)){
|
if(await createPlayroom(formData.roomName)){
|
||||||
ElMessage.success('创建成功')
|
ElMessage.success('创建成功')
|
||||||
}else{
|
}else{
|
||||||
ElMessage.error('创建失败')
|
ElMessage.error('创建失败')
|
||||||
}
|
}
|
||||||
formData.roomName = ''
|
formData.roomName = ''
|
||||||
createWindow.value = false
|
createWindow.value = false
|
||||||
rooms.value = await getPlayrooms()
|
rooms.value = await getPlayrooms()
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getrooms = async () => {
|
const getrooms = async () => {
|
||||||
try {
|
try {
|
||||||
rooms.value = await getPlayrooms()
|
rooms.value = await getPlayrooms()
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
ElMessage.error('获取房间列表失败' + error)
|
ElMessage.error('获取房间列表失败' + error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getrooms()
|
getrooms()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.container {
|
.container {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
width: 800px;
|
width: 800px;
|
||||||
height: 600px;
|
height: 600px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile {
|
.profile {
|
||||||
top: 50px;
|
top: 50px;
|
||||||
left: 100px;
|
left: 100px;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile img {
|
.profile img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,149 +1,162 @@
|
|||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { userInfoStore } from "@/store/user";
|
import { userInfoStore } from "@/store/user";
|
||||||
import { ElMessage } from "element-plus";
|
|
||||||
import { PlayroomStore } from "@/store/playroom";
|
|
||||||
|
// userinfo 实例
|
||||||
// userinfo 实例
|
const userinfo = userInfoStore();
|
||||||
const userinfo = userInfoStore();
|
|
||||||
|
const roomid = ref<number>(0);
|
||||||
// 延迟获取 playroom 实例,避免循环依赖
|
|
||||||
const getPlayroom = () => {
|
|
||||||
return PlayroomStore();
|
// WebSocket 实例
|
||||||
};
|
const socket = ref<WebSocket | null>(null);
|
||||||
|
|
||||||
|
const isManualClose = ref<boolean>(false);
|
||||||
// WebSocket 实例
|
|
||||||
const socket = ref(null);
|
const reconnectScheduled = ref<boolean>(false);
|
||||||
|
|
||||||
const isManualClose = ref(false);
|
const retryCount = ref<number>(0);
|
||||||
|
|
||||||
const reconnectScheduled = ref(false);
|
const getRetryCount = () => {
|
||||||
|
return retryCount.value;
|
||||||
const retryCount = ref(0);
|
};
|
||||||
|
|
||||||
export const getRetryCount = () => {
|
const addRetryCount = () => {
|
||||||
return retryCount.value;
|
retryCount.value = retryCount.value + 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const addRetryCount = () => {
|
const resetRetryCount = () => {
|
||||||
retryCount.value = retryCount.value + 1;
|
retryCount.value = 0;
|
||||||
};
|
};
|
||||||
|
const setReconnectScheduled = (value: boolean) => {
|
||||||
export const resetRetryCount = () => {
|
reconnectScheduled.value = value;
|
||||||
retryCount.value = 0;
|
};
|
||||||
};
|
|
||||||
export const setReconnectScheduled = (value) => {
|
const getReconnectScheduled = () => {
|
||||||
reconnectScheduled.value = value;
|
return reconnectScheduled.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getReconnectScheduled = () => {
|
export const setIsManualClose = (value: boolean) => {
|
||||||
return reconnectScheduled.value;
|
isManualClose.value = value;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setIsManualClose = (value) => {
|
const getIsManualClose = () => {
|
||||||
isManualClose.value = value;
|
return isManualClose.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getIsManualClose = () => {
|
// 连接WebSocket
|
||||||
return isManualClose.value;
|
export const connectWebSocket = (r_id: number) => {
|
||||||
};
|
roomid.value = r_id;
|
||||||
|
const protocol = window.location.protocol === "https:" ? "wss://" : "ws://";
|
||||||
// 连接WebSocket
|
const host = window.location.host;
|
||||||
export const connectWebSocket = () => {
|
const socketUrl = `${protocol}${host}/ws/playroom?r_id=${r_id}`;
|
||||||
const playroom = getPlayroom();
|
|
||||||
const protocol = window.location.protocol === "https:" ? "wss://" : "ws://";
|
// const socketUrl = `ws://localhost:8080/online?u_id=${userinfo.user.u_id}&u_name=${userinfo.user.u_name}`;
|
||||||
const host = window.location.host;
|
if (socket.value && socket.value.readyState !== WebSocket.CLOSED) {
|
||||||
const socketUrl = `${protocol}${host}/ws/video?r_id=${playroom.getCurrentId()}`;
|
console.log("还在重连中...");
|
||||||
|
return;
|
||||||
// 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) {
|
const retrytime = getRetryCount();
|
||||||
console.log("还在重连中...");
|
if (retrytime >= 10) {
|
||||||
return;
|
console.log("重连失败,请稍后再试");
|
||||||
}
|
return;
|
||||||
const retrytime = getRetryCount();
|
}
|
||||||
if (retrytime >= 10) {
|
console.log(retrytime);
|
||||||
console.log("重连失败,请稍后再试");
|
socket.value = new WebSocket(socketUrl, "token-"+ userinfo.token);
|
||||||
return;
|
|
||||||
}
|
socket.value.onopen = (event: any) => {
|
||||||
console.log(retrytime);
|
console.log("WebSocket for video 连接已建立", event);
|
||||||
socket.value = new WebSocket(socketUrl, "token-"+ userinfo.token);
|
setReconnectScheduled(false);
|
||||||
|
setIsManualClose(false);
|
||||||
socket.value.onopen = (event) => {
|
resetRetryCount();
|
||||||
console.log("WebSocket for video 连接已建立", event);
|
};
|
||||||
setReconnectScheduled(false);
|
|
||||||
setIsManualClose(false);
|
//处理消息逻辑
|
||||||
resetRetryCount();
|
socket.value.onmessage = (event) => {
|
||||||
};
|
console.log("从服务器收到消息:", event.data);
|
||||||
|
try{
|
||||||
//处理消息逻辑
|
const MessageData = JSON.parse(event.data);
|
||||||
socket.value.onmessage = (event) => {
|
const cmd = MessageData.cmd;
|
||||||
console.log("从服务器收到消息:", event.data);
|
switch(cmd){
|
||||||
try{
|
case "PING":
|
||||||
const MessageData = JSON.parse(event.data);
|
console.log("收到PING消息");
|
||||||
const cmd = MessageData.cmd;
|
const msg = {
|
||||||
switch(cmd){
|
cmd: "PONG",
|
||||||
case "VIDEO_SYNC":
|
from: MessageData.to,
|
||||||
console.log("视频同步消息", MessageData);
|
// 可扩展字段
|
||||||
break;
|
time: new Date().toLocaleString()
|
||||||
}
|
}
|
||||||
}catch(error){
|
sendMessage(msg);
|
||||||
console.error("解析 JSON 失败:", error);
|
break;
|
||||||
}
|
case "VIDEO_SYNC":
|
||||||
|
// console.log("视频同步消息", MessageData);
|
||||||
};
|
break;
|
||||||
|
}
|
||||||
socket.value.onerror = (error) => {
|
}catch(error){
|
||||||
console.error("WebSocket for video 发生错误:", error);
|
console.error("解析 JSON 失败:", error);
|
||||||
// console.log(error);
|
}
|
||||||
setReconnectScheduled(true);
|
|
||||||
socket.value.close();
|
};
|
||||||
};
|
|
||||||
|
socket.value.onerror = (error) => {
|
||||||
socket.value.onclose = (event) => {
|
console.error("WebSocket for video 发生错误:", error);
|
||||||
if (!getIsManualClose()) {
|
// console.log(error);
|
||||||
if (getReconnectScheduled()) {
|
setReconnectScheduled(true);
|
||||||
socket.value = null;
|
socket.value.close();
|
||||||
addRetryCount();
|
};
|
||||||
setTimeout(reConnectWebSocket, 5000);
|
|
||||||
setReconnectScheduled(false);
|
socket.value.onclose = (event) => {
|
||||||
} else {
|
if (!getIsManualClose()) {
|
||||||
// console.log("websocket因为浏览器省电设置断开");
|
if (getReconnectScheduled()) {
|
||||||
console.log("WebSocket for video 连接已关闭", event);
|
socket.value = null;
|
||||||
}
|
addRetryCount();
|
||||||
}
|
setTimeout(reConnectWebSocket, 5000);
|
||||||
};
|
setReconnectScheduled(false);
|
||||||
};
|
} else {
|
||||||
|
// console.log("websocket因为浏览器省电设置断开");
|
||||||
// 断开WebSocket连接
|
console.log("WebSocket for video 连接已关闭", event);
|
||||||
export const disconnectWebSocket = () => {
|
}
|
||||||
if (socket.value && socket.value.readyState === WebSocket.OPEN) {
|
}
|
||||||
socket.value.close();
|
};
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
// 断开WebSocket连接
|
||||||
// 重连机制
|
export const disconnectWebSocket = () => {
|
||||||
export const reConnectWebSocket = () => {
|
if (socket.value && socket.value.readyState === WebSocket.OPEN) {
|
||||||
connectWebSocket();
|
roomid.value = 0;
|
||||||
};
|
socket.value.close();
|
||||||
|
}
|
||||||
// 发送消息
|
};
|
||||||
export const sendMessage = (message) => {
|
|
||||||
if (socket.value && socket.value.readyState === WebSocket.OPEN) {
|
// 重连机制
|
||||||
socket.value.send(message);
|
export const reConnectWebSocket = () => {
|
||||||
} else {
|
connectWebSocket(roomid.value);
|
||||||
console.warn("WebSocket for video 未连接,无法发送消息");
|
};
|
||||||
}
|
|
||||||
};
|
// 发送消息
|
||||||
|
export const sendMessage = (message: any) => {
|
||||||
//没有错误的重连,只是浏览器在后台断开了连接
|
try{
|
||||||
document.addEventListener("visibilitychange", () => {
|
const jsonmessage = JSON.stringify(message);
|
||||||
if (document.hidden) {
|
if (socket.value && socket.value.readyState === WebSocket.OPEN) {
|
||||||
} else {
|
socket.value.send(jsonmessage);
|
||||||
if (!getIsManualClose() && socket.value.readyState === WebSocket.CLOSED) {
|
} else {
|
||||||
if (getReconnectScheduled()) {
|
console.warn("WebSocket for video 未连接,无法发送消息");
|
||||||
return;
|
}
|
||||||
}
|
}
|
||||||
reConnectWebSocket();
|
catch(error){
|
||||||
}
|
console.error("Failed to stringify message:", error);
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
|
//没有错误的重连,只是浏览器在后台断开了连接
|
||||||
|
document.addEventListener("visibilitychange", () => {
|
||||||
|
if (document.hidden) {
|
||||||
|
} else {
|
||||||
|
if (!getIsManualClose() && socket.value.readyState === WebSocket.CLOSED) {
|
||||||
|
if (getReconnectScheduled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reConnectWebSocket();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -29,11 +29,6 @@ export default defineConfig({
|
|||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
ws: true,
|
ws: true,
|
||||||
},
|
},
|
||||||
// '/ws': {
|
|
||||||
// target: 'ws://localhost:8080',
|
|
||||||
// changeOrigin: true,
|
|
||||||
// ws: true,
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
port: 5173,
|
port: 5173,
|
||||||
|
|||||||
Reference in New Issue
Block a user