feat: socket heartbeat

feat: video face mode
feat: change nickname
feat: update self connect css
feat: docker-compose inner env
fix: some lang error
This commit is contained in:
https://blog.iamtsm.cn
2023-08-20 23:26:40 +08:00
parent 52ad9ebd92
commit b1c62419aa
19 changed files with 604 additions and 41 deletions

View File

@@ -0,0 +1,184 @@
## !!!!!!用于docker-compose部署并启动官方镜像!!!!!!
## !!!!!!内置配置的形式启动!!!!!!!!!!!!!!!!!!!!!!!
version: '3'
################## 按需修改好配置
x-tlrtcfile-env: &tlrtcfile-env
## api服务端口
tl_rtc_file_api_port: 9092
## websocket服务端口
tl_rtc_file_socket_port: 8444
## websocket服务地址
tl_rtc_file_socket_host: 127.0.0.1:8444
## webrtc-stun中继服务地址
tl_rtc_file_webrtc_stun_host: stun:127.0.0.1:3478
## webrtc-turn中继服务地址
tl_rtc_file_webrtc_turn_host: turn:127.0.0.1:3478?transport=udp
## webrtc中继服务用户名
tl_rtc_file_webrtc_turn_username: tlrtcfile
## webrtc中继服务密码
tl_rtc_file_webrtc_turn_credential: tlrtcfile
## webrtc中继服务Secret
tl_rtc_file_webrtc_turn_secret: tlrtcfile
## webrtc中继服务帐号过期时间 (毫秒)
tl_rtc_file_webrtc_turn_expire: 86400000
## 是否开启数据库
tl_rtc_file_db_open: true
## 数据库地址
tl_rtc_file_db_mysql_host: mysql
## 数据库端口
tl_rtc_file_db_mysql_port: 3306
## 数据库名称
tl_rtc_file_db_mysql_dbName: webchat
## 数据库用户名
tl_rtc_file_db_mysql_user: tlrtcfile
## 数据库密码
tl_rtc_file_db_mysql_password: tlrtcfile
## oss-seafile存储库ID
tl_rtc_file_oss_seafile_repoid:
## oss-seafile地址
tl_rtc_file_oss_seafile_host:
## oss-seafile用户名
tl_rtc_file_oss_seafile_username:
## oss-seafile密码
tl_rtc_file_oss_seafile_password:
## oss-alyun存储accessKey
tl_rtc_file_oss_alyun_AccessKey:
## oss-aly存储SecretKey
tl_rtc_file_oss_alyun_Secretkey:
## oss-aly存储bucket
tl_rtc_file_oss_alyun_bucket:
## oss-txyun存储accessKey
tl_rtc_file_oss_txyun_AccessKey:
## oss-txyunt存储SecretKey
tl_rtc_file_oss_txyun_Secretkey:
## oss-txyun存储bucket
tl_rtc_file_oss_txyun_bucket:
## oss-qiniuyun存储accessKey
tl_rtc_file_oss_qiniuyun_AccessKey:
## oss-qiniuyunt存储SecretKey
tl_rtc_file_oss_qiniuyun_Secretkey:
## oss-qiniuyun存储bucket
tl_rtc_file_oss_qiniuyun_bucket:
## 管理后台房间号
tl_rtc_file_manage_room: tlrtcfile
## 管理后台密码
tl_rtc_file_manage_password: tlrtcfile
## openai-key如果有多个key逗号分隔
tl_rtc_file_openai_keys:
## 企业微信通知开关
tl_rtc_file_notify_open: false
## 企业微信通知机器人KEY正常通知如果有多个key逗号分隔
tl_rtc_file_notify_qiwei_normal:
## 企业微信通知机器人KEY错误通知如果有多个key逗号分隔
tl_rtc_file_notify_qiwei_error:
services:
#http模式启动api服务
api-http:
profiles: ['http']
image: iamtsm/tl-rtc-file-api
container_name: api
environment:
<<: *tlrtcfile-env
tl_rtc_file_env_mode: http
command:
- tlapi
ports:
- 9092:9092
links:
- mysql
depends_on:
- mysql
- coturn
#https模式启动api服务
api-https:
profiles: ['https']
image: iamtsm/tl-rtc-file-api
container_name: api
environment:
<<: *tlrtcfile-env
tl_rtc_file_env_mode: https
command:
- tlapi
ports:
- 9092:9092
links:
- mysql
depends_on:
- mysql
- coturn
#http模式启动socket服务
socket-http:
profiles: ['http']
image: iamtsm/tl-rtc-file-socket
container_name: socket
command:
- tlsocket
environment:
<<: *tlrtcfile-env
tl_rtc_file_env_mode: http
ports:
- 8444:8444
links:
- mysql
depends_on:
- mysql
- coturn
#https模式启动socket服务
socket-https:
profiles: ['https']
image: iamtsm/tl-rtc-file-socket
container_name: socket
command:
- tlsocket
environment:
<<: *tlrtcfile-env
tl_rtc_file_env_mode: https
ports:
- 8444:8444
links:
- mysql
depends_on:
- mysql
- coturn
#mysql服务
mysql:
profiles: ['http','https']
image: iamtsm/tl-rtc-file-mysql
container_name: mysql
restart: always
environment:
#设置root密码
MYSQL_ROOT_PASSWORD: tlrtcfile
#设置数据库
MYSQL_DATABASE: webchat
#设置用户
MYSQL_USER: tlrtcfile
#设置用户密码
MYSQL_PASSWORD: tlrtcfile
ports:
- 3306:3306
volumes:
- ./db:/var/lib/mysql
- ./my.cnf:/etc/mysql/conf.d/my.cnf
- ./log:/var/log/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
#coturn服务
coturn:
profiles: ['http','https']
image: iamtsm/tl-rtc-file-coturn
container_name: coturn
ports:
- "3478:3478/udp"
- "3478:3478/tcp"
volumes:
- ./turnserver-with-secret-user.conf:/etc/turnserver.conf

View File

@@ -1,5 +1,5 @@
{ {
"version": "10.4.5", "version": "10.4.6",
"socket": { "socket": {
"port": "请到 tlrtcfile.env 中进行配置", "port": "请到 tlrtcfile.env 中进行配置",
"host": "请到 tlrtcfile.env 中进行配置" "host": "请到 tlrtcfile.env 中进行配置"

View File

@@ -258,7 +258,6 @@ body {
.tl-rtc-file-user { .tl-rtc-file-user {
padding: 10px 20px 0px 10px; padding: 10px 20px 0px 10px;
cursor: pointer; cursor: pointer;
background-color: rgb(248, 253, 255);
border-radius: 10px; border-radius: 10px;
box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 3px; box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 3px;
transition: box-shadow 0.3s; transition: box-shadow 0.3s;
@@ -1081,7 +1080,6 @@ body {
user-select: none; user-select: none;
} }
.chating_input_body{ .chating_input_body{
bottom: 0px; bottom: 0px;
position: absolute; position: absolute;
@@ -1111,7 +1109,6 @@ body {
border-radius: 5px; border-radius: 5px;
} }
.remote_user_info{ .remote_user_info{
text-align: left; text-align: left;
padding: 20px 20px 0 20px; padding: 20px 20px 0 20px;
@@ -1142,7 +1139,7 @@ body {
max-width: 40px; max-width: 40px;
width: 5% !important; width: 5% !important;
margin-left: 50% !important; margin-left: 50% !important;
} }
.tl-rtc-file-tool-mobile { .tl-rtc-file-tool-mobile {
padding: 5px !important; padding: 5px !important;
@@ -1157,7 +1154,7 @@ body {
word-break: keep-all !important; word-break: keep-all !important;
margin: 0 !important; margin: 0 !important;
bottom: 0 !important; bottom: 0 !important;
-webkit-transform-origin-x: 0 !important; -webkit-transform-origin-x: 0 !important;
transform: scale(0.6) !important; transform: scale(0.6) !important;
-webkit-transform: scale(0.6) !important; -webkit-transform: scale(0.6) !important;
} }
@@ -1167,18 +1164,18 @@ body {
margin: 10px 20px; margin: 10px 20px;
border-radius: 8px; border-radius: 8px;
cursor: pointer; cursor: pointer;
transition: all 0.5s; transition: all 0.3s;
font-weight: bold; font-weight: bold;
color: #595252e3; color: black;
} }
.tl-rtc-file-live-user-choose svg{ .tl-rtc-file-live-user-choose svg{
font-size: 3rem; font-size: 2rem;
margin-bottom: 5px; margin-bottom: 5px;
} }
.tl-rtc-file-live-user-choose:hover{ .tl-rtc-file-live-user-choose:hover{
color: black; color: #79b0e8;
} }
/* 500px以下 */ /* 500px以下 */

View File

@@ -330,11 +330,13 @@
<!-- 自己 --> <!-- 自己 -->
<div class="layui-row" style="margin-top: 30px;" v-show="socketId !== 0"> <div class="layui-row" style="margin-top: 30px;" v-show="socketId !== 0">
<div class="layui-col-xs12 tl-rtc-file-user-list"> <div class="layui-col-xs12 tl-rtc-file-user-list">
<div class="tl-rtc-file-user"> <div class="tl-rtc-file-user" style="background: #f6fdff;">
<div class="tl-rtc-file-user-body"> <div class="tl-rtc-file-user-body" @click="changeNickName()">
<svg class="icon" aria-hidden="true" style="width: 32px;height: 32px;"> <div>
<use xlink:href="#icon-rtc-file-icon-test"></use> <svg class="icon" aria-hidden="true" style="width: 32px;height: 32px;">
</svg> <use xlink:href="#icon-rtc-file-jiangbei-"></use>
</svg>
</div>
<div class="tl-rtc-file-user-body-left"> <div class="tl-rtc-file-user-body-left">
<b class="tl-rtc-file-user-body-left-nick"> <b class="tl-rtc-file-user-body-left-nick">
<b v-show="owner" style="color: #51d788;">【{{roomTypeName}}】-</b> <b v-show="owner" style="color: #51d788;">【{{roomTypeName}}】-</b>
@@ -1035,13 +1037,18 @@
</svg> </svg>
</div> </div>
<div class="tl-rtc-file-mask-media-video-tool"> <div class="tl-rtc-file-mask-media-video-tool">
<div class="tl-rtc-file-mask-media-video-tool-item" @click="changeShareStream('video','video')"> <div class="tl-rtc-file-mask-media-video-tool-item" @click="changeVideoShareMediaTrackAndStream()">
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
<use xlink:href="#icon-rtc-file-xiangjifanzhuan"></use>
</svg>
</div>
<div class="tl-rtc-file-mask-media-video-tool-item" @click="changeShareStreamSwitch('video','video')">
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;"> <svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
<use v-if="isCameraEnabled" xlink:href="#icon-rtc-file-shexiangtou"></use> <use v-if="isCameraEnabled" xlink:href="#icon-rtc-file-shexiangtou"></use>
<use v-else xlink:href="#icon-rtc-file-shexiangtou_guanbi"></use> <use v-else xlink:href="#icon-rtc-file-shexiangtou_guanbi"></use>
</svg> </svg>
</div> </div>
<div class="tl-rtc-file-mask-media-video-tool-item" @click="changeShareStream('video','audio')"> <div class="tl-rtc-file-mask-media-video-tool-item" @click="changeShareStreamSwitch('video','audio')">
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;"> <svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
<use v-if="isAudioEnabled" xlink:href="#icon-rtc-file-maikefeng-XDY"></use> <use v-if="isAudioEnabled" xlink:href="#icon-rtc-file-maikefeng-XDY"></use>
<use v-else xlink:href="#icon-rtc-file-guanbimaikefeng"></use> <use v-else xlink:href="#icon-rtc-file-guanbimaikefeng"></use>
@@ -1083,7 +1090,7 @@
</svg> </svg>
</div> </div>
<div class="tl-rtc-file-mask-media-video-tool"> <div class="tl-rtc-file-mask-media-video-tool">
<div class="tl-rtc-file-mask-media-video-tool-item" @click="changeShareStream('screen','video')"> <div class="tl-rtc-file-mask-media-video-tool-item" @click="changeShareStreamSwitch('screen','video')">
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;"> <svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
<use v-if="isCameraEnabled" xlink:href="#icon-rtc-file-pingmugongxiang"></use> <use v-if="isCameraEnabled" xlink:href="#icon-rtc-file-pingmugongxiang"></use>
<use v-else xlink:href="#icon-rtc-file-guanbipingmu"></use> <use v-else xlink:href="#icon-rtc-file-guanbipingmu"></use>
@@ -1126,6 +1133,11 @@
</svg> </svg>
</div> </div>
<div class="tl-rtc-file-mask-media-video-tool" v-show="owner"> <div class="tl-rtc-file-mask-media-video-tool" v-show="owner">
<div v-show="liveShareMode === 'video'" class="tl-rtc-file-mask-media-video-tool-item" @click="changeLiveVideoShareMediaTrackAndStream()">
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
<use xlink:href="#icon-rtc-file-xiangjifanzhuan"></use>
</svg>
</div>
<div v-show="changeLiveShareMediaTrackAndStreamTime === 0" class="tl-rtc-file-mask-media-video-tool-item" @click="changeLiveShareMediaTrackAndStream()"> <div v-show="changeLiveShareMediaTrackAndStreamTime === 0" class="tl-rtc-file-mask-media-video-tool-item" @click="changeLiveShareMediaTrackAndStream()">
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;"> <svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
<use xlink:href="#icon-rtc-file-fanzhuanjingtou"></use> <use xlink:href="#icon-rtc-file-fanzhuanjingtou"></use>
@@ -1134,7 +1146,7 @@
<div v-show="changeLiveShareMediaTrackAndStreamTime > 0" class="tl-rtc-file-mask-media-video-tool-item layui-disabled" style="padding: 2px 10px 2px 10px"> <div v-show="changeLiveShareMediaTrackAndStreamTime > 0" class="tl-rtc-file-mask-media-video-tool-item layui-disabled" style="padding: 2px 10px 2px 10px">
{{changeLiveShareMediaTrackAndStreamTime}} {{changeLiveShareMediaTrackAndStreamTime}}
</div> </div>
<div class="tl-rtc-file-mask-media-video-tool-item" @click="changeShareStream('live', 'video')"> <div class="tl-rtc-file-mask-media-video-tool-item" @click="changeShareStreamSwitch('live', 'video')">
<svg v-if="liveShareMode === 'video'" class="icon" aria-hidden="true" style="width: 18px;height: 18px;"> <svg v-if="liveShareMode === 'video'" class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
<use v-if="isCameraEnabled" xlink:href="#icon-rtc-file-shexiangtou"></use> <use v-if="isCameraEnabled" xlink:href="#icon-rtc-file-shexiangtou"></use>
<use v-else xlink:href="#icon-rtc-file-shexiangtou_guanbi"></use> <use v-else xlink:href="#icon-rtc-file-shexiangtou_guanbi"></use>
@@ -1144,7 +1156,7 @@
<use v-else xlink:href="#icon-rtc-file-guanbipingmu"></use> <use v-else xlink:href="#icon-rtc-file-guanbipingmu"></use>
</svg> </svg>
</div> </div>
<div class="tl-rtc-file-mask-media-video-tool-item" @click="changeShareStream('live','audio')"> <div class="tl-rtc-file-mask-media-video-tool-item" @click="changeShareStreamSwitch('live','audio')">
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;"> <svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
<use v-if="isAudioEnabled" xlink:href="#icon-rtc-file-maikefeng-XDY"></use> <use v-if="isAudioEnabled" xlink:href="#icon-rtc-file-maikefeng-XDY"></use>
<use v-else xlink:href="#icon-rtc-file-guanbimaikefeng"></use> <use v-else xlink:href="#icon-rtc-file-guanbimaikefeng"></use>
@@ -1189,7 +1201,7 @@
</svg> </svg>
</div> </div>
<div class="tl-rtc-file-mask-media-video-tool"> <div class="tl-rtc-file-mask-media-video-tool">
<div class="tl-rtc-file-mask-media-video-tool-item" @click="changeShareStream('audio','audio')"> <div class="tl-rtc-file-mask-media-video-tool-item" @click="changeShareStreamSwitch('audio','audio')">
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;"> <svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
<use v-if="isAudioEnabled" xlink:href="#icon-rtc-file-maikefeng-XDY"></use> <use v-if="isAudioEnabled" xlink:href="#icon-rtc-file-maikefeng-XDY"></use>
<use v-else xlink:href="#icon-rtc-file-guanbimaikefeng"></use> <use v-else xlink:href="#icon-rtc-file-guanbimaikefeng"></use>

View File

@@ -135,6 +135,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
aiAnsweringTxtIntervalId: 0, //实现等待动画 aiAnsweringTxtIntervalId: 0, //实现等待动画
aiAnsweringTxt: "思考中...", //ai思考中的文字 aiAnsweringTxt: "思考中...", //ai思考中的文字
logsFilter: "", //日志过滤参数 logsFilter: "", //日志过滤参数
changeLiveShareMediaTrackAndStreamTimeLimit : 9, //切换直播媒体流的时间限制, 允许9s内切换一次 changeLiveShareMediaTrackAndStreamTimeLimit : 9, //切换直播媒体流的时间限制, 允许9s内切换一次
changeLiveShareMediaTrackAndStreamTime : 0, //可以下一次切换直播媒体流的时间 changeLiveShareMediaTrackAndStreamTime : 0, //可以下一次切换直播媒体流的时间
changeLiveShareMediaTrackAndStreamIntervalId : 0, //切换直播媒体流的时间间隔id changeLiveShareMediaTrackAndStreamIntervalId : 0, //切换直播媒体流的时间间隔id
@@ -245,15 +246,85 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
} }
}, },
methods: { methods: {
changeNickName : function(){
let that = this;
layer.prompt({
formType: 0,
title: that.lang.changeNickName,
value: "",
}, function (value, index, elem) {
if(value.length > 10 || tlrtcfile.containSymbol(value)){
layer.msg(that.lang.changeNickNameLimit)
return
}
that.socket.emit("changeNickName", {
room: that.roomId,
from : that.socketId,
nickName : value,
preNickName : that.nickName
})
that.nickName = value;
layer.close(index);
that.addUserLogs(that.lang.changeNickName + "," + value);
return false
});
},
//切换音视频场景下的摄像头前后置
//切换媒体流之前,先将已关闭的媒体流打开之后再进行切换
changeVideoShareMediaTrackAndStream : async function(){
await this.changeShareStreamSwitch('video', 'video', 'open')
await this.changeShareStreamSwitch('video', 'audio', 'open')
//切换摄像头前后置
this.facingMode = this.facingMode === 'user' ? 'environment' : 'user'
let that = this;
window.Bus.$emit("changeVideoShareMediaTrackAndStream", {
kind : 'video',
constraints : this.videoConstraints,
rtcConns : this.rtcConns,
callback : (success) => {
if(!success){ //没有成功,切换回来
this.facingMode = this.facingMode === 'user' ? 'environment' : 'user'
}
}
});
},
//切换直播场景下的视频摄像头前后置媒体流
//切换媒体流之前,先将已关闭的媒体流打开之后再进行切换
changeLiveVideoShareMediaTrackAndStream : async function(){
await this.changeShareStreamSwitch('live', 'video', 'open')
await this.changeShareStreamSwitch('live', 'audio', 'open')
//切换摄像头前后置
this.facingMode = this.facingMode === 'user' ? 'environment' : 'user'
let that = this;
window.Bus.$emit("changeLiveShareMediaTrackAndStream", {
type : 'video',
kind : 'video',
constraints : this.videoConstraints,
rtcConns : this.rtcConns,
callback : (success) => {
if(!success){ //没有成功,切换回来
that.facingMode = that.facingMode === 'user' ? 'environment' : 'user'
}
}
});
},
//切换直播媒体流,比如切换视频流和屏幕共享流
//切换媒体流之前,先将已关闭的媒体流打开之后再进行切换
changeLiveShareMediaTrackAndStream : async function(){ changeLiveShareMediaTrackAndStream : async function(){
//切换媒体流之前,先将已关闭的媒体流打开之后再进行切换
if(this.liveShareMode === 'live'){ if(this.liveShareMode === 'live'){
//屏幕共享流只需要打开video通道即可 //屏幕共享流只需要打开video通道即可
await this.changeShareStream('live', 'video', 'open') await this.changeShareStreamSwitch('live', 'video', 'open')
}else if(this.liveShareMode === 'video'){ }else if(this.liveShareMode === 'video'){
//视频流需要打开video和audio通道 //视频流需要打开video和audio通道
await this.changeShareStream('live', 'video', 'open') await this.changeShareStreamSwitch('live', 'video', 'open')
await this.changeShareStream('live', 'audio', 'open') await this.changeShareStreamSwitch('live', 'audio', 'open')
} }
let that = this; let that = this;
@@ -282,7 +353,8 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
}, 1000); }, 1000);
} }
}, },
changeShareStream : async function(type, kind, value){ //改变媒体流开关状态
changeShareStreamSwitch : async function(type, kind, value){
let stream = null; let stream = null;
if(type === 'video'){ if(type === 'video'){
@@ -333,6 +405,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
this.isAudioEnabled = !this.isAudioEnabled this.isAudioEnabled = !this.isAudioEnabled
} }
} }
this.socket.emit('message', { this.socket.emit('message', {
emitType: "openCamera", emitType: "openCamera",
room: this.roomId, room: this.roomId,
@@ -342,7 +415,6 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
isCameraEnabled : this.isCameraEnabled, isCameraEnabled : this.isCameraEnabled,
isAudioEnabled : this.isAudioEnabled isAudioEnabled : this.isAudioEnabled
}); });
}, },
updateRemoteRtcState : async function(){ updateRemoteRtcState : async function(){
for(let id in this.remoteMap){ for(let id in this.remoteMap){
@@ -1660,8 +1732,6 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
}) })
} }
}); });
} }
}, },
// 创建/加入语音连麦房间 // 创建/加入语音连麦房间
@@ -1693,8 +1763,8 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
return return
} }
if (this.isJoined) { if (this.isJoined) {
layer.msg(this.lang.please_exit_then_join_screen) layer.msg(this.lang.please_exit_then_join_audio)
this.addUserLogs(this.lang.please_exit_then_join_screen) this.addUserLogs(this.lang.please_exit_then_join_audio)
return return
} }
let that = this; let that = this;
@@ -3381,10 +3451,19 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
socketListener: function () { socketListener: function () {
let that = this; let that = this;
this.socket.on("heartbeat", data => {
if(data.status === 'ok'){
that.addSysLogs("心跳检查正常 : " + data.status);
}else{
that.addSysLogs("socket心跳检查失败" + JSON.stringify(data));
}
})
this.socket.on('connect_error', error => { this.socket.on('connect_error', error => {
console.error('connect_error', error); console.error('connect_error', error);
if(error){ if(error){
layer.msg("socket服务连接失败请检查socket服务是否正常启动 " + error.message); layer.msg("socket服务连接失败请检查socket服务是否正常启动 " + error.message);
that.addSysLogs("socket服务连接失败请检查socket服务是否正常启动" + error.message);
} }
}); });
@@ -3771,6 +3850,14 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
that.addSysLogs(that.lang.current_number + ":" + data.mc + that.lang.online_number) that.addSysLogs(that.lang.current_number + ":" + data.mc + that.lang.online_number)
}); });
//更新昵称
this.socket.on('changeNickName', function (data) {
that.setRemoteInfo(data.from, {
nickName : data.nickName
})
that.addSysLogs(data.from + + that.lang.changeNickNameTo + " : " + data.nickName)
});
//提示 //提示
this.socket.on('tips', function (data) { this.socket.on('tips', function (data) {
if (window.layer) { if (window.layer) {
@@ -4339,6 +4426,10 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
}, 5000); }, 5000);
this.addSysLogs(this.lang.webrtc_check_init_done); this.addSysLogs(this.lang.webrtc_check_init_done);
setInterval(async () => {
this.socket.emit("heartbeat", {})
}, 10000);
this.addSysLogs(this.lang.message_box_init); this.addSysLogs(this.lang.message_box_init);
this.startPopUpMsg() this.startPopUpMsg()
this.addSysLogs(this.lang.message_box_init_done); this.addSysLogs(this.lang.message_box_init_done);

View File

@@ -8,6 +8,9 @@
const local_lang = { const local_lang = {
"en": { "en": {
"changeNickNameTo" : "Change nickname to",
"changeNickNameLimit" : "The nickname can only be Chinese or English within 10 characters",
"changeNickName" : "Change nickname",
"iamLiveViewer" : "i am live viewer", "iamLiveViewer" : "i am live viewer",
"iamLiveOwner" : "i am Live Owner", "iamLiveOwner" : "i am Live Owner",
"chooseRoleEnter" : "Choose role enter", "chooseRoleEnter" : "Choose role enter",
@@ -201,6 +204,7 @@ const local_lang = {
"please_enter_audio_sharing_room_num" : "Please enter the audio sharing room number", "please_enter_audio_sharing_room_num" : "Please enter the audio sharing room number",
"please_enter_screen_sharing_room_num": "Please enter the screen sharing room number", "please_enter_screen_sharing_room_num": "Please enter the screen sharing room number",
"please_enter_video_call_room_num": "Please enter the audio and video call room number", "please_enter_video_call_room_num": "Please enter the audio and video call room number",
"please_exit_then_join_audio": "Please exit the room first and then enter the audio room",
"please_exit_then_join_live": "Please exit the room first and then enter the live room", "please_exit_then_join_live": "Please exit the room first and then enter the live room",
"please_exit_then_join_password_room": "Please exit the room first and then enter the password room", "please_exit_then_join_password_room": "Please exit the room first and then enter the password room",
"please_exit_then_join_screen": "Please exit the room first and then initiate screen sharing", "please_exit_then_join_screen": "Please exit the room first and then initiate screen sharing",
@@ -344,6 +348,9 @@ const local_lang = {
"webrtc_ice_state" : "webrtc state" "webrtc_ice_state" : "webrtc state"
}, },
"zh": { "zh": {
"changeNickNameTo" : "更新了昵称为",
"changeNickName" : "修改昵称",
"changeNickNameLimit" : "昵称长度限制为10个以内中英文字符",
"iamLiveViewer" : "我是观众", "iamLiveViewer" : "我是观众",
"iamLiveOwner" : "我是主播", "iamLiveOwner" : "我是主播",
"chooseRoleEnter" : "选择身份进入", "chooseRoleEnter" : "选择身份进入",
@@ -547,6 +554,7 @@ const local_lang = {
"please_enter_audio_sharing_room_num" : "请输入语音连麦房间号", "please_enter_audio_sharing_room_num" : "请输入语音连麦房间号",
"please_enter_screen_sharing_room_num": "请输入屏幕共享房间号", "please_enter_screen_sharing_room_num": "请输入屏幕共享房间号",
"please_enter_video_call_room_num": "请输入音视频通话房间号", "please_enter_video_call_room_num": "请输入音视频通话房间号",
"please_exit_then_join_audio": "请先退出房间,然后进入语音连麦房间",
"please_exit_then_join_live": "请先退出房间,然后进入直播间", "please_exit_then_join_live": "请先退出房间,然后进入直播间",
"please_exit_then_join_password_room": "请先退出房间,然后进入密码房间", "please_exit_then_join_password_room": "请先退出房间,然后进入密码房间",
"please_exit_then_join_screen": "请先退出房间,然后发起屏幕共享", "please_exit_then_join_screen": "请先退出房间,然后发起屏幕共享",

View File

@@ -179,7 +179,7 @@ var videoShare = new Vue({
this.videoDeviceList = list.videoDeviceList; this.videoDeviceList = list.videoDeviceList;
this.loudspeakerDeviceList = list.loudspeakerDeviceList; this.loudspeakerDeviceList = list.loudspeakerDeviceList;
}, },
changeVideoShareDevice: async function ({constraints, kind, rtcConnect}) { changeVideoShareMediaTrackAndStream: async function ({constraints, kind, rtcConns, callback}) {
//重新获取流 //重新获取流
let newStream = null; let newStream = null;
try{ try{
@@ -263,7 +263,7 @@ var videoShare = new Vue({
window.Bus.$on("startVideoShare", this.startVideoShare); window.Bus.$on("startVideoShare", this.startVideoShare);
window.Bus.$on("stopVideoShare", this.stopVideoShare); window.Bus.$on("stopVideoShare", this.stopVideoShare);
window.Bus.$on("getVideoShareTrackAndStream", this.getVideoShareTrackAndStream); window.Bus.$on("getVideoShareTrackAndStream", this.getVideoShareTrackAndStream);
window.Bus.$on("changeVideoShareDevice", this.changeVideoShareDevice); window.Bus.$on("changeVideoShareMediaTrackAndStream", this.changeVideoShareMediaTrackAndStream);
window.Bus.$on("getVideoShareDeviceList", this.getVideoShareDeviceList); window.Bus.$on("getVideoShareDeviceList", this.getVideoShareDeviceList);
} }
}) })

View File

@@ -58,6 +58,23 @@ function sendFileDoneNotify(data) {
} }
/**
* 发送修改昵称通知
* @param {*} data
*/
function sendChangeNickNameNotify(data) {
let notifyMsg = `## <font color='info'>文件传输通知</font> - <font color="warning">${data.title}</font>` +
` - <font color="comment">${data.room}</font>\n` +
`库记录ID: ${data.recoderId}\n` +
`旧的昵称: ${data.oldNickName}\n` +
`新的昵称: ${data.nickName}\n` +
`当前时间: ${utils.formateDateTime(new Date(), "yyyy-MM-dd hh:mm:ss")}\n` +
`访问IP: ${data.ip}\n` +
`访问设备: ${data.userAgent}\n`;
notify.requestMsg(notifyMsg)
}
/** /**
* 发送文本内容通知 * 发送文本内容通知
* @param {*} data * @param {*} data
@@ -507,5 +524,6 @@ module.exports = {
prepareCodeFileNotify, prepareCodeFileNotify,
sendSystemErrorMsg, sendSystemErrorMsg,
sendStartRemoteDrawNotify, sendStartRemoteDrawNotify,
sendStopRemoteDrawNotify sendStopRemoteDrawNotify,
sendChangeNickNameNotify
} }

View File

@@ -15,6 +15,8 @@ const rtcChatingRoom = require("./rtcChatingRoom/chatingRoom");
const rtcOpenai = require("./rtcOpenai/openai"); const rtcOpenai = require("./rtcOpenai/openai");
const rtcDraw = require("./rtcDraw/draw"); const rtcDraw = require("./rtcDraw/draw");
const rtcPrepareCodeFile = require("./rtcCodeFile/prepareCodeFile"); const rtcPrepareCodeFile = require("./rtcCodeFile/prepareCodeFile");
const rtcChangeNickName = require("./rtcChangeNickName/changeNickName")
const rtcHeartbeat = require("./rtcHeartbeat/heartbeat");
const rtcAddCodeFile = require("./rtcCodeFile/addCodeFile"); const rtcAddCodeFile = require("./rtcCodeFile/addCodeFile");
const rtcGetCodeFile = require("./rtcCodeFile/getCodeFile"); const rtcGetCodeFile = require("./rtcCodeFile/getCodeFile");
const rtcServerEvent = require("./rtcConstant").rtcServerEvent const rtcServerEvent = require("./rtcConstant").rtcServerEvent
@@ -118,4 +120,14 @@ module.exports = (io, socket, tables, dbClient) => {
socket.on(rtcServerEvent.getCodeFile, (data) => { socket.on(rtcServerEvent.getCodeFile, (data) => {
rtcGetCodeFile.getCodeFile(io, socket, tables, dbClient, data) rtcGetCodeFile.getCodeFile(io, socket, tables, dbClient, data)
}); });
// 心跳
socket.on(rtcServerEvent.heartbeat, (data) => {
rtcHeartbeat.heartbeat(io, socket, tables, dbClient, data)
});
// 修改昵称
socket.on(rtcServerEvent.changeNickName, (data) => {
rtcChangeNickName.changeNickName(io, socket, tables, dbClient, data)
});
} }

View File

@@ -0,0 +1,105 @@
const daoDog = require("./../../dao/dog/dog")
const bussinessNotify = require("./../../bussiness/notify/notifyHandler")
const utils = require("./../../utils/utils");
const rtcConstant = require("../rtcConstant");
const rtcClientEvent = rtcConstant.rtcClientEvent
const check = require("../../bussiness/check/content");
/**
* 房间内更新昵称
* 指定了to : 就会发送给指定的用户
* 没有指定to : 广播给除了自己外的房间内的所有用户
* @param {*} io socketio对象
* @param {*} socket 单个socket连接
* @param {*} tables 数据表对象
* @param {*} dbClient sequelize-orm对象
* @param {*} data event参数
* @returns
*/
async function changeNickName(io, socket, tables, dbClient, data){
try {
let {handshake, userAgent, ip} = utils.getSocketClientInfo(socket);
let { nickName = '', preNickName = ''} = data;
if(nickName && nickName.length > 10){
nickName = nickName.toString().substr(0, 9);
}
await daoDog.addDogData({
name: "修改个人昵称",
roomId: data.room || "",
socketId: "",
device: userAgent,
flag: 0,
content: JSON.stringify(data),
handshake: JSON.stringify(handshake),
ip: ip
}, tables, dbClient);
data.nickName = check.contentFilter(nickName);
bussinessNotify.sendChangeNickNameNotify({
title: "修改个人昵称",
room: data.room,
nickName: data.nickName,
preNickName: preNickName,
userAgent: userAgent,
ip: ip
})
//更新下服务端存下的昵称
io.sockets.connected[socket.id].nickName = nickName;
// 指定发送
if(data.to && data.to !== ''){
let toOtherSocket = io.sockets.connected[data.to];
if(toOtherSocket){
toOtherSocket.emit(rtcClientEvent.chatingRoom, data);
}
return
}
// 没指定,走广播(除了自己)
let clientsInRoom = io.sockets.adapter.rooms[data.room];
if (!clientsInRoom) {
return
}
let otherSocketIds = Object.keys(clientsInRoom.sockets);
for (let i = 0; i < otherSocketIds.length; i++) {
if(data.from === otherSocketIds[i]){ //跳过自己
continue;
}
let otherSocket = io.sockets.connected[otherSocketIds[i]];
if(!otherSocket){
continue;
}
otherSocket.emit(rtcClientEvent.changeNickName, data);
}
} catch (e) {
utils.tlConsole(e)
socket.emit("tips", {
room: data.room,
to: socket.id,
msg: "系统错误"
});
bussinessNotify.sendSystemErrorMsg({
title: "socket-changeNickName",
data: JSON.stringify(data),
room: data.room,
from : socket.id,
msg : JSON.stringify({
message: e.message,
fileName: e.fileName,
lineNumber: e.lineNumber,
stack: e.stack,
name: e.name
}, null, '\t')
})
}
}
module.exports = {
changeNickName
}

View File

@@ -44,6 +44,10 @@ const rtcServerEvent = {
addCodeFile : "addCodeFile", addCodeFile : "addCodeFile",
//获取取件码文件 //获取取件码文件
getCodeFile : "getCodeFile", getCodeFile : "getCodeFile",
//心跳
heartbeat : "heartbeat",
//修改昵称
changeNickName : "changeNickName",
} }
/** /**
@@ -124,6 +128,10 @@ let rtcClientEvent = {
addCodeFile : "addCodeFile", addCodeFile : "addCodeFile",
//获取取件码文件 //获取取件码文件
getCodeFile : "getCodeFile", getCodeFile : "getCodeFile",
//心跳
heartbeat : "heartbeat",
//修改昵称
changeNickName : "changeNickName",
} }
/** /**

View File

@@ -0,0 +1,26 @@
const rtcConstant = require("../rtcConstant");
const rtcClientEvent = rtcConstant.rtcClientEvent
const utils = require("../../../src/utils/utils");
/**
* 心跳
* @param {*} io socketio对象
* @param {*} socket 单个socket连接
* @param {*} tables 数据表对象
* @param {*} dbClient sequelize-orm对象
* @param {*} data event参数
* @returns
*/
async function heartbeat(io, socket, tables, dbClient, data){
try{
socket.emit(rtcClientEvent.heartbeat, {
status : "ok"
})
}catch(e){
utils.tlConsole(e)
}
}
module.exports = {
heartbeat
}

View File

@@ -54,6 +54,24 @@
<div class="content unicode" style="display: block;"> <div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box"> <ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont">&#xe6de;</span>
<div class="name">相机反转</div>
<div class="code-name">&amp;#xe6de;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe6bc;</span>
<div class="name">奖杯7-3</div>
<div class="code-name">&amp;#xe6bc;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe6ea;</span>
<div class="name">奖杯</div>
<div class="code-name">&amp;#xe6ea;</div>
</li>
<li class="dib"> <li class="dib">
<span class="icon iconfont">&#xe81f;</span> <span class="icon iconfont">&#xe81f;</span>
<div class="name">老师</div> <div class="name">老师</div>
@@ -708,9 +726,9 @@
<pre><code class="language-css" <pre><code class="language-css"
>@font-face { >@font-face {
font-family: 'iconfont'; font-family: 'iconfont';
src: url('iconfont.woff2?t=1692323109723') format('woff2'), src: url('iconfont.woff2?t=1692448975663') format('woff2'),
url('iconfont.woff?t=1692323109723') format('woff'), url('iconfont.woff?t=1692448975663') format('woff'),
url('iconfont.ttf?t=1692323109723') format('truetype'); url('iconfont.ttf?t=1692448975663') format('truetype');
} }
</code></pre> </code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3> <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -736,6 +754,33 @@
<div class="content font-class"> <div class="content font-class">
<ul class="icon_lists dib-box"> <ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont icon-rtc-file-xiangjifanzhuan"></span>
<div class="name">
相机反转
</div>
<div class="code-name">.icon-rtc-file-xiangjifanzhuan
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-rtc-file-jiangbei-"></span>
<div class="name">
奖杯7-3
</div>
<div class="code-name">.icon-rtc-file-jiangbei-
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-rtc-file-jiangbei"></span>
<div class="name">
奖杯
</div>
<div class="code-name">.icon-rtc-file-jiangbei
</div>
</li>
<li class="dib"> <li class="dib">
<span class="icon iconfont icon-rtc-file-laoshi"></span> <span class="icon iconfont icon-rtc-file-laoshi"></span>
<div class="name"> <div class="name">
@@ -1717,6 +1762,30 @@
<div class="content symbol"> <div class="content symbol">
<ul class="icon_lists dib-box"> <ul class="icon_lists dib-box">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-rtc-file-xiangjifanzhuan"></use>
</svg>
<div class="name">相机反转</div>
<div class="code-name">#icon-rtc-file-xiangjifanzhuan</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-rtc-file-jiangbei-"></use>
</svg>
<div class="name">奖杯7-3</div>
<div class="code-name">#icon-rtc-file-jiangbei-</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-rtc-file-jiangbei"></use>
</svg>
<div class="name">奖杯</div>
<div class="code-name">#icon-rtc-file-jiangbei</div>
</li>
<li class="dib"> <li class="dib">
<svg class="icon svg-icon" aria-hidden="true"> <svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-rtc-file-laoshi"></use> <use xlink:href="#icon-rtc-file-laoshi"></use>

View File

@@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 4147343 */ font-family: "iconfont"; /* Project id 4147343 */
src: url('iconfont.woff2?t=1692323109723') format('woff2'), src: url('iconfont.woff2?t=1692448975663') format('woff2'),
url('iconfont.woff?t=1692323109723') format('woff'), url('iconfont.woff?t=1692448975663') format('woff'),
url('iconfont.ttf?t=1692323109723') format('truetype'); url('iconfont.ttf?t=1692448975663') format('truetype');
} }
.iconfont { .iconfont {
@@ -13,6 +13,18 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-rtc-file-xiangjifanzhuan:before {
content: "\e6de";
}
.icon-rtc-file-jiangbei-:before {
content: "\e6bc";
}
.icon-rtc-file-jiangbei:before {
content: "\e6ea";
}
.icon-rtc-file-laoshi:before { .icon-rtc-file-laoshi:before {
content: "\e81f"; content: "\e81f";
} }

File diff suppressed because one or more lines are too long

View File

@@ -5,6 +5,27 @@
"css_prefix_text": "icon-rtc-file-", "css_prefix_text": "icon-rtc-file-",
"description": "", "description": "",
"glyphs": [ "glyphs": [
{
"icon_id": "3405264",
"name": "相机反转",
"font_class": "xiangjifanzhuan",
"unicode": "e6de",
"unicode_decimal": 59102
},
{
"icon_id": "6162508",
"name": "奖杯7-3",
"font_class": "jiangbei-",
"unicode": "e6bc",
"unicode_decimal": 59068
},
{
"icon_id": "12592991",
"name": "奖杯",
"font_class": "jiangbei",
"unicode": "e6ea",
"unicode_decimal": 59114
},
{ {
"icon_id": "22960451", "icon_id": "22960451",
"name": "老师", "name": "老师",