diff --git a/app/src/Room.js b/app/src/Room.js index 7c99cd63..ee098fa3 100644 --- a/app/src/Room.js +++ b/app/src/Room.js @@ -39,6 +39,7 @@ module.exports = class Room { }; // ########################## this._moderator = { + video_start_privacy: false, audio_start_muted: false, video_start_hidden: false, audio_cant_unmute: false, @@ -424,6 +425,9 @@ module.exports = class Room { updateRoomModerator(data) { log.debug('Update room moderator', data); switch (data.type) { + case 'video_start_privacy': + this._moderator.video_start_privacy = data.status; + break; case 'audio_start_muted': this._moderator.audio_start_muted = data.status; break; diff --git a/app/src/Server.js b/app/src/Server.js index 6d3fcaf1..5ffe7790 100644 --- a/app/src/Server.js +++ b/app/src/Server.js @@ -44,7 +44,7 @@ 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.5.26 + * @version 1.5.27 * */ @@ -1827,6 +1827,8 @@ function startServer() { const peer = room.getPeer(socket.id); + if (!peer) return; + peer.updatePeerInfo(data); if (data.broadcast) { @@ -2590,6 +2592,8 @@ function startServer() { const peer = room.getPeer(id); + if (!peer) return false; + const { peer_name } = peer; return peer_name == name; diff --git a/package.json b/package.json index 2f43dc01..18d55d1d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mirotalksfu", - "version": "1.5.26", + "version": "1.5.27", "description": "WebRTC SFU browser-based video calls", "main": "Server.js", "scripts": { diff --git a/public/css/Room.css b/public/css/Room.css index ce458da1..f3541a2a 100644 --- a/public/css/Room.css +++ b/public/css/Room.css @@ -608,6 +608,7 @@ th { width: 65%; background-color: var(--body-bg); min-height: 480px; + max-height: 680px; overflow-x: hidden; overflow-y: auto; } @@ -1042,6 +1043,12 @@ th { # Common --------------------------------------------------------------*/ +h1, +h2, +h3 { + color: #c2c2c2; +} + select { height: 40px; line-height: 40px; @@ -1405,7 +1412,8 @@ hr { } .wa { - width: auto; + /* width: auto; */ + width: 320px; } .ml-5 { diff --git a/public/js/LocalStorage.js b/public/js/LocalStorage.js index 273c140e..0f3294c3 100644 --- a/public/js/LocalStorage.js +++ b/public/js/LocalStorage.js @@ -20,6 +20,7 @@ class LocalStorage { show_chat_on_msg: true, // show chat on new message transcript_show_on_msg: true, // show transcript on new message speech_in_msg: false, // speech incoming message + moderator_video_start_privacy: false, // Everyone starts video in privacy mode moderator_audio_start_muted: false, // Everyone starts muted in the room moderator_video_start_hidden: false, // Everyone starts hidden in the room moderator_audio_cant_unmute: false, // Everyone can't unmute themselves diff --git a/public/js/Room.js b/public/js/Room.js index 79d6b3df..9ab077b4 100644 --- a/public/js/Room.js +++ b/public/js/Room.js @@ -11,7 +11,7 @@ if (location.href.substr(0, 5) !== 'https') location.href = 'https' + location.h * @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.5.26 + * @version 1.5.27 * */ @@ -2432,6 +2432,16 @@ function handleSelects() { toggleCanvasGrid(); }; // room moderator rules + switchEveryonePrivacy.onchange = (e) => { + const videoStartPrivacy = e.currentTarget.checked; + isVideoPrivacyActive = !videoStartPrivacy; + rc.toggleVideoPrivacyMode(); + rc.updateRoomModerator({ type: 'video_start_privacy', status: videoStartPrivacy }); + rc.roomMessage('video_start_privacy', videoStartPrivacy); + localStorageSettings.moderator_video_start_privacy = videoStartPrivacy; + lS.setSettings(localStorageSettings); + e.target.blur(); + }; switchEveryoneMute.onchange = (e) => { const audioStartMuted = e.currentTarget.checked; rc.updateRoomModerator({ type: 'audio_start_muted', status: audioStartMuted }); @@ -4304,7 +4314,7 @@ function showAbout() { imageUrl: image.about, customClass: { image: 'img-about' }, position: 'center', - title: 'WebRTC SFU v1.5.26', + title: 'WebRTC SFU v1.5.27', html: `
diff --git a/public/js/RoomClient.js b/public/js/RoomClient.js index 088afbc4..c85bcd24 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.5.26 + * @version 1.5.27 * */ @@ -210,6 +210,7 @@ class RoomClient { // Moderator this._moderator = { + video_start_privacy: false, audio_start_muted: false, video_start_hidden: false, audio_cant_unmute: false, @@ -506,6 +507,7 @@ class RoomClient { if (room.moderator && (!isRulesActive || !isPresenter)) { console.log('07.2 ----> ROOM MODERATOR', room.moderator); const { + video_start_privacy, audio_start_muted, video_start_hidden, audio_cant_unmute, @@ -515,6 +517,7 @@ class RoomClient { chat_cant_chatgpt, } = room.moderator; + this._moderator.video_start_privacy = video_start_privacy; this._moderator.audio_start_muted = audio_start_muted; this._moderator.video_start_hidden = video_start_hidden; this._moderator.audio_cant_unmute = audio_cant_unmute; @@ -522,7 +525,18 @@ class RoomClient { this._moderator.screen_cant_share = screen_cant_share; this._moderator.chat_cant_privately = chat_cant_privately; this._moderator.chat_cant_chatgpt = chat_cant_chatgpt; + // + if (this._moderator.video_start_privacy || localStorageSettings.moderator_video_start_privacy) { + this.peer_info.peer_video_privacy = true; + this.emitCmd({ + type: 'privacy', + peer_id: this.peer_id, + active: true, + broadcast: true, + }); + this.userLog('warning', 'The Moderator starts video in privacy mode', 'top-end'); + } if (this._moderator.audio_start_muted && this._moderator.video_start_hidden) { this.userLog('warning', 'The Moderator disabled your audio and video', 'top-end'); } else { @@ -1853,7 +1867,7 @@ class RoomClient { pn.id = id + '__pin'; pn.className = html.pin; vp = document.createElement('button'); - vp.id = this.peer_id + +'__vp'; + vp.id = this.peer_id + '__vp'; vp.className = html.videoPrivacy; au = document.createElement('button'); au.id = this.peer_id + '__audio'; @@ -1888,7 +1902,7 @@ class RoomClient { d.appendChild(p); d.appendChild(vb); this.videoMediaContainer.appendChild(d); - this.attachMediaStream(elem, stream, type, 'Producer'); + await this.attachMediaStream(elem, stream, type, 'Producer'); this.myVideoEl = elem; this.isVideoPictureInPictureSupported && this.handlePIP(elem.id, pip.id); this.isVideoFullScreenSupported && this.handleFS(elem.id, fs.id); @@ -1919,7 +1933,7 @@ class RoomClient { elem.volume = 0; this.myAudioEl = elem; this.localAudioEl.appendChild(elem); - this.attachMediaStream(elem, stream, type, 'Producer'); + await this.attachMediaStream(elem, stream, type, 'Producer'); console.log('[addProducer] audio-element-count', this.localAudioEl.childElementCount); break; default: @@ -2194,7 +2208,7 @@ class RoomClient { }; } - handleConsumer(id, type, stream, peer_name, peer_info) { + async handleConsumer(id, type, stream, peer_name, peer_info) { let elem, vb, d, p, i, cm, au, pip, fs, ts, sf, sm, sv, gl, ban, ko, pb, pm, pv, pn; let eDiv, eBtn, eVc; // expand buttons @@ -2318,7 +2332,7 @@ class RoomClient { d.appendChild(pm); d.appendChild(vb); this.videoMediaContainer.appendChild(d); - this.attachMediaStream(elem, stream, type, 'Consumer'); + await this.attachMediaStream(elem, stream, type, 'Consumer'); this.isVideoPictureInPictureSupported && this.handlePIP(elem.id, pip.id); this.isVideoFullScreenSupported && this.handleFS(elem.id, fs.id); this.handleDD(elem.id, remotePeerId); @@ -2363,7 +2377,7 @@ class RoomClient { elem.autoplay = true; elem.audio = 1.0; this.remoteAudioEl.appendChild(elem); - this.attachMediaStream(elem, stream, type, 'Consumer'); + await this.attachMediaStream(elem, stream, type, 'Consumer'); let audioConsumerId = remotePeerId + '___pVolume'; this.audioConsumers.set(audioConsumerId, id); let inputPv = this.getId(audioConsumerId); @@ -2648,7 +2662,7 @@ class RoomClient { // HELPERS // #################################################### - attachMediaStream(elem, stream, type, who) { + async attachMediaStream(elem, stream, type, who) { let track; switch (type) { case mediaType.audio: @@ -3473,23 +3487,33 @@ class RoomClient { // #################################################### handleVP(elemId, vpId) { + const startVideoInPrivacyMode = + this._moderator.video_start_privacy || localStorageSettings.moderator_video_start_privacy; let videoPlayer = this.getId(elemId); let btnVp = this.getId(vpId); if (btnVp && videoPlayer) { btnVp.addEventListener('click', () => { this.sound('click'); - isVideoPrivacyActive = !isVideoPrivacyActive; - this.setVideoPrivacyStatus(this.peer_id, isVideoPrivacyActive); - this.emitCmd({ - type: 'privacy', - peer_id: this.peer_id, - active: isVideoPrivacyActive, - broadcast: true, - }); + this.toggleVideoPrivacyMode(); }); + + if (startVideoInPrivacyMode) { + btnVp.click(); + } } } + toggleVideoPrivacyMode() { + isVideoPrivacyActive = !isVideoPrivacyActive; + this.setVideoPrivacyStatus(this.peer_id, isVideoPrivacyActive); + this.emitCmd({ + type: 'privacy', + peer_id: this.peer_id, + active: isVideoPrivacyActive, + broadcast: true, + }); + } + setVideoPrivacyStatus(elemName, privacy) { let videoPlayer = this.getName(elemName)[0]; if (privacy) { @@ -5807,6 +5831,13 @@ class RoomClient { 'top-end', ); break; + case 'video_start_privacy': + this.userLog( + 'info', + `${icons.moderator} Moderator: everyone starts in privacy mode ${status}`, + 'top-end', + ); + break; case 'audio_start_muted': this.userLog('info', `${icons.moderator} Moderator: everyone starts muted ${status}`, 'top-end'); break; diff --git a/public/js/Rules.js b/public/js/Rules.js index 5d5ce4e1..2302cc47 100644 --- a/public/js/Rules.js +++ b/public/js/Rules.js @@ -162,6 +162,7 @@ function handleRules(isPresenter) { switchHostOnlyRecording.checked = hostOnlyRecording; rc.roomAction(hostOnlyRecording ? 'hostOnlyRecordingOn' : 'hostOnlyRecordingOff', true, false); // Room moderator + switchEveryonePrivacy.checked = localStorageSettings.moderator_video_start_privacy; switchEveryoneMute.checked = localStorageSettings.moderator_audio_start_muted; switchEveryoneHidden.checked = localStorageSettings.moderator_video_start_hidden; switchEveryoneCantUnmute.checked = localStorageSettings.moderator_audio_cant_unmute; @@ -173,6 +174,7 @@ function handleRules(isPresenter) { // Update moderator settings... const moderatorData = { + video_start_privacy: switchEveryonePrivacy.checked, audio_start_muted: switchEveryoneMute.checked, video_start_hidden: switchEveryoneHidden.checked, audio_cant_unmute: switchEveryoneCantUnmute.checked, diff --git a/public/views/Room.html b/public/views/Room.html index eb6df9a7..736f9d07 100644 --- a/public/views/Room.html +++ b/public/views/Room.html @@ -638,7 +638,27 @@ access to use this app.
+

Moderator options

+
+ + + +
+
+ +
+