mirror of https://github.com/OpenVidu/openvidu.git
openvidu-components: Emitted event for updating recording list
Non moderators participants didn't update their recording list because they didn'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.pull/803/head
parent
9be838308c
commit
fb101bc2c7
|
@ -15,6 +15,7 @@
|
|||
(onStartRecordingClicked)="_onStartRecordingClicked()"
|
||||
(onStopRecordingClicked)="_onStopRecordingClicked()"
|
||||
(onDeleteRecordingClicked)="_onDeleteRecordingClicked($event)"
|
||||
(onForceRecordingUpdate)="_onForceRecordingUpdate()"
|
||||
></ov-recording-activity>
|
||||
<ov-broadcasting-activity
|
||||
*ngIf="showBroadcastingActivity"
|
||||
|
|
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
|
|
@ -117,6 +117,7 @@
|
|||
</button>
|
||||
|
||||
<button
|
||||
*ngIf="isSessionCreator"
|
||||
mat-icon-button
|
||||
class="delete-recording-btn"
|
||||
id="delete-recording-btn"
|
||||
|
|
|
@ -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',
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -76,6 +85,7 @@ export class RecordingActivityComponent implements OnInit {
|
|||
private recordingStatusSubscription: Subscription;
|
||||
private recordingListSubscription: Subscription;
|
||||
private recordingErrorSub: Subscription;
|
||||
private forceRecordingUpdateSub: Subscription;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
|
@ -85,6 +95,7 @@ export class RecordingActivityComponent implements OnInit {
|
|||
private participantService: ParticipantService,
|
||||
private libService: OpenViduAngularConfigService,
|
||||
private actionService: ActionService,
|
||||
private openviduService: OpenViduService,
|
||||
private cd: ChangeDetectorRef
|
||||
) {}
|
||||
|
||||
|
@ -94,6 +105,7 @@ export class RecordingActivityComponent implements OnInit {
|
|||
ngOnInit(): void {
|
||||
this.subscribeToRecordingStatus();
|
||||
this.subscribeToRecordingActivityDirective();
|
||||
this.subscribeToForceRecordingUpdate();
|
||||
this.isSessionCreator = this.participantService.amIModerator();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
});
|
||||
|
||||
|
@ -202,4 +223,10 @@ export class RecordingActivityComponent implements OnInit {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
private subscribeToForceRecordingUpdate() {
|
||||
this.forceRecordingUpdateSub = this.recordingService.forceUpdateRecordingsObs.subscribe(() => {
|
||||
this.onForceRecordingUpdate.emit();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
@ -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);
|
||||
|
@ -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() {
|
||||
|
|
|
@ -100,6 +100,7 @@
|
|||
(onStartRecordingClicked)="onStartRecordingClicked('panel')"
|
||||
(onStopRecordingClicked)="onStopRecordingClicked('panel')"
|
||||
(onDeleteRecordingClicked)="onDeleteRecordingClicked($event)"
|
||||
(onForceRecordingUpdate)="onForceRecordingUpdate()"
|
||||
(onStartBroadcastingClicked)="onStartBroadcastingClicked($event)"
|
||||
(onStopBroadcastingClicked)="onStopBroadcastingClicked('panel')"
|
||||
></ov-activities-panel>
|
||||
|
|
|
@ -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}.
|
||||
*/
|
||||
|
@ -692,6 +699,14 @@ export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewIni
|
|||
this.onActivitiesPanelDeleteRecordingClicked.emit(recordingId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
||||
onForceRecordingUpdate() {
|
||||
this.onActivitiesPanelForceRecordingUpdate.emit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
|
|
@ -3,5 +3,6 @@
|
|||
*/
|
||||
export enum Signal {
|
||||
NICKNAME_CHANGED = 'nicknameChanged',
|
||||
CHAT = 'chat'
|
||||
CHAT = 'chat',
|
||||
RECORDING_DELETED = 'recordingDeleted',
|
||||
}
|
||||
|
|
|
@ -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 = {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -656,4 +659,8 @@ export class OpenViduService {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private cleanConnectionData(data: string): string {
|
||||
return data.split('%/%')[0];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
@ -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 };
|
||||
|
@ -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}`;
|
||||
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
@ -41,6 +41,7 @@
|
|||
(onActivitiesPanelStartRecordingClicked)="onStartRecordingClicked()"
|
||||
(onActivitiesPanelStopRecordingClicked)="onStopRecordingClicked()"
|
||||
(onActivitiesPanelDeleteRecordingClicked)="onDeleteRecordingClicked($event)"
|
||||
(onActivitiesPanelForceRecordingUpdate)="onForceRecordingUpdate()"
|
||||
(onActivitiesPanelStartBroadcastingClicked)="onStartBroadcastingClicked($event)"
|
||||
(onActivitiesPanelStopBroadcastingClicked)="onStopBroadcastingClicked()"
|
||||
(onToolbarStopBroadcastingClicked)="onStopBroadcastingClicked()"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue