[mirotalksfu] - add moderator

هذا الالتزام موجود في:
Miroslav Pejic
2023-11-15 18:23:20 +01:00
الأصل b3a6c254c9
التزام cfbe8a3e5f
11 ملفات معدلة مع 168 إضافات و58 حذوفات

عرض الملف

@@ -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]),

عرض الملف

@@ -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;

عرض الملف

@@ -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"
}
}

عرض الملف

@@ -439,7 +439,7 @@ th {
@media screen and (max-width: 540px) {
.tab {
font-size: 0.5rem;
display: inline;
}
}

عرض الملف

@@ -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,

عرض الملف

@@ -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;

عرض الملف

@@ -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) {

عرض الملف

@@ -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)

عرض الملف

@@ -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) {

عرض الملف

@@ -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);

عرض الملف

@@ -185,6 +185,9 @@ access to use this app.
<button id="tabAudioDevicesBtn" class="fas fa-microphone tablinks">
<p class="tabButtonText">Audio</p>
</button>
<button id="tabModeratorBtn" class="fas fa-m tablinks">
<p class="tabButtonText">Moderator</p>
</button>
<button id="tabRecordingBtn" class="fas fa-record-vinyl tablinks">
<p class="tabButtonText">Recording</p>
</button>
@@ -588,6 +591,42 @@ access to use this app.
<br />
</div>
<div id="tabModerator" class="tabcontent">
<table class="settingsTable">
<tr id="everyoneMuteBtn">
<td style="width: auto">
<div class="title">
<i class="fas fa-microphone-slash"></i>
<p>Everyone starts muted</p>
</div>
</td>
<td>
<div class="form-check form-switch form-switch-md title">
<input id="switchEveryoneMute" class="form-check-input" type="checkbox" />
</div>
</td>
</tr>
<tr id="everyoneHideBtn">
<td style="width: auto">
<div class="title">
<i class="fas fa-video-slash"></i>
<p>Everyone starts hidden</p>
</div>
</td>
<td>
<div class="form-check form-switch form-switch-md">
<input
id="switchEveryoneHidden"
class="form-check-input"
type="checkbox"
checked
/>
</div>
</td>
</tr>
</table>
</div>
<div id="tabRecording" class="tabcontent">
<img id="recImage" src="../images/recording.png" />
<div id="roomRecording" class="hidden">