Skip to content

Commit

Permalink
openvidu-components: Emitted event for updating recording list
Browse files Browse the repository at this point in the history
Non moderators participants don't update their recording list because they don't know when a recording has been deleted or stopped. Now all standard participants know all recording events using custom signals and emitted an event to the application with the aim of updating the recording list.
  • Loading branch information
CSantosM committed Apr 28, 2023
1 parent 9be8383 commit 0e837a5
Show file tree
Hide file tree
Showing 12 changed files with 92 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ <h3 class="panel-title">Activities</h3>
(onStartRecordingClicked)="_onStartRecordingClicked()"
(onStopRecordingClicked)="_onStopRecordingClicked()"
(onDeleteRecordingClicked)="_onDeleteRecordingClicked($event)"
(onForceRecordingUpdate)="_onForceRecordingUpdate()"
></ov-recording-activity>
<ov-broadcasting-activity
*ngIf="showBroadcastingActivity"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ export class ActivitiesPanelComponent implements OnInit {
*/
@Output() onDeleteRecordingClicked: EventEmitter<string> = new EventEmitter<string>();

/**
* Provides event notifications that fire when a participant needs update the recordings information
* (usually when recording is stopped by the session moderator or recording panel is opened).
* The recordings should be updated using the REST API.
*/
@Output() onForceRecordingUpdate: EventEmitter<void> = new EventEmitter<void>();

/**
* Provides event notifications that fire when start broadcasting button has been clicked.
* The broadcasting should be started using the REST API.
Expand Down Expand Up @@ -112,6 +119,10 @@ export class ActivitiesPanelComponent implements OnInit {
this.onStopBroadcastingClicked.emit();
}

_onForceRecordingUpdate() {
this.onForceRecordingUpdate.emit();
}

private subscribeToPanelToggling() {
this.panelSubscription = this.panelService.panelOpenedObs.subscribe((ev: PanelEvent) => {
if (ev.type === PanelType.ACTIVITIES && !!ev.expand) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ <h2 *ngIf="recordingsList.length === 0">{{ 'PANEL.RECORDING.CONTENT_TITLE' | tra
</button>

<button
*ngIf="isSessionCreator"
mat-icon-button
class="delete-recording-btn"
id="delete-recording-btn"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { ActionService } from '../../../../services/action/action.service';
import { OpenViduAngularConfigService } from '../../../../services/config/openvidu-angular.config.service';
import { ParticipantService } from '../../../../services/participant/participant.service';
import { RecordingService } from '../../../../services/recording/recording.service';
import { OpenViduService } from '../../../../services/openvidu/openvidu.service';
import { Signal } from '../../../../models/signal.model';

@Component({
selector: 'ov-recording-activity',
Expand Down Expand Up @@ -36,6 +38,13 @@ export class RecordingActivityComponent implements OnInit {
*/
@Output() onDeleteRecordingClicked: EventEmitter<string> = new EventEmitter<string>();

/**
* Provides event notifications that fire when a participant needs update the recordings information
* (usually when recording is stopped by the session moderator or recording panel is opened).
* The recordings should be updated using the REST API.
*/
@Output() onForceRecordingUpdate: EventEmitter<void> = new EventEmitter<void>();

/**
* @internal
*/
Expand Down Expand Up @@ -76,6 +85,7 @@ export class RecordingActivityComponent implements OnInit {
private recordingStatusSubscription: Subscription;
private recordingListSubscription: Subscription;
private recordingErrorSub: Subscription;
private forceRecordingUpdateSub: Subscription;

/**
* @internal
Expand All @@ -85,6 +95,7 @@ export class RecordingActivityComponent implements OnInit {
private participantService: ParticipantService,
private libService: OpenViduAngularConfigService,
private actionService: ActionService,
private openviduService: OpenViduService,
private cd: ChangeDetectorRef
) {}

Expand All @@ -94,6 +105,7 @@ export class RecordingActivityComponent implements OnInit {
ngOnInit(): void {
this.subscribeToRecordingStatus();
this.subscribeToRecordingActivityDirective();
this.subscribeToForceRecordingUpdate();
this.isSessionCreator = this.participantService.amIModerator();
}

Expand All @@ -104,6 +116,7 @@ export class RecordingActivityComponent implements OnInit {
if (this.recordingStatusSubscription) this.recordingStatusSubscription.unsubscribe();
if (this.recordingListSubscription) this.recordingListSubscription.unsubscribe();
if (this.recordingErrorSub) this.recordingErrorSub.unsubscribe();
if (this.forceRecordingUpdateSub) this.forceRecordingUpdateSub.unsubscribe();
}

/**
Expand Down Expand Up @@ -156,8 +169,10 @@ export class RecordingActivityComponent implements OnInit {
deleteRecording(id: string) {
const succsessCallback = () => {
this.onDeleteRecordingClicked.emit(id);
// Sending signal to all participants with the aim of updating their recordings list
this.openviduService.sendSignal(Signal.RECORDING_DELETED, this.openviduService.getRemoteConnections());
};
this.actionService.openDeleteRecordingDialog(succsessCallback);
this.actionService.openDeleteRecordingDialog(succsessCallback.bind(this));
}

/**
Expand All @@ -180,6 +195,10 @@ export class RecordingActivityComponent implements OnInit {
if (ev?.info) {
if (this.recordingStatus !== RecordingStatus.FAILED) {
this.oldRecordingStatus = this.recordingStatus;
if (ev.info.status === RecordingStatus.STOPPED && !this.isSessionCreator) {
// Force update recording list when recording is stopped by moderator
this.onForceRecordingUpdate.emit();
}
}
this.recordingStatus = ev.info.status;
this.recordingAlive = ev.info.status === RecordingStatus.STARTED;
Expand All @@ -192,6 +211,8 @@ export class RecordingActivityComponent implements OnInit {
private subscribeToRecordingActivityDirective() {
this.recordingListSubscription = this.libService.recordingsListObs.subscribe((recordingList: RecordingInfo[]) => {
this.recordingsList = recordingList;
// si envio aqui el signal, estos eventos serian infinitos
// this.openviduService.sendSignal(Signal.RECORDING_DELETED, this.openviduService.getRemoteConnections());
this.cd.markForCheck();
});

Expand All @@ -202,4 +223,10 @@ export class RecordingActivityComponent implements OnInit {
}
});
}

private subscribeToForceRecordingUpdate() {
this.forceRecordingUpdateSub = this.recordingService.forceUpdateRecordingsObs.subscribe(() => {
this.onForceRecordingUpdate.emit();
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,29 +64,23 @@ export class SessionComponent implements OnInit, OnDestroy {
@ContentChild('toolbar', { read: TemplateRef }) toolbarTemplate: TemplateRef<any>;
@ContentChild('panel', { read: TemplateRef }) panelTemplate: TemplateRef<any>;
@ContentChild('layout', { read: TemplateRef }) layoutTemplate: TemplateRef<any>;

@Input() usedInPrejoinPage = false;

@Output() onNodeCrashed = new EventEmitter<any>();

session: Session;
sessionScreen: Session;

sideMenu: MatSidenav;

sidenavMode: SidenavMode = SidenavMode.SIDE;
settingsPanelOpened: boolean;
drawer: MatDrawerContainer;
preparing: boolean = true;

private isSessionCreator: boolean = false;
protected readonly SIDENAV_WIDTH_LIMIT_MODE = 790;

protected menuSubscription: Subscription;
protected layoutWidthSubscription: Subscription;

protected updateLayoutInterval: NodeJS.Timer;
private captionLanguageSubscription: Subscription;

protected log: ILogger;

constructor(
Expand Down Expand Up @@ -168,6 +162,7 @@ export class SessionComponent implements OnInit, OnDestroy {

async ngOnInit() {
if (!this.usedInPrejoinPage) {
this.isSessionCreator = this.participantService.amIModerator();
if (!this.openviduService.getScreenToken()) {
// Hide screenshare button if screen token does not exist
this.libService.screenshareButton.next(false);
Expand Down Expand Up @@ -439,6 +434,11 @@ export class SessionComponent implements OnInit, OnDestroy {
private subscribeToBroadcastingEvents() {
this.session.on('broadcastStarted', () => this.broadcastingService.startBroadcasting());
this.session.on('broadcastStopped', () => this.broadcastingService.stopBroadcasting());

if (!this.isSessionCreator) {
// Listen to recording delete events from moderator
this.session.on(`signal:${Signal.RECORDING_DELETED}`, () => this.recordingService.forceUpdateRecordings());
}
}

private startUpdateLayoutInterval() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
(onStartRecordingClicked)="onStartRecordingClicked('panel')"
(onStopRecordingClicked)="onStopRecordingClicked('panel')"
(onDeleteRecordingClicked)="onDeleteRecordingClicked($event)"
(onForceRecordingUpdate)="onForceRecordingUpdate()"
(onStartBroadcastingClicked)="onStartBroadcastingClicked($event)"
(onStopBroadcastingClicked)="onStopBroadcastingClicked('panel')"
></ov-activities-panel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,13 @@ export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewIni
*/
@Output() onActivitiesPanelDeleteRecordingClicked: EventEmitter<string> = new EventEmitter<string>();

/**
* Provides event notifications that fire when a participant needs update the recordings information
* (usually when recording is stopped by the session moderator or recording panel is opened) from {@link ActivitiesPanelComponent}.
* The recordings should be updated using the REST API.
*/
@Output() onActivitiesPanelForceRecordingUpdate: EventEmitter<void> = new EventEmitter<void>();

/**
* Provides event notifications that fire when play recording button is clicked from {@link ActivitiesPanelComponent}.
*/
Expand Down Expand Up @@ -692,6 +699,14 @@ export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewIni
this.onActivitiesPanelDeleteRecordingClicked.emit(recordingId);
}

/**
* @internal
*/

onForceRecordingUpdate() {
this.onActivitiesPanelForceRecordingUpdate.emit();
}

/**
* @internal
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
*/
export enum Signal {
NICKNAME_CHANGED = 'nicknameChanged',
CHAT = 'chat'
CHAT = 'chat',
RECORDING_DELETED = 'recordingDeleted',
}
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,9 @@ export class OpenViduService {

/**
* @internal
*
* @param type: type of signal
* @param connections: if undefined, the signal will be sent to all participants
*/
sendSignal(type: Signal, connections?: Connection[], data?: any): void {
const signalOptions: SignalOptions = {
Expand Down Expand Up @@ -611,7 +614,7 @@ export class OpenViduService {
needSendNicknameSignal(): boolean {
let oldNickname: string;
try {
const connData = JSON.parse(this.webcamSession.connection.data.split('%/%')[0]);
const connData = JSON.parse(this.cleanConnectionData(this.webcamSession.connection.data));
oldNickname = connData.clientData;
} catch (error) {
this.log.e(error);
Expand All @@ -637,7 +640,7 @@ export class OpenViduService {
// Avoid screen connections
const remoteCameraConnections: Connection[] = Array.from(this.webcamSession.remoteConnections.values()).filter((conn) => {
let type: VideoType;
type = JSON.parse(conn.data).type;
type = JSON.parse(this.cleanConnectionData(conn.data)).type;
return type !== VideoType.SCREEN;
});
return remoteCameraConnections;
Expand All @@ -656,4 +659,8 @@ export class OpenViduService {
}
}
}

private cleanConnectionData(data: string): string {
return data.split('%/%')[0];
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { RecordingEvent } from 'openvidu-browser';
import { BehaviorSubject, Observable } from 'rxjs';
import { BehaviorSubject, Observable, Subject } from 'rxjs';
import { RecordingInfo, RecordingStatus } from '../../models/recording.model';
import { ActionService } from '../action/action.service';

Expand All @@ -13,6 +13,10 @@ export class RecordingService {
*/
recordingStatusObs: Observable<{ info: RecordingInfo; time?: Date } | undefined>;

/**
* @internal
*/
forceUpdateRecordingsObs: Subject<void> = new Subject();
private recordingTime: Date | undefined;
private recordingTimeInterval: NodeJS.Timer;
private currentRecording: RecordingInfo = { status: RecordingStatus.STOPPED };
Expand Down Expand Up @@ -86,7 +90,6 @@ export class RecordingService {
const recordingId = recording.id;
// Only COMPOSED recording is supported. The extension will allways be 'mp4'.
const extension = 'mp4'; //recording.url?.split('.').pop() || 'mp4';

const link = document.createElement('a');
link.href = `/recordings/${recordingId}/${recordingId}.${extension}`;
link.download = `${recordingId}.${extension}`;
Expand All @@ -104,6 +107,10 @@ export class RecordingService {
}, 100);
}

forceUpdateRecordings() {
this.forceUpdateRecordingsObs.next();
}

private startRecordingTime() {
this.recordingTime = new Date();
this.recordingTime.setHours(0, 0, 0, 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[lang]="'en'"
[captionsLang]=""
[captionsLangOptions]="[{ name: 'Spanish', ISO: 'es-ES' },{ name: 'English', ISO: 'en-US' }]"
[prejoin]="false"
[prejoin]="true"
[participantName]="'Participant'"
[videoMuted]="false"
[audioMuted]="false"
Expand Down Expand Up @@ -41,6 +41,7 @@
(onActivitiesPanelStartRecordingClicked)="onStartRecordingClicked()"
(onActivitiesPanelStopRecordingClicked)="onStopRecordingClicked()"
(onActivitiesPanelDeleteRecordingClicked)="onDeleteRecordingClicked($event)"
(onActivitiesPanelForceRecordingUpdate)="onForceRecordingUpdate()"
(onActivitiesPanelStartBroadcastingClicked)="onStartBroadcastingClicked($event)"
(onActivitiesPanelStopBroadcastingClicked)="onStopBroadcastingClicked()"
(onToolbarStopBroadcastingClicked)="onStopBroadcastingClicked()"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export class CallComponent implements OnInit {
this.recordingList = await this.restService.stopRecording(this.sessionId);
console.log('RECORDING LIST ', this.recordingList);
} catch (error) {
console.log(await this.restService.getRecordings())
this.recordingError = error;
}
}
Expand All @@ -137,6 +138,11 @@ export class CallComponent implements OnInit {
}
}

async onForceRecordingUpdate() {
console.warn('FORCE RECORDING UPDATE');
this.recordingList = await this.restService.getRecordings();
}

private async requestForTokens() {
const { broadcastingEnabled, recordingEnabled, recordings, cameraToken, screenToken, isRecordingActive, isBroadcastingActive } =
await this.restService.getTokensFromBackend(this.sessionId);
Expand Down

0 comments on commit 0e837a5

Please sign in to comment.