From d6467d5bbfaaea68c6e193e65fa04368844c411e Mon Sep 17 00:00:00 2001 From: Sean Date: Wed, 18 Nov 2020 10:25:26 +0100 Subject: [PATCH] Autoconnect the next X speaker to jitsi In config, set "general_system_conference_auto_connect_next_speakers" to let the next X speakers on the current list of speakers automatically join the jitsi conference. Updates automatically --- .../app/core/translate/marked-translations.ts | 2 + .../components/jitsi/jitsi.component.ts | 43 +++++++++++++++---- .../agenda/models/view-list-of-speakers.ts | 26 ++++++++++- server/openslides/core/config_variables.py | 22 +++++++--- 4 files changed, 76 insertions(+), 17 deletions(-) diff --git a/client/src/app/core/translate/marked-translations.ts b/client/src/app/core/translate/marked-translations.ts index 8bb6819bf..78984c981 100644 --- a/client/src/app/core/translate/marked-translations.ts +++ b/client/src/app/core/translate/marked-translations.ts @@ -38,6 +38,8 @@ _('Livestream url'); _('Remove URL to deactivate livestream. Check extra group permission to see livestream.'); _('Livestream poster image url'); _('Shows if livestream is not started. Recommended image format: 500x281px, PNG or JPG'); +_('Number of next speakers automatically connecting to the live conference'); +_('Live conference has to be active. Choose 0 to disable auto connect.'); _('Show this text on the login page'); _('OpenSlides Theme'); _('Export'); diff --git a/client/src/app/shared/components/jitsi/jitsi.component.ts b/client/src/app/shared/components/jitsi/jitsi.component.ts index e0ac0991f..5a361aae8 100644 --- a/client/src/app/shared/components/jitsi/jitsi.component.ts +++ b/client/src/app/shared/components/jitsi/jitsi.component.ts @@ -12,6 +12,7 @@ import { OperatorService } from 'app/core/core-services/operator.service'; import { Deferred } from 'app/core/promises/deferred'; import { UserRepositoryService } from 'app/core/repositories/users/user-repository.service'; import { ConfigService } from 'app/core/ui-services/config.service'; +import { UserListIndexType } from 'app/site/agenda/models/view-list-of-speakers'; import { BaseViewComponentDirective } from 'app/site/base/base-view'; import { CurrentListOfSpeakersService } from 'app/site/projector/services/current-list-of-speakers.service'; @@ -85,6 +86,7 @@ export class JitsiComponent extends BaseViewComponentDirective implements OnInit public restricted = false; public videoStreamUrl: string; + private nextSpeakerAmount: number; // do not set the password twice private isPasswortSet = false; @@ -330,6 +332,11 @@ export class JitsiComponent extends BaseViewComponentDirective implements OnInit this.configService.get('general_system_conference_los_restriction').subscribe(restricted => { this.restricted = restricted; }), + this.configService + .get('general_system_conference_auto_connect_next_speakers') + .subscribe(nextSpeakerAmount => { + this.nextSpeakerAmount = nextSpeakerAmount; + }), this.configService.get('general_system_stream_url').subscribe(url => { this.videoStreamUrl = url; this.configsLoaded.resolve(); @@ -359,17 +366,10 @@ export class JitsiComponent extends BaseViewComponentDirective implements OnInit // check if the operator is on the clos, remove from room if not permitted this.closService.currentListOfSpeakersObservable .pipe( - map(los => (los ? los.isUserOnList(this.operator.user.id) : false)), + map(los => los?.findUserIndexOnList(this.operator.user.id) ?? -1), distinctUntilChanged() ) - .subscribe(isOnList => { - this.isOnCurrentLos = isOnList; - this.triggerMeetingRoomButtonAnimation(); - - if (!this.isAccessPermitted) { - this.viewStream(); - } - }) + .subscribe(userLosIndex => this.autoJoinJitsiByLosIndex(userLosIndex)) ); } @@ -446,6 +446,31 @@ export class JitsiComponent extends BaseViewComponentDirective implements OnInit } } + private autoJoinJitsiByLosIndex(operatorClosIndex: number): void { + if (operatorClosIndex !== UserListIndexType.NotOnList) { + if (!this.isOnCurrentLos) { + this.isOnCurrentLos = true; + this.triggerMeetingRoomButtonAnimation(); + } + + if ( + this.nextSpeakerAmount && + this.nextSpeakerAmount > 0 && + operatorClosIndex > UserListIndexType.Active && + operatorClosIndex <= this.nextSpeakerAmount && + !this.isJitsiActive + ) { + this.enterConversation(); + } + } else { + this.isOnCurrentLos = false; + } + + if (!this.isAccessPermitted) { + this.viewStream(); + } + } + private setRoomPassword(): void { if (this.roomPassword && !this.isPasswortSet) { // You can only set the password after the server has recognized that you are diff --git a/client/src/app/site/agenda/models/view-list-of-speakers.ts b/client/src/app/site/agenda/models/view-list-of-speakers.ts index 158980da0..f11000ef3 100644 --- a/client/src/app/site/agenda/models/view-list-of-speakers.ts +++ b/client/src/app/site/agenda/models/view-list-of-speakers.ts @@ -12,6 +12,12 @@ export interface ListOfSpeakersTitleInformation { title_information: object; } +export enum UserListIndexType { + Finished = -2, + NotOnList = -1, + Active = 0 +} + /** * TODO: Resolve potential circular dependencies with {@link BaseViewModelWithListOfSpeakers}. */ @@ -25,6 +31,10 @@ export class ViewListOfSpeakers return this._model; } + public get activeSpeaker(): ViewSpeaker { + return this.speakers.find(speaker => speaker.state === SpeakerState.CURRENT); + } + public get finishedSpeakers(): ViewSpeaker[] { return this.speakers.filter(speaker => speaker.state === SpeakerState.FINISHED); } @@ -65,8 +75,20 @@ export class ViewListOfSpeakers return this.finishedSpeakers.findIndex(speaker => speaker.user_id === checkSpeaker.user_id) !== -1; } - public isUserOnList(userId: number): boolean { - return !!this.speakers.find(speaker => speaker.user_id === userId); + public findUserIndexOnList(userId: number): number { + if (this.activeSpeaker?.user.id === userId) { + return UserListIndexType.Active; + } else { + const waitingSpeakersIndex = this.waitingSpeakers.findIndex(speaker => speaker.user_id === userId); + const finishedSpeakersIndex = this.finishedSpeakers.findIndex(speaker => speaker.user_id === userId); + if (waitingSpeakersIndex !== -1) { + return waitingSpeakersIndex + 1; + } else if (finishedSpeakersIndex !== -1) { + return UserListIndexType.Finished; + } else { + return UserListIndexType.NotOnList; + } + } } } interface IListOfSpeakersRelations { diff --git a/server/openslides/core/config_variables.py b/server/openslides/core/config_variables.py index 35a0f40d9..abfc262ab 100644 --- a/server/openslides/core/config_variables.py +++ b/server/openslides/core/config_variables.py @@ -102,31 +102,41 @@ def get_config_variables(): ) yield ConfigVariable( - name="general_system_conference_auto_connect", + name="general_system_conference_los_restriction", default_value=False, input_type="boolean", - label="Connect all users to live conference automatically", + label="Allow only current speakers and list of speakers managers to enter the live conference", help_text="Server settings required to activate Jitsi Meet integration.", weight=141, subgroup="Live conference", ) yield ConfigVariable( - name="general_system_conference_los_restriction", + name="general_system_conference_auto_connect", default_value=False, input_type="boolean", - label="Allow only current speakers and list of speakers managers to enter the live conference", + label="Connect all users to live conference automatically", help_text="Server settings required to activate Jitsi Meet integration.", weight=142, subgroup="Live conference", ) + yield ConfigVariable( + name="general_system_conference_auto_connect_next_speakers", + default_value=0, + input_type="integer", + label="Number of next speakers automatically connecting to the live conference", + help_text="Live conference has to be active. Choose 0 to disable auto connect.", + weight=143, + subgroup="Live conference", + ) + yield ConfigVariable( name="general_system_stream_url", default_value="", label="Livestream url", help_text="Remove URL to deactivate livestream. Check extra group permission to see livestream.", - weight=143, + weight=146, subgroup="Live conference", ) @@ -135,7 +145,7 @@ def get_config_variables(): default_value="", label="Livestream poster image url", help_text="Shows if livestream is not started. Recommended image format: 500x281px, PNG or JPG", - weight=144, + weight=147, subgroup="Live conference", )