backend: implement getRecordingMedia method in StorageProvider and S3StorageProvider, refactor RecordingService to use it

This commit is contained in:
Carlos Santos 2025-05-29 14:07:29 +02:00
parent e4b77eb2f6
commit 37ed8003f5
5 changed files with 55 additions and 4 deletions

View File

@ -432,7 +432,7 @@ export class RecordingService {
return this.getFullStreamResponse(recordingPath, fileSize);
}
const fileStream = await this.s3Service.getObjectAsStream(recordingPath, MEET_S3_BUCKET, {
const fileStream = await this.storageService.getRecordingMedia(recordingPath, {
start,
end
});
@ -446,7 +446,7 @@ export class RecordingService {
recordingPath: string,
fileSize: number
): Promise<{ fileSize: number; fileStream: Readable }> {
const fileStream = await this.s3Service.getObjectAsStream(recordingPath, MEET_S3_BUCKET);
const fileStream = await this.storageService.getRecordingMedia(recordingPath);
return { fileSize, fileStream };
}

View File

@ -187,8 +187,8 @@ export class S3Service {
async getObjectAsStream(
name: string,
bucket: string = MEET_S3_BUCKET,
range?: { start: number; end: number }
range?: { start: number; end: number },
bucket: string = MEET_S3_BUCKET
): Promise<Readable> {
try {
const obj = await this.getObject(name, bucket, range);

View File

@ -5,6 +5,7 @@ import INTERNAL_CONFIG from '../../../config/internal-config.js';
import { errorRecordingNotFound, OpenViduMeetError, RedisKeyName } from '../../../models/index.js';
import { LoggerService, RedisService, S3Service, StorageProvider } from '../../index.js';
import { RecordingHelper } from '../../../helpers/recording.helper.js';
import { Readable } from 'stream';
/**
* Implementation of the StorageProvider interface using AWS S3 for persistent storage
@ -384,6 +385,22 @@ export class S3StorageProvider<
}
}
async getRecordingMedia(
recordingPath: string,
range?: {
end: number;
start: number;
}
): Promise<Readable> {
try {
this.logger.debug(`Retrieving recording media from S3 at path: ${recordingPath}`);
return await this.s3Service.getObjectAsStream(recordingPath, range);
} catch (error) {
this.handleError(error, `Error fetching recording media for path ${recordingPath}`);
throw error;
}
}
/**
* Deletes multiple recording binary files from S3 storage using their file paths.
*

View File

@ -1,4 +1,5 @@
import { GlobalPreferences, MeetRecordingInfo, MeetRoom } from '@typings-ce';
import { Readable } from 'stream';
/**
* An interface that defines the contract for storage providers in the OpenVidu Meet application.
@ -149,6 +150,19 @@ export interface StorageProvider<
*/
deleteRecordingMetadataByPaths(metadataPaths: string[]): Promise<void>;
/**
* Retrieves the media content of a recording file.
*
* @param recordingPath - The path of the recording file to retrieve.
* @param range - An optional range object specifying the start and end byte positions to retrieve.
*/
getRecordingMedia(
recordingPath: string,
range?: {
end: number;
start: number;
}
): Promise<Readable>;
/**
* Deletes multiple recording binary files by their paths.
*

View File

@ -5,6 +5,7 @@ import { MEET_NAME_ID, MEET_SECRET, MEET_USER, MEET_WEBHOOK_ENABLED, MEET_WEBHOO
import { MeetLock, PasswordHelper } from '../../helpers/index.js';
import { errorRoomNotFound, internalError, OpenViduMeetError } from '../../models/error.model.js';
import { LoggerService, MutexService, StorageFactory, StorageProvider } from '../index.js';
import { Readable } from 'stream';
/**
* A service for managing storage operations related to OpenVidu Meet rooms and preferences.
@ -221,6 +222,25 @@ export class MeetStorageService<
}>;
}
/**
* Retrieves recording media as a readable stream from the storage provider.
*
* @param recordingPath - The path to the recording file in storage
* @param range - Optional byte range for partial content retrieval
* @param range.start - Starting byte position
* @param range.end - Ending byte position
* @returns A Promise that resolves to a Readable stream of the recording media
*/
async getRecordingMedia(
recordingPath: string,
range?: {
end: number;
start: number;
}
): Promise<Readable> {
return this.storageProvider.getRecordingMedia(recordingPath, range) as Promise<Readable>;
}
/**
* Deletes multiple recording metadata files by their paths.
*