diff --git a/app/src/Room.js b/app/src/Room.js
index 10e3123a..c2791e6f 100644
--- a/app/src/Room.js
+++ b/app/src/Room.js
@@ -16,6 +16,10 @@ module.exports = class Room {
this._isLobbyEnabled = false;
this._roomPassword = null;
this._hostOnlyRecording = false;
+ this._moderator = {
+ start_audio_muted: false,
+ start_video_hidden: false,
+ };
this.survey = config.survey;
this.redirect = config.redirect;
this.peers = new Map();
@@ -98,6 +102,23 @@ module.exports = class Room {
return this.router.rtpCapabilities;
}
+ // ####################################################
+ // ROOM MODERATOR
+ // ####################################################
+
+ updateRoomModerator(data) {
+ log.debug('Update room moderator', data);
+ switch (data.type) {
+ case 'audio':
+ this._moderator.start_audio_muted = data.status;
+ break;
+ case 'video':
+ this._moderator.start_video_hidden = data.status;
+ default:
+ break;
+ }
+ }
+
// ####################################################
// ROOM INFO
// ####################################################
@@ -110,6 +131,7 @@ module.exports = class Room {
isLobbyEnabled: this._isLobbyEnabled,
hostOnlyRecording: this._hostOnlyRecording,
},
+ moderator: this._moderator,
survey: this.survey,
redirect: this.redirect,
peers: JSON.stringify([...this.peers]),
diff --git a/app/src/Server.js b/app/src/Server.js
index c664e846..79a9dafd 100644
--- a/app/src/Server.js
+++ b/app/src/Server.js
@@ -40,7 +40,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.2.1
+ * @version 1.2.2
*
*/
@@ -750,6 +750,16 @@ function startServer() {
room.broadCast(socket.id, 'updatePeerInfo', data);
});
+ socket.on('updateRoomModerator', (dataObject) => {
+ if (!roomList.has(socket.room_id)) return;
+
+ const data = checkXSS(dataObject);
+
+ const room = roomList.get(socket.room_id);
+
+ room.updateRoomModerator(data);
+ });
+
socket.on('fileInfo', (dataObject) => {
if (!roomList.has(socket.room_id)) return;
diff --git a/package.json b/package.json
index 72f9fd17..b11b665d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "mirotalksfu",
- "version": "1.2.1",
+ "version": "1.2.2",
"description": "WebRTC SFU browser-based video calls",
"main": "Server.js",
"scripts": {
@@ -34,9 +34,9 @@
"author": "Miroslav Pejic",
"license": "AGPL-3.0",
"dependencies": {
- "@sentry/integrations": "7.79.0",
- "@sentry/node": "7.79.0",
- "axios": "^1.6.1",
+ "@sentry/integrations": "7.80.1",
+ "@sentry/node": "7.80.1",
+ "axios": "^1.6.2",
"body-parser": "1.20.2",
"colors": "1.4.0",
"compression": "1.7.4",
@@ -47,7 +47,7 @@
"mediasoup": "3.13.1",
"mediasoup-client": "3.7.0",
"ngrok": "^4.3.3",
- "openai": "^4.17.0",
+ "openai": "^4.18.0",
"qs": "6.11.2",
"socket.io": "4.7.2",
"swagger-ui-express": "5.0.0",
@@ -58,6 +58,6 @@
"devDependencies": {
"node-fetch": "^3.3.2",
"nodemon": "^3.0.1",
- "prettier": "3.0.3"
+ "prettier": "3.1.0"
}
}
diff --git a/public/css/Room.css b/public/css/Room.css
index 02e6c600..2602dde6 100644
--- a/public/css/Room.css
+++ b/public/css/Room.css
@@ -439,7 +439,7 @@ th {
@media screen and (max-width: 540px) {
.tab {
- font-size: 0.5rem;
+ display: inline;
}
}
diff --git a/public/js/LocalStorage.js b/public/js/LocalStorage.js
index 49c61bbc..0ea063f8 100644
--- a/public/js/LocalStorage.js
+++ b/public/js/LocalStorage.js
@@ -20,6 +20,8 @@ class LocalStorage {
show_chat_on_msg: true, // show chat on new message
show_transcript_on_msg: true, // show transcript on new message
speech_in_msg: false, // speech incoming message
+ moderator_audio_muted: false, // Everyone starts muted in the room
+ moderator_video_hidden: false, // Everyone starts hidden in the room
mic_auto_gain_control: false,
mic_echo_cancellations: true,
mic_noise_suppression: true,
diff --git a/public/js/Room.js b/public/js/Room.js
index 6c1fc74d..8fbb5d61 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.2.1
+ * @version 1.2.2
*
*/
@@ -1158,6 +1158,9 @@ function handleButtons() {
tabAspectBtn.onclick = (e) => {
rc.openTab(e, 'tabAspect');
};
+ tabModeratorBtn.onclick = (e) => {
+ rc.openTab(e, 'tabModerator');
+ };
tabProfileBtn.onclick = (e) => {
rc.openTab(e, 'tabProfile');
};
@@ -1796,6 +1799,21 @@ function handleSelects() {
wbIsBgTransparent = !wbIsBgTransparent;
wbIsBgTransparent ? wbCanvasBackgroundColor('rgba(0, 0, 0, 0.100)') : setTheme();
};
+ // room moderator rules
+ switchEveryoneMute.onchange = (e) => {
+ const startMuted = e.currentTarget.checked;
+ rc.updateRoomModerator({ type: 'audio', status: startMuted });
+ lsSettings.moderator_audio_muted = startMuted;
+ lS.setSettings(lsSettings);
+ e.target.blur();
+ };
+ switchEveryoneHidden.onchange = (e) => {
+ const startHidden = e.currentTarget.checked;
+ rc.updateRoomModerator({ type: 'video', status: startHidden });
+ lsSettings.moderator_video_hidden = startHidden;
+ lS.setSettings(lsSettings);
+ e.target.blur();
+ };
}
// ####################################################
@@ -1954,6 +1972,7 @@ function loadSettingsFromLocalStorage() {
sampleRateSelect.selectedIndex = lsSettings.mic_sample_rate;
sampleSizeSelect.selectedIndex = lsSettings.mic_sample_size;
channelCountSelect.selectedIndex = lsSettings.mic_channel_count;
+
micLatencyRange.value = lsSettings.mic_latency || 50;
micLatencyValue.innerText = lsSettings.mic_latency || 50;
micVolumeRange.value = lsSettings.mic_volume || 100;
@@ -2050,7 +2069,7 @@ function handleRoomClientEvents() {
show(stopVideoButton);
setColor(startVideoButton, 'red');
setVideoButtonsDisabled(false);
- if (isParticipantsListOpen) getRoomParticipants(true);
+ // if (isParticipantsListOpen) getRoomParticipants();
});
rc.on(RoomClient.EVENTS.pauseVideo, () => {
console.log('Room event: Client pause video');
@@ -2068,13 +2087,13 @@ function handleRoomClientEvents() {
show(startVideoButton);
setVideoButtonsDisabled(false);
isVideoPrivacyActive = false;
- if (isParticipantsListOpen) getRoomParticipants(true);
+ // if (isParticipantsListOpen) getRoomParticipants();
});
rc.on(RoomClient.EVENTS.startScreen, () => {
console.log('Room event: Client start screen');
hide(startScreenButton);
show(stopScreenButton);
- if (isParticipantsListOpen) getRoomParticipants(true);
+ // if (isParticipantsListOpen) getRoomParticipants();
});
rc.on(RoomClient.EVENTS.pauseScreen, () => {
console.log('Room event: Client pause screen');
@@ -2086,7 +2105,7 @@ function handleRoomClientEvents() {
console.log('Room event: Client stop screen');
hide(stopScreenButton);
show(startScreenButton);
- if (isParticipantsListOpen) getRoomParticipants(true);
+ // if (isParticipantsListOpen) getRoomParticipants();
});
rc.on(RoomClient.EVENTS.roomLock, () => {
console.log('Room event: Client lock room');
@@ -2867,7 +2886,7 @@ async function saveRoomPeers() {
saveObjToJsonFile(peersToSave, 'PARTICIPANTS');
}
-async function getRoomParticipants(refresh = false) {
+async function getRoomParticipants() {
const peers = await getRoomPeers();
const lists = await getParticipantsList(peers);
participantsCount = peers.size;
diff --git a/public/js/RoomClient.js b/public/js/RoomClient.js
index a38474a1..4fbf83d4 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.2.1
+ * @version 1.2.2
*
*/
@@ -158,6 +158,10 @@ class RoomClient {
this.peer_uuid = peer_uuid;
this.peer_info = peer_info;
+ // Moderator
+ this.start_muted = false;
+ this.start_hidden = false;
+
this.isAudioAllowed = isAudioAllowed;
this.isVideoAllowed = isVideoAllowed;
this.isScreenAllowed = isScreenAllowed;
@@ -363,6 +367,13 @@ class RoomClient {
? (console.log('07.1 ----> WARNING Room Host only recording enabled'),
this.event(_EVENTS.hostOnlyRecordingOn))
: this.event(_EVENTS.hostOnlyRecordingOff);
+
+ // Handle Room moderator rules
+ if (!isRulesActive || !isPresenter) {
+ console.log('07.2 ----> MODERATOR', room.moderator);
+ this.start_muted = room.moderator && room.moderator.start_audio_muted;
+ this.start_hidden = room.moderator && room.moderator.start_video_hidden;
+ }
}
adaptAspectRatio(participantsCount);
for (let peer of Array.from(peers.keys()).filter((id) => id !== this.peer_id)) {
@@ -586,7 +597,7 @@ class RoomClient {
this.lobbyRemoveMe(data.peer_id);
participantsCount = data.peer_counts;
adaptAspectRatio(participantsCount);
- if (isParticipantsListOpen) getRoomParticipants(true);
+ if (isParticipantsListOpen) getRoomParticipants();
}.bind(this),
);
@@ -807,21 +818,26 @@ class RoomClient {
async startLocalMedia() {
console.log('08 ----> Start local media');
- if (this.isAudioAllowed) {
+ if (this.isAudioAllowed && !this.start_muted) {
console.log('09 ----> Start audio media');
this.produce(mediaType.audio, microphoneSelect.value);
} else {
- setColor(startAudioButton, 'red');
console.log('09 ----> Audio is off');
+ setColor(startAudioButton, 'red');
+ this.setIsAudio(this.peer_id, false);
+ this.event(_EVENTS.stopAudio);
+ this.updatePeerInfo(this.peer_name, this.peer_id, 'audio', false);
}
- if (this.isVideoAllowed) {
+ if (this.isVideoAllowed && !this.start_hidden) {
console.log('10 ----> Start video media');
this.produce(mediaType.video, videoSelect.value);
} else {
- setColor(startVideoButton, 'red');
console.log('10 ----> Video is off');
+ setColor(startVideoButton, 'red');
this.setVideoOff(this.peer_info, false);
this.sendVideoOff();
+ this.event(_EVENTS.stopVideo);
+ this.updatePeerInfo(this.peer_name, this.peer_id, 'video', false);
}
if (this.joinRoomWithScreen) {
console.log('08 ----> Start Screen media');
@@ -1504,7 +1520,7 @@ class RoomClient {
this.myAudioEl = elem;
this.localAudioEl.appendChild(elem);
this.attachMediaStream(elem, stream, type, 'Producer');
- if (this.isAudioAllowed && !speakerSelect.disabled) {
+ if (this.isAudioAllowed && !this.start_muted && !speakerSelect.disabled) {
this.attachSinkId(elem, speakerSelect.value);
}
console.log('[addProducer] audio-element-count', this.localAudioEl.childElementCount);
@@ -1634,8 +1650,6 @@ class RoomClient {
async produceScreenAudio(stream) {
try {
- //this.stopMyAudio();
-
if (this.producerLabel.has(mediaType.audioTab)) {
return console.log('Producer already exists for this type ' + mediaType.audioTab);
}
@@ -1658,7 +1672,6 @@ class RoomClient {
producerSa.on('trackended', () => {
this.closeProducer(mediaType.audioTab);
- // this.startMyAudio();
});
producerSa.on('transportclose', () => {
@@ -1687,20 +1700,6 @@ class RoomClient {
}
}
- startMyAudio() {
- startAudioButton.click();
- this.setIsAudio(this.peer_id, true);
- this.event(_EVENTS.startAudio);
- setAudioButtonsDisabled(false);
- }
-
- stopMyAudio() {
- stopAudioButton.click();
- this.setIsAudio(this.peer_id, false);
- this.event(_EVENTS.stopAudio);
- setAudioButtonsDisabled(true);
- }
-
// ####################################################
// CONSUMER
// ####################################################
@@ -1717,7 +1716,7 @@ class RoomClient {
this.consumers.set(consumer.id, consumer);
if (kind === 'video') {
- if (isParticipantsListOpen) getRoomParticipants(true);
+ if (isParticipantsListOpen) getRoomParticipants();
}
this.handleConsumer(consumer.id, type, stream, peer_name, peer_info);
@@ -2056,7 +2055,7 @@ class RoomClient {
this.setVideoAvatarImgName(i.id, peer_name);
this.getId(i.id).style.display = 'block';
handleAspectRatio();
- if (isParticipantsListOpen) getRoomParticipants(true);
+ if (isParticipantsListOpen) getRoomParticipants();
if (!this.isMobileDevice && remotePeer) {
this.setTippy(sm.id, 'Send message', 'bottom');
this.setTippy(sf.id, 'Send file', 'bottom');
@@ -2927,7 +2926,7 @@ class RoomClient {
const chatRoom = this.getId('chatRoom');
chatRoom.classList.toggle('show');
if (!this.isChatOpen) {
- await getRoomParticipants(true);
+ await getRoomParticipants();
hide(chatMinButton);
if (!this.isMobileDevice) {
show(chatMaxButton);
@@ -5118,7 +5117,7 @@ class RoomClient {
}).then(() => {
switch (action) {
case 'refresh':
- getRoomParticipants(true);
+ getRoomParticipants();
break;
case 'eject':
this.exit();
@@ -5369,11 +5368,22 @@ class RoomClient {
elemDisplay('chatPrivateMessages', false);
}
+ // ####################################################
+ // UPDATE ROOM MODERATOR
+ // ####################################################
+
+ updateRoomModerator(data) {
+ if (!isRulesActive || isPresenter) {
+ this.socket.emit('updateRoomModerator', data);
+ }
+ }
+
// ####################################################
// UPDATE PEER INFO
// ####################################################
updatePeerInfo(peer_name, peer_id, type, status, emit = true) {
+ alert(type);
if (emit) {
switch (type) {
case 'audio':
@@ -5437,7 +5447,7 @@ class RoomClient {
break;
}
}
- if (isParticipantsListOpen) getRoomParticipants(true);
+ if (isParticipantsListOpen) getRoomParticipants();
}
checkPeerInfoStatus(peer_info) {
diff --git a/public/js/Rules.js b/public/js/Rules.js
index 4d349660..597d3812 100644
--- a/public/js/Rules.js
+++ b/public/js/Rules.js
@@ -30,6 +30,7 @@ const BUTTONS = {
unlockRoomButton: true, // presenter
lobbyButton: true, // presenter
micOptionsButton: true, // presenter
+ tabModerator: true, // presenter
tabRecording: true,
pushToTalk: true,
host_only_recording: true, // presenter
@@ -87,6 +88,7 @@ function handleRules(isPresenter) {
BUTTONS.settings.unlockRoomButton = false;
BUTTONS.settings.lobbyButton = false;
BUTTONS.settings.micOptionsButton = false;
+ BUTTONS.settings.tabModerator = false;
BUTTONS.videoOff.muteAudioButton = false;
BUTTONS.videoOff.ejectButton = false;
BUTTONS.consumerVideo.ejectButton = false;
@@ -100,6 +102,7 @@ function handleRules(isPresenter) {
BUTTONS.settings.unlockRoomButton = isRoomLocked;
BUTTONS.settings.lobbyButton = true;
BUTTONS.settings.micOptionsButton = true;
+ BUTTONS.settings.tabModerator = true;
BUTTONS.videoOff.muteAudioButton = true;
BUTTONS.videoOff.ejectButton = true;
BUTTONS.consumerVideo.ejectButton = true;
@@ -120,13 +123,18 @@ function handleRules(isPresenter) {
hostOnlyRecording = lsSettings.host_only_recording;
switchHostOnlyRecording.checked = hostOnlyRecording;
rc.roomAction(hostOnlyRecording ? 'hostOnlyRecordingOn' : 'hostOnlyRecordingOff', true, false);
- //...
+ // Room moderator
+ switchEveryoneMute.checked = lsSettings.moderator_audio_muted;
+ switchEveryoneHidden.checked = lsSettings.moderator_video_hidden;
+ rc.updateRoomModerator({ type: 'audio', status: switchEveryoneMute.checked });
+ rc.updateRoomModerator({ type: 'video', status: switchEveryoneHidden.checked });
}
// main. settings...
BUTTONS.settings.lockRoomButton ? show(lockRoomButton) : hide(lockRoomButton);
BUTTONS.settings.unlockRoomButton ? show(unlockRoomButton) : hide(unlockRoomButton);
BUTTONS.settings.lobbyButton ? show(lobbyButton) : hide(lobbyButton);
!BUTTONS.settings.micOptionsButton && hide(micOptionsButton);
+ !BUTTONS.settings.tabModerator && hide(tabModeratorBtn);
BUTTONS.participantsList.saveInfoButton ? show(participantsSaveBtn) : hide(participantsSaveBtn);
BUTTONS.whiteboard.whiteboardLockButton
? elemDisplay('whiteboardLockButton', true)
diff --git a/public/js/landing.js b/public/js/landing.js
index 52666d71..72bf1727 100644
--- a/public/js/landing.js
+++ b/public/js/landing.js
@@ -182,14 +182,14 @@
i
? (i.classList.add('is-active'), l.classList.add('is-active'))
: 'next' === t
- ? (e.firstElementChild.classList.add('is-active'),
- e.parentNode
- .getElementsByClassName('carousel-bullets')[0]
- .firstElementChild.classList.add('is-active'))
- : (e.lastElementChild.classList.add('is-active'),
- e.parentNode
- .getElementsByClassName('carousel-bullets')[0]
- .lastElementChild.classList.add('is-active'));
+ ? (e.firstElementChild.classList.add('is-active'),
+ e.parentNode
+ .getElementsByClassName('carousel-bullets')[0]
+ .firstElementChild.classList.add('is-active'))
+ : (e.lastElementChild.classList.add('is-active'),
+ e.parentNode
+ .getElementsByClassName('carousel-bullets')[0]
+ .lastElementChild.classList.add('is-active'));
}
function i(e, t) {
diff --git a/public/sfu/MediasoupClient.js b/public/sfu/MediasoupClient.js
index 9e32f2bd..5b85277b 100644
--- a/public/sfu/MediasoupClient.js
+++ b/public/sfu/MediasoupClient.js
@@ -14084,10 +14084,10 @@
typeof global !== 'undefined'
? global
: typeof self !== 'undefined'
- ? self
- : typeof window !== 'undefined'
- ? window
- : {},
+ ? self
+ : typeof window !== 'undefined'
+ ? window
+ : {},
);
},
{},
@@ -14649,8 +14649,8 @@
var keyLocation = obj.push
? {} // blank object that will be pushed
: needsBlank
- ? location[obj.name]
- : location; // otherwise, named location or root
+ ? location[obj.name]
+ : location; // otherwise, named location or root
attachProperties(content.match(obj.reg), keyLocation, obj.names, obj.name);
diff --git a/public/views/Room.html b/public/views/Room.html
index ab7d3b65..630d5216 100644
--- a/public/views/Room.html
+++ b/public/views/Room.html
@@ -185,6 +185,9 @@ access to use this app.
+
@@ -588,6 +591,42 @@ access to use this app.
+
|
+
+
+
+ Everyone starts muted + |
+
+
+
+
+ |
+
|
+
+
+
+ Everyone starts hidden + |
+
+
+
+
+ |
+