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:
Дамян Минков 2022-02-17 16:25:31 -06:00 committed by GitHub
parent 40353cf762
commit 59e51f107e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 77 additions and 26 deletions

View File

@ -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));
}
}, },
/** /**

View File

@ -1167,6 +1167,7 @@ var config = {
forceJVB121Ratio forceJVB121Ratio
forceTurnRelay forceTurnRelay
hiddenDomain hiddenDomain
hiddenFromRecorderFeatureEnabled
ignoreStartMuted ignoreStartMuted
websocketKeepAlive websocketKeepAlive
websocketKeepAliveUrl websocketKeepAliveUrl

10
package-lock.json generated
View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -102,16 +102,32 @@ function _onFollowMeCommand(attributes = {}, id, store) {
const participantSendingCommand = getParticipantById(state, id); const participantSendingCommand = getParticipantById(state, id);
// The Command(s) API will send us our own commands and we don't want if (participantSendingCommand) {
// to act upon them. // The Command(s) API will send us our own commands and we don't want
if (participantSendingCommand.local) { // to act upon them.
return; if (participantSendingCommand.local) {
} return;
}
if (participantSendingCommand.role !== 'moderator') { if (participantSendingCommand.role !== 'moderator') {
logger.warn('Received follow-me command not from moderator'); logger.warn('Received follow-me command not from moderator');
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)) {