diff --git a/app/src/Room.js b/app/src/Room.js
index 4481bf8f..d8148f6d 100644
--- a/app/src/Room.js
+++ b/app/src/Room.js
@@ -12,6 +12,9 @@ module.exports = class Room {
this.audioLevelObserver = null;
this.audioLevelObserverEnabled = true;
this.audioLastUpdateTime = 0;
+ // ##########################
+ this._isBroadcasting = false;
+ // ##########################
this._isLocked = false;
this._isLobbyEnabled = false;
this._roomPassword = null;
@@ -109,6 +112,11 @@ module.exports = class Room {
// ROOM MODERATOR
// ####################################################
+ updateRoomModeratorALL(data) {
+ this._moderator = data;
+ log.debug('Update room moderator all data', this._moderator);
+ }
+
updateRoomModerator(data) {
log.debug('Update room moderator', data);
switch (data.type) {
@@ -137,6 +145,7 @@ module.exports = class Room {
toJson() {
return {
id: this.id,
+ broadcasting: this._isBroadcasting,
config: {
isLocked: this._isLocked,
isLobbyEnabled: this._isLobbyEnabled,
@@ -312,6 +321,10 @@ module.exports = class Room {
// ROOM STATUS
// ####################################################
+ // GET
+ isBroadcasting() {
+ return this._isBroadcasting;
+ }
getPassword() {
return this._roomPassword;
}
@@ -324,6 +337,11 @@ module.exports = class Room {
isHostOnlyRecording() {
return this._hostOnlyRecording;
}
+
+ // SET
+ setIsBroadcasting(status) {
+ this._isBroadcasting = status;
+ }
setLocked(status, password) {
this._isLocked = status;
this._roomPassword = password;
diff --git a/app/src/Server.js b/app/src/Server.js
index 7b17a790..3ef3906e 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.8
+ * @version 1.2.9
*
*/
@@ -630,7 +630,7 @@ function startServer() {
const room = roomList.get(socket.room_id);
- let peerCounts = room.getPeersCount();
+ const peerCounts = room.getPeersCount();
log.debug('Peer counts', { peerCounts: peerCounts });
@@ -668,7 +668,13 @@ function startServer() {
const room = roomList.get(socket.room_id);
log.debug('Room action:', data);
+
switch (data.action) {
+ case 'broadcasting':
+ if (!isPresenter) return;
+ room.setIsBroadcasting(data.room_broadcasting);
+ room.broadCast(socket.id, 'roomAction', data.action);
+ break;
case 'lock':
if (!isPresenter) return;
if (!room.isLocked()) {
@@ -716,6 +722,7 @@ function startServer() {
break;
}
log.debug('Room status', {
+ broadcasting: room.isBroadcasting(),
locked: room.isLocked(),
lobby: room.isLobbyEnabled(),
hostOnlyRecording: room.isHostOnlyRecording(),
@@ -780,31 +787,58 @@ function startServer() {
const room = roomList.get(socket.room_id);
- // update my peer_info status to all in the room
room.getPeers().get(socket.id).updatePeerInfo(data);
- room.broadCast(socket.id, 'updatePeerInfo', data);
+
+ if (data.broadcast) {
+ log.info('updatePeerInfo broadcast data');
+ room.broadCast(socket.id, 'updatePeerInfo', data);
+ }
});
- socket.on('updateRoomModerator', (dataObject) => {
+ socket.on('updateRoomModerator', async (dataObject) => {
if (!roomList.has(socket.room_id)) return;
const data = checkXSS(dataObject);
const room = roomList.get(socket.room_id);
- room.updateRoomModerator(data);
+ const isPresenter = await isPeerPresenter(socket.room_id, socket.id, data.peer_name, data.peer_uuid);
- switch (data.type) {
+ if (!isPresenter) return;
+
+ const moderator = data.moderator;
+
+ room.updateRoomModerator(moderator);
+
+ switch (moderator.type) {
case 'audio_cant_unmute':
case 'video_cant_unhide':
case 'screen_cant_share':
- room.broadCast(socket.id, 'updateRoomModerator', data);
+ room.broadCast(socket.id, 'updateRoomModerator', moderator);
break;
default:
break;
}
});
+ socket.on('updateRoomModeratorALL', async (dataObject) => {
+ if (!roomList.has(socket.room_id)) return;
+
+ const data = checkXSS(dataObject);
+
+ const room = roomList.get(socket.room_id);
+
+ const isPresenter = await isPeerPresenter(socket.room_id, socket.id, data.peer_name, data.peer_uuid);
+
+ if (!isPresenter) return;
+
+ const moderator = data.moderator;
+
+ room.updateRoomModeratorALL(moderator);
+
+ room.broadCast(socket.id, 'updateRoomModeratorALL', moderator);
+ });
+
socket.on('fileInfo', (dataObject) => {
if (!roomList.has(socket.room_id)) return;
@@ -886,9 +920,10 @@ function startServer() {
const data = checkXSS(dataObject);
+ log.debug('Video off data', data.peer_name);
+
const room = roomList.get(socket.room_id);
- log.debug('Video off', getPeerName(room));
room.broadCast(socket.id, 'setVideoOff', data);
});
@@ -930,7 +965,7 @@ function startServer() {
const isPeerValid = isAuthPeer(peer_username, peer_password);
- log.debug('[' + socket.id + '] JOIN ROOM - HOST PROTECTED - USER AUTH check peer', {
+ log.debug('[Join] - HOST PROTECTED - USER AUTH check peer', {
ip: peer_ip,
peer_username: peer_username,
peer_password: peer_password,
@@ -953,8 +988,10 @@ function startServer() {
if (!(socket.room_id in presenters)) presenters[socket.room_id] = {};
- const peer_name = room.getPeers()?.get(socket.id)?.peer_info?.peer_name;
- const peer_uuid = room.getPeers()?.get(socket.id)?.peer_info?.peer_uuid;
+ const peer = room.getPeers()?.get(socket.id)?.peer_info;
+ const peer_id = peer.peer_id;
+ const peer_name = peer.peer_name;
+ const peer_uuid = peer.peer_uuid;
// Set the presenters
const presenter = {
@@ -995,8 +1032,8 @@ function startServer() {
'The user is currently waiting to join the room because the lobby is enabled, and they are not a presenter',
);
room.broadCast(socket.id, 'roomLobby', {
- peer_id: data.peer_info.peer_id,
- peer_name: data.peer_info.peer_name,
+ peer_id: peer_id,
+ peer_name: peer_name,
lobby_status: 'waiting',
});
return cb('isLobby');
@@ -1235,8 +1272,10 @@ function startServer() {
const room = roomList.get(socket.room_id);
- const peerName = room.getPeers()?.get(socket.id)?.peer_info?.peer_name || '';
- const peerUuid = room.getPeers()?.get(socket.id)?.peer_info?.peer_uuid || '';
+ const peer = room.getPeers()?.get(socket.id)?.peer_info;
+
+ const peerName = peer.peer_name || '';
+ const peerUuid = peer.peer_uuid || '';
const isPresenter = await isPeerPresenter(socket.room_id, socket.id, peerName, peerUuid);
@@ -1245,12 +1284,7 @@ function startServer() {
room.removePeer(socket.id);
if (room.getPeers().size === 0) {
- if (room.isLocked()) {
- room.setLocked(false);
- }
- if (room.isLobbyEnabled()) {
- room.setLobbyEnabled(false);
- }
+ //
roomList.delete(socket.room_id);
const activeRooms = getActiveRooms();
@@ -1275,8 +1309,11 @@ function startServer() {
const room = roomList.get(socket.room_id);
- const peerName = room.getPeers()?.get(socket.id)?.peer_info?.peer_name || '';
- const peerUuid = room.getPeers()?.get(socket.id)?.peer_info?.peer_uuid || '';
+ const peer = room.getPeers()?.get(socket.id)?.peer_info;
+
+ const peerName = peer.peer_name || '';
+ const peerUuid = peer.peer_uuid || '';
+
const isPresenter = await isPeerPresenter(socket.room_id, socket.id, peerName, peerUuid);
log.debug('Exit room', peerName);
@@ -1287,6 +1324,7 @@ function startServer() {
room.broadCast(socket.id, 'removeMe', removeMeData(room, peerName, isPresenter));
if (room.getPeers().size === 0) {
+ //
roomList.delete(socket.room_id);
delete presenters[socket.room_id];
diff --git a/app/src/config.template.js b/app/src/config.template.js
index a368f6ee..d68cee2e 100644
--- a/app/src/config.template.js
+++ b/app/src/config.template.js
@@ -62,7 +62,7 @@ module.exports = {
Additional layers can be added to specify valid presenters and co-presenters by setting designated usernames.
*/
'Miroslav Pejic',
- 'MiroTalk Admin',
+ 'MiroTalk SFU',
],
console: {
debug: true,
diff --git a/package.json b/package.json
index dd471532..22d8560f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "mirotalksfu",
- "version": "1.2.8",
+ "version": "1.2.9",
"description": "WebRTC SFU browser-based video calls",
"main": "Server.js",
"scripts": {
@@ -34,8 +34,8 @@
"author": "Miroslav Pejic",
"license": "AGPL-3.0",
"dependencies": {
- "@sentry/integrations": "7.81.1",
- "@sentry/node": "7.81.1",
+ "@sentry/integrations": "7.83.0",
+ "@sentry/node": "7.83.0",
"axios": "^1.6.2",
"body-parser": "1.20.2",
"colors": "1.4.0",
@@ -44,10 +44,10 @@
"crypto-js": "4.2.0",
"express": "4.18.2",
"httpolyglot": "0.1.2",
- "mediasoup": "3.13.5",
+ "mediasoup": "3.13.6",
"mediasoup-client": "3.7.0",
"ngrok": "^4.3.3",
- "openai": "^4.20.0",
+ "openai": "^4.20.1",
"qs": "6.11.2",
"socket.io": "4.7.2",
"swagger-ui-express": "5.0.0",
diff --git a/public/css/GroupChat.css b/public/css/GroupChat.css
index 7358482a..1a966f22 100644
--- a/public/css/GroupChat.css
+++ b/public/css/GroupChat.css
@@ -252,7 +252,7 @@
.chat .chat-history .other-message {
background: var(--left-msg-bg);
- text-align: right;
+ text-align: left;
}
.chat .chat-history .other-message:after {
diff --git a/public/js/LocalStorage.js b/public/js/LocalStorage.js
index fa735811..ec027f6d 100644
--- a/public/js/LocalStorage.js
+++ b/public/js/LocalStorage.js
@@ -35,6 +35,7 @@ class LocalStorage {
mic_volume: 100, // %
video_fps: 0, // default 1280x768 30fps
screen_fps: 0, // max 30fps
+ broadcasting: false, // default false (one to many a/v streaming)
lobby: false, // default false
pitch_bar: true, // volume indicator
sounds: true, // room notify sounds
diff --git a/public/js/Room.js b/public/js/Room.js
index fb00797d..5e241885 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.8
+ * @version 1.2.9
*
*/
@@ -34,6 +34,8 @@ let redirect = {
};
const _PEER = {
+ presenter: '',
+ guest: '',
audioOn: '',
audioOff: '',
videoOn: '',
@@ -99,6 +101,7 @@ let isPushToTalkActive = false;
let isSpaceDown = false;
let isPitchBarEnabled = true;
let isSoundEnabled = true;
+let isBroadcastingEnabled = false;
let isLobbyEnabled = false;
let isLobbyOpen = false;
let hostOnlyRecording = false;
@@ -164,6 +167,11 @@ function initClient() {
);
setTippy('lobbyAcceptAllBtn', 'Accept', 'top');
setTippy('lobbyRejectAllBtn', 'Reject', 'top');
+ setTippy(
+ 'switchBroadcasting',
+ 'Broadcasting is the dissemination of audio or video content to a large audience (one to many)',
+ 'right',
+ );
setTippy(
'switchLobby',
'Lobby mode lets you protect your meeting by only allowing people to enter after a formal approval by a moderator',
@@ -252,7 +260,7 @@ function refreshMainButtonsToolTipPlacement() {
setTippy('stopRecButton', 'Stop recording', placement);
setTippy('raiseHandButton', 'Raise your hand', placement);
setTippy('lowerHandButton', 'Lower your hand', placement);
- setTippy('roomEmojiPicker', 'Toggle emoji reaction', placement);
+ setTippy('emojiRoomButton', 'Toggle emoji reaction', placement);
setTippy('swapCameraButton', 'Swap the camera', placement);
setTippy('chatButton', 'Toggle the chat', placement);
setTippy('transcriptionButton', 'Toggle transcription', placement);
@@ -345,7 +353,7 @@ async function initEnumerateVideoDevices() {
}
function enumerateVideoDevices(stream) {
- console.log('03 ----> Get Video Devices');
+ console.log('02 ----> Get Video Devices');
navigator.mediaDevices
.enumerateDevices()
.then((devices) =>
@@ -383,7 +391,7 @@ async function initEnumerateAudioDevices() {
}
function enumerateAudioDevices(stream) {
- console.log('02 ----> Get Audio Devices');
+ console.log('03 ----> Get Audio Devices');
navigator.mediaDevices
.enumerateDevices()
.then((devices) =>
@@ -673,7 +681,7 @@ function getPeerInfo() {
// ####################################################
function whoAreYou() {
- console.log('04 ----> Who are you');
+ console.log('04 ----> Who are you?');
hide(loadingDiv);
document.body.style.background = 'var(--body-bg)';
@@ -1001,7 +1009,7 @@ function roomIsReady() {
}
BUTTONS.main.chatButton && show(chatButton);
BUTTONS.main.raiseHandButton && show(raiseHandButton);
- BUTTONS.main.emojiRoomButton && show(roomEmojiPicker);
+ BUTTONS.main.emojiRoomButton && show(emojiRoomButton);
!BUTTONS.chat.chatSaveButton && hide(chatSaveButton);
BUTTONS.chat.chatEmojiButton && show(chatEmojiButton);
BUTTONS.chat.chatMarkdownButton && show(chatMarkdownButton);
@@ -1058,6 +1066,7 @@ function roomIsReady() {
isVideoAllowed ? show(stopVideoButton) : BUTTONS.main.startVideoButton && show(startVideoButton);
show(fileShareButton);
BUTTONS.settings.lockRoomButton && show(lockRoomButton);
+ BUTTONS.settings.broadcastingButton && show(broadcastingButton);
BUTTONS.settings.lobbyButton && show(lobbyButton);
BUTTONS.settings.host_only_recording && show(roomRecording);
BUTTONS.main.aboutButton && show(aboutButton);
@@ -1513,7 +1522,7 @@ function handleSelectsInit() {
function setSelectsInit() {
const localStorageDevices = lS.getLocalStorageDevices();
- console.log('04 ----> Get Local Storage Devices before', localStorageDevices);
+ console.log('04.0 ----> Get Local Storage Devices before', localStorageDevices);
if (localStorageDevices) {
initMicrophoneSelect.selectedIndex = localStorageDevices.audio.index;
initSpeakerSelect.selectedIndex = localStorageDevices.speaker.index;
@@ -1703,6 +1712,13 @@ function handleSelects() {
}
});
// room
+ switchBroadcasting.onchange = (e) => {
+ isBroadcastingEnabled = e.currentTarget.checked;
+ rc.roomAction('broadcasting');
+ lsSettings.broadcasting = isBroadcastingEnabled;
+ lS.setSettings(lsSettings);
+ e.target.blur();
+ };
switchLobby.onchange = (e) => {
isLobbyEnabled = e.currentTarget.checked;
rc.roomAction(isLobbyEnabled ? 'lobbyOn' : 'lobbyOff');
@@ -1951,10 +1967,16 @@ function handleInputs() {
':N': '🥶',
':J': '🥴',
};
- for (let i in chatInputEmoji) {
- let regex = new RegExp(i.replace(/([()[{*+.$^\\|?])/g, '\\$1'), 'gim');
- this.value = this.value.replace(regex, chatInputEmoji[i]);
- }
+ // Create a regular expression pattern for all keys in chatInputEmoji
+ const regexPattern = new RegExp(
+ Object.keys(chatInputEmoji)
+ .map((key) => key.replace(/([()[{*+.$^\\|?])/g, '\\$1'))
+ .join('|'),
+ 'gim',
+ );
+ // Replace matching patterns with corresponding emojis
+ this.value = this.value.replace(regexPattern, (match) => chatInputEmoji[match]);
+
rc.checkLineBreaks();
};
@@ -1992,7 +2014,7 @@ function handleRoomEmojiPicker() {
emojiPickerContainer.appendChild(emojiRoomPicker);
emojiPickerContainer.style.display = 'none';
- roomEmojiPicker.onclick = () => {
+ emojiRoomButton.onclick = () => {
toggleEmojiPicker();
};
closeEmojiPickerContainer.onclick = () => {
@@ -2016,10 +2038,10 @@ function handleRoomEmojiPicker() {
function toggleEmojiPicker() {
if (emojiPickerContainer.style.display === 'block') {
emojiPickerContainer.style.display = 'none';
- setColor(roomEmojiPicker, 'white');
+ setColor(emojiRoomButton, 'white');
} else {
emojiPickerContainer.style.display = 'block';
- setColor(roomEmojiPicker, 'yellow');
+ setColor(emojiRoomButton, 'yellow');
}
}
}
@@ -2968,6 +2990,7 @@ async function getRoomParticipants() {
participantsList.innerHTML = lists;
refreshParticipantsCount(participantsCount, false);
setParticipantsTippy(peers);
+ console.log('*** Refresh Chat participant lists ***');
}
async function getParticipantsList(peers) {
@@ -3045,6 +3068,7 @@ async function getParticipantsList(peers) {
for (const peer of Array.from(peers.keys())) {
const peer_info = peers.get(peer).peer_info;
const peer_name = peer_info.peer_name;
+ //const peer_presenter = peer_info.peer_presenter ? _PEER.presenter : _PEER.guest;
const peer_audio = peer_info.peer_audio ? _PEER.audioOn : _PEER.audioOff;
const peer_video = peer_info.peer_video ? _PEER.videoOn : _PEER.videoOff;
const peer_screen = peer_info.peer_screen ? _PEER.screenOn : _PEER.screenOff;
@@ -3100,6 +3124,9 @@ async function getParticipantsList(peers) {
`;
+ // li += `
+ // `;
+
if (peer_info.peer_hand) {
li += `
`;
@@ -3127,7 +3154,11 @@ async function getParticipantsList(peers) {
${peer_name}
online
+ `;
+ // NO ROOM BROADCASTING
+ if (!isBroadcastingEnabled) {
+ li += `
+ `;
+ }
+ li += `
@@ -3151,10 +3185,15 @@ async function getParticipantsList(peers) {
${peer_video}
${peer_screen}
`;
+
+ // li += `
+ // ${peer_presenter}`;
+
if (peer_info.peer_hand) {
li += `
${peer_hand}`;
}
+
li += `
@@ -3175,9 +3214,14 @@ function setParticipantsTippy(peers) {
for (let peer of Array.from(peers.keys())) {
const peer_info = peers.get(peer).peer_info;
const peer_id = peer_info.peer_id;
- setTippy(peer_id + '___pAudio', 'Mute', 'top');
- setTippy(peer_id + '___pVideo', 'Hide', 'top');
- setTippy(peer_id + '___pScreen', 'Stop', 'top');
+
+ const peerAudioBtn = rc.getId(peer_id + '___pAudio');
+ const peerVideoBtn = rc.getId(peer_id + '___pVideo');
+ const peerScreenBtn = rc.getId(peer_id + '___pScreen');
+
+ if (peerAudioBtn) setTippy(peerAudioBtn.id, 'Mute', 'top');
+ if (peerVideoBtn) setTippy(peerVideoBtn.id, 'Hide', 'top');
+ if (peerScreenBtn) setTippy(peerScreenBtn.id, 'Stop', 'top');
}
}
}
diff --git a/public/js/RoomClient.js b/public/js/RoomClient.js
index 800f1c81..ca706996 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.8
+ * @version 1.2.9
*
*/
@@ -42,6 +42,7 @@ const html = {
};
const icons = {
+ room: '',
chat: '',
user: '',
transcript: '',
@@ -358,7 +359,11 @@ class RoomClient {
this.device = await this.loadDevice(data);
console.log('07.3 ----> Get Router Rtp Capabilities codecs: ', this.device.rtpCapabilities.codecs);
await this.initTransports(this.device);
- await this.startLocalMedia();
+ if (isBroadcastingEnabled) {
+ isPresenter ? await this.startLocalMedia() : await this.handleRoomBroadcasting();
+ } else {
+ await this.startLocalMedia();
+ }
this.socket.emit('getProducers');
}
@@ -369,13 +374,19 @@ class RoomClient {
redirect = room.redirect;
let peers = new Map(JSON.parse(room.peers));
participantsCount = peers.size;
+ // ME
for (let peer of Array.from(peers.keys()).filter((id) => id == this.peer_id)) {
let my_peer_info = peers.get(peer).peer_info;
console.log('07.1 ----> My Peer info', my_peer_info);
isPresenter = window.localStorage.isReconnected === 'true' ? isPresenter : my_peer_info.peer_presenter;
+ this.peer_info.peer_presenter = isPresenter;
this.getId('isUserPresenter').innerText = isPresenter;
window.localStorage.isReconnected = false;
handleRules(isPresenter);
+ // ###################################################################################################
+ isBroadcastingEnabled = isPresenter && !room.broadcasting ? isBroadcastingEnabled : room.broadcasting;
+ console.log('07.1 ----> ROOM BROADCASTING', isBroadcastingEnabled);
+ // ###################################################################################################
room.config.hostOnlyRecording
? (console.log('07.1 ----> WARNING Room Host only recording enabled'),
this.event(_EVENTS.hostOnlyRecordingOn))
@@ -404,13 +415,17 @@ class RoomClient {
this._moderator.video_cant_unhide ? hide(tabVideoDevicesBtn) : show(tabVideoDevicesBtn);
}
}
- adaptAspectRatio(participantsCount);
+ // PARTICIPANTS
for (let peer of Array.from(peers.keys()).filter((id) => id !== this.peer_id)) {
let peer_info = peers.get(peer).peer_info;
// console.log('07.1 ----> Remote Peer info', peer_info);
- if (!peer_info.peer_video) {
+ const canSetVideoOff = !isBroadcastingEnabled || (isBroadcastingEnabled && peer_info.peer_presenter);
+
+ if (!peer_info.peer_video && canSetVideoOff) {
+ console.log('Detected peer video off ' + peer_info.peer_name);
await this.setVideoOff(peer_info, true);
}
+
if (peer_info.peer_recording) {
this.handleRecordingAction({
peer_id: peer_info.id,
@@ -419,8 +434,11 @@ class RoomClient {
});
}
}
+
this.refreshParticipantsCount();
+
console.log('07.2 Participants Count ---->', participantsCount);
+
// notify && participantsCount == 1 ? shareRoom() : sound('joined');
if (notify && participantsCount == 1) {
shareRoom();
@@ -605,7 +623,7 @@ class RoomClient {
this.socket.on(
'consumerClosed',
function ({ consumer_id, consumer_kind }) {
- console.log('Closing consumer', { consumer_id: consumer_id, consumer_kind: consumer_kind });
+ console.log('SocketOn Closing consumer', { consumer_id: consumer_id, consumer_kind: consumer_kind });
this.removeConsumer(consumer_id, consumer_kind);
}.bind(this),
);
@@ -613,19 +631,24 @@ class RoomClient {
this.socket.on(
'setVideoOff',
function (data) {
- console.log('Video off:', data);
- this.setVideoOff(data, true);
+ if (!isBroadcastingEnabled || (isBroadcastingEnabled && data.peer_presenter)) {
+ console.log('SocketOn setVideoOff', {
+ peer_name: data.peer_name,
+ peer_presenter: data.peer_presenter,
+ });
+ this.setVideoOff(data, true);
+ }
}.bind(this),
);
this.socket.on(
'removeMe',
function (data) {
- console.log('Remove me:', data);
+ console.log('SocketOn Remove me:', data);
this.removeVideoOff(data.peer_id);
this.lobbyRemoveMe(data.peer_id);
participantsCount = data.peer_counts;
- adaptAspectRatio(participantsCount);
+ if (!isBroadcastingEnabled) adaptAspectRatio(participantsCount);
if (isParticipantsListOpen) getRoomParticipants();
}.bind(this),
);
@@ -633,9 +656,14 @@ class RoomClient {
this.socket.on(
'refreshParticipantsCount',
function (data) {
- console.log('Participants Count:', data);
+ console.log('SocketOn Participants Count:', data);
participantsCount = data.peer_counts;
- adaptAspectRatio(participantsCount);
+ if (isBroadcastingEnabled) {
+ if (isParticipantsListOpen) getRoomParticipants();
+ wbUpdate();
+ } else {
+ adaptAspectRatio(participantsCount);
+ }
}.bind(this),
);
@@ -643,7 +671,7 @@ class RoomClient {
'newProducers',
async function (data) {
if (data.length > 0) {
- console.log('New producers', data);
+ console.log('SocketOn New producers', data);
for (let { producer_id, peer_name, peer_info, type } of data) {
await this.consume(producer_id, peer_name, peer_info, type);
}
@@ -654,7 +682,7 @@ class RoomClient {
this.socket.on(
'message',
function (data) {
- console.log('New message:', data);
+ console.log('SocketOn New message:', data);
this.showMessage(data);
}.bind(this),
);
@@ -662,7 +690,7 @@ class RoomClient {
this.socket.on(
'roomAction',
function (data) {
- console.log('Room action:', data);
+ console.log('SocketOn Room action:', data);
this.roomAction(data, false);
}.bind(this),
);
@@ -670,7 +698,7 @@ class RoomClient {
this.socket.on(
'roomPassword',
function (data) {
- console.log('Room password:', data.password);
+ console.log('SocketOn Room password:', data.password);
this.roomPassword(data);
}.bind(this),
);
@@ -678,7 +706,7 @@ class RoomClient {
this.socket.on(
'roomLobby',
function (data) {
- console.log('Room lobby:', data);
+ console.log('SocketOn Room lobby:', data);
this.roomLobby(data);
}.bind(this),
);
@@ -686,7 +714,7 @@ class RoomClient {
this.socket.on(
'cmd',
function (data) {
- console.log('Peer cmd:', data);
+ console.log('SocketOn Peer cmd:', data);
this.handleCmd(data);
}.bind(this),
);
@@ -694,7 +722,7 @@ class RoomClient {
this.socket.on(
'peerAction',
function (data) {
- console.log('Peer action:', data);
+ console.log('SocketOn Peer action:', data);
this.peerAction(data.from_peer_name, data.peer_id, data.action, false, data.broadcast);
}.bind(this),
);
@@ -702,15 +730,15 @@ class RoomClient {
this.socket.on(
'updatePeerInfo',
function (data) {
- console.log('Peer info update:', data);
- this.updatePeerInfo(data.peer_name, data.peer_id, data.type, data.status, false);
+ console.log('SocketOn Peer info update:', data);
+ this.updatePeerInfo(data.peer_name, data.peer_id, data.type, data.status, false, data.peer_presenter);
}.bind(this),
);
this.socket.on(
'fileInfo',
function (data) {
- console.log('File info:', data);
+ console.log('SocketOn File info:', data);
this.handleFileInfo(data);
}.bind(this),
);
@@ -739,7 +767,7 @@ class RoomClient {
this.socket.on(
'wbCanvasToJson',
function (data) {
- console.log('Received whiteboard canvas JSON');
+ console.log('SocketOn Received whiteboard canvas JSON');
JsonToWbCanvas(data);
}.bind(this),
);
@@ -762,15 +790,23 @@ class RoomClient {
this.socket.on(
'updateRoomModerator',
function (data) {
- console.log('Update room moderator', data);
+ console.log('SocketOn Update room moderator', data);
this.handleUpdateRoomModerator(data);
}.bind(this),
);
+ this.socket.on(
+ 'updateRoomModeratorALL',
+ function (data) {
+ console.log('SocketOn Update room moderator ALL', data);
+ this.handleUpdateRoomModeratorALL(data);
+ }.bind(this),
+ );
+
this.socket.on(
'recordingAction',
function (data) {
- console.log('Recording action:', data);
+ console.log('SocketOn Recording action:', data);
this.handleRecordingAction(data);
}.bind(this),
);
@@ -778,7 +814,7 @@ class RoomClient {
this.socket.on(
'connect',
function () {
- console.log('Connected to signaling server!');
+ console.log('SocketOn Connected to signaling server!');
this._isConnected = true;
// location.reload();
getPeerName() ? location.reload() : openURL(this.getReconnectDirectJoinURL());
@@ -849,6 +885,34 @@ class RoomClient {
});
}
+ // ####################################################
+ // HANDLE ROOM BROADCASTING
+ // ####################################################
+
+ async handleRoomBroadcasting() {
+ console.log('07.4 ----> Room Broadcasting is currently active, and you are not the designated presenter');
+
+ this.peer_info.peer_audio = false;
+ this.peer_info.peer_video = false;
+ this.peer_info.peer_screen = false;
+
+ const mediaTypes = ['audio', 'video', 'screen'];
+
+ mediaTypes.forEach((type) => {
+ const data = {
+ peer_name: this.peer_name,
+ peer_id: this.peer_id,
+ peer_presenter: isPresenter,
+ type: type,
+ status: false,
+ broadcast: true,
+ };
+ this.socket.emit('updatePeerInfo', data);
+ });
+
+ handleRulesBroadcasting();
+ }
+
// ####################################################
// START LOCAL AUDIO VIDEO MEDIA
// ####################################################
@@ -1077,7 +1141,7 @@ class RoomClient {
this.event(_EVENTS.startScreen);
break;
default:
- return;
+ break;
}
this.sound('joined');
} catch (err) {
@@ -1682,7 +1746,7 @@ class RoomClient {
this.event(_EVENTS.stopScreen);
break;
default:
- return;
+ break;
}
this.sound('left');
@@ -1749,7 +1813,7 @@ class RoomClient {
wbUpdate();
this.getConsumeStream(producer_id, peer_info.peer_id, type).then(
- function ({ consumer, stream, kind }) {
+ async function ({ consumer, stream, kind }) {
console.log('CONSUMER MEDIA TYPE ----> ' + type);
console.log('CONSUMER', consumer);
@@ -1814,12 +1878,12 @@ class RoomClient {
let remotePeerId = peer_info.peer_id;
let remoteIsScreen = type == mediaType.screen;
+ let remotePeerAudio = peer_info.peer_audio;
let remotePrivacyOn = peer_info.peer_video_privacy;
switch (type) {
case mediaType.video:
case mediaType.screen:
- let remotePeerAudio = peer_info.peer_audio;
this.removeVideoOff(remotePeerId);
d = document.createElement('div');
d.className = 'Camera';
@@ -1939,6 +2003,7 @@ class RoomClient {
this.setTippy(pv.id, '🔊 Volume', 'bottom');
this.setTippy(ko.id, 'Eject', 'bottom');
}
+ this.setPeerAudio(remotePeerId, remotePeerAudio);
break;
case mediaType.audio:
elem = document.createElement('audio');
@@ -1952,6 +2017,7 @@ class RoomClient {
let inputPv = this.getId(audioConsumerId);
if (inputPv) {
this.handlePV(id + '___' + audioConsumerId);
+ this.setPeerAudio(remotePeerId, remotePeerAudio);
}
console.log('[Add audioConsumers]', this.audioConsumers);
break;
@@ -2016,6 +2082,7 @@ class RoomClient {
// ####################################################
async setVideoOff(peer_info, remotePeer = false) {
+ //console.log('setVideoOff', peer_info);
let d, vb, i, h, au, sf, sm, sv, ko, p, pm, pb, pv;
let peer_id = peer_info.peer_id;
let peer_name = peer_info.peer_name;
@@ -2104,6 +2171,8 @@ class RoomClient {
this.setTippy(pv.id, '🔊 Volume', 'bottom');
this.setTippy(ko.id, 'Eject', 'bottom');
}
+ remotePeer ? this.setPeerAudio(peer_id, peer_audio) : this.setIsAudio(peer_id, peer_audio);
+
console.log('[setVideoOff] Video-element-count', this.videoMediaContainer.childElementCount);
//
wbUpdate();
@@ -2315,25 +2384,44 @@ class RoomClient {
return 'data:image/svg+xml,' + svg.replace(/#/g, '%23').replace(/"/g, "'").replace(/&/g, '&');
}
+ setPeerAudio(peer_id, status) {
+ if (!isBroadcastingEnabled || (isBroadcastingEnabled && isPresenter)) {
+ console.log('Set peer audio enabled: ' + status);
+ const audioStatus = this.getPeerAudioBtn(peer_id); // producer, consumers
+ const audioVolume = this.getPeerAudioVolumeBtn(peer_id); // consumers
+ if (audioStatus) audioStatus.className = status ? html.audioOn : html.audioOff;
+ if (audioVolume) status ? show(audioVolume) : hide(audioVolume);
+ }
+ }
+
setIsAudio(peer_id, status) {
- this.peer_info.peer_audio = status;
- let b = this.getPeerAudioBtn(peer_id);
- if (b) b.className = this.peer_info.peer_audio ? html.audioOn : html.audioOff;
+ if (!isBroadcastingEnabled || (isBroadcastingEnabled && isPresenter)) {
+ console.log('Set audio enabled: ' + status);
+ this.peer_info.peer_audio = status;
+ const audioStatus = this.getPeerAudioBtn(peer_id); // producer, consumers
+ if (audioStatus) audioStatus.className = status ? html.audioOn : html.audioOff;
+ }
}
setIsVideo(status) {
- this.peer_info.peer_video = status;
- if (!this.peer_info.peer_video) {
- this.setVideoOff(this.peer_info, false);
- this.sendVideoOff();
+ if (!isBroadcastingEnabled || (isBroadcastingEnabled && isPresenter)) {
+ this.peer_info.peer_video = status;
+ if (!this.peer_info.peer_video) {
+ console.log('Set video enabled: ' + status);
+ this.setVideoOff(this.peer_info, false);
+ this.sendVideoOff();
+ }
}
}
setIsScreen(status) {
- this.peer_info.peer_screen = status;
- if (!this.peer_info.peer_screen && !this.peer_info.peer_video) {
- this.setVideoOff(this.peer_info, false);
- this.sendVideoOff();
+ if (!isBroadcastingEnabled || (isBroadcastingEnabled && isPresenter)) {
+ this.peer_info.peer_screen = status;
+ if (!this.peer_info.peer_screen && !this.peer_info.peer_video) {
+ console.log('Set screen enabled: ' + status);
+ this.setVideoOff(this.peer_info, false);
+ this.sendVideoOff();
+ }
}
}
@@ -2394,6 +2482,10 @@ class RoomClient {
return this.getId(peer_id + '__audio');
}
+ getPeerAudioVolumeBtn(peer_id) {
+ return this.getId(peer_id + '___pVolume');
+ }
+
getPeerHandBtn(peer_id) {
return this.getId(peer_id + '__hand');
}
@@ -4462,7 +4554,8 @@ class RoomClient {
// ####################################################
roomAction(action, emit = true, popup = true) {
- let data = {
+ const data = {
+ room_broadcasting: isBroadcastingEnabled,
room_id: this.room_id,
peer_id: this.peer_id,
peer_name: this.peer_name,
@@ -4472,6 +4565,10 @@ class RoomClient {
};
if (emit) {
switch (action) {
+ case 'broadcasting':
+ this.socket.emit('roomAction', data);
+ if (popup) this.roomStatus(action);
+ break;
case 'lock':
if (room_password) {
this.socket
@@ -4481,10 +4578,11 @@ class RoomClient {
// Only the presenter can lock the room
if (isPresenter || res.peerCounts == 1) {
isPresenter = true;
+ this.peer_info.peer_presenter = isPresenter;
this.getId('isUserPresenter').innerText = isPresenter;
data.password = room_password;
this.socket.emit('roomAction', data);
- this.roomStatus(action);
+ if (popup) this.roomStatus(action);
}
}.bind(this),
)
@@ -4519,7 +4617,7 @@ class RoomClient {
break;
case 'unlock':
this.socket.emit('roomAction', data);
- this.roomStatus(action);
+ if (popup) this.roomStatus(action);
break;
case 'lobbyOn':
this.socket.emit('roomAction', data);
@@ -4547,6 +4645,9 @@ class RoomClient {
roomStatus(action) {
switch (action) {
+ case 'broadcasting':
+ this.userLog('info', `${icons.room} BROADCASTING ${isBroadcastingEnabled ? 'On' : 'Off'}`, 'top-end');
+ break;
case 'lock':
this.sound('locked');
this.event(_EVENTS.roomLock);
@@ -4666,7 +4767,7 @@ class RoomClient {
// ROOM LOBBY
// ####################################################
- roomLobby(data) {
+ async roomLobby(data) {
console.log('LOBBY--->', data);
switch (data.lobby_status) {
case 'waiting':
@@ -4702,7 +4803,7 @@ class RoomClient {
}
break;
case 'accept':
- this.joinAllowed(data.room);
+ await this.joinAllowed(data.room);
control.style.display = 'flex';
this.msgPopup('info', 'Your join meeting was be accepted by moderator');
break;
@@ -5464,10 +5565,26 @@ class RoomClient {
updateRoomModerator(data) {
if (!isRulesActive || isPresenter) {
- this.socket.emit('updateRoomModerator', data);
+ const moderator = this.getModeratorData(data);
+ this.socket.emit('updateRoomModerator', moderator);
}
}
+ updateRoomModeratorALL(data) {
+ if (!isRulesActive || isPresenter) {
+ const moderator = this.getModeratorData(data);
+ this.socket.emit('updateRoomModeratorALL', moderator);
+ }
+ }
+
+ getModeratorData(data) {
+ return {
+ peer_name: this.peer_name,
+ peer_uuid: this.peer_uuid,
+ moderator: data,
+ };
+ }
+
handleUpdateRoomModerator(data) {
switch (data.type) {
case 'audio_cant_unmute':
@@ -5488,6 +5605,11 @@ class RoomClient {
}
}
+ handleUpdateRoomModeratorALL(data) {
+ this._moderator = data;
+ console.log('Update Room Moderator data all', this._moderator);
+ }
+
getModerator() {
console.log('Get Moderator', this._moderator);
return this._moderator;
@@ -5497,7 +5619,7 @@ class RoomClient {
// UPDATE PEER INFO
// ####################################################
- updatePeerInfo(peer_name, peer_id, type, status, emit = true) {
+ updatePeerInfo(peer_name, peer_id, type, status, emit = true, presenter = false) {
if (emit) {
switch (type) {
case 'audio':
@@ -5524,23 +5646,23 @@ class RoomClient {
default:
break;
}
- let data = {
+ const data = {
peer_name: peer_name,
peer_id: peer_id,
type: type,
status: status,
+ broadcast: true,
};
this.socket.emit('updatePeerInfo', data);
} else {
+ const canUpdateMediaStatus = !isBroadcastingEnabled || (isBroadcastingEnabled && presenter);
switch (type) {
case 'audio':
- this.setIsAudio(peer_id, status);
+ if (canUpdateMediaStatus) this.setPeerAudio(peer_id, status);
break;
case 'video':
- this.setIsVideo(status);
break;
case 'screen':
- this.setIsScreen(status);
break;
case 'hand':
let peer_hand = this.getPeerHandBtn(peer_id);
diff --git a/public/js/Rules.js b/public/js/Rules.js
index 8ab8ee96..aed474c0 100644
--- a/public/js/Rules.js
+++ b/public/js/Rules.js
@@ -28,6 +28,7 @@ const BUTTONS = {
settings: {
lockRoomButton: true, // presenter
unlockRoomButton: true, // presenter
+ broadcastingButton: true, // presenter
lobbyButton: true, // presenter
micOptionsButton: true, // presenter
tabModerator: true, // presenter
@@ -80,7 +81,7 @@ const BUTTONS = {
};
function handleRules(isPresenter) {
- console.log('06.1 ----> IsPresenter: ' + isPresenter);
+ console.log('07.1 ----> IsPresenter: ' + isPresenter);
if (!isRulesActive) return;
if (!isPresenter) {
// ##################################
@@ -89,6 +90,7 @@ function handleRules(isPresenter) {
BUTTONS.participantsList.saveInfoButton = false;
BUTTONS.settings.lockRoomButton = false;
BUTTONS.settings.unlockRoomButton = false;
+ BUTTONS.settings.broadcastingButton = false;
BUTTONS.settings.lobbyButton = false;
BUTTONS.settings.micOptionsButton = false;
BUTTONS.settings.tabModerator = false;
@@ -106,6 +108,7 @@ function handleRules(isPresenter) {
BUTTONS.participantsList.saveInfoButton = true;
BUTTONS.settings.lockRoomButton = !isRoomLocked;
BUTTONS.settings.unlockRoomButton = isRoomLocked;
+ BUTTONS.settings.broadcastingButton = true;
BUTTONS.settings.lobbyButton = true;
BUTTONS.settings.micOptionsButton = true;
BUTTONS.settings.tabModerator = true;
@@ -121,6 +124,10 @@ function handleRules(isPresenter) {
// Auto detected rules for presenter
// ##################################
+ // Room broadcasting
+ isBroadcastingEnabled = lsSettings.broadcasting;
+ switchBroadcasting.checked = isBroadcastingEnabled;
+ rc.roomAction('broadcasting', true, false);
// Room lobby
isLobbyEnabled = lsSettings.lobby;
switchLobby.checked = isLobbyEnabled;
@@ -135,15 +142,20 @@ function handleRules(isPresenter) {
switchEveryoneCantUnmute.checked = lsSettings.moderator_audio_cant_unmute;
switchEveryoneCantUnhide.checked = lsSettings.moderator_video_cant_unhide;
switchEveryoneCantShareScreen.checked = lsSettings.moderator_screen_cant_share;
- rc.updateRoomModerator({ type: 'audio_start_muted', status: switchEveryoneMute.checked });
- rc.updateRoomModerator({ type: 'video_start_hidden', status: switchEveryoneHidden.checked });
- rc.updateRoomModerator({ type: 'audio_cant_unmute', status: switchEveryoneCantUnmute.checked });
- rc.updateRoomModerator({ type: 'video_cant_unhide', status: switchEveryoneCantUnhide.checked });
- rc.updateRoomModerator({ type: 'screen_cant_share', status: switchEveryoneCantShareScreen.checked });
+ // Update moderator settings...
+ const moderatorData = {
+ audio_start_muted: switchEveryoneMute.checked,
+ video_start_hidden: switchEveryoneHidden.checked,
+ audio_cant_unmute: switchEveryoneCantUnmute.checked,
+ video_cant_unhide: switchEveryoneCantUnhide.checked,
+ screen_cant_share: switchEveryoneCantShareScreen.checked,
+ };
+ rc.updateRoomModeratorALL(moderatorData);
}
// main. settings...
BUTTONS.settings.lockRoomButton ? show(lockRoomButton) : hide(lockRoomButton);
BUTTONS.settings.unlockRoomButton ? show(unlockRoomButton) : hide(unlockRoomButton);
+ BUTTONS.settings.broadcastingButton ? show(broadcastingButton) : hide(broadcastingButton);
BUTTONS.settings.lobbyButton ? show(lobbyButton) : hide(lobbyButton);
!BUTTONS.settings.micOptionsButton && hide(micOptionsButton);
!BUTTONS.settings.tabModerator && hide(tabModeratorBtn);
@@ -153,3 +165,50 @@ function handleRules(isPresenter) {
: elemDisplay('whiteboardLockButton', false, 'flex');
//...
}
+
+function handleRulesBroadcasting() {
+ console.log('07.2 ----> handleRulesBroadcasting');
+ BUTTONS.main.shareButton = false;
+ BUTTONS.main.hideMeButton = false;
+ BUTTONS.main.startAudioButton = false;
+ BUTTONS.main.startVideoButton = false;
+ BUTTONS.main.startScreenButton = false;
+ BUTTONS.main.swapCameraButton = false;
+ BUTTONS.main.raiseHandButton = false;
+ BUTTONS.main.whiteboardButton = false;
+ //BUTTONS.main.emojiRoomButton = false,
+ BUTTONS.main.transcriptionButton = false;
+ BUTTONS.main.settingsButton = false;
+ BUTTONS.participantsList.saveInfoButton = false;
+ BUTTONS.settings.lockRoomButton = false;
+ BUTTONS.settings.unlockRoomButton = false;
+ BUTTONS.settings.lobbyButton = false;
+ BUTTONS.videoOff.muteAudioButton = false;
+ BUTTONS.videoOff.ejectButton = false;
+ BUTTONS.consumerVideo.sendMessageButton = false;
+ BUTTONS.consumerVideo.sendFileButton = false;
+ BUTTONS.consumerVideo.sendVideoButton = false;
+ BUTTONS.consumerVideo.ejectButton = false;
+ BUTTONS.consumerVideo.muteAudioButton = false;
+ BUTTONS.consumerVideo.muteVideoButton = false;
+ BUTTONS.whiteboard.whiteboardLockButton = false;
+ //...
+ elemDisplay('shareButton', false);
+ elemDisplay('hideMeButton', false);
+ elemDisplay('startAudioButton', false);
+ elemDisplay('stopAudioButton', false);
+ elemDisplay('startVideoButton', false);
+ elemDisplay('stopVideoButton', false);
+ elemDisplay('startScreenButton', false);
+ elemDisplay('stopScreenButton', false);
+ elemDisplay('swapCameraButton', false);
+ elemDisplay('raiseHandButton', false);
+ elemDisplay('whiteboardButton', false);
+ //elemDisplay('emojiRoomButton', false);
+ elemDisplay('transcriptionButton', false);
+ elemDisplay('lockRoomButton', false);
+ elemDisplay('unlockRoomButton', false);
+ elemDisplay('lobbyButton', false);
+ elemDisplay('settingsButton', false);
+ //...
+}
diff --git a/public/views/Room.html b/public/views/Room.html
index 2bf2829e..936eb22b 100644
--- a/public/views/Room.html
+++ b/public/views/Room.html
@@ -154,7 +154,7 @@ access to use this app.
-
+
@@ -230,6 +230,19 @@ access to use this app.