backend: Enhance Livekit webhook handling with OpenVidu integration for recording events
This commit is contained in:
parent
e644e1b434
commit
9f0877780e
@ -1,7 +1,6 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { LoggerService } from '../services/logger.service.js';
|
||||
import { LivekitWebhookService } from '../services/livekit-webhook.service.js';
|
||||
import { RoomService } from '../services/room.service.js';
|
||||
import { WebhookEvent } from 'livekit-server-sdk';
|
||||
import { OpenViduWebhookService } from '../services/openvidu-webhook.service.js';
|
||||
import { container } from '../config/dependency-injector.config.js';
|
||||
|
||||
@ -1,3 +1,24 @@
|
||||
export enum OpenViduWebhookEvent {
|
||||
ROOM_FINISHED = 'room_finished',
|
||||
import { RecordingStatus } from './recording.model.js';
|
||||
|
||||
export interface OpenViduWebhookEvent {
|
||||
createdAt: number;
|
||||
event: OpenViduWebhookEventType;
|
||||
data: RoomWebhookData | RecordingWebhookData;
|
||||
}
|
||||
|
||||
export enum OpenViduWebhookEventType {
|
||||
RECORDING_STARTED = 'recording_started',
|
||||
RECORDING_STOPPED = 'recording_stopped',
|
||||
ROOM_FINISHED = 'room_finished'
|
||||
}
|
||||
|
||||
export interface RecordingWebhookData {
|
||||
recordingId: string;
|
||||
filename?: string;
|
||||
roomName: string;
|
||||
status: RecordingStatus;
|
||||
}
|
||||
|
||||
export interface RoomWebhookData {
|
||||
roomName: string;
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import { RoomService } from './room.service.js';
|
||||
import { S3Service } from './s3.service.js';
|
||||
import { RoomStatusData } from '../models/room.model.js';
|
||||
import { RecordingService } from './recording.service.js';
|
||||
import { OpenViduWebhookService } from './openvidu-webhook.service.js';
|
||||
|
||||
@injectable()
|
||||
export class LivekitWebhookService {
|
||||
@ -20,7 +21,8 @@ export class LivekitWebhookService {
|
||||
@inject(RecordingService) protected recordingService: RecordingService,
|
||||
@inject(LiveKitService) protected livekitService: LiveKitService,
|
||||
@inject(RoomService) protected roomService: RoomService,
|
||||
@inject(LoggerService) protected logger: LoggerService
|
||||
@inject(LoggerService) protected logger: LoggerService,
|
||||
@inject(OpenViduWebhookService) protected openViduWebhookService: OpenViduWebhookService
|
||||
) {
|
||||
this.webhookReceiver = new WebhookReceiver(LIVEKIT_API_KEY, LIVEKIT_API_SECRET);
|
||||
}
|
||||
@ -86,18 +88,24 @@ export class LivekitWebhookService {
|
||||
|
||||
const { roomName } = egressInfo;
|
||||
|
||||
let payload: RecordingInfo | undefined = undefined;
|
||||
let recordingInfo: RecordingInfo | undefined = undefined;
|
||||
|
||||
this.logger.info(`Recording egress '${egressInfo.egressId}' updated: ${egressInfo.status}`);
|
||||
const topic: DataTopic = RecordingHelper.getDataTopicFromStatus(egressInfo);
|
||||
payload = RecordingHelper.toRecordingInfo(egressInfo);
|
||||
recordingInfo = RecordingHelper.toRecordingInfo(egressInfo);
|
||||
|
||||
// Add recording metadata
|
||||
const metadataPath = this.generateMetadataPath(payload);
|
||||
await Promise.all([
|
||||
this.s3Service.saveObject(metadataPath, payload),
|
||||
this.roomService.sendSignal(roomName, payload, { topic })
|
||||
]);
|
||||
const metadataPath = this.generateMetadataPath(recordingInfo);
|
||||
const promises = [
|
||||
this.s3Service.saveObject(metadataPath, recordingInfo),
|
||||
this.roomService.sendSignal(roomName, recordingInfo, { topic })
|
||||
];
|
||||
|
||||
if(recordingInfo.status === RecordingStatus.STARTED) {
|
||||
promises.push(this.openViduWebhookService.sendRecordingStartedWebhook(recordingInfo));
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
} catch (error) {
|
||||
this.logger.warn(`Error sending data on egress updated: ${error}`);
|
||||
}
|
||||
@ -124,7 +132,8 @@ export class LivekitWebhookService {
|
||||
const metadataPath = this.generateMetadataPath(payload);
|
||||
await Promise.all([
|
||||
this.s3Service.saveObject(metadataPath, payload),
|
||||
this.roomService.sendSignal(roomName, payload, { topic })
|
||||
this.roomService.sendSignal(roomName, payload, { topic }),
|
||||
this.openViduWebhookService.sendRecordingStoppedWebhook(payload)
|
||||
]);
|
||||
} catch (error) {
|
||||
this.logger.warn(`Error sending data on egress ended: ${error}`);
|
||||
|
||||
@ -3,31 +3,59 @@ import { inject, injectable } from '../config/dependency-injector.config.js';
|
||||
import { Room } from 'livekit-server-sdk';
|
||||
import { LoggerService } from './logger.service.js';
|
||||
import { MEET_API_KEY, MEET_WEBHOOK_ENABLED, MEET_WEBHOOK_URL } from '../environment.js';
|
||||
import { OpenViduWebhookEvent } from '../models/webhook.model.js';
|
||||
import { OpenViduWebhookEvent, OpenViduWebhookEventType } from '../models/webhook.model.js';
|
||||
import { RecordingInfo } from '../models/recording.model.js';
|
||||
|
||||
@injectable()
|
||||
export class OpenViduWebhookService {
|
||||
constructor(@inject(LoggerService) protected logger: LoggerService) {}
|
||||
|
||||
async sendRoomFinishedWebhook(room: Room) {
|
||||
await this.sendWebhookEvent(OpenViduWebhookEvent.ROOM_FINISHED, {
|
||||
room: {
|
||||
name: room.name
|
||||
const data: OpenViduWebhookEvent = {
|
||||
event: OpenViduWebhookEventType.ROOM_FINISHED,
|
||||
createdAt: Date.now(),
|
||||
data: {
|
||||
roomName: room.name
|
||||
}
|
||||
});
|
||||
};
|
||||
await this.sendWebhookEvent(data);
|
||||
}
|
||||
|
||||
private async sendWebhookEvent(eventType: OpenViduWebhookEvent, data: object) {
|
||||
async sendRecordingStartedWebhook(recordingInfo: RecordingInfo) {
|
||||
const data: OpenViduWebhookEvent = {
|
||||
event: OpenViduWebhookEventType.RECORDING_STARTED,
|
||||
createdAt: Date.now(),
|
||||
data: {
|
||||
recordingId: recordingInfo.id,
|
||||
filename: recordingInfo.filename,
|
||||
roomName: recordingInfo.roomName,
|
||||
status: recordingInfo.status
|
||||
}
|
||||
};
|
||||
await this.sendWebhookEvent(data);
|
||||
}
|
||||
|
||||
async sendRecordingStoppedWebhook(recordingInfo: RecordingInfo) {
|
||||
const data: OpenViduWebhookEvent = {
|
||||
event: OpenViduWebhookEventType.RECORDING_STOPPED,
|
||||
createdAt: Date.now(),
|
||||
data: {
|
||||
recordingId: recordingInfo.id,
|
||||
filename: recordingInfo.filename,
|
||||
roomName: recordingInfo.roomName,
|
||||
status: recordingInfo.status
|
||||
}
|
||||
};
|
||||
await this.sendWebhookEvent(data);
|
||||
}
|
||||
|
||||
private async sendWebhookEvent(data: OpenViduWebhookEvent) {
|
||||
if (!this.isWebhookEnabled()) return;
|
||||
|
||||
const payload = {
|
||||
event: eventType,
|
||||
...data
|
||||
};
|
||||
const timestamp = Date.now();
|
||||
const signature = this.generateWebhookSignature(timestamp, payload);
|
||||
const timestamp = data.createdAt;
|
||||
const signature = this.generateWebhookSignature(timestamp, data);
|
||||
|
||||
this.logger.verbose(`Sending webhook event ${eventType}`);
|
||||
this.logger.info(`Sending webhook event ${data.event}`);
|
||||
|
||||
try {
|
||||
await this.fetchWithRetry(MEET_WEBHOOK_URL, {
|
||||
@ -37,10 +65,10 @@ export class OpenViduWebhookService {
|
||||
'X-Timestamp': timestamp.toString(),
|
||||
'X-Signature': signature
|
||||
},
|
||||
body: JSON.stringify(payload)
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error(`Error sending webhook event ${eventType}: ${error}`);
|
||||
this.logger.error(`Error sending webhook event ${data.event}: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@ -64,7 +92,7 @@ export class OpenViduWebhookService {
|
||||
throw new Error(`Request failed: ${error}`);
|
||||
}
|
||||
|
||||
this.logger.verbose(`Retrying in ${delay / 1000} seconds... (${retries} retries left)`);
|
||||
this.logger.warn(`Retrying in ${delay / 1000} seconds... (${retries} retries left)`);
|
||||
await new Promise((resolve) => setTimeout(resolve, delay));
|
||||
// Retry the request after a delay with exponential backoff
|
||||
return this.fetchWithRetry(url, options, retries - 1, delay * 2);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user