ov-components: enhance recording status management and elapsed time calculation

master
Carlos Santos 2025-04-30 19:18:29 +02:00
parent cff617b0c3
commit 8ce155df6a
2 changed files with 113 additions and 77 deletions

View File

@ -47,6 +47,7 @@ import {
Track Track
} from 'livekit-client'; } from 'livekit-client';
import { ParticipantLeftEvent, ParticipantLeftReason, ParticipantModel } from '../../models/participant.model'; import { ParticipantLeftEvent, ParticipantLeftReason, ParticipantModel } from '../../models/participant.model';
import { RecordingStatus } from '../../models/recording.model';
/** /**
* @internal * @internal
@ -456,7 +457,8 @@ export class SessionComponent implements OnInit, OnDestroy {
this.recordingService.setRecordingList(recordingList); this.recordingService.setRecordingList(recordingList);
if (isRecordingStarted) { if (isRecordingStarted) {
this.recordingService.setRecordingStarted(); const recordingActive = recordingList.find((recording) => recording.status === RecordingStatus.STARTED);
this.recordingService.setRecordingStarted(recordingActive);
} }
if (isBroadcastingStarted) { if (isBroadcastingStarted) {
this.broadcastingService.setBroadcastingStarted(broadcastingId); this.broadcastingService.setBroadcastingStarted(broadcastingId);

View File

@ -15,87 +15,92 @@ export class RecordingService {
*/ */
recordingStatusObs: Observable<RecordingStatusInfo>; recordingStatusObs: Observable<RecordingStatusInfo>;
private recordingTimeInterval: NodeJS.Timeout; private recordingTimeInterval: NodeJS.Timeout;
private recordingStartTimestamp: number | null = null;
private recordingStatus = <BehaviorSubject<RecordingStatusInfo>>new BehaviorSubject({ private recordingStatus = <BehaviorSubject<RecordingStatusInfo>>new BehaviorSubject({
status: RecordingStatus.STOPPED, status: RecordingStatus.STOPPED,
recordingList: [] as RecordingInfo[], recordingList: [] as RecordingInfo[],
recordingElapsedTime: new Date(0, 0, 0, 0, 0, 0, 0) recordingElapsedTime: new Date(0, 0, 0, 0, 0, 0),
}); });
private log: ILogger; private log: ILogger;
/** /**
* @internal * @internal
*/ */
constructor(private actionService: ActionService, private libService: OpenViduComponentsConfigService, private loggerService: LoggerService) { constructor(
private actionService: ActionService,
private libService: OpenViduComponentsConfigService,
private loggerService: LoggerService
) {
this.log = this.loggerService.get('RecordingService'); this.log = this.loggerService.get('RecordingService');
this.recordingStatusObs = this.recordingStatus.asObservable(); this.recordingStatusObs = this.recordingStatus.asObservable();
} }
/** /**
* Initializes the recording status with the given parameters and the timer to calculate the elapsed time.
* @internal * @internal
* @param event
*/ */
setRecordingStarted(recordingInfo?: RecordingInfo) { setRecordingStarted(recordingInfo?: RecordingInfo, startTimestamp?: number) {
this.startRecordingTime(); // Register the start timestamp of the recording
const { recordingElapsedTime, recordingList } = this.recordingStatus.getValue(); // to calculate the elapsed time
debugger
this.recordingStartTimestamp = recordingInfo?.startedAt || Date.now();
// Initialize the recording elapsed time
this.startRecordingTimer();
const { recordingList } = this.recordingStatus.getValue();
let updatedRecordingList = [...recordingList];
if (recordingInfo) { if (recordingInfo) {
const existingRecordingIndex = recordingList.findIndex((recording) => recording.id === recordingInfo.id); const existingIndex = updatedRecordingList.findIndex((recording) => recording.id === recordingInfo.id);
if (existingRecordingIndex !== -1) { if (existingIndex !== -1) {
// Replace existing recording info // Replace existing recording info
recordingList[existingRecordingIndex] = recordingInfo; updatedRecordingList[existingIndex] = recordingInfo;
} else { } else {
// Add new recording info // Add new recording info
recordingList.unshift(recordingInfo); updatedRecordingList = [recordingInfo, ...updatedRecordingList];
} }
} }
const statusInfo: RecordingStatusInfo = { const recordingElapsedTime = new Date(0, 0, 0, 0, 0, 0);
status: RecordingStatus.STARTED, if (startTimestamp) {
recordingList, const elapsedSeconds = Math.floor((Date.now() - startTimestamp) / 1000);
recordingElapsedTime recordingElapsedTime.setSeconds(elapsedSeconds);
};
this.updateStatus(statusInfo);
} }
/**
* Deletes a recording from the recording list.
*
* @param rec - The recording to be deleted.
* @internal
*/
deleteRecording(rec: RecordingInfo) {
const { recordingList } = this.recordingStatus.getValue();
const index = recordingList.findIndex((recording) => recording.id === rec.id);
if (index !== -1) {
recordingList.splice(index, 1);
this.updateStatus({ this.updateStatus({
status: this.recordingStatus.getValue().status, status: RecordingStatus.STARTED,
recordingList, recordingList: updatedRecordingList,
recordingElapsedTime: this.recordingStatus.getValue().recordingElapsedTime recordingElapsedTime
}); });
} }
}
/** /**
* Stops the recording timer and updates the recording status to **stopped**.
* @internal * @internal
* @param event
*/ */
setRecordingStopped(recordingInfo?: RecordingInfo) { setRecordingStopped(recordingInfo?: RecordingInfo) {
this.stopRecordingTime(); this.stopRecordingTimer();
const { recordingElapsedTime, recordingList } = this.recordingStatus.getValue(); const { recordingList } = this.recordingStatus.getValue();
let updatedRecordingList = [...recordingList];
// Update the recording list with the new recording info
if (recordingInfo) { if (recordingInfo) {
const existingRecordingIndex = recordingList.findIndex((recording) => recording.id === recordingInfo.id); const existingIndex = updatedRecordingList.findIndex((recording) => recording.id === recordingInfo.id);
if (existingRecordingIndex !== -1) { if (existingIndex !== -1) {
// Replace existing recording info with the new one updatedRecordingList[existingIndex] = recordingInfo;
recordingList[existingRecordingIndex] = recordingInfo;
} else { } else {
recordingList.unshift(recordingInfo); updatedRecordingList = [recordingInfo, ...updatedRecordingList];
} }
} }
const statusInfo: RecordingStatusInfo = {
this.updateStatus({
status: RecordingStatus.STOPPED, status: RecordingStatus.STOPPED,
recordingList, recordingList: updatedRecordingList,
recordingElapsedTime recordingElapsedTime: new Date(0, 0, 0, 0, 0, 0)
}; });
this.updateStatus(statusInfo);
this.recordingStartTimestamp = null;
} }
/** /**
@ -104,12 +109,11 @@ export class RecordingService {
*/ */
setRecordingStarting() { setRecordingStarting() {
const { recordingList, recordingElapsedTime } = this.recordingStatus.getValue(); const { recordingList, recordingElapsedTime } = this.recordingStatus.getValue();
const statusInfo: RecordingStatusInfo = { this.updateStatus({
status: RecordingStatus.STARTING, status: RecordingStatus.STARTING,
recordingList, recordingList,
recordingElapsedTime recordingElapsedTime
}; });
this.updateStatus(statusInfo);
} }
/** /**
@ -117,7 +121,7 @@ export class RecordingService {
* @param error * @param error
*/ */
setRecordingFailed(error: string) { setRecordingFailed(error: string) {
this.stopRecordingTime(); this.stopRecordingTimer();
const { recordingElapsedTime, recordingList } = this.recordingStatus.getValue(); const { recordingElapsedTime, recordingList } = this.recordingStatus.getValue();
const statusInfo: RecordingStatusInfo = { const statusInfo: RecordingStatusInfo = {
status: RecordingStatus.FAILED, status: RecordingStatus.FAILED,
@ -134,12 +138,12 @@ export class RecordingService {
*/ */
setRecordingStopping() { setRecordingStopping() {
const { recordingElapsedTime, recordingList } = this.recordingStatus.getValue(); const { recordingElapsedTime, recordingList } = this.recordingStatus.getValue();
const statusInfo: RecordingStatusInfo = {
this.updateStatus({
status: RecordingStatus.STOPPING, status: RecordingStatus.STOPPING,
recordingList, recordingList,
recordingElapsedTime recordingElapsedTime
}; });
this.updateStatus(statusInfo);
} }
/** /**
@ -174,10 +178,29 @@ export class RecordingService {
view: window view: window
}) })
); );
setTimeout(() => {
// For Firefox it is necessary to delay revoking the ObjectURL // For Firefox it is necessary to delay revoking the ObjectURL
link.remove(); setTimeout(() => link.remove(), 100);
}, 100); }
/**
* Deletes a recording from the recording list.
*
* @param recording - The recording to be deleted.
* @internal
*/
deleteRecording(recording: RecordingInfo) {
const { recordingList, status, recordingElapsedTime } = this.recordingStatus.getValue();
const updatedList = recordingList.filter((item) => item.id !== recording.id);
if (updatedList.length !== recordingList.length) {
this.updateStatus({
status,
recordingList: updatedList,
recordingElapsedTime
});
return true;
}
return false;
} }
/** /**
@ -187,13 +210,12 @@ export class RecordingService {
*/ */
setRecordingList(recordings: RecordingInfo[]) { setRecordingList(recordings: RecordingInfo[]) {
const { status, recordingElapsedTime, error } = this.recordingStatus.getValue(); const { status, recordingElapsedTime, error } = this.recordingStatus.getValue();
const statusInfo: RecordingStatusInfo = { this.updateStatus({
status, status,
recordingList: recordings, recordingList: recordings,
recordingElapsedTime, recordingElapsedTime,
error error
}; });
this.updateStatus(statusInfo);
} }
/** /**
@ -210,23 +232,35 @@ export class RecordingService {
}); });
} }
private startRecordingTime() { private startRecordingTimer() {
if (this.recordingStartTimestamp === null) {
this.recordingStartTimestamp = Date.now();
}
if (this.recordingTimeInterval) {
clearInterval(this.recordingTimeInterval);
}
this.recordingTimeInterval = setInterval(() => { this.recordingTimeInterval = setInterval(() => {
let { recordingElapsedTime, recordingList, status } = this.recordingStatus.getValue(); if (!this.recordingStartTimestamp) return;
let { recordingElapsedTime } = this.recordingStatus.getValue();
if (recordingElapsedTime) { if (recordingElapsedTime) {
recordingElapsedTime.setSeconds(recordingElapsedTime.getSeconds() + 1); // Calculamos con precisión el tiempo transcurrido
recordingElapsedTime = new Date(recordingElapsedTime.getTime()); const elapsedSeconds = Math.floor((Date.now() - this.recordingStartTimestamp) / 1000);
const statusInfo: RecordingStatusInfo = { const updatedElapsedTime = new Date(0, 0, 0, 0, 0, 0);
updatedElapsedTime.setSeconds(elapsedSeconds);
const { recordingList, status } = this.recordingStatus.getValue();
this.updateStatus({
status, status,
recordingList, recordingList,
recordingElapsedTime recordingElapsedTime: updatedElapsedTime
}; });
this.updateStatus(statusInfo);
} }
}, 1000); }, 1000);
} }
private stopRecordingTime() { private stopRecordingTimer() {
clearInterval(this.recordingTimeInterval); clearInterval(this.recordingTimeInterval);
const { recordingList, status, error } = this.recordingStatus.getValue(); const { recordingList, status, error } = this.recordingStatus.getValue();
const statusInfo: RecordingStatusInfo = { const statusInfo: RecordingStatusInfo = {