diff --git a/README.md b/README.md index 8a931904..ad488030 100644 --- a/README.md +++ b/README.md @@ -287,25 +287,82 @@ To embed a meeting within `your service or app` using an iframe, you can use the - `Rest API:` The [API documentation](https://docs.mirotalk.com/mirotalk-sfu/api/) uses [swagger](https://swagger.io/) at https://localhost:3010/api/v1/docs or check it on live [here](https://sfu.mirotalk.com/api/v1/docs). +### 1. Get Server Statistics + ```bash -# The response will give you the total of rooms and users. -$ curl -X GET "http://localhost:3010/api/v1/stats" -H "authorization: mirotalksfu_default_secret" -H "Content-Type: application/json" -$ curl -X GET "https://sfu.mirotalk.com/api/v1/stats" -H "authorization: mirotalksfu_default_secret" -H "Content-Type: application/json" -# The response will give you the active meetings (default disabled). -$ curl -X GET "http://localhost:3010/api/v1/meetings" -H "authorization: mirotalksfu_default_secret" -H "Content-Type: application/json" -$ curl -X GET "https://sfu.mirotalk.com/api/v1/meetings" -H "authorization: mirotalksfu_default_secret" -H "Content-Type: application/json" -# The response will give you a entrypoint / Room URL for your meeting. -$ curl -X POST "http://localhost:3010/api/v1/meeting" -H "authorization: mirotalksfu_default_secret" -H "Content-Type: application/json" -$ curl -X POST "https://sfu.mirotalk.com/api/v1/meeting" -H "authorization: mirotalksfu_default_secret" -H "Content-Type: application/json" -# The response will give you a entrypoint / URL for the direct join to the meeting. -$ curl -X POST "http://localhost:3010/api/v1/join" -H "authorization: mirotalksfu_default_secret" -H "Content-Type: application/json" --data '{"room":"test","roomPassword":"false","name":"mirotalksfu","audio":"false","video":"false","screen":"false","notify":"false","duration":"unlimited"}' -$ curl -X POST "https://sfu.mirotalk.com/api/v1/join" -H "authorization: mirotalksfu_default_secret" -H "Content-Type: application/json" --data '{"room":"test","roomPassword":"false","name":"mirotalksfu","audio":"false","video":"false","screen":"false","notify":"false","duration":"unlimited"}' -# The response will give you a entrypoint / URL for the direct join to the meeting with a token. -$ curl -X POST "http://localhost:3010/api/v1/join" -H "authorization: mirotalksfu_default_secret" -H "Content-Type: application/json" --data '{"room":"test","roomPassword":"false","name":"mirotalksfu","audio":"false","video":"false","screen":"false","notify":"false","duration":"unlimited","token":{"username":"username","password":"password","presenter":"true", "expire":"1h"}}' -$ curl -X POST "https://sfu.mirotalk.com/api/v1/join" -H "authorization: mirotalksfu_default_secret" -H "Content-Type: application/json" --data '{"room":"test","roomPassword":"false","name":"mirotalksfu","audio":"false","video":"false","screen":"false","notify":"false","duration":"unlimited","token":{"username":"username","password":"password","presenter":"true", "expire":"1h"}}' -# The response will give you a valid token for a meeting (default diabled) -$ curl -X POST "http://localhost:3010/api/v1/token" -H "authorization: mirotalksfu_default_secret" -H "Content-Type: application/json" --data '{"username":"username","password":"password","presenter":"true", "expire":"1h"}' -$ curl -X POST "https://sfu.mirotalk.com/api/v1/token" -H "authorization: mirotalksfu_default_secret" -H "Content-Type: application/json" --data '{"username":"username","password":"password","presenter":"true", "expire":"1h"}' +curl -X GET "http://localhost:3010/api/v1/stats" \ + -H "authorization: mirotalksfu_default_secret" \ + -H "Content-Type: application/json" + +curl -X GET "https://sfu.mirotalk.com/api/v1/stats" \ + -H "authorization: mirotalksfu_default_secret" \ + -H "Content-Type: application/json" +``` + +### 2. Get Active Meetings + +```bash +curl -X GET "http://localhost:3010/api/v1/meetings" \ + -H "authorization: mirotalksfu_default_secret" \ + -H "Content-Type: application/json" + +curl -X GET "https://sfu.mirotalk.com/api/v1/meetings" \ + -H "authorization: mirotalksfu_default_secret" \ + -H "Content-Type: application/json" +``` + +### 3. Create Meeting + +```bash +curl -X POST "http://localhost:3010/api/v1/meeting" \ + -H "authorization: mirotalksfu_default_secret" \ + -H "Content-Type: application/json" + +curl -X POST "https://sfu.mirotalk.com/api/v1/meeting" \ + -H "authorization: mirotalksfu_default_secret" \ + -H "Content-Type: application/json" +``` + +### 4. Join Meeting (Basic) + +```bash +curl -X POST "http://localhost:3010/api/v1/join" \ + -H "authorization: mirotalksfu_default_secret" \ + -H "Content-Type: application/json" \ + --data '{"room":"test","roomPassword":false,"avatar":false,"name":"mirotalksfu","audio":false,"video":false,"screen":false,"notify":false,"duration":"unlimited"}' + +curl -X POST "https://sfu.mirotalk.com/api/v1/join" \ + -H "authorization: mirotalksfu_default_secret" \ + -H "Content-Type: application/json" \ + --data '{"room":"test","roomPassword":false,"name":"mirotalksfu","avatar":false,"audio":false,"video":false,"screen":false,"notify":false,"duration":"unlimited"}' +``` + +### 5. Join Meeting with Token + +```bash +curl -X POST "http://localhost:3010/api/v1/join" \ + -H "authorization: mirotalksfu_default_secret" \ + -H "Content-Type: application/json" \ + --data '{"room":"test","roomPassword":false,"name":"mirotalksfu","audio":false,"video":false,"screen":false,"notify":false,"duration":"unlimited","token":{"username":"username","password":"password","presenter":true,"expire":"1h"}}' + +curl -X POST "https://sfu.mirotalk.com/api/v1/join" \ + -H "authorization: mirotalksfu_default_secret" \ + -H "Content-Type: application/json" \ + --data '{"room":"test","roomPassword":false,"name":"mirotalksfu","audio":false,"video":false,"screen":false,"notify":false,"duration":"unlimited","token":{"username":"username","password":"password","presenter":true,"expire":"1h"}}' +``` + +### 6. Generate Token + +```bash +curl -X POST "http://localhost:3010/api/v1/token" \ + -H "authorization: mirotalksfu_default_secret" \ + -H "Content-Type: application/json" \ + --data '{"username":"username","password":"password","presenter":true,"expire":"1h"}' + +curl -X POST "https://sfu.mirotalk.com/api/v1/token" \ + -H "authorization: mirotalksfu_default_secret" \ + -H "Content-Type: application/json" \ + --data '{"username":"username","password":"password","presenter":true,"expire":"1h"}' ``` diff --git a/app/api/join/join.js b/app/api/join/join.js index 0a6efe9d..6e5c287f 100644 --- a/app/api/join/join.js +++ b/app/api/join/join.js @@ -19,9 +19,10 @@ async function getJoin() { room: 'test', roomPassword: false, name: 'mirotalksfu', - audio: true, - video: true, - screen: true, + avatar: false, + audio: false, + video: false, + screen: false, hide: false, notify: true, duration: 'unlimited', diff --git a/app/api/join/join.php b/app/api/join/join.php index eafb0626..6c223cc4 100644 --- a/app/api/join/join.php +++ b/app/api/join/join.php @@ -20,9 +20,10 @@ $data = array( "room" => "test", "roomPassword" => false, "name" => "mirotalksfu", - "audio" => true, - "video" => true, - "screen" => true, + "avatar" => false, + "audio" => false, + "video" => false, + "screen" => false, "hide" => false, "notify" => true, "duration" => "unlimited", diff --git a/app/api/join/join.py b/app/api/join/join.py index 2e737087..24dba577 100644 --- a/app/api/join/join.py +++ b/app/api/join/join.py @@ -15,9 +15,10 @@ data = { "room": "test", "roomPassword": "false", "name": "mirotalksfu", - "audio": "true", - "video": "true", - "screen": "true", + "avatar": "false", + "audio": "false", + "video": "false", + "screen": "false", "hide": "false", "notify": "true", "duration": "unlimited", diff --git a/app/api/join/join.sh b/app/api/join/join.sh index df765457..6c7df860 100755 --- a/app/api/join/join.sh +++ b/app/api/join/join.sh @@ -1,11 +1,33 @@ #!/bin/bash +# Configuration API_KEY_SECRET="mirotalksfu_default_secret" MIROTALK_URL="https://sfu.mirotalk.com/api/v1/join" +# Alternative URL for local testing: # MIROTALK_URL="http://localhost:3010/api/v1/join" -curl $MIROTALK_URL \ - --header "authorization: $API_KEY_SECRET" \ - --header "Content-Type: application/json" \ - --data '{"room":"test","roomPassword":"false","name":"mirotalksfu","audio":"true","video":"true","screen":"false","hide":"false","notify":"true","duration":"unlimited","token":{"username":"username","password":"password","presenter":"true", "expire":"1h"}}' \ - --request POST \ No newline at end of file +# Request data with proper JSON formatting +REQUEST_DATA='{ + "room": "test", + "roomPassword": false, + "name": "mirotalksfu", + "avatar": false, + "audio": false, + "video": false, + "screen": false, + "hide": false, + "notify": true, + "duration": "unlimited", + "token": { + "username": "username", + "password": "password", + "presenter": true, + "expire": "1h" + } +}' + +# Make the API request +curl -X POST "$MIROTALK_URL" \ + -H "Authorization: $API_KEY_SECRET" \ + -H "Content-Type: application/json" \ + -d "$REQUEST_DATA" \ No newline at end of file diff --git a/app/api/swagger.yaml b/app/api/swagger.yaml index 0ece6177..0eeea95c 100644 --- a/app/api/swagger.yaml +++ b/app/api/swagger.yaml @@ -162,6 +162,9 @@ definitions: name: type: string default: 'mirotalksfu' + avatar: + type: ['boolean', 'string'] # Allow boolean or string type + default: false audio: type: boolean default: false @@ -212,6 +215,8 @@ definitions: properties: name: type: string + avatar: + type: string presenter: type: boolean video: diff --git a/app/src/Peer.js b/app/src/Peer.js index 0cf114e0..bad882f7 100644 --- a/app/src/Peer.js +++ b/app/src/Peer.js @@ -10,6 +10,7 @@ module.exports = class Peer { const { peer_uuid, peer_name, + peer_avatar, peer_presenter, peer_audio, peer_audio_volume, @@ -23,6 +24,7 @@ module.exports = class Peer { this.peer_info = peer_info; this.peer_uuid = peer_uuid; this.peer_name = peer_name; + this.peer_avatar = peer_avatar; this.peer_presenter = peer_presenter; this.peer_audio = peer_audio; this.peer_video = peer_video; diff --git a/app/src/Server.js b/app/src/Server.js index a87adb74..d0672592 100644 --- a/app/src/Server.js +++ b/app/src/Server.js @@ -64,7 +64,7 @@ dev dependencies: { * @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon * @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970 * @author Miroslav Pejic - miroslav.pejic.85@gmail.com - * @version 1.8.12 + * @version 1.8.13 * */ diff --git a/app/src/ServerApi.js b/app/src/ServerApi.js index 9f600ca5..afb20204 100644 --- a/app/src/ServerApi.js +++ b/app/src/ServerApi.js @@ -68,11 +68,12 @@ module.exports = class ServerApi { getJoinURL(data) { // Get data - const { room, roomPassword, name, audio, video, screen, hide, notify, duration, token } = data; + const { room, roomPassword, name, avatar, audio, video, screen, hide, notify, duration, token } = data; const roomValue = room || uuidV4(); - const nameValue = name || 'User-' + this.getRandomNumber(); const roomPasswordValue = roomPassword || false; + const nameValue = name || 'User-' + this.getRandomNumber(); + const avatarValue = avatar || false; const audioValue = audio || false; const videoValue = video || false; const screenValue = screen || false; @@ -88,6 +89,7 @@ module.exports = class ServerApi { `room=${roomValue}` + `&roomPassword=${roomPasswordValue}` + `&name=${encodeURIComponent(nameValue)}` + + `&avatar=${encodeURIComponent(avatarValue)}` + `&audio=${audioValue}` + `&video=${videoValue}` + `&screen=${screenValue}` + diff --git a/package.json b/package.json index bc3f0224..cd527336 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mirotalksfu", - "version": "1.8.12", + "version": "1.8.13", "description": "WebRTC SFU browser-based video calls", "main": "Server.js", "scripts": { @@ -81,7 +81,7 @@ "mediasoup": "3.15.7", "mediasoup-client": "3.9.5", "nodemailer": "^6.10.0", - "openai": "^4.91.1", + "openai": "^4.92.1", "qs": "6.14.0", "sanitize-filename": "^1.6.3", "socket.io": "4.8.1", @@ -99,7 +99,7 @@ "proxyquire": "^2.1.3", "should": "^13.2.3", "sinon": "^20.0.0", - "webpack": "^5.98.0", + "webpack": "^5.99.1", "webpack-cli": "^6.0.1" } } diff --git a/public/images/admin.png b/public/images/admin.png new file mode 100644 index 00000000..07856acd Binary files /dev/null and b/public/images/admin.png differ diff --git a/public/js/Brand.js b/public/js/Brand.js index 041c0ff6..4dcd51a6 100644 --- a/public/js/Brand.js +++ b/public/js/Brand.js @@ -64,7 +64,7 @@ let BRAND = { }, about: { imageUrl: '../images/mirotalk-logo.gif', - title: 'WebRTC SFU v1.8.12', + title: 'WebRTC SFU v1.8.13', html: ` ', peer_info); const peer_name = peer_info.peer_name; + const peer_avatar = peer_info.peer_avatar; const peer_name_limited = peer_name.length > 15 ? peer_name.substring(0, 10) + '*****' : peer_name; //const peer_presenter = peer_info.peer_presenter ? _PEER.presenter : _PEER.guest; const peer_audio = peer_info.peer_audio ? _PEER.audioOn : _PEER.audioOff; @@ -4524,7 +4548,7 @@ function getParticipantsList(peers) { const peer_geoLocation = _PEER.geoLocation; const peer_sendFile = _PEER.sendFile; const peer_id = peer_info.peer_id; - const avatarImg = getParticipantAvatar(peer_name); + const avatarImg = getParticipantAvatar(peer_name, peer_avatar); const peer_chat_active = rc.chatPeerId === peer_id ? ' active' : ''; @@ -4538,7 +4562,7 @@ function getParticipantsList(peers) { data-to-id="${peer_id}" data-to-name="${peer_name}" class="clearfix${peer_chat_active}" - onclick="rc.showPeerAboutAndMessages(this.id, '${peer_name}', event)" + onclick="rc.showPeerAboutAndMessages(this.id, '${peer_name}', '${peer_avatar}', event)" > diff --git a/public/js/RoomClient.js b/public/js/RoomClient.js index d07dbf37..1e0ce51c 100644 --- a/public/js/RoomClient.js +++ b/public/js/RoomClient.js @@ -9,7 +9,7 @@ * @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon * @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970 * @author Miroslav Pejic - miroslav.pejic.85@gmail.com - * @version 1.8.12 + * @version 1.8.13 * */ @@ -245,6 +245,7 @@ class RoomClient { this.room_id = room_id; this.peer_id = socket.id; this.peer_name = peer_name; + this.peer_avatar = peer_info.peer_avatar; this.peer_uuid = peer_uuid; this.peer_info = peer_info; @@ -279,6 +280,7 @@ class RoomClient { this.chatMessageSpamCountToBan = 10; this.chatPeerId = 'all'; this.chatPeerName = 'all'; + this.chatPeerAvatar = ''; // HeyGen Video AI this.videoAIContainer = null; @@ -639,7 +641,7 @@ class RoomClient { let peer_info = this.peers.get(peer).peer_info; // console.log('07.1 ----> Remote Peer info', peer_info); - const { peer_id, peer_name, peer_presenter, peer_video, peer_recording } = peer_info; + const { peer_id, peer_name, peer_avatar, peer_presenter, peer_video, peer_recording } = peer_info; const canSetVideoOff = !isBroadcastingEnabled || (isBroadcastingEnabled && peer_presenter); @@ -652,6 +654,7 @@ class RoomClient { this.handleRecordingAction({ peer_id: peer_id, peer_name: peer_name, + peer_avatar: peer_avatar, action: enums.recording.started, }); } @@ -3080,7 +3083,7 @@ class RoomClient { //console.log('setVideoOff', peer_info); let d, vb, i, h, au, sf, sm, sv, gl, ban, ko, p, pm, pb, pv; - const { peer_id, peer_name, peer_audio, peer_presenter } = peer_info; + const { peer_id, peer_name, peer_avatar, peer_audio, peer_presenter } = peer_info; this.removeVideoOff(peer_id); @@ -3169,7 +3172,7 @@ class RoomClient { this.handleDD(d.id, peer_id, !remotePeer); this.popupPeerInfo(p.id, peer_info); this.checkPeerInfoStatus(peer_info); - this.setVideoAvatarImgName(i.id, peer_name); + this.setVideoAvatarImgName(i.id, peer_name, peer_avatar); this.getId(i.id).style.display = 'block'; if (isParticipantsListOpen) getRoomParticipants(); @@ -3391,9 +3394,11 @@ class RoomClient { } } - setVideoAvatarImgName(elemId, peer_name) { + setVideoAvatarImgName(elemId, peer_name, peer_avatar = false) { let elem = this.getId(elemId); - if (cfg.useAvatarSvg) { + if (peer_avatar && rc.isImageURL(peer_avatar)) { + elem.setAttribute('src', peer_avatar); + } else if (cfg.useAvatarSvg) { rc.isValidEmail(peer_name) ? elem.setAttribute('src', this.genGravatar(peer_name)) : elem.setAttribute('src', this.genAvatarSvg(peer_name, 250)); @@ -4528,7 +4533,7 @@ class RoomClient { } this.chatCenter(); this.sound('open'); - this.showPeerAboutAndMessages(this.chatPeerId, this.chatPeerName); + this.showPeerAboutAndMessages(this.chatPeerId, this.chatPeerName, this.chatPeerAvatar); } isParticipantsListOpen = !isParticipantsListOpen; this.isChatOpen = !this.isChatOpen; @@ -4737,6 +4742,7 @@ class RoomClient { const data = { room_id: this.room_id, peer_name: this.peer_name, + peer_avatar: this.peer_avatar, peer_id: this.peer_id, to_peer_id: 'ChatGPT', to_peer_name: 'ChatGPT', @@ -4746,7 +4752,7 @@ class RoomClient { if (isChatGPTOn) { console.log('Send message:', data); this.socket.emit('message', data); - this.setMsgAvatar('left', this.peer_name); + this.setMsgAvatar('left', this.peer_name, this.peer_avatar); this.appendMessage( 'left', this.leftMsgAvatar, @@ -4792,7 +4798,7 @@ class RoomClient { data.to_peer_name = li.getAttribute('data-to-name'); console.log('Send message:', data); this.socket.emit('message', data); - this.setMsgAvatar('left', this.peer_name); + this.setMsgAvatar('left', this.peer_name, this.peer_avatar); this.appendMessage( 'left', this.leftMsgAvatar, @@ -4835,6 +4841,7 @@ class RoomClient { const toPeerName = filterXSS(to_peer_name); let data = { peer_name: this.peer_name, + peer_avatar: this.peer_avatar, peer_id: this.peer_id, to_peer_id: to_peer_id, to_peer_name: toPeerName, @@ -4842,7 +4849,7 @@ class RoomClient { }; console.log('Send message:', data); this.socket.emit('message', data); - this.setMsgAvatar('left', this.peer_name); + this.setMsgAvatar('left', this.peer_name, this.peer_avatar); this.appendMessage( 'left', this.leftMsgAvatar, @@ -4859,7 +4866,7 @@ class RoomClient { async showMessage(data) { if (!this.isChatOpen && this.showChatOnMessage) await this.toggleChat(); - this.setMsgAvatar('right', data.peer_name); + this.setMsgAvatar('right', data.peer_name, data.peer_avatar); this.appendMessage( 'right', this.rightMsgAvatar, @@ -4895,8 +4902,13 @@ class RoomClient { } } - setMsgAvatar(avatar, peerName) { - let avatarImg = rc.isValidEmail(peerName) ? this.genGravatar(peerName) : this.genAvatarSvg(peerName, 32); + setMsgAvatar(avatar, peerName, peerAvatar = false) { + const avatarImg = + peerAvatar && this.isImageURL(peerAvatar) + ? peerAvatar + : this.isValidEmail(peerName) + ? this.genGravatar(peerName) + : this.genAvatarSvg(peerName, 32); avatar === 'left' ? (this.leftMsgAvatar = avatarImg) : (this.rightMsgAvatar = avatarImg); } @@ -5137,8 +5149,15 @@ class RoomClient { } } - isImageURL(input) { - return input.match(/\.(jpeg|jpg|gif|png|tiff|bmp)$/) != null; + async isImageURL(input) { + if (!input) return false; + try { + const response = await fetch(input, { method: 'HEAD' }); + const contentType = response.headers.get('content-type'); + return contentType && contentType.startsWith('image/'); + } catch { + return false; + } } getImage(input) { @@ -6325,12 +6344,13 @@ class RoomClient { handleRecordingAction(data) { console.log('Handle recording action', data); - const { peer_name, peer_id, action } = data; + const { peer_name, peer_avatar, peer_id, action } = data; const recAction = { side: 'left', img: this.leftMsgAvatar, peer_name: peer_name, + peer_avatar: peer_avatar, peer_id: peer_id, peer_msg: `🔴 ${action}`, to_peer_id: 'all', @@ -6514,11 +6534,12 @@ class RoomClient { peer_id: peer_id, broadcast: broadcast, peer_name: this.peer_name, + peer_avatar: this.peer_avatar, fileName: this.fileToSend.name, fileSize: this.fileToSend.size, fileType: this.fileToSend.type, }; - this.setMsgAvatar('left', this.peer_name); + this.setMsgAvatar('left', this.peer_name, this.peer_avatar); this.appendMessage( 'left', this.leftMsgAvatar, @@ -6560,7 +6581,7 @@ class RoomClient { html.newline + ' File size: ' + this.bytesToSize(this.incomingFileInfo.fileSize); - this.setMsgAvatar('right', this.incomingFileInfo.peer_name); + this.setMsgAvatar('right', this.incomingFileInfo.peer_name, this.incomingFileInfo.peer_avatar); this.appendMessage( 'right', this.rightMsgAvatar, @@ -7308,9 +7329,15 @@ class RoomClient { let lobbyTr = ''; let peer_id = data.peer_id; let peer_name = data.peer_name; - let avatarImg = rc.isValidEmail(peer_name) - ? this.genGravatar(peer_name) - : this.genAvatarSvg(peer_name, 32); + let peer_avatar = data.peer_avatar; + + const avatarImg = + peer_avatar && this.isImageURL(peer_avatar) + ? peer_avatar + : this.isValidEmail(peer_name) + ? this.genGravatar(peer_name) + : this.genAvatarSvg(peer_name, 32); + let lobbyTb = this.getId('lobbyTb'); let lobbyAccept = _PEER.acceptPeer; let lobbyReject = _PEER.ejectPeer; @@ -8621,11 +8648,12 @@ class RoomClient { // SHOW PEER ABOUT AND MESSAGES // #################################################### - showPeerAboutAndMessages(peer_id, peer_name, event = null) { + showPeerAboutAndMessages(peer_id, peer_name, peer_avatar = false, event = null) { this.hidePeerMessages(); this.chatPeerId = peer_id; this.chatPeerName = peer_name; + this.chatPeerAvatar = peer_avatar; const chatAbout = this.getId('chatAbout'); const participant = this.getId(peer_id); @@ -8633,7 +8661,7 @@ class RoomClient { const chatPrivateMessages = this.getId('chatPrivateMessages'); const messagePrivateListItems = chatPrivateMessages.getElementsByTagName('li'); const participantsListItems = participantsList.getElementsByTagName('li'); - const avatarImg = getParticipantAvatar(peer_name); + const avatarImg = getParticipantAvatar(peer_name, peer_avatar); const generateChatAboutHTML = (imgSrc, title, status = 'online', participants = '') => { const isSensitiveChat = !['all', 'ChatGPT'].includes(peer_id) && title.length > 15; diff --git a/public/js/Transcription.js b/public/js/Transcription.js index d9d8598b..45efaf31 100644 --- a/public/js/Transcription.js +++ b/public/js/Transcription.js @@ -120,6 +120,7 @@ class Transcription { type: 'transcript', room_id: room_id, peer_name: peer_name, + peer_avatar: peer_avatar, text_data: transcript, time_stamp: new Date(), broadcast: true, @@ -185,10 +186,17 @@ class Transcription { transcriptionData.text_data = filterXSS(transcriptionData.text_data); transcriptionData.peer_name = filterXSS(transcriptionData.peer_name); + transcriptionData.peer_avatar = filterXSS(transcriptionData.peer_avatar); - const { peer_name, text_data } = transcriptionData; + const { peer_name, peer_avatar, text_data } = transcriptionData; const time_stamp = rc.getTimeNow(); - const avatar_image = rc.isValidEmail(peer_name) ? rc.genGravatar(peer_name) : rc.genAvatarSvg(peer_name, 32); + + const avatar_image = + peer_avatar && rc.isImageURL(peer_avatar) + ? peer_avatar + : rc.isValidEmail(peer_name) + ? rc.genGravatar(peer_name) + : rc.genAvatarSvg(peer_name, 32); if (this.isHidden) { if (this.showOnMessage) { @@ -468,6 +476,7 @@ class Transcription { data: { peer_id: rc.peer_id, peer_name: rc.peer_name, + peer_avatar: rc.peer_avatar, transcriptionLanguageIndex: transcriptionLanguage.selectedIndex, transcriptionDialectIndex: transcriptionDialect.selectedIndex, }, diff --git a/tests/test-ServerAPI.js b/tests/test-ServerAPI.js index dd886cec..db4dabc7 100644 --- a/tests/test-ServerAPI.js +++ b/tests/test-ServerAPI.js @@ -172,6 +172,7 @@ describe('test-ServerAPI', () => { room: 'room1', roomPassword: 'password123', name: 'John Doe', + avatar: 'test.jpg', audio: true, video: false, screen: false, @@ -185,7 +186,7 @@ describe('test-ServerAPI', () => { const result = serverApi.getJoinURL(data); result.should.equal( - 'https://example.com/join?room=room1&roomPassword=password123&name=John%20Doe&audio=true&video=false&screen=false&hide=false¬ify=false&duration=00:30:00&token=testToken', + 'https://example.com/join?room=room1&roomPassword=password123&name=John%20Doe&avatar=test.jpg&audio=true&video=false&screen=false&hide=false¬ify=false&duration=00:30:00&token=testToken', ); tokenStub.restore(); @@ -203,7 +204,7 @@ describe('test-ServerAPI', () => { const result = serverApi.getJoinURL({}); result.should.equal( - 'https://example.com/join?room=room1&roomPassword=false&name=User-123456&audio=false&video=false&screen=false&hide=false¬ify=false&duration=unlimited', + 'https://example.com/join?room=room1&roomPassword=false&name=User-123456&avatar=false&audio=false&video=false&screen=false&hide=false¬ify=false&duration=unlimited', ); }); });