feat: Handles hidden-from-recorder from jwt. (#10973)
* feat: Handles hidden-from-recorder from jwt. Hides the participant that has this flag in jwt from the recorder. A hidden meeting moderator. Makes sure follows me works and no tracks are being added. * squash: Skips showing notification when disabling local audio and video. * squash: Fixes comments. * squash: Updates with ljm changes.
This commit is contained in:
parent
40353cf762
commit
59e51f107e
|
@ -148,6 +148,7 @@ import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/Au
|
||||||
import { createPresenterEffect } from './react/features/stream-effects/presenter';
|
import { createPresenterEffect } from './react/features/stream-effects/presenter';
|
||||||
import { createRnnoiseProcessor } from './react/features/stream-effects/rnnoise';
|
import { createRnnoiseProcessor } from './react/features/stream-effects/rnnoise';
|
||||||
import { endpointMessageReceived } from './react/features/subtitles';
|
import { endpointMessageReceived } from './react/features/subtitles';
|
||||||
|
import { muteLocal } from './react/features/video-menu/actions.any';
|
||||||
import UIEvents from './service/UI/UIEvents';
|
import UIEvents from './service/UI/UIEvents';
|
||||||
|
|
||||||
const logger = Logger.getLogger(__filename);
|
const logger = Logger.getLogger(__filename);
|
||||||
|
@ -1144,7 +1145,8 @@ export default {
|
||||||
* Used by Jibri to detect when it's alone and the meeting should be terminated.
|
* Used by Jibri to detect when it's alone and the meeting should be terminated.
|
||||||
*/
|
*/
|
||||||
get membersCount() {
|
get membersCount() {
|
||||||
return room.getParticipants().filter(p => !p.isHidden()).length + 1;
|
return room.getParticipants()
|
||||||
|
.filter(p => !p.isHidden() || !(config.iAmRecorder && p.isHiddenFromRecorder())).length + 1;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2063,6 +2065,10 @@ export default {
|
||||||
APP.store.dispatch(updateRemoteParticipantFeatures(user));
|
APP.store.dispatch(updateRemoteParticipantFeatures(user));
|
||||||
});
|
});
|
||||||
room.on(JitsiConferenceEvents.USER_JOINED, (id, user) => {
|
room.on(JitsiConferenceEvents.USER_JOINED, (id, user) => {
|
||||||
|
if (config.iAmRecorder && user.isHiddenFromRecorder()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// The logic shared between RN and web.
|
// The logic shared between RN and web.
|
||||||
commonUserJoinedHandling(APP.store, room, user);
|
commonUserJoinedHandling(APP.store, room, user);
|
||||||
|
|
||||||
|
@ -2112,6 +2118,14 @@ export default {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.iAmRecorder) {
|
||||||
|
const participant = room.getParticipantById(track.getParticipantId());
|
||||||
|
|
||||||
|
if (participant.isHiddenFromRecorder()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
APP.store.dispatch(trackAdded(track));
|
APP.store.dispatch(trackAdded(track));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2599,13 +2613,24 @@ export default {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_onConferenceJoined() {
|
_onConferenceJoined() {
|
||||||
|
const { dispatch } = APP.store;
|
||||||
|
|
||||||
APP.UI.initConference();
|
APP.UI.initConference();
|
||||||
|
|
||||||
if (!config.disableShortcuts) {
|
if (!config.disableShortcuts) {
|
||||||
APP.keyboardshortcut.init();
|
APP.keyboardshortcut.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
APP.store.dispatch(conferenceJoined(room));
|
dispatch(conferenceJoined(room));
|
||||||
|
|
||||||
|
const jwt = APP.store.getState()['features/base/jwt'];
|
||||||
|
|
||||||
|
if (jwt?.user?.hiddenFromRecorder) {
|
||||||
|
dispatch(muteLocal(true, MEDIA_TYPE.AUDIO));
|
||||||
|
dispatch(muteLocal(true, MEDIA_TYPE.VIDEO));
|
||||||
|
dispatch(setAudioUnmutePermissions(true, true));
|
||||||
|
dispatch(setVideoUnmutePermissions(true, true));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1167,6 +1167,7 @@ var config = {
|
||||||
forceJVB121Ratio
|
forceJVB121Ratio
|
||||||
forceTurnRelay
|
forceTurnRelay
|
||||||
hiddenDomain
|
hiddenDomain
|
||||||
|
hiddenFromRecorderFeatureEnabled
|
||||||
ignoreStartMuted
|
ignoreStartMuted
|
||||||
websocketKeepAlive
|
websocketKeepAlive
|
||||||
websocketKeepAliveUrl
|
websocketKeepAliveUrl
|
||||||
|
|
|
@ -67,7 +67,7 @@
|
||||||
"jquery-i18next": "1.2.1",
|
"jquery-i18next": "1.2.1",
|
||||||
"js-md5": "0.6.1",
|
"js-md5": "0.6.1",
|
||||||
"jwt-decode": "2.2.0",
|
"jwt-decode": "2.2.0",
|
||||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1360.0.0+9eb93e0e/lib-jitsi-meet.tgz",
|
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1361.0.0+9e98e989/lib-jitsi-meet.tgz",
|
||||||
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
|
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"moment": "2.29.1",
|
"moment": "2.29.1",
|
||||||
|
@ -12003,8 +12003,8 @@
|
||||||
},
|
},
|
||||||
"node_modules/lib-jitsi-meet": {
|
"node_modules/lib-jitsi-meet": {
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1360.0.0+9eb93e0e/lib-jitsi-meet.tgz",
|
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1361.0.0+9e98e989/lib-jitsi-meet.tgz",
|
||||||
"integrity": "sha512-qeQOjeZcAVd7aACEwRu8tBD85ZIPS9V+U8Htm9QvB8FpHi+5hYxN3N0SDgLEJlQEKMxsDzpfpjkInX5UCWt/KQ==",
|
"integrity": "sha512-dIg6vWsiWIu77TRHsTSGhTvbLqRw9MAlzScMEw/0ooZTq/ztSCvRZQqQ3quP64+4F0FGm6n0P3y0YBp5t44f4g==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jitsi/js-utils": "2.0.0",
|
"@jitsi/js-utils": "2.0.0",
|
||||||
|
@ -29109,8 +29109,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lib-jitsi-meet": {
|
"lib-jitsi-meet": {
|
||||||
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1360.0.0+9eb93e0e/lib-jitsi-meet.tgz",
|
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1361.0.0+9e98e989/lib-jitsi-meet.tgz",
|
||||||
"integrity": "sha512-qeQOjeZcAVd7aACEwRu8tBD85ZIPS9V+U8Htm9QvB8FpHi+5hYxN3N0SDgLEJlQEKMxsDzpfpjkInX5UCWt/KQ==",
|
"integrity": "sha512-dIg6vWsiWIu77TRHsTSGhTvbLqRw9MAlzScMEw/0ooZTq/ztSCvRZQqQ3quP64+4F0FGm6n0P3y0YBp5t44f4g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@jitsi/js-utils": "2.0.0",
|
"@jitsi/js-utils": "2.0.0",
|
||||||
"@jitsi/logger": "2.0.0",
|
"@jitsi/logger": "2.0.0",
|
||||||
|
|
|
@ -72,7 +72,7 @@
|
||||||
"jquery-i18next": "1.2.1",
|
"jquery-i18next": "1.2.1",
|
||||||
"js-md5": "0.6.1",
|
"js-md5": "0.6.1",
|
||||||
"jwt-decode": "2.2.0",
|
"jwt-decode": "2.2.0",
|
||||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1360.0.0+9eb93e0e/lib-jitsi-meet.tgz",
|
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1361.0.0+9e98e989/lib-jitsi-meet.tgz",
|
||||||
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
|
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"moment": "2.29.1",
|
"moment": "2.29.1",
|
||||||
|
|
|
@ -220,10 +220,11 @@ function _undoOverwriteLocalParticipant(
|
||||||
* avatarURL: ?string,
|
* avatarURL: ?string,
|
||||||
* email: ?string,
|
* email: ?string,
|
||||||
* id: ?string,
|
* id: ?string,
|
||||||
* name: ?string
|
* name: ?string,
|
||||||
|
* hidden-from-recorder: ?boolean
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
function _user2participant({ avatar, avatarUrl, email, id, name }) {
|
function _user2participant({ avatar, avatarUrl, email, id, name, 'hidden-from-recorder': hiddenFromRecorder }) {
|
||||||
const participant = {};
|
const participant = {};
|
||||||
|
|
||||||
if (typeof avatarUrl === 'string') {
|
if (typeof avatarUrl === 'string') {
|
||||||
|
@ -241,5 +242,9 @@ function _user2participant({ avatar, avatarUrl, email, id, name }) {
|
||||||
participant.name = name.trim();
|
participant.name = name.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hiddenFromRecorder === 'true' || hiddenFromRecorder === true) {
|
||||||
|
participant.hiddenFromRecorder = true;
|
||||||
|
}
|
||||||
|
|
||||||
return Object.keys(participant).length ? participant : undefined;
|
return Object.keys(participant).length ? participant : undefined;
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,12 +65,14 @@ export function setAudioMuted(muted: boolean, ensureTrack: boolean = false) {
|
||||||
* Action to disable/enable the audio mute icon.
|
* Action to disable/enable the audio mute icon.
|
||||||
*
|
*
|
||||||
* @param {boolean} blocked - True if the audio mute icon needs to be disabled.
|
* @param {boolean} blocked - True if the audio mute icon needs to be disabled.
|
||||||
|
* @param {boolean|undefined} skipNotification - True if we want to skip showing the notification.
|
||||||
* @returns {Function}
|
* @returns {Function}
|
||||||
*/
|
*/
|
||||||
export function setAudioUnmutePermissions(blocked: boolean) {
|
export function setAudioUnmutePermissions(blocked: boolean, skipNotification: boolean = false) {
|
||||||
return {
|
return {
|
||||||
type: SET_AUDIO_UNMUTE_PERMISSIONS,
|
type: SET_AUDIO_UNMUTE_PERMISSIONS,
|
||||||
blocked
|
blocked,
|
||||||
|
skipNotification
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,12 +157,14 @@ export function setVideoMuted(
|
||||||
* Action to disable/enable the video mute icon.
|
* Action to disable/enable the video mute icon.
|
||||||
*
|
*
|
||||||
* @param {boolean} blocked - True if the video mute icon needs to be disabled.
|
* @param {boolean} blocked - True if the video mute icon needs to be disabled.
|
||||||
|
* @param {boolean|undefined} skipNotification - True if we want to skip showing the notification.
|
||||||
* @returns {Function}
|
* @returns {Function}
|
||||||
*/
|
*/
|
||||||
export function setVideoUnmutePermissions(blocked: boolean) {
|
export function setVideoUnmutePermissions(blocked: boolean, skipNotification: boolean = false) {
|
||||||
return {
|
return {
|
||||||
type: SET_VIDEO_UNMUTE_PERMISSIONS,
|
type: SET_VIDEO_UNMUTE_PERMISSIONS,
|
||||||
blocked
|
blocked,
|
||||||
|
skipNotification
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,12 +86,12 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
}
|
}
|
||||||
|
|
||||||
case SET_AUDIO_UNMUTE_PERMISSIONS: {
|
case SET_AUDIO_UNMUTE_PERMISSIONS: {
|
||||||
const { blocked } = action;
|
const { blocked, skipNotification } = action;
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
const tracks = state['features/base/tracks'];
|
const tracks = state['features/base/tracks'];
|
||||||
const isAudioMuted = isLocalTrackMuted(tracks, MEDIA_TYPE.AUDIO);
|
const isAudioMuted = isLocalTrackMuted(tracks, MEDIA_TYPE.AUDIO);
|
||||||
|
|
||||||
if (blocked && isAudioMuted) {
|
if (blocked && isAudioMuted && !skipNotification) {
|
||||||
store.dispatch(showWarningNotification({
|
store.dispatch(showWarningNotification({
|
||||||
descriptionKey: 'notify.audioUnmuteBlockedDescription',
|
descriptionKey: 'notify.audioUnmuteBlockedDescription',
|
||||||
titleKey: 'notify.audioUnmuteBlockedTitle'
|
titleKey: 'notify.audioUnmuteBlockedTitle'
|
||||||
|
@ -111,13 +111,13 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
}
|
}
|
||||||
|
|
||||||
case SET_VIDEO_UNMUTE_PERMISSIONS: {
|
case SET_VIDEO_UNMUTE_PERMISSIONS: {
|
||||||
const { blocked } = action;
|
const { blocked, skipNotification } = action;
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
const tracks = state['features/base/tracks'];
|
const tracks = state['features/base/tracks'];
|
||||||
const isVideoMuted = isLocalTrackMuted(tracks, MEDIA_TYPE.VIDEO);
|
const isVideoMuted = isLocalTrackMuted(tracks, MEDIA_TYPE.VIDEO);
|
||||||
const isMediaShared = isScreenMediaShared(state);
|
const isMediaShared = isScreenMediaShared(state);
|
||||||
|
|
||||||
if (blocked && isVideoMuted && !isMediaShared) {
|
if (blocked && isVideoMuted && !isMediaShared && !skipNotification) {
|
||||||
store.dispatch(showWarningNotification({
|
store.dispatch(showWarningNotification({
|
||||||
descriptionKey: 'notify.videoUnmuteBlockedDescription',
|
descriptionKey: 'notify.videoUnmuteBlockedDescription',
|
||||||
titleKey: 'notify.videoUnmuteBlockedTitle'
|
titleKey: 'notify.videoUnmuteBlockedTitle'
|
||||||
|
|
|
@ -102,6 +102,7 @@ function _onFollowMeCommand(attributes = {}, id, store) {
|
||||||
|
|
||||||
const participantSendingCommand = getParticipantById(state, id);
|
const participantSendingCommand = getParticipantById(state, id);
|
||||||
|
|
||||||
|
if (participantSendingCommand) {
|
||||||
// The Command(s) API will send us our own commands and we don't want
|
// The Command(s) API will send us our own commands and we don't want
|
||||||
// to act upon them.
|
// to act upon them.
|
||||||
if (participantSendingCommand.local) {
|
if (participantSendingCommand.local) {
|
||||||
|
@ -113,6 +114,21 @@ function _onFollowMeCommand(attributes = {}, id, store) {
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// This is the case of jibri receiving commands from a hidden participant.
|
||||||
|
const { iAmRecorder } = state['features/base/config'];
|
||||||
|
const { conference } = state['features/base/conference'];
|
||||||
|
|
||||||
|
// As this participant is not stored in redux store we do the checks on the JitsiParticipant from lib-jitsi-meet
|
||||||
|
const participant = conference.getParticipantById(id);
|
||||||
|
|
||||||
|
if (!iAmRecorder || !participant || participant.getRole() !== 'moderator'
|
||||||
|
|| !participant.isHiddenFromRecorder()) {
|
||||||
|
logger.warn('Something went wrong with follow-me command');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!isFollowMeActive(state)) {
|
if (!isFollowMeActive(state)) {
|
||||||
store.dispatch(setFollowMeModerator(id));
|
store.dispatch(setFollowMeModerator(id));
|
||||||
|
|
Loading…
Reference in New Issue