backend: refactor recording start process and add room validation checks

This commit is contained in:
Carlos Santos 2025-04-23 18:13:22 +02:00
parent b87e548cdf
commit 7a8f3fbe69

View File

@ -57,50 +57,43 @@ export class RecordingService {
async startRecording(roomId: string): Promise<MeetRecordingInfo> {
let acquiredLock: RedisLock | null = null;
let eventListener!: (info: Record<string, unknown>) => void;
try {
const room = await this.roomService.getMeetRoom(roomId);
if (!room) throw errorRoomNotFound(roomId);
//TODO: Check if the room has participants before starting the recording
//room.numParticipants === 0 ? throw errorNoParticipants(roomId);
const lkRoom = await this.livekitService.getRoom(roomId);
if (!lkRoom) throw errorRoomNotFound(roomId);
const hasParticipants = await this.livekitService.roomHasParticipants(roomId);
if (!hasParticipants) throw errorRoomHasNoParticipants(roomId);
await this.validateRoomsPreconditions(roomId);
// Attempt to acquire lock. If the lock is not acquired, the recording is already active.
acquiredLock = await this.acquireRoomRecordingActiveLock(roomId);
if (!acquiredLock) throw errorRecordingAlreadyStarted(roomId);
let resolveRecording!: (r: MeetRecordingInfo) => void;
let rejectRecording!: (e: unknown) => void;
const recordingPromise = new Promise<MeetRecordingInfo>((res, rej) => {
resolveRecording = res;
rejectRecording = rej;
});
let recordingId = '';
eventListener = (info: Record<string, unknown>) => {
// This listener is triggered only for the instance that started the recording.
// Check if the recording ID matches the one that was started
const isEventForCurrentRecording = info?.recordingId === recordingId && info?.roomId === roomId;
if (isEventForCurrentRecording) {
this.taskSchedulerService.cancelTask(`${roomId}_recording_timeout`);
this.systemEventService.off(SystemEventType.RECORDING_ACTIVE, eventListener);
resolveRecording(info as unknown as MeetRecordingInfo);
}
};
this.systemEventService.on(SystemEventType.RECORDING_ACTIVE, eventListener);
this.registerRecordingTimeout(roomId, recordingId, eventListener, rejectRecording);
const options = this.generateCompositeOptionsFromRequest();
const output = this.generateFileOutputFromRequest(roomId);
const egressInfo = await this.livekitService.startRoomComposite(roomId, output, options);
const recordingInfo = RecordingHelper.toRecordingInfo(egressInfo);
const { recordingId } = recordingInfo;
const recordingPromise = new Promise<MeetRecordingInfo>((resolve, reject) => {
const eventListener = (info: Record<string, unknown>) => {
// This listener is triggered only for the instance that started the recording.
// Check if the recording ID matches the one that was started
const isEventForCurrentRecording = info?.recordingId === recordingId && info?.roomId === roomId;
if (isEventForCurrentRecording) {
this.taskSchedulerService.cancelTask(`${roomId}_recording_timeout`);
this.systemEventService.off(SystemEventType.RECORDING_ACTIVE, eventListener);
resolve(info as unknown as MeetRecordingInfo);
}
};
this.registerRecordingTimeout(roomId, recordingId, eventListener, reject);
this.systemEventService.on(SystemEventType.RECORDING_ACTIVE, eventListener);
});
recordingId = recordingInfo.recordingId;
return await recordingPromise;
} catch (error) {
@ -108,6 +101,10 @@ export class RecordingService {
if (acquiredLock) await this.releaseRoomRecordingActiveLock(roomId);
if (eventListener) this.systemEventService.off(SystemEventType.RECORDING_ACTIVE, eventListener);
this.taskSchedulerService.cancelTask(`${roomId}_recording_timeout`);
throw error;
}
}
@ -369,6 +366,22 @@ export class RecordingService {
return this.roomService.sendSignal(roomId, payload, options);
}
protected async validateRoomsPreconditions(roomId: string): Promise<void> {
const room = await this.roomService.getMeetRoom(roomId);
if (!room) throw errorRoomNotFound(roomId);
//TODO: Check if the room has participants before starting the recording
//room.numParticipants === 0 ? throw errorNoParticipants(roomId);
const lkRoom = await this.livekitService.getRoom(roomId);
if (!lkRoom) throw errorRoomNotFound(roomId);
const hasParticipants = await this.livekitService.roomHasParticipants(roomId);
if (!hasParticipants) throw errorRoomHasNoParticipants(roomId);
}
/**
* Retrieves the data required to delete a recording, including the file paths
* to be deleted and the recording's metadata information.