src/conferences/ConferenceManager.ts
import EventEmitter from 'eventemitter3';
import {cloneDeep} from 'lodash-es';
import Logs from '../Logs';
import {Conference, MediaTypes, PermissionManager} from '../Sippo';
import {Conference as Room} from '../wac-proxy/stacks/Janus/Conference';
import {Conferences as Rooms} from '../wac-proxy/stacks/Janus/Conferences';
import {ErrorResponse} from '../wac-proxy/stacks/Janus/signaling/ErrorResponse';
import {KManageService} from '../wac-proxy/wac-stack/KManageService';
import {ConferenceLogService} from '../wac-proxy/wac-stack/conference-log/ConferenceLogService';
import {ConferenceLog} from './ConferenceLog';
import {AccessBeforeTimeError} from './errors/AccessBeforeTimeError';
import {WrongPasswordError} from './errors/WrongPasswordError';
const log = Logs.instance.getLogger('SippoJS/calls/ConferenceManager');
interface Events {
conferenceInvitation: Conference;
}
/**
* This class provides access to every conference related feature
* This class must never be directly instantiated, instances of this class
* can be obtained using Session#getConferenceManager.
*
* ## Events
*
* - `conferenceInvitation` {@link Conference} - Emitted every time an invitation to
* a conference room is received
*/
export class ConferenceManager {
emitter = new EventEmitter<Events>();
conferences = new Map();
private conferenceLog?: ConferenceLog;
private defaultMediaConstraints: MediaStreamConstraints = {
audio: true,
video: true,
};
/** @nodoc */
private constructor(
private conferenceService: Rooms,
private kManageService: KManageService,
private conferenceLogService: ConferenceLogService,
private permissionManager: PermissionManager,
private ownId: string,
) {
this.onIncomingConference = this.onIncomingConference.bind(this);
this.conferenceService.emitter.on('incomingConference', this.onIncomingConference);
}
private async onIncomingConference(room: Room, mediaTypes: MediaTypes): Promise<void> {
const conference = await this.getConferenceForRoom(room);
await conference.getLocalMediaHandler().setMediaTypes(mediaTypes);
this.conferences.set(room.id, conference);
this.emitter.emit('conferenceInvitation', conference);
}
/**
* Returns current media constraints used by default when creating conferences
*/
getDefaultMediaConstraints(): MediaStreamConstraints {
return cloneDeep(this.defaultMediaConstraints);
}
/**
* Sets the constraints that will be used as initial value for media requests when
* initializing conferences. For more information about valid values see
* {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamConstraints}
* @example <caption>Enabling noise reduction techniques</caption>
* conferenceManager.setDefaultMediaConstraints({
* audio: {
* autoGainControl: true,
* echoCancellation: true,
* noiseSuppression: true,
* },
* video: true,
* });
* @param {MediaStreamConstraints} defaultMediaConstraints
*/
setDefaultMediaConstraints(defaultMediaConstraints: MediaStreamConstraints): void {
this.defaultMediaConstraints = cloneDeep(defaultMediaConstraints);
}
/**
* Creates a new conference room
* @param options
* @param options.recording boolean indicating whether the conference is going to be recorded or not. This applies
* only if the user has {@link PermissionManager.canSetRecordOption} permission
*/
async createConference(options: {recording?: boolean} = {recording: false}): Promise<Conference> {
let recording;
try {
recording = this.permissionManager.canSetRecordOption() ? options.recording : this.permissionManager.canAlwaysRecord();
} catch (error) {
if (error instanceof Error) {
log.error('Error acquiring permissions: ', error.message);
recording = false;
} else {
throw error;
}
}
const room = await this.conferenceService.create(recording);
return this.getConferenceForRoom(room);
}
/**
* Returns a ConferenceLog reference
*/
getConferenceLog(): ConferenceLog {
if (this.conferenceLog === undefined) {
this.conferenceLog = new ConferenceLog(this.conferenceLogService, this.ownId);
}
return this.conferenceLog;
}
/**
* Asks the server for the current invitations available to the user
*/
async getCurrentInvitations(): Promise<Conference[]> {
log.debug('calls/ConferenceManager.js/getCurrentInvitations');
return this.conferenceService.getCurrentInvitations().then(async (rooms) => {
log.debug('calls/ConferenceManager.js/getCurrentInvitations/rooms', rooms);
const conferences = rooms.map(async (room) => {
const conference = await this.getConferenceForRoom(room);
if (room.invite) {
await conference.getLocalMediaHandler().setMediaTypes(room.invite.mediatypes);
}
this.conferences.set(room.id, conference);
this.emitter.emit('conferenceInvitation', conference);
return conference;
});
return Promise.all(conferences);
});
}
/**
* Get a Conference by uri
*
* @param uri
* @deprecated Use "getMeetingConference" or "getQuickConference" instead
*/
async getConferenceByUri(uri: string): Promise<Conference> {
const room = await this.conferenceService.getRoomByUri(uri);
return this.getConferenceForRoom(room);
}
/**
* Get a conference for a meeting
*
* @param meetingId
* @param password
* @returns fulfilled when the response is received
* @throws {AccessBeforeTimeError} thrown when trying to access the conference before scheduled time
* @throws {WrongPasswordError} thrown when the conference is protected by password and the password is not recived or is not valid.
*/
async getMeetingConference(meetingId: string, password?: string): Promise<Conference> {
try {
const conferenceRoom = await this.conferenceService.getMeetingConference(meetingId, password);
return await this.getConferenceForRoom(conferenceRoom);
} catch (error) {
if (error instanceof ErrorResponse) {
switch (error.message) {
case 410:
throw new AccessBeforeTimeError();
case 403:
throw new WrongPasswordError();
}
}
throw error;
}
}
/**
* Gets a quick conference
*
* @param quickConferenceId
* @returns fulfilled when the response is received
*
*/
async getQuickConference(quickConferenceId: string): Promise<Conference> {
const conferenceRoom = await this.conferenceService.getQuickConference(quickConferenceId);
return await this.getConferenceForRoom(conferenceRoom);
}
private async getConferenceForRoom(room: Room): Promise<Conference> {
return Conference.create(room, this.kManageService, this.defaultMediaConstraints);
}
}