backend: Adds layout property to recording info
Adds the 'layout' property to recording information. This allows tracking the layout used during a recording, enhancing recording metadata. Updates recording schema and adds layout information to API responses.
This commit is contained in:
parent
a1acc9ba22
commit
4ecd086f21
@ -11,6 +11,7 @@ content:
|
||||
roomId: 'room-123'
|
||||
roomName: 'room'
|
||||
status: 'complete'
|
||||
layout: 'grid'
|
||||
filename: 'room-123--XX445.mp4'
|
||||
startDate: 1600000000000
|
||||
endDate: 1600000003600
|
||||
@ -25,5 +26,6 @@ content:
|
||||
roomId: 'room-456'
|
||||
roomName: 'room'
|
||||
status: 'active'
|
||||
layout: 'grid'
|
||||
filename: 'room-456--QR789.mp4'
|
||||
startDate: 1682500000000
|
||||
|
||||
@ -19,6 +19,7 @@ content:
|
||||
roomId: 'room-123'
|
||||
roomName: 'room'
|
||||
status: 'active'
|
||||
layout: 'grid'
|
||||
filename: 'room-123--XX445.mp4'
|
||||
startDate: 1620000000000
|
||||
endDate: 1620000003600
|
||||
@ -29,6 +30,7 @@ content:
|
||||
roomId: 'room-456'
|
||||
roomName: 'room'
|
||||
status: 'complete'
|
||||
layout: 'grid'
|
||||
filename: 'room-456--XX678.mp4'
|
||||
startDate: 1625000000000
|
||||
endDate: 1625000007200
|
||||
|
||||
@ -22,6 +22,10 @@ properties:
|
||||
enum: ['starting', 'active', 'ending', 'complete', 'failed', 'aborted', 'limit_reached']
|
||||
example: 'active'
|
||||
description: The status of the recording.
|
||||
layout:
|
||||
type: string
|
||||
example: 'grid'
|
||||
description: The layout of the recording.
|
||||
filename:
|
||||
type: string
|
||||
example: 'room-123--XX445.mp4'
|
||||
|
||||
@ -35,9 +35,9 @@ MeetRecordingConfig:
|
||||
- grid
|
||||
- speaker
|
||||
- single-speaker
|
||||
- grid-light
|
||||
- speaker-light
|
||||
- single-speaker-light
|
||||
# - grid-light
|
||||
# - speaker-light
|
||||
# - single-speaker-light
|
||||
default: grid
|
||||
example: grid
|
||||
description: |
|
||||
@ -45,9 +45,9 @@ MeetRecordingConfig:
|
||||
- `grid`: All participants are shown in a grid layout.
|
||||
- `speaker`: The active speaker is shown prominently, with other participants in smaller thumbnails.
|
||||
- `single-speaker`: Only the active speaker is shown in the recording.
|
||||
- `grid-light`: Similar to `grid` but with a light-themed background.
|
||||
- `speaker-light`: Similar to `speaker` but with a light-themed background.
|
||||
- `single-speaker-light`: Similar to `single-speaker` but with a light
|
||||
# - `grid-light`: Similar to `grid` but with a light-themed background.
|
||||
# - `speaker-light`: Similar to `speaker` but with a light-themed background.
|
||||
# - `single-speaker-light`: Similar to `single-speaker` but with a light-themed background.
|
||||
allowAccessTo:
|
||||
type: string
|
||||
enum:
|
||||
|
||||
@ -22,6 +22,11 @@ properties:
|
||||
status:
|
||||
type: string
|
||||
description: The status of the recording.
|
||||
example: active
|
||||
layout:
|
||||
type: string
|
||||
description: The layout of the recording.
|
||||
example: grid
|
||||
filename:
|
||||
type: string
|
||||
description: The name of the recording file.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { EgressStatus } from '@livekit/protocol';
|
||||
import { MeetRecordingInfo, MeetRecordingStatus } from '@openvidu-meet/typings';
|
||||
import { MeetRecordingInfo, MeetRecordingLayout, MeetRecordingStatus } from '@openvidu-meet/typings';
|
||||
import { EgressInfo } from 'livekit-server-sdk';
|
||||
import { container } from '../config/dependency-injector.config.js';
|
||||
import { RoomService } from '../services/room.service.js';
|
||||
@ -19,7 +19,7 @@ export class RecordingHelper {
|
||||
const filename = RecordingHelper.extractFilename(egressInfo);
|
||||
const recordingId = RecordingHelper.extractRecordingIdFromEgress(egressInfo);
|
||||
const { roomName: roomId, errorCode, error, details } = egressInfo;
|
||||
|
||||
const layout = RecordingHelper.extractRecordingLayout(egressInfo);
|
||||
const roomService = container.get(RoomService);
|
||||
const { roomName } = await roomService.getMeetRoom(roomId);
|
||||
|
||||
@ -28,6 +28,7 @@ export class RecordingHelper {
|
||||
roomId,
|
||||
roomName,
|
||||
// outputMode,
|
||||
layout,
|
||||
status,
|
||||
filename,
|
||||
startDate: startDateMs,
|
||||
@ -138,6 +139,23 @@ export class RecordingHelper {
|
||||
return `${meetRoomId}--${egressId}--${uid}`;
|
||||
}
|
||||
|
||||
static extractRecordingLayout(egressInfo: EgressInfo): MeetRecordingLayout | undefined {
|
||||
if (egressInfo.request.case !== 'roomComposite') return undefined;
|
||||
|
||||
const { layout } = egressInfo.request.value;
|
||||
|
||||
switch (layout) {
|
||||
case 'grid':
|
||||
return MeetRecordingLayout.GRID;
|
||||
case 'speaker':
|
||||
return MeetRecordingLayout.SPEAKER;
|
||||
case 'single-speaker':
|
||||
return MeetRecordingLayout.SINGLE_SPEAKER;
|
||||
default:
|
||||
return MeetRecordingLayout.GRID; // Default layout
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the room name, egressId, and UID from the given recordingId.
|
||||
* @param recordingId ${roomId}--${egressId}--${uid}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { MeetRecordingInfo, MeetRecordingStatus } from '@openvidu-meet/typings';
|
||||
import { MeetRecordingInfo, MeetRecordingLayout, MeetRecordingStatus } from '@openvidu-meet/typings';
|
||||
import { Document, model, Schema } from 'mongoose';
|
||||
import { INTERNAL_CONFIG } from '../../config/internal-config.js';
|
||||
|
||||
@ -43,6 +43,11 @@ const MeetRecordingSchema = new Schema<MeetRecordingDocument>(
|
||||
enum: Object.values(MeetRecordingStatus),
|
||||
required: true
|
||||
},
|
||||
layout: {
|
||||
type: String,
|
||||
enum: Object.values(MeetRecordingLayout),
|
||||
required: false
|
||||
},
|
||||
filename: {
|
||||
type: String,
|
||||
required: false
|
||||
|
||||
@ -195,6 +195,12 @@ export const expectValidRecording = (
|
||||
expect(recording.status).toBe(status);
|
||||
expect(recording.filename).toBeDefined();
|
||||
expect(recording.details).toBeDefined();
|
||||
expect(recording.layout).toBeDefined();
|
||||
|
||||
// Validate layout is a valid value
|
||||
if (recording.layout !== undefined) {
|
||||
expect(Object.values(MeetRecordingLayout)).toContain(recording.layout);
|
||||
}
|
||||
};
|
||||
|
||||
export const expectValidRoomWithFields = (room: MeetRoom, fields: string[] = []) => {
|
||||
@ -373,9 +379,15 @@ export const expectValidStartRecordingResponse = (response: Response, roomId: st
|
||||
expect(response.body).toHaveProperty('startDate');
|
||||
expect(response.body).toHaveProperty('status', 'active');
|
||||
expect(response.body).toHaveProperty('filename');
|
||||
expect(response.body).toHaveProperty('layout');
|
||||
expect(response.body).not.toHaveProperty('duration');
|
||||
expect(response.body).not.toHaveProperty('endDate');
|
||||
expect(response.body).not.toHaveProperty('size');
|
||||
|
||||
// Validate layout is a valid value
|
||||
if (response.body.layout !== undefined) {
|
||||
expect(Object.values(MeetRecordingLayout)).toContain(response.body.layout);
|
||||
}
|
||||
};
|
||||
|
||||
export const expectValidStopRecordingResponse = (
|
||||
@ -393,6 +405,12 @@ export const expectValidStopRecordingResponse = (
|
||||
expect(response.body).toHaveProperty('filename');
|
||||
expect(response.body).toHaveProperty('startDate');
|
||||
expect(response.body).toHaveProperty('duration', expect.any(Number));
|
||||
expect(response.body).toHaveProperty('layout');
|
||||
|
||||
// Validate layout is a valid value
|
||||
if (response.body.layout !== undefined) {
|
||||
expect(Object.values(MeetRecordingLayout)).toContain(response.body.layout);
|
||||
}
|
||||
|
||||
expectValidRecordingLocationHeader(response);
|
||||
};
|
||||
@ -432,6 +450,16 @@ export const expectValidGetRecordingResponse = (
|
||||
})
|
||||
);
|
||||
|
||||
// Validate layout property
|
||||
expect(body).toHaveProperty('layout');
|
||||
|
||||
if (body.layout !== undefined) {
|
||||
expect(body.layout).toBeDefined();
|
||||
expect(typeof body.layout).toBe('string');
|
||||
// Validate it's a valid MeetRecordingLayout value
|
||||
expect(Object.values(MeetRecordingLayout)).toContain(body.layout);
|
||||
}
|
||||
|
||||
expect(body.status).toBeDefined();
|
||||
|
||||
if (status !== undefined) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, it } from '@jest/globals';
|
||||
import { MeetRecordingInfo, MeetRecordingStatus, MeetWebhookEvent, MeetWebhookEventType } from '@openvidu-meet/typings';
|
||||
import { MeetRecordingInfo, MeetRecordingLayout, MeetRecordingStatus, MeetWebhookEvent, MeetWebhookEventType } from '@openvidu-meet/typings';
|
||||
import { Request } from 'express';
|
||||
import http from 'http';
|
||||
import {
|
||||
@ -148,6 +148,11 @@ describe('Webhook Integration Tests', () => {
|
||||
expect(recordingStartedWebhook?.headers['x-timestamp']).toBeDefined();
|
||||
expect(recordingStartedWebhook?.body.event).toBe(MeetWebhookEventType.RECORDING_STARTED);
|
||||
expect(data.status).toBe(MeetRecordingStatus.STARTING);
|
||||
expect(data.layout).toBeDefined();
|
||||
|
||||
if (data.layout) {
|
||||
expect(Object.values(MeetRecordingLayout)).toContain(data.layout);
|
||||
}
|
||||
|
||||
// Check recording_updated webhook
|
||||
const recordingUpdatedWebhook = receivedWebhooks.find(
|
||||
@ -163,6 +168,11 @@ describe('Webhook Integration Tests', () => {
|
||||
expect(recordingUpdatedWebhook?.headers['x-timestamp']).toBeDefined();
|
||||
expect(recordingUpdatedWebhook?.body.event).toBe(MeetWebhookEventType.RECORDING_UPDATED);
|
||||
expect(data.status).toBe(MeetRecordingStatus.ACTIVE);
|
||||
expect(data.layout).toBeDefined();
|
||||
|
||||
if (data.layout) {
|
||||
expect(Object.values(MeetRecordingLayout)).toContain(data.layout);
|
||||
}
|
||||
|
||||
// Check recording_ended webhook
|
||||
const recordingEndedWebhook = receivedWebhooks.find(
|
||||
@ -179,5 +189,10 @@ describe('Webhook Integration Tests', () => {
|
||||
expect(recordingEndedWebhook?.body.event).toBe(MeetWebhookEventType.RECORDING_ENDED);
|
||||
expect(data.status).not.toBe(MeetRecordingStatus.ENDING);
|
||||
expect(data.status).toBe(MeetRecordingStatus.COMPLETE);
|
||||
expect(data.layout).toBeDefined();
|
||||
|
||||
if (data.layout) {
|
||||
expect(Object.values(MeetRecordingLayout)).toContain(data.layout);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -30,8 +30,8 @@ export interface MeetRecordingInfo {
|
||||
roomId: string;
|
||||
roomName: string;
|
||||
// outputMode: MeetRecordingOutputMode;
|
||||
// layout: MeetRecordingLayout;
|
||||
status: MeetRecordingStatus;
|
||||
layout?: MeetRecordingLayout;
|
||||
filename?: string;
|
||||
startDate?: number;
|
||||
endDate?: number;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user