diff --git a/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.html b/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.html
index 962e731..3771830 100644
--- a/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.html
+++ b/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.html
@@ -11,16 +11,15 @@
[toolbarScreenshareButton]="features().showScreenShare"
[toolbarLeaveButton]="!features().canModerateRoom"
[toolbarRecordingButton]="features().canRecordRoom"
- [toolbarViewRecordingsButton]="features().showRecordings"
+ [toolbarViewRecordingsButton]="features().canRetrieveRecordings && hasRecordings"
[toolbarBroadcastingButton]="false"
[toolbarChatPanelButton]="features().showChat"
[toolbarBackgroundEffectsButton]="features().showBackgrounds"
[toolbarParticipantsPanelButton]="features().showParticipantList"
[toolbarSettingsButton]="features().showSettings"
[toolbarFullscreenButton]="features().showFullscreen"
- [toolbarActivitiesPanelButton]="features().showRecordings"
- [activitiesPanelRecordingActivity]="features().showRecordings"
- [recordingActivityReadOnly]="!features().canRecordRoom"
+ [toolbarActivitiesPanelButton]="features().showRecordingPanel"
+ [activitiesPanelRecordingActivity]="features().showRecordingPanel"
[recordingActivityShowControls]="{
play: false,
download: false,
@@ -28,7 +27,7 @@
externalView: true
}"
[recordingActivityStartStopRecordingButton]="features().canRecordRoom"
- [recordingActivityViewRecordingsButton]="features().showRecordings"
+ [recordingActivityViewRecordingsButton]="features().canRetrieveRecordings && hasRecordings"
[recordingActivityShowRecordingsList]="false"
[activitiesPanelBroadcastingActivity]="false"
[showDisconnectionDialog]="false"
@@ -37,8 +36,7 @@
(onParticipantLeft)="onParticipantLeft($event)"
(onRecordingStartRequested)="onRecordingStartRequested($event)"
(onRecordingStopRequested)="onRecordingStopRequested($event)"
- (onViewRecordingsClicked)="onViewRecordingsClicked(undefined)"
- (onViewRecordingClicked)="onViewRecordingsClicked($event)"
+ (onViewRecordingsClicked)="onViewRecordingsClicked()"
>
@if (features().canModerateRoom) {
diff --git a/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.ts b/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.ts
index 4b26c6f..36547a4 100644
--- a/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.ts
+++ b/frontend/projects/shared-meet-components/src/lib/pages/meeting/meeting.component.ts
@@ -84,6 +84,8 @@ export class MeetingComponent implements OnInit {
participantForm = new FormGroup({
name: new FormControl('', [Validators.required, Validators.minLength(4)])
});
+
+ hasRecordings = false;
showRecordingCard = false;
showBackButton = true;
@@ -175,13 +177,23 @@ export class MeetingComponent implements OnInit {
*/
private async checkForRecordings() {
try {
- await this.recordingService.generateRecordingToken(this.roomId, this.roomSecret);
+ const { canRetrieveRecordings } = await this.recordingService.generateRecordingToken(
+ this.roomId,
+ this.roomSecret
+ );
+
+ if (!canRetrieveRecordings) {
+ this.showRecordingCard = false;
+ return;
+ }
+
const { recordings } = await this.recordingService.listRecordings({
maxItems: 1,
roomId: this.roomId,
fields: 'recordingId'
});
- this.showRecordingCard = recordings.length > 0;
+ this.hasRecordings = recordings.length > 0;
+ this.showRecordingCard = this.hasRecordings;
} catch (error) {
console.error('Error checking for recordings:', error);
this.showRecordingCard = false;
@@ -326,10 +338,34 @@ export class MeetingComponent implements OnInit {
const event = JSON.parse(new TextDecoder().decode(payload));
switch (topic) {
+ case 'recordingStopped': {
+ // If a 'recordingStopped' event is received and there was no previous recordings,
+ // update the hasRecordings flag and refresh the recording token
+ if (this.hasRecordings) return;
+
+ this.hasRecordings = true;
+
+ try {
+ await this.recordingService.generateRecordingToken(this.roomId, this.roomSecret);
+ } catch (error) {
+ console.error('Error refreshing recording token:', error);
+ }
+
+ break;
+ }
case MeetSignalType.MEET_ROOM_PREFERENCES_UPDATED: {
// Update room preferences
const { preferences } = event as MeetRoomPreferencesUpdatedPayload;
this.featureConfService.setRoomPreferences(preferences);
+
+ // Refresh recording token if recording is enabled
+ if (preferences.recordingPreferences.enabled) {
+ try {
+ await this.recordingService.generateRecordingToken(this.roomId, this.roomSecret);
+ } catch (error) {
+ console.error('Error refreshing recording token:', error);
+ }
+ }
break;
}
case MeetSignalType.MEET_PARTICIPANT_ROLE_UPDATED: {
@@ -504,11 +540,7 @@ Remember that by default, a recording uses 4 CPUs for each room.`
}
}
- async onViewRecordingsClicked(recordingId?: any) {
- if (recordingId) {
- await this.recordingService.playRecording(recordingId);
- } else {
- window.open(`/room/${this.roomId}/recordings?secret=${this.roomSecret}`, '_blank');
- }
+ async onViewRecordingsClicked() {
+ window.open(`/room/${this.roomId}/recordings?secret=${this.roomSecret}`, '_blank');
}
}
diff --git a/frontend/projects/shared-meet-components/src/lib/services/feature-configuration.service.ts b/frontend/projects/shared-meet-components/src/lib/services/feature-configuration.service.ts
index 5215821..2506766 100644
--- a/frontend/projects/shared-meet-components/src/lib/services/feature-configuration.service.ts
+++ b/frontend/projects/shared-meet-components/src/lib/services/feature-configuration.service.ts
@@ -1,5 +1,11 @@
import { computed, Injectable, signal } from '@angular/core';
-import { MeetRoomPreferences, ParticipantPermissions, ParticipantRole, TrackSource } from '@lib/typings/ce';
+import {
+ MeetRoomPreferences,
+ ParticipantPermissions,
+ ParticipantRole,
+ RecordingPermissions,
+ TrackSource
+} from '@lib/typings/ce';
import { LoggerService } from 'openvidu-components-angular';
/**
@@ -14,7 +20,7 @@ export interface ApplicationFeatures {
showScreenShare: boolean;
// UI Features
- showRecordings: boolean;
+ showRecordingPanel: boolean;
showChat: boolean;
showBackgrounds: boolean;
showParticipantList: boolean;
@@ -24,6 +30,7 @@ export interface ApplicationFeatures {
// Permissions
canModerateRoom: boolean;
canRecordRoom: boolean;
+ canRetrieveRecordings: boolean;
}
/**
@@ -36,7 +43,7 @@ const DEFAULT_FEATURES: ApplicationFeatures = {
showMicrophone: true,
showScreenShare: true,
- showRecordings: true,
+ showRecordingPanel: true,
showChat: true,
showBackgrounds: true,
showParticipantList: true,
@@ -44,7 +51,8 @@ const DEFAULT_FEATURES: ApplicationFeatures = {
showFullscreen: true,
canModerateRoom: false,
- canRecordRoom: false
+ canRecordRoom: false,
+ canRetrieveRecordings: false
};
/**
@@ -61,10 +69,16 @@ export class FeatureConfigurationService {
protected roomPreferences = signal
(undefined);
protected participantPermissions = signal(undefined);
protected participantRole = signal(undefined);
+ protected recordingPermissions = signal(undefined);
// Computed signal to derive features based on current configurations
public readonly features = computed(() =>
- this.calculateFeatures(this.roomPreferences(), this.participantPermissions(), this.participantRole())
+ this.calculateFeatures(
+ this.roomPreferences(),
+ this.participantPermissions(),
+ this.participantRole(),
+ this.recordingPermissions()
+ )
);
constructor(protected loggerService: LoggerService) {
@@ -95,6 +109,14 @@ export class FeatureConfigurationService {
this.participantRole.set(role);
}
+ /**
+ * Updates recording permissions
+ */
+ setRecordingPermissions(permissions: RecordingPermissions): void {
+ this.log.d('Updating recording permissions', permissions);
+ this.recordingPermissions.set(permissions);
+ }
+
/**
* Checks if a specific feature is enabled
*/
@@ -108,14 +130,15 @@ export class FeatureConfigurationService {
protected calculateFeatures(
roomPrefs?: MeetRoomPreferences,
participantPerms?: ParticipantPermissions,
- role?: ParticipantRole
+ role?: ParticipantRole,
+ recordingPerms?: RecordingPermissions
): ApplicationFeatures {
// Start with default configuration
const features: ApplicationFeatures = { ...DEFAULT_FEATURES };
// Apply room configurations
if (roomPrefs) {
- features.showRecordings = roomPrefs.recordingPreferences.enabled;
+ features.showRecordingPanel = roomPrefs.recordingPreferences.enabled;
features.showChat = roomPrefs.chatPreferences.enabled;
features.showBackgrounds = roomPrefs.virtualBackgroundPreferences.enabled;
}
@@ -123,8 +146,7 @@ export class FeatureConfigurationService {
// Apply participant permissions (these can restrict enabled features)
if (participantPerms) {
// Only restrict if the feature is already enabled
- if (features.showRecordings) {
- // features.showRecordings = !!recordingRole;
+ if (features.showRecordingPanel) {
features.canRecordRoom = participantPerms.openvidu.canRecord;
}
if (features.showChat) {
@@ -149,6 +171,11 @@ export class FeatureConfigurationService {
features.canModerateRoom = role === ParticipantRole.MODERATOR;
}
+ // Apply recording permissions
+ if (recordingPerms) {
+ features.canRetrieveRecordings = recordingPerms.canRetrieveRecordings;
+ }
+
this.log.d('Calculated features', features);
return features;
}
diff --git a/frontend/projects/shared-meet-components/src/lib/services/recording.service.ts b/frontend/projects/shared-meet-components/src/lib/services/recording.service.ts
index 9a95318..913d2db 100644
--- a/frontend/projects/shared-meet-components/src/lib/services/recording.service.ts
+++ b/frontend/projects/shared-meet-components/src/lib/services/recording.service.ts
@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { ShareRecordingDialogComponent } from '@lib/components';
-import { AuthService, HttpService, ParticipantService } from '@lib/services';
+import { AuthService, FeatureConfigurationService, HttpService, ParticipantService } from '@lib/services';
import { MeetRecordingFilters, MeetRecordingInfo, RecordingPermissions } from '@lib/typings/ce';
import { getValidDecodedToken } from '@lib/utils';
import { LoggerService } from 'openvidu-components-angular';
@@ -25,6 +25,7 @@ export class RecordingService {
private httpService: HttpService,
protected participantService: ParticipantService,
protected authService: AuthService,
+ protected featureConfService: FeatureConfigurationService,
protected dialog: MatDialog
) {
this.log = this.loggerService.get('OpenVidu Meet - RecordingManagerService');
@@ -159,10 +160,18 @@ export class RecordingService {
*/
async generateRecordingToken(roomId: string, secret: string): Promise {
const path = `${HttpService.INTERNAL_API_PATH_PREFIX}/rooms/${roomId}/recording-token`;
- const { token } = await this.httpService.postRequest<{ token: string }>(path, { secret });
- this.setRecordingPermissionsFromToken(token);
- return this.recordingPermissions;
+ try {
+ const { token } = await this.httpService.postRequest<{ token: string }>(path, { secret });
+ this.setRecordingPermissionsFromToken(token);
+ return this.recordingPermissions;
+ } catch (error) {
+ this.featureConfService.setRecordingPermissions({
+ canRetrieveRecordings: false,
+ canDeleteRecordings: false
+ });
+ throw error;
+ }
}
/**
@@ -174,6 +183,9 @@ export class RecordingService {
try {
const decodedToken = getValidDecodedToken(token);
this.recordingPermissions = decodedToken.metadata.recordingPermissions;
+
+ // Update feature configuration
+ this.featureConfService.setRecordingPermissions(this.recordingPermissions);
} catch (error) {
this.log.e('Error setting recording permissions from token', error);
throw new Error('Error setting recording permissions from token');