From 027e85052ac620442936608ab968ff634ac8db02 Mon Sep 17 00:00:00 2001 From: csantosm <4a.santos@gmail.com> Date: Wed, 22 Feb 2023 23:23:43 +0100 Subject: [PATCH] openvidu-node-client: Added .prettierrc file and formated the code --- openvidu-node-client/.prettierrc | 10 + openvidu-node-client/src/Connection.ts | 30 +- .../src/ConnectionProperties.ts | 11 +- openvidu-node-client/src/ConnectionType.ts | 3 +- .../src/IceServerProperties.ts | 4 +- openvidu-node-client/src/MediaMode.ts | 1 - openvidu-node-client/src/OpenVidu.ts | 1401 ++++++++--------- openvidu-node-client/src/OpenViduRole.ts | 3 +- openvidu-node-client/src/Publisher.ts | 5 +- openvidu-node-client/src/Recording.ts | 47 +- openvidu-node-client/src/RecordingLayout.ts | 1 - openvidu-node-client/src/RecordingMode.ts | 1 - .../src/RecordingProperties.ts | 4 +- openvidu-node-client/src/Session.ts | 254 +-- openvidu-node-client/src/SessionProperties.ts | 3 +- openvidu-node-client/src/TokenOptions.ts | 15 +- openvidu-node-client/src/VideoCodec.ts | 2 +- openvidu-node-client/src/index.ts | 2 +- 18 files changed, 897 insertions(+), 900 deletions(-) create mode 100644 openvidu-node-client/.prettierrc diff --git a/openvidu-node-client/.prettierrc b/openvidu-node-client/.prettierrc new file mode 100644 index 00000000..bc72fe80 --- /dev/null +++ b/openvidu-node-client/.prettierrc @@ -0,0 +1,10 @@ +{ + "singleQuote": true, + "printWidth": 140, + "trailingComma": "none", + "semi": true, + "bracketSpacing": true, + "useTabs": false, + "jsxSingleQuote": true, + "tabWidth": 4 +} diff --git a/openvidu-node-client/src/Connection.ts b/openvidu-node-client/src/Connection.ts index c6fd7e09..81dba051 100644 --- a/openvidu-node-client/src/Connection.ts +++ b/openvidu-node-client/src/Connection.ts @@ -24,7 +24,6 @@ import { IceServerProperties } from './IceServerProperties'; * See {@link Session.connections} */ export class Connection { - /** * Identifier of the Connection. You can call methods {@link Session.forceDisconnect} * or {@link Session.updateConnection} passing this property as parameter @@ -119,7 +118,6 @@ export class Connection { * @hidden */ resetWithJson(json): Connection { - this.connectionId = json.connectionId; this.status = json.status; this.createdAt = json.createdAt; @@ -139,7 +137,7 @@ export class Connection { this.connectionProperties.adaptativeBitrate = json.adaptativeBitrate; this.connectionProperties.onlyPlayWithSubscribers = json.onlyPlayWithSubscribers; this.connectionProperties.networkCache = json.networkCache; - this.connectionProperties.customIceServers = json.customIceServers ?? [] + this.connectionProperties.customIceServers = json.customIceServers ?? []; } else { this.connectionProperties = { type: json.type, @@ -152,21 +150,19 @@ export class Connection { onlyPlayWithSubscribers: json.onlyPlayWithSubscribers, networkCache: json.networkCache, customIceServers: json.customIceServers ?? [] - } + }; } this.role = json.role; this.serverData = json.serverData; // publishers may be null if (json.publishers != null) { - // 1. Array to store fetched Publishers and later remove closed ones const fetchedPublisherIds: string[] = []; - json.publishers.forEach(jsonPublisher => { - + json.publishers.forEach((jsonPublisher) => { const publisherObj: Publisher = new Publisher(jsonPublisher); fetchedPublisherIds.push(publisherObj.streamId); - let storedPublisher = this.publishers.find(c => c.streamId === publisherObj.streamId); + let storedPublisher = this.publishers.find((c) => c.streamId === publisherObj.streamId); if (!!storedPublisher) { // 2. Update existing Publisher @@ -183,16 +179,14 @@ export class Connection { this.publishers.splice(i, 1); } } - } // subscribers may be null if (json.subscribers != null) { - // 1. Array to store fetched Subscribers and later remove closed ones const fetchedSubscriberIds: string[] = []; - json.subscribers.forEach(jsonSubscriber => { - fetchedSubscriberIds.push(jsonSubscriber.streamId) + json.subscribers.forEach((jsonSubscriber) => { + fetchedSubscriberIds.push(jsonSubscriber.streamId); if (this.subscribers.indexOf(jsonSubscriber.streamId) === -1) { // 2. Add new Subscriber this.subscribers.push(jsonSubscriber.streamId); @@ -214,7 +208,7 @@ export class Connection { * @hidden */ equalTo(other: Connection): boolean { - let equals: boolean = ( + let equals: boolean = this.connectionId === other.connectionId && this.status === other.status && this.createdAt === other.createdAt && @@ -234,18 +228,19 @@ export class Connection { this.platform === other.platform && this.clientData === other.clientData && this.subscribers.length === other.subscribers.length && - this.publishers.length === other.publishers.length); + this.publishers.length === other.publishers.length; if (equals) { if (this.connectionProperties.kurentoOptions != null) { - equals = JSON.stringify(this.connectionProperties.kurentoOptions) === JSON.stringify(other.connectionProperties.kurentoOptions); + equals = + JSON.stringify(this.connectionProperties.kurentoOptions) === JSON.stringify(other.connectionProperties.kurentoOptions); } else { - equals = (this.connectionProperties.kurentoOptions === other.connectionProperties.kurentoOptions); + equals = this.connectionProperties.kurentoOptions === other.connectionProperties.kurentoOptions; } } if (equals) { if (this.connectionProperties.customIceServers != null) { // Order alphabetically Ice servers using url just to keep the same list order. - const simpleIceComparator = (a: IceServerProperties, b: IceServerProperties) => (a.url > b.url) ? 1 : -1 + const simpleIceComparator = (a: IceServerProperties, b: IceServerProperties) => (a.url > b.url ? 1 : -1); const sortedIceServers = this.connectionProperties.customIceServers.sort(simpleIceComparator); const sortedOtherIceServers = other.connectionProperties.customIceServers.sort(simpleIceComparator); equals = JSON.stringify(sortedIceServers) === JSON.stringify(sortedOtherIceServers); @@ -280,5 +275,4 @@ export class Connection { this.connectionProperties.role = newConnectionProperties.role; } } - } diff --git a/openvidu-node-client/src/ConnectionProperties.ts b/openvidu-node-client/src/ConnectionProperties.ts index 7508f943..718454b7 100644 --- a/openvidu-node-client/src/ConnectionProperties.ts +++ b/openvidu-node-client/src/ConnectionProperties.ts @@ -20,7 +20,6 @@ import { ConnectionType } from './ConnectionType'; import { OpenViduRole } from './OpenViduRole'; export interface ConnectionProperties { - /** * Type of Connection. The {@link ConnectionType} dictates what properties will have effect: * @@ -85,11 +84,11 @@ export interface ConnectionProperties { * **Only for {@link ConnectionType.WEBRTC}** */ kurentoOptions?: { - videoMaxRecvBandwidth?: number, - videoMinRecvBandwidth?: number, - videoMaxSendBandwidth?: number, - videoMinSendBandwidth?: number, - allowedFilters?: string[] + videoMaxRecvBandwidth?: number; + videoMinRecvBandwidth?: number; + videoMaxSendBandwidth?: number; + videoMinSendBandwidth?: number; + allowedFilters?: string[]; }; /** diff --git a/openvidu-node-client/src/ConnectionType.ts b/openvidu-node-client/src/ConnectionType.ts index 554fc2bb..c658d14a 100644 --- a/openvidu-node-client/src/ConnectionType.ts +++ b/openvidu-node-client/src/ConnectionType.ts @@ -19,7 +19,6 @@ * See {@link Session.createConnection} */ export enum ConnectionType { - /** * WebRTC connection. This is the normal type of Connection for a regular user * connecting to a session from an application. @@ -31,4 +30,4 @@ export enum ConnectionType { * connect to a session. */ IPCAM = 'IPCAM' -} \ No newline at end of file +} diff --git a/openvidu-node-client/src/IceServerProperties.ts b/openvidu-node-client/src/IceServerProperties.ts index a2174332..9acb3a3b 100644 --- a/openvidu-node-client/src/IceServerProperties.ts +++ b/openvidu-node-client/src/IceServerProperties.ts @@ -16,7 +16,6 @@ */ export interface IceServerProperties { - /** * Set the url for the ICE Server you want to use. * It should follow a valid format: @@ -46,5 +45,4 @@ export interface IceServerProperties { * This parameter should be defined only for TURN, not for STUN ICE Servers. */ credential?: string; - -} \ No newline at end of file +} diff --git a/openvidu-node-client/src/MediaMode.ts b/openvidu-node-client/src/MediaMode.ts index a678ca49..b5e071cd 100644 --- a/openvidu-node-client/src/MediaMode.ts +++ b/openvidu-node-client/src/MediaMode.ts @@ -19,7 +19,6 @@ * See {@link SessionProperties.mediaMode} */ export enum MediaMode { - /** * _(not available yet)_ The session will attempt to transmit streams directly between clients */ diff --git a/openvidu-node-client/src/OpenVidu.ts b/openvidu-node-client/src/OpenVidu.ts index 5c78a6a2..54b5b0c4 100644 --- a/openvidu-node-client/src/OpenVidu.ts +++ b/openvidu-node-client/src/OpenVidu.ts @@ -25,727 +25,718 @@ import { SessionProperties } from './SessionProperties'; /** * @hidden */ -interface ObjMap { [s: string]: T; } - +interface ObjMap { + [s: string]: T; +} export class OpenVidu { + private Buffer = require('buffer/').Buffer; - private Buffer = require('buffer/').Buffer; + /** + * @hidden + */ + public host: string; + /** + * @hidden + */ + public basicAuth: string; - /** - * @hidden - */ - public host: string; - /** - * @hidden - */ - public basicAuth: string; + /** + * @hidden + */ + static readonly API_PATH: string = '/openvidu/api'; + /** + * @hidden + */ + static readonly API_SESSIONS = OpenVidu.API_PATH + '/sessions'; + /** + * @hidden + */ + static readonly API_TOKENS = OpenVidu.API_PATH + '/tokens'; + /** + * @hidden + */ + static readonly API_RECORDINGS: string = OpenVidu.API_PATH + '/recordings'; + /** + * @hidden + */ + static readonly API_RECORDINGS_START: string = OpenVidu.API_RECORDINGS + '/start'; + /** + * @hidden + */ + static readonly API_RECORDINGS_STOP: string = OpenVidu.API_RECORDINGS + '/stop'; + /** + * @hidden + */ + static readonly API_BROADCAST: string = OpenVidu.API_PATH + '/broadcast'; + /** + * @hidden + */ + static readonly API_BROADCAST_START: string = OpenVidu.API_BROADCAST + '/start'; + /** + * @hidden + */ + static readonly API_BROADCAST_STOP: string = OpenVidu.API_BROADCAST + '/stop'; - /** - * @hidden - */ - static readonly API_PATH: string = '/openvidu/api'; - /** - * @hidden - */ - static readonly API_SESSIONS = OpenVidu.API_PATH + '/sessions'; - /** - * @hidden - */ - static readonly API_TOKENS = OpenVidu.API_PATH + '/tokens'; - /** - * @hidden - */ - static readonly API_RECORDINGS: string = OpenVidu.API_PATH + '/recordings'; - /** - * @hidden - */ - static readonly API_RECORDINGS_START: string = OpenVidu.API_RECORDINGS + '/start'; - /** - * @hidden - */ - static readonly API_RECORDINGS_STOP: string = OpenVidu.API_RECORDINGS + '/stop'; - /** - * @hidden - */ - static readonly API_BROADCAST: string = OpenVidu.API_PATH + '/broadcast'; - /** - * @hidden - */ - static readonly API_BROADCAST_START: string = OpenVidu.API_BROADCAST + '/start'; - /** - * @hidden - */ - static readonly API_BROADCAST_STOP: string = OpenVidu.API_BROADCAST + '/stop'; + /** + * Array of active sessions. **This value will remain unchanged since the last time method {@link OpenVidu.fetch} + * was called**. Exceptions to this rule are: + * + * - Calling {@link OpenVidu.createSession} automatically adds the new Session object to the local collection. + * - Calling {@link Session.fetch} updates that specific Session status + * - Calling {@link Session.close} automatically removes the Session from the list of active Sessions + * - Calling {@link Session.forceDisconnect} automatically updates the inner affected connections for that specific Session + * - Calling {@link Session.forceUnpublish} also automatically updates the inner affected connections for that specific Session + * - Calling {@link Session.updateConnection} automatically updates the inner affected connection for that specific Session + * - Calling {@link OpenVidu.startRecording} and {@link OpenVidu.stopRecording} automatically updates the recording status of the Session ({@link Session.recording}) + * + * To get the array of active sessions with their current actual value, you must call {@link OpenVidu.fetch} before consulting + * property {@link activeSessions} + */ + activeSessions: Session[] = []; + /** + * @param hostname URL where your OpenVidu deployment is up an running. + * It must be the full URL (e.g. `https://12.34.56.78:1234/`) + * + * @param secret Secret configured in your OpenVidu deployment + */ + constructor(private hostname: string, secret: string) { + this.setHostnameAndPort(); + this.basicAuth = this.getBasicAuth(secret); + } - - /** - * Array of active sessions. **This value will remain unchanged since the last time method {@link OpenVidu.fetch} - * was called**. Exceptions to this rule are: - * - * - Calling {@link OpenVidu.createSession} automatically adds the new Session object to the local collection. - * - Calling {@link Session.fetch} updates that specific Session status - * - Calling {@link Session.close} automatically removes the Session from the list of active Sessions - * - Calling {@link Session.forceDisconnect} automatically updates the inner affected connections for that specific Session - * - Calling {@link Session.forceUnpublish} also automatically updates the inner affected connections for that specific Session - * - Calling {@link Session.updateConnection} automatically updates the inner affected connection for that specific Session - * - Calling {@link OpenVidu.startRecording} and {@link OpenVidu.stopRecording} automatically updates the recording status of the Session ({@link Session.recording}) - * - * To get the array of active sessions with their current actual value, you must call {@link OpenVidu.fetch} before consulting - * property {@link activeSessions} - */ - activeSessions: Session[] = []; - - /** - * @param hostname URL where your OpenVidu deployment is up an running. - * It must be the full URL (e.g. `https://12.34.56.78:1234/`) - * - * @param secret Secret configured in your OpenVidu deployment - */ - constructor(private hostname: string, secret: string) { - this.setHostnameAndPort(); - this.basicAuth = this.getBasicAuth(secret); - } - - /** - * Creates an OpenVidu session. The session identifier will be available at property {@link Session.sessionId} - * - * @returns A Promise that is resolved to the {@link Session} if success and rejected with an - * [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) object if not. - * This Error object has as `message` property with a status code carrying a specific meaning - * (see [REST API](/en/stable/reference-docs/REST-API/#post-recording-start)). - * - * This method will never return an Error with status `409`. If a session with the same `customSessionId` already - * exists in OpenVidu Server, a {@link Session.fetch} operation is performed in the background and the updated Session - * object is returned. - */ - public createSession(properties?: SessionProperties): Promise { - return new Promise((resolve, reject) => { - const session = new Session(this, properties); - session.getSessionHttp() - .then(response => { - this.activeSessions.push(session); - resolve(session); - }) - .catch(error => { - reject(error); - }); - }); - } - - public startRecording(sessionId: string): Promise; - public startRecording(sessionId: string, name: string): Promise; - public startRecording(sessionId: string, properties: RecordingProperties): Promise; - - /** - * Starts the recording of a {@link Session} - * - * @param sessionId The `sessionId` of the {@link Session} you want to start recording - * @param name The name you want to give to the video file. You can access this same value in your clients on recording events (`recordingStarted`, `recordingStopped`) - * @param properties Custom RecordingProperties to apply to this Recording. This will override the global default values set to the Session with {@link SessionProperties.defaultRecordingProperties} - * - * @returns A Promise that is resolved to the {@link Recording} if it successfully started (the recording can be stopped with guarantees) and rejected with an - * [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) object if not. - * This Error object has as `message` property with a status code carrying a specific meaning - * (see [REST API](/en/stable/reference-docs/REST-API/#post-recording-start)). - */ - public startRecording(sessionId: string, param2?: string | RecordingProperties): Promise { - return new Promise((resolve, reject) => { - - let data; - - if (param2 != null) { - if (typeof param2 === 'string') { - data = JSON.stringify({ - session: sessionId, - name: param2 - }); - } else { - const properties: RecordingProperties = param2 as RecordingProperties; - data = { - session: sessionId, - name: properties.name, - outputMode: properties.outputMode, - recordingLayout: properties.recordingLayout, - customLayout: properties.customLayout, - ignoreFailedStreams: properties.ignoreFailedStreams, - resolution: properties.resolution, - frameRate: properties.frameRate, - hasAudio: properties.hasAudio, - hasVideo: properties.hasVideo, - shmSize: properties.shmSize, - mediaNode: properties.mediaNode - }; - data = JSON.stringify(data); - } - } else { - data = JSON.stringify({ - session: sessionId, - name: '' - }); - } - - axios.post( - this.host + OpenVidu.API_RECORDINGS_START, - data, - { - headers: { - 'Authorization': this.basicAuth, - 'Content-Type': 'application/json' - } - } - ) - .then(res => { - if (res.status === 200) { - // SUCCESS response from openvidu-server (Recording in JSON format). Resolve new Recording - const r: Recording = new Recording(res.data); - const activeSession = this.activeSessions.find(s => s.sessionId === r.sessionId); - if (!!activeSession) { - activeSession.recording = true; - } else { - console.warn("No active session found for sessionId '" + r.sessionId + "'. This instance of OpenVidu Node Client didn't create this session"); - } - resolve(r); - } else { - // ERROR response from openvidu-server. Resolve HTTP status - reject(new Error(res.status.toString())); - } - }).catch(error => { - this.handleError(error, reject); - }); - }); - } - - /** - * Stops the recording of a {@link Session} - * - * @param recordingId The `id` property of the {@link Recording} you want to stop - * - * @returns A Promise that is resolved to the {@link Recording} if it successfully stopped and rejected with an - * [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) object if not. - * This Error object has as `message` property with a status code carrying a specific meaning - * (see [REST API](/en/stable/reference-docs/REST-API/#post-recording-stop)). - */ - public stopRecording(recordingId: string): Promise { - return new Promise((resolve, reject) => { - - axios.post( - this.host + OpenVidu.API_RECORDINGS_STOP + '/' + recordingId, - undefined, - { - headers: { - 'Authorization': this.basicAuth, - 'Content-Type': 'application/x-www-form-urlencoded' - } - } - ) - .then(res => { - if (res.status === 200) { - // SUCCESS response from openvidu-server (Recording in JSON format). Resolve new Recording - const r: Recording = new Recording(res.data); - const activeSession = this.activeSessions.find(s => s.sessionId === r.sessionId); - if (!!activeSession) { - activeSession.recording = false; - } else { - console.warn("No active session found for sessionId '" + r.sessionId + "'. This instance of OpenVidu Node Client didn't create this session"); - } - resolve(r); - } else { - // ERROR response from openvidu-server. Resolve HTTP status - reject(new Error(res.status.toString())); - } - }).catch(error => { - this.handleError(error, reject); - }); - }); - } - - /** - * Gets an existing {@link Recording} - * - * @param recordingId The `id` property of the {@link Recording} you want to retrieve - * - * @returns A Promise that is resolved to the {@link Recording} if it successfully stopped and rejected with an - * [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) object if not. - * This Error object has as `message` property with a status code carrying a specific meaning - * (see [REST API](/en/stable/reference-docs/REST-API/#get-recording)). - */ - public getRecording(recordingId: string): Promise { - return new Promise((resolve, reject) => { - - axios.get( - this.host + OpenVidu.API_RECORDINGS + '/' + recordingId, - { - headers: { - 'Authorization': this.basicAuth, - 'Content-Type': 'application/x-www-form-urlencoded' - } - } - ) - .then(res => { - if (res.status === 200) { - // SUCCESS response from openvidu-server (Recording in JSON format). Resolve new Recording - resolve(new Recording(res.data)); - } else { - // ERROR response from openvidu-server. Resolve HTTP status - reject(new Error(res.status.toString())); - } - }).catch(error => { - this.handleError(error, reject); - }); - }); - } - - /** - * Lists all existing recordings - * - * @returns A Promise that is resolved to an array with all existing recordings - */ - public listRecordings(): Promise { - return new Promise((resolve, reject) => { - - axios.get( - this.host + OpenVidu.API_RECORDINGS, - { - headers: { - Authorization: this.basicAuth - } - } - ) - .then(res => { - if (res.status === 200) { - // SUCCESS response from openvidu-server (JSON arrays of recordings in JSON format). Resolve list of new recordings - const recordingArray: Recording[] = []; - const responseItems = res.data.items; - for (const item of responseItems) { - recordingArray.push(new Recording(item)); - } - // Order recordings by time of creation (newest first) - recordingArray.sort((r1, r2) => (r1.createdAt < r2.createdAt) ? 1 : ((r2.createdAt < r1.createdAt) ? -1 : 0)); - resolve(recordingArray); - } else { - // ERROR response from openvidu-server. Resolve HTTP status - reject(new Error(res.status.toString())); - } - }).catch(error => { - this.handleError(error, reject); - }); - }); - } - - /** - * Deletes a {@link Recording}. The recording must have status `stopped`, `ready` or `failed` - * - * @param recordingId - * - * @returns A Promise that is resolved if the Recording was successfully deleted and rejected with an - * [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) object if not. - * This Error object has as `message` property with a status code carrying a specific meaning - * (see [REST API](/en/stable/reference-docs/REST-API/#delete-recording)). - */ - public deleteRecording(recordingId: string): Promise { - return new Promise((resolve, reject) => { - - axios.delete( - this.host + OpenVidu.API_RECORDINGS + '/' + recordingId, - { - headers: { - 'Authorization': this.basicAuth, - 'Content-Type': 'application/x-www-form-urlencoded' - } - } - ) - .then(res => { - if (res.status === 204) { - // SUCCESS response from openvidu-server. Resolve undefined - resolve(undefined); - } else { - // ERROR response from openvidu-server. Resolve HTTP status - reject(new Error(res.status.toString())); - } - }).catch(error => { - this.handleError(error, reject); - }); - }); - } - - public startBroadcast(sessionId: string, broadcastUrl: string): Promise; - public startBroadcast(sessionId: string, broadcastUrl: string, properties: RecordingProperties): Promise; - - /** - * Starts the broadcast of a {@link Session} - * - * @param sessionId The `sessionId` of the {@link Session} you want to start broadcasting - * @param broadcastUrl The URL where to broadcast - * @param properties The configuration for this broadcast. It uses a subset of the {@link RecordingProperties}: - * - {@link RecordingProperties.hasAudio} - * - {@link RecordingProperties.resolution} - * - {@link RecordingProperties.frameRate} - * - {@link RecordingProperties.recordingLayout} - * - {@link RecordingProperties.customLayout} - * - {@link RecordingProperties.shmSize} - * - {@link RecordingProperties.mediaNode} - * - * @returns A Promise that is resolved if the broadcast successfully started and rejected with an - * [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) object if not. - * This Error object has as `message` property with a status code carrying a specific meaning - * (see [REST API](/en/stable/reference-docs/REST-API/#start-broadcast)). - */ - public startBroadcast(sessionId: string, broadcastUrl: string, properties?: RecordingProperties): Promise { - return new Promise((resolve, reject) => { - - let data; - - if (properties != undefined) { - data = { - session: sessionId, - broadcastUrl, - recordingLayout: properties.recordingLayout, - customLayout: properties.customLayout, - resolution: properties.resolution, - frameRate: properties.frameRate, - hasAudio: properties.hasAudio, - shmSize: properties.shmSize, - mediaNode: properties.mediaNode - }; - data = JSON.stringify(data); - } else { - data = { - session: sessionId, - broadcastUrl - } - } - - axios.post( - this.host + OpenVidu.API_BROADCAST_START, - data, - { - headers: { - 'Authorization': this.basicAuth, - 'Content-Type': 'application/json' - } - } - ) - .then(res => { - if (res.status === 200) { - const activeSession = this.activeSessions.find(s => s.sessionId === sessionId); - if (!!activeSession) { - activeSession.broadcasting = true; - } else { - console.warn("No active session found for sessionId '" + sessionId + "'. This instance of OpenVidu Node Client didn't create this session"); - } - resolve(); - } else { - // ERROR response from openvidu-server. Resolve HTTP status - reject(new Error(res.status.toString())); - } - }).catch(error => { - this.handleError(error, reject); - }); - }); - } - - /** - * Stops the broadcast of a {@link Session} - * - * @param sessionId The `sessionId` of the {@link Session} you want to stop broadcasting - * - * @returns A Promise that is resolved if the broadcast successfully stopped and rejected with an - * [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) object if not. - * This Error object has as `message` property with a status code carrying a specific meaning - * (see [REST API](/en/stable/reference-docs/REST-API/#stop-broadcast)). - */ - public stopBroadcast(sessionId: string): Promise { - return new Promise((resolve, reject) => { - axios.post( - this.host + OpenVidu.API_BROADCAST_STOP, - { session: sessionId }, - { - headers: { - 'Authorization': this.basicAuth, - 'Content-Type': 'application/json' - } - } - ) - .then(res => { - if (res.status === 200) { - // SUCCESS response from openvidu-server - const activeSession = this.activeSessions.find(s => s.sessionId === sessionId); - if (!!activeSession) { - activeSession.broadcasting = false; - } else { - console.warn("No active session found for sessionId '" + sessionId + "'. This instance of OpenVidu Node Client didn't create this session"); - } - resolve(); - } else { - // ERROR response from openvidu-server. Resolve HTTP status - reject(new Error(res.status.toString())); - } - }).catch(error => { - this.handleError(error, reject); - }); - }); - } - - /** - * Updates every property of every active Session with the current status they have in OpenVidu Server. - * After calling this method you can access the updated array of active sessions in {@link activeSessions} - * - * @returns A promise resolved to true if any Session status has changed with respect to the server, or to false if not. - * This applies to any property or sub-property of any of the sessions locally stored in OpenVidu Node Client - */ - public fetch(): Promise { - return new Promise((resolve, reject) => { - axios.get( - this.host + OpenVidu.API_SESSIONS + '?pendingConnections=true', - { - headers: { - Authorization: this.basicAuth - } - } - ) - .then(res => { - if (res.status === 200) { - - // Boolean to store if any Session has changed - let hasChanged = false; - - // 1. Array to store fetched sessionIds and later remove closed ones - const fetchedSessionIds: string[] = []; - res.data.content.forEach(jsonSession => { - - const fetchedSession: Session = new Session(this, jsonSession); - fetchedSessionIds.push(fetchedSession.sessionId); - let storedSession = this.activeSessions.find(s => s.sessionId === fetchedSession.sessionId); - - if (!!storedSession) { - - // 2. Update existing Session - const changed: boolean = !storedSession.equalTo(fetchedSession); - storedSession.resetWithJson(jsonSession); - console.log("Available session '" + storedSession.sessionId + "' info fetched. Any change: " + changed); - hasChanged = hasChanged || changed; - - } else { - - // 3. Add new Session - this.activeSessions.push(fetchedSession); - console.log("New session '" + fetchedSession.sessionId + "' info fetched"); - hasChanged = true; - } - }); - - // 4. Remove closed sessions from local collection - for (var i = this.activeSessions.length - 1; i >= 0; --i) { - let sessionId = this.activeSessions[i].sessionId; - if (!fetchedSessionIds.includes(sessionId)) { - console.log("Removing closed session '" + sessionId + "'"); - hasChanged = true; - this.activeSessions.splice(i, 1); - } - } - - console.log('Active sessions info fetched: ', fetchedSessionIds); - resolve(hasChanged); - } else { - // ERROR response from openvidu-server. Resolve HTTP status - reject(new Error(res.status.toString())); - } - }).catch(error => { - this.handleError(error, reject); - }); - }); - } - - /** - * @hidden - * @returns A map paring every existing sessionId with true or false depending on whether it has changed or not - */ - fetchWebRtc(): Promise { - - // tslint:disable:no-string-literal - const addWebRtcStatsToConnections = (connection: Connection, connectionsExtendedInfo: any) => { - const connectionExtended = connectionsExtendedInfo.find(c => c.connectionId === connection.connectionId); - if (!!connectionExtended) { - connection.publishers.forEach(pub => { - const publisherExtended = connectionExtended.publishers.find(p => p.streamId === pub.streamId); - pub['webRtc'] = { - kms: { - events: publisherExtended.events, - localCandidate: publisherExtended.localCandidate, - remoteCandidate: publisherExtended.remoteCandidate, - clientIceCandidates: publisherExtended.clientIceCandidates, - serverIceCandidates: publisherExtended.serverIceCandidates, - webrtcEndpointName: publisherExtended.webrtcEndpointName, - localSdp: publisherExtended.localSdp, - remoteSdp: publisherExtended.remoteSdp - } - }; - pub['localCandidatePair'] = parseRemoteCandidatePair(pub['webRtc'].kms.remoteCandidate); - if (!!publisherExtended.serverStats) { - pub['webRtc'].kms.serverStats = publisherExtended.serverStats; - } - }); - const subscriberArray = []; - connection.subscribers.forEach(sub => { - const subscriberExtended = connectionExtended.subscribers.find(s => s.streamId === sub); - const subAux = {}; - // Standard properties - subAux['streamId'] = sub; - subAux['publisher'] = subscriberExtended.publisher; - // WebRtc properties - subAux['createdAt'] = subscriberExtended.createdAt; - subAux['webRtc'] = { - kms: { - events: subscriberExtended.events, - localCandidate: subscriberExtended.localCandidate, - remoteCandidate: subscriberExtended.remoteCandidate, - clientIceCandidates: subscriberExtended.clientIceCandidates, - serverIceCandidates: subscriberExtended.serverIceCandidates, - webrtcEndpointName: subscriberExtended.webrtcEndpointName, - localSdp: subscriberExtended.localSdp, - remoteSdp: subscriberExtended.remoteSdp - } - }; - subAux['localCandidatePair'] = parseRemoteCandidatePair(subAux['webRtc'].kms.remoteCandidate); - if (!!subscriberExtended.serverStats) { - subAux['webRtc'].kms.serverStats = subscriberExtended.serverStats; - } - subscriberArray.push(subAux); - }); - connection.subscribers = subscriberArray; - } - }; - - const parseRemoteCandidatePair = (candidateStr: string) => { - if (!candidateStr) { - return 'ERROR: No remote candidate available'; - } - const array = candidateStr.split(/\s+/); - return { - portNumber: array[5], - ipAddress: array[4], - transport: array[2].toLowerCase(), - candidateType: array[7], - priority: array[3], - raw: candidateStr - }; - }; - - return new Promise<{ changes: boolean, sessionChanges: ObjMap }>((resolve, reject) => { - axios.get( - this.host + OpenVidu.API_SESSIONS + '?webRtcStats=true', - { - headers: { - Authorization: this.basicAuth - } - } - ) - .then(res => { - if (res.status === 200) { - - // Global changes - let globalChanges = false; - // Collection of sessionIds telling whether each one of them has changed or not - const sessionChanges: ObjMap = {}; - - // 1. Array to store fetched sessionIds and later remove closed ones - const fetchedSessionIds: string[] = []; - res.data.content.forEach(jsonSession => { - - const fetchedSession: Session = new Session(this, jsonSession); - fetchedSession.connections.forEach(connection => { - addWebRtcStatsToConnections(connection, jsonSession.connections.content); - }); - fetchedSessionIds.push(fetchedSession.sessionId); - let storedSession = this.activeSessions.find(s => s.sessionId === fetchedSession.sessionId); - - if (!!storedSession) { - - // 2. Update existing Session - let changed = !storedSession.equalTo(fetchedSession); - if (!changed) { // Check if server webrtc information has changed in any Publisher object (Session.equalTo does not check Publisher.webRtc auxiliary object) - fetchedSession.connections.forEach((connection, index1) => { - for (let index2 = 0; (index2 < connection['publishers'].length && !changed); index2++) { - changed = changed || JSON.stringify(connection['publishers'][index2]['webRtc']) !== JSON.stringify(storedSession.connections[index1]['publishers'][index2]['webRtc']); - } - }); - } - - storedSession.resetWithJson(jsonSession); - storedSession.connections.forEach(connection => { - addWebRtcStatsToConnections(connection, jsonSession.connections.content); + /** + * Creates an OpenVidu session. The session identifier will be available at property {@link Session.sessionId} + * + * @returns A Promise that is resolved to the {@link Session} if success and rejected with an + * [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) object if not. + * This Error object has as `message` property with a status code carrying a specific meaning + * (see [REST API](/en/stable/reference-docs/REST-API/#post-recording-start)). + * + * This method will never return an Error with status `409`. If a session with the same `customSessionId` already + * exists in OpenVidu Server, a {@link Session.fetch} operation is performed in the background and the updated Session + * object is returned. + */ + public createSession(properties?: SessionProperties): Promise { + return new Promise((resolve, reject) => { + const session = new Session(this, properties); + session + .getSessionHttp() + .then((response) => { + this.activeSessions.push(session); + resolve(session); + }) + .catch((error) => { + reject(error); }); - console.log("Available session '" + storedSession.sessionId + "' info fetched. Any change: " + changed); - sessionChanges[storedSession.sessionId] = changed; - globalChanges = globalChanges || changed; + }); + } - } else { + public startRecording(sessionId: string): Promise; + public startRecording(sessionId: string, name: string): Promise; + public startRecording(sessionId: string, properties: RecordingProperties): Promise; - // 3. Add new Session - this.activeSessions.push(fetchedSession); - console.log("New session '" + fetchedSession.sessionId + "' info fetched"); - sessionChanges[fetchedSession.sessionId] = true; - globalChanges = true; + /** + * Starts the recording of a {@link Session} + * + * @param sessionId The `sessionId` of the {@link Session} you want to start recording + * @param name The name you want to give to the video file. You can access this same value in your clients on recording events (`recordingStarted`, `recordingStopped`) + * @param properties Custom RecordingProperties to apply to this Recording. This will override the global default values set to the Session with {@link SessionProperties.defaultRecordingProperties} + * + * @returns A Promise that is resolved to the {@link Recording} if it successfully started (the recording can be stopped with guarantees) and rejected with an + * [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) object if not. + * This Error object has as `message` property with a status code carrying a specific meaning + * (see [REST API](/en/stable/reference-docs/REST-API/#post-recording-start)). + */ + public startRecording(sessionId: string, param2?: string | RecordingProperties): Promise { + return new Promise((resolve, reject) => { + let data; - } - }); - - // 4. Remove closed sessions from local collection - for (var i = this.activeSessions.length - 1; i >= 0; --i) { - let sessionId = this.activeSessions[i].sessionId; - if (!fetchedSessionIds.includes(sessionId)) { - console.log("Removing closed session '" + sessionId + "'"); - sessionChanges[sessionId] = true; - globalChanges = true; - this.activeSessions.splice(i, 1); - } + if (param2 != null) { + if (typeof param2 === 'string') { + data = JSON.stringify({ + session: sessionId, + name: param2 + }); + } else { + const properties: RecordingProperties = param2 as RecordingProperties; + data = { + session: sessionId, + name: properties.name, + outputMode: properties.outputMode, + recordingLayout: properties.recordingLayout, + customLayout: properties.customLayout, + ignoreFailedStreams: properties.ignoreFailedStreams, + resolution: properties.resolution, + frameRate: properties.frameRate, + hasAudio: properties.hasAudio, + hasVideo: properties.hasVideo, + shmSize: properties.shmSize, + mediaNode: properties.mediaNode + }; + data = JSON.stringify(data); + } + } else { + data = JSON.stringify({ + session: sessionId, + name: '' + }); } - console.log('Active sessions info fetched: ', fetchedSessionIds); - resolve({ changes: globalChanges, sessionChanges }); - } else { - // ERROR response from openvidu-server. Resolve HTTP status - reject(new Error(res.status.toString())); - } - }).catch(error => { - this.handleError(error, reject); + axios + .post(this.host + OpenVidu.API_RECORDINGS_START, data, { + headers: { + Authorization: this.basicAuth, + 'Content-Type': 'application/json' + } + }) + .then((res) => { + if (res.status === 200) { + // SUCCESS response from openvidu-server (Recording in JSON format). Resolve new Recording + const r: Recording = new Recording(res.data); + const activeSession = this.activeSessions.find((s) => s.sessionId === r.sessionId); + if (!!activeSession) { + activeSession.recording = true; + } else { + console.warn( + "No active session found for sessionId '" + + r.sessionId + + "'. This instance of OpenVidu Node Client didn't create this session" + ); + } + resolve(r); + } else { + // ERROR response from openvidu-server. Resolve HTTP status + reject(new Error(res.status.toString())); + } + }) + .catch((error) => { + this.handleError(error, reject); + }); }); - }); - } - // tslint:enable:no-string-literal - - private getBasicAuth(secret: string): string { - return 'Basic ' + this.Buffer('OPENVIDUAPP:' + secret).toString('base64'); - } - - private setHostnameAndPort(): void { - let url: URL; - try { - url = new URL(this.hostname); - } catch (error) { - throw new Error('URL format incorrect: ' + error); } - this.host = url.protocol + '//' + url.host; - } - /** - * @hidden - */ - handleError(error: AxiosError, reject: (reason?: any) => void) { - if (error.response) { - // The request was made and the server responded with a status code (not 2xx) - reject(new Error(error.response.status.toString())); - } else if (error.request) { - // The request was made but no response was received - // `error.request` is an instance of XMLHttpRequest in the browser and an instance of - // http.ClientRequest in node.js - reject(new Error(error.request)); - } else { - // Something happened in setting up the request that triggered an Error - reject(new Error(error.message)); + /** + * Stops the recording of a {@link Session} + * + * @param recordingId The `id` property of the {@link Recording} you want to stop + * + * @returns A Promise that is resolved to the {@link Recording} if it successfully stopped and rejected with an + * [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) object if not. + * This Error object has as `message` property with a status code carrying a specific meaning + * (see [REST API](/en/stable/reference-docs/REST-API/#post-recording-stop)). + */ + public stopRecording(recordingId: string): Promise { + return new Promise((resolve, reject) => { + axios + .post(this.host + OpenVidu.API_RECORDINGS_STOP + '/' + recordingId, undefined, { + headers: { + Authorization: this.basicAuth, + 'Content-Type': 'application/x-www-form-urlencoded' + } + }) + .then((res) => { + if (res.status === 200) { + // SUCCESS response from openvidu-server (Recording in JSON format). Resolve new Recording + const r: Recording = new Recording(res.data); + const activeSession = this.activeSessions.find((s) => s.sessionId === r.sessionId); + if (!!activeSession) { + activeSession.recording = false; + } else { + console.warn( + "No active session found for sessionId '" + + r.sessionId + + "'. This instance of OpenVidu Node Client didn't create this session" + ); + } + resolve(r); + } else { + // ERROR response from openvidu-server. Resolve HTTP status + reject(new Error(res.status.toString())); + } + }) + .catch((error) => { + this.handleError(error, reject); + }); + }); } - } + /** + * Gets an existing {@link Recording} + * + * @param recordingId The `id` property of the {@link Recording} you want to retrieve + * + * @returns A Promise that is resolved to the {@link Recording} if it successfully stopped and rejected with an + * [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) object if not. + * This Error object has as `message` property with a status code carrying a specific meaning + * (see [REST API](/en/stable/reference-docs/REST-API/#get-recording)). + */ + public getRecording(recordingId: string): Promise { + return new Promise((resolve, reject) => { + axios + .get(this.host + OpenVidu.API_RECORDINGS + '/' + recordingId, { + headers: { + Authorization: this.basicAuth, + 'Content-Type': 'application/x-www-form-urlencoded' + } + }) + .then((res) => { + if (res.status === 200) { + // SUCCESS response from openvidu-server (Recording in JSON format). Resolve new Recording + resolve(new Recording(res.data)); + } else { + // ERROR response from openvidu-server. Resolve HTTP status + reject(new Error(res.status.toString())); + } + }) + .catch((error) => { + this.handleError(error, reject); + }); + }); + } + + /** + * Lists all existing recordings + * + * @returns A Promise that is resolved to an array with all existing recordings + */ + public listRecordings(): Promise { + return new Promise((resolve, reject) => { + axios + .get(this.host + OpenVidu.API_RECORDINGS, { + headers: { + Authorization: this.basicAuth + } + }) + .then((res) => { + if (res.status === 200) { + // SUCCESS response from openvidu-server (JSON arrays of recordings in JSON format). Resolve list of new recordings + const recordingArray: Recording[] = []; + const responseItems = res.data.items; + for (const item of responseItems) { + recordingArray.push(new Recording(item)); + } + // Order recordings by time of creation (newest first) + recordingArray.sort((r1, r2) => (r1.createdAt < r2.createdAt ? 1 : r2.createdAt < r1.createdAt ? -1 : 0)); + resolve(recordingArray); + } else { + // ERROR response from openvidu-server. Resolve HTTP status + reject(new Error(res.status.toString())); + } + }) + .catch((error) => { + this.handleError(error, reject); + }); + }); + } + + /** + * Deletes a {@link Recording}. The recording must have status `stopped`, `ready` or `failed` + * + * @param recordingId + * + * @returns A Promise that is resolved if the Recording was successfully deleted and rejected with an + * [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) object if not. + * This Error object has as `message` property with a status code carrying a specific meaning + * (see [REST API](/en/stable/reference-docs/REST-API/#delete-recording)). + */ + public deleteRecording(recordingId: string): Promise { + return new Promise((resolve, reject) => { + axios + .delete(this.host + OpenVidu.API_RECORDINGS + '/' + recordingId, { + headers: { + Authorization: this.basicAuth, + 'Content-Type': 'application/x-www-form-urlencoded' + } + }) + .then((res) => { + if (res.status === 204) { + // SUCCESS response from openvidu-server. Resolve undefined + resolve(undefined); + } else { + // ERROR response from openvidu-server. Resolve HTTP status + reject(new Error(res.status.toString())); + } + }) + .catch((error) => { + this.handleError(error, reject); + }); + }); + } + + public startBroadcast(sessionId: string, broadcastUrl: string): Promise; + public startBroadcast(sessionId: string, broadcastUrl: string, properties: RecordingProperties): Promise; + + /** + * Starts the broadcast of a {@link Session} + * + * @param sessionId The `sessionId` of the {@link Session} you want to start broadcasting + * @param broadcastUrl The URL where to broadcast + * @param properties The configuration for this broadcast. It uses a subset of the {@link RecordingProperties}: + * - {@link RecordingProperties.hasAudio} + * - {@link RecordingProperties.resolution} + * - {@link RecordingProperties.frameRate} + * - {@link RecordingProperties.recordingLayout} + * - {@link RecordingProperties.customLayout} + * - {@link RecordingProperties.shmSize} + * - {@link RecordingProperties.mediaNode} + * + * @returns A Promise that is resolved if the broadcast successfully started and rejected with an + * [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) object if not. + * This Error object has as `message` property with a status code carrying a specific meaning + * (see [REST API](/en/stable/reference-docs/REST-API/#start-broadcast)). + */ + public startBroadcast(sessionId: string, broadcastUrl: string, properties?: RecordingProperties): Promise { + return new Promise((resolve, reject) => { + let data; + + if (properties != undefined) { + data = { + session: sessionId, + broadcastUrl, + recordingLayout: properties.recordingLayout, + customLayout: properties.customLayout, + resolution: properties.resolution, + frameRate: properties.frameRate, + hasAudio: properties.hasAudio, + shmSize: properties.shmSize, + mediaNode: properties.mediaNode + }; + data = JSON.stringify(data); + } else { + data = { + session: sessionId, + broadcastUrl + }; + } + + axios + .post(this.host + OpenVidu.API_BROADCAST_START, data, { + headers: { + Authorization: this.basicAuth, + 'Content-Type': 'application/json' + } + }) + .then((res) => { + if (res.status === 200) { + const activeSession = this.activeSessions.find((s) => s.sessionId === sessionId); + if (!!activeSession) { + activeSession.broadcasting = true; + } else { + console.warn( + "No active session found for sessionId '" + + sessionId + + "'. This instance of OpenVidu Node Client didn't create this session" + ); + } + resolve(); + } else { + // ERROR response from openvidu-server. Resolve HTTP status + reject(new Error(res.status.toString())); + } + }) + .catch((error) => { + this.handleError(error, reject); + }); + }); + } + + /** + * Stops the broadcast of a {@link Session} + * + * @param sessionId The `sessionId` of the {@link Session} you want to stop broadcasting + * + * @returns A Promise that is resolved if the broadcast successfully stopped and rejected with an + * [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) object if not. + * This Error object has as `message` property with a status code carrying a specific meaning + * (see [REST API](/en/stable/reference-docs/REST-API/#stop-broadcast)). + */ + public stopBroadcast(sessionId: string): Promise { + return new Promise((resolve, reject) => { + axios + .post( + this.host + OpenVidu.API_BROADCAST_STOP, + { session: sessionId }, + { + headers: { + Authorization: this.basicAuth, + 'Content-Type': 'application/json' + } + } + ) + .then((res) => { + if (res.status === 200) { + // SUCCESS response from openvidu-server + const activeSession = this.activeSessions.find((s) => s.sessionId === sessionId); + if (!!activeSession) { + activeSession.broadcasting = false; + } else { + console.warn( + "No active session found for sessionId '" + + sessionId + + "'. This instance of OpenVidu Node Client didn't create this session" + ); + } + resolve(); + } else { + // ERROR response from openvidu-server. Resolve HTTP status + reject(new Error(res.status.toString())); + } + }) + .catch((error) => { + this.handleError(error, reject); + }); + }); + } + + /** + * Updates every property of every active Session with the current status they have in OpenVidu Server. + * After calling this method you can access the updated array of active sessions in {@link activeSessions} + * + * @returns A promise resolved to true if any Session status has changed with respect to the server, or to false if not. + * This applies to any property or sub-property of any of the sessions locally stored in OpenVidu Node Client + */ + public fetch(): Promise { + return new Promise((resolve, reject) => { + axios + .get(this.host + OpenVidu.API_SESSIONS + '?pendingConnections=true', { + headers: { + Authorization: this.basicAuth + } + }) + .then((res) => { + if (res.status === 200) { + // Boolean to store if any Session has changed + let hasChanged = false; + + // 1. Array to store fetched sessionIds and later remove closed ones + const fetchedSessionIds: string[] = []; + res.data.content.forEach((jsonSession) => { + const fetchedSession: Session = new Session(this, jsonSession); + fetchedSessionIds.push(fetchedSession.sessionId); + let storedSession = this.activeSessions.find((s) => s.sessionId === fetchedSession.sessionId); + + if (!!storedSession) { + // 2. Update existing Session + const changed: boolean = !storedSession.equalTo(fetchedSession); + storedSession.resetWithJson(jsonSession); + console.log("Available session '" + storedSession.sessionId + "' info fetched. Any change: " + changed); + hasChanged = hasChanged || changed; + } else { + // 3. Add new Session + this.activeSessions.push(fetchedSession); + console.log("New session '" + fetchedSession.sessionId + "' info fetched"); + hasChanged = true; + } + }); + + // 4. Remove closed sessions from local collection + for (var i = this.activeSessions.length - 1; i >= 0; --i) { + let sessionId = this.activeSessions[i].sessionId; + if (!fetchedSessionIds.includes(sessionId)) { + console.log("Removing closed session '" + sessionId + "'"); + hasChanged = true; + this.activeSessions.splice(i, 1); + } + } + + console.log('Active sessions info fetched: ', fetchedSessionIds); + resolve(hasChanged); + } else { + // ERROR response from openvidu-server. Resolve HTTP status + reject(new Error(res.status.toString())); + } + }) + .catch((error) => { + this.handleError(error, reject); + }); + }); + } + + /** + * @hidden + * @returns A map paring every existing sessionId with true or false depending on whether it has changed or not + */ + fetchWebRtc(): Promise { + // tslint:disable:no-string-literal + const addWebRtcStatsToConnections = (connection: Connection, connectionsExtendedInfo: any) => { + const connectionExtended = connectionsExtendedInfo.find((c) => c.connectionId === connection.connectionId); + if (!!connectionExtended) { + connection.publishers.forEach((pub) => { + const publisherExtended = connectionExtended.publishers.find((p) => p.streamId === pub.streamId); + pub['webRtc'] = { + kms: { + events: publisherExtended.events, + localCandidate: publisherExtended.localCandidate, + remoteCandidate: publisherExtended.remoteCandidate, + clientIceCandidates: publisherExtended.clientIceCandidates, + serverIceCandidates: publisherExtended.serverIceCandidates, + webrtcEndpointName: publisherExtended.webrtcEndpointName, + localSdp: publisherExtended.localSdp, + remoteSdp: publisherExtended.remoteSdp + } + }; + pub['localCandidatePair'] = parseRemoteCandidatePair(pub['webRtc'].kms.remoteCandidate); + if (!!publisherExtended.serverStats) { + pub['webRtc'].kms.serverStats = publisherExtended.serverStats; + } + }); + const subscriberArray = []; + connection.subscribers.forEach((sub) => { + const subscriberExtended = connectionExtended.subscribers.find((s) => s.streamId === sub); + const subAux = {}; + // Standard properties + subAux['streamId'] = sub; + subAux['publisher'] = subscriberExtended.publisher; + // WebRtc properties + subAux['createdAt'] = subscriberExtended.createdAt; + subAux['webRtc'] = { + kms: { + events: subscriberExtended.events, + localCandidate: subscriberExtended.localCandidate, + remoteCandidate: subscriberExtended.remoteCandidate, + clientIceCandidates: subscriberExtended.clientIceCandidates, + serverIceCandidates: subscriberExtended.serverIceCandidates, + webrtcEndpointName: subscriberExtended.webrtcEndpointName, + localSdp: subscriberExtended.localSdp, + remoteSdp: subscriberExtended.remoteSdp + } + }; + subAux['localCandidatePair'] = parseRemoteCandidatePair(subAux['webRtc'].kms.remoteCandidate); + if (!!subscriberExtended.serverStats) { + subAux['webRtc'].kms.serverStats = subscriberExtended.serverStats; + } + subscriberArray.push(subAux); + }); + connection.subscribers = subscriberArray; + } + }; + + const parseRemoteCandidatePair = (candidateStr: string) => { + if (!candidateStr) { + return 'ERROR: No remote candidate available'; + } + const array = candidateStr.split(/\s+/); + return { + portNumber: array[5], + ipAddress: array[4], + transport: array[2].toLowerCase(), + candidateType: array[7], + priority: array[3], + raw: candidateStr + }; + }; + + return new Promise<{ changes: boolean; sessionChanges: ObjMap }>((resolve, reject) => { + axios + .get(this.host + OpenVidu.API_SESSIONS + '?webRtcStats=true', { + headers: { + Authorization: this.basicAuth + } + }) + .then((res) => { + if (res.status === 200) { + // Global changes + let globalChanges = false; + // Collection of sessionIds telling whether each one of them has changed or not + const sessionChanges: ObjMap = {}; + + // 1. Array to store fetched sessionIds and later remove closed ones + const fetchedSessionIds: string[] = []; + res.data.content.forEach((jsonSession) => { + const fetchedSession: Session = new Session(this, jsonSession); + fetchedSession.connections.forEach((connection) => { + addWebRtcStatsToConnections(connection, jsonSession.connections.content); + }); + fetchedSessionIds.push(fetchedSession.sessionId); + let storedSession = this.activeSessions.find((s) => s.sessionId === fetchedSession.sessionId); + + if (!!storedSession) { + // 2. Update existing Session + let changed = !storedSession.equalTo(fetchedSession); + if (!changed) { + // Check if server webrtc information has changed in any Publisher object (Session.equalTo does not check Publisher.webRtc auxiliary object) + fetchedSession.connections.forEach((connection, index1) => { + for (let index2 = 0; index2 < connection['publishers'].length && !changed; index2++) { + changed = + changed || + JSON.stringify(connection['publishers'][index2]['webRtc']) !== + JSON.stringify(storedSession.connections[index1]['publishers'][index2]['webRtc']); + } + }); + } + + storedSession.resetWithJson(jsonSession); + storedSession.connections.forEach((connection) => { + addWebRtcStatsToConnections(connection, jsonSession.connections.content); + }); + console.log("Available session '" + storedSession.sessionId + "' info fetched. Any change: " + changed); + sessionChanges[storedSession.sessionId] = changed; + globalChanges = globalChanges || changed; + } else { + // 3. Add new Session + this.activeSessions.push(fetchedSession); + console.log("New session '" + fetchedSession.sessionId + "' info fetched"); + sessionChanges[fetchedSession.sessionId] = true; + globalChanges = true; + } + }); + + // 4. Remove closed sessions from local collection + for (var i = this.activeSessions.length - 1; i >= 0; --i) { + let sessionId = this.activeSessions[i].sessionId; + if (!fetchedSessionIds.includes(sessionId)) { + console.log("Removing closed session '" + sessionId + "'"); + sessionChanges[sessionId] = true; + globalChanges = true; + this.activeSessions.splice(i, 1); + } + } + + console.log('Active sessions info fetched: ', fetchedSessionIds); + resolve({ changes: globalChanges, sessionChanges }); + } else { + // ERROR response from openvidu-server. Resolve HTTP status + reject(new Error(res.status.toString())); + } + }) + .catch((error) => { + this.handleError(error, reject); + }); + }); + } + // tslint:enable:no-string-literal + + private getBasicAuth(secret: string): string { + return 'Basic ' + this.Buffer('OPENVIDUAPP:' + secret).toString('base64'); + } + + private setHostnameAndPort(): void { + let url: URL; + try { + url = new URL(this.hostname); + } catch (error) { + throw new Error('URL format incorrect: ' + error); + } + this.host = url.protocol + '//' + url.host; + } + + /** + * @hidden + */ + handleError(error: AxiosError, reject: (reason?: any) => void) { + if (error.response) { + // The request was made and the server responded with a status code (not 2xx) + reject(new Error(error.response.status.toString())); + } else if (error.request) { + // The request was made but no response was received + // `error.request` is an instance of XMLHttpRequest in the browser and an instance of + // http.ClientRequest in node.js + reject(new Error(error.request)); + } else { + // Something happened in setting up the request that triggered an Error + reject(new Error(error.message)); + } + } } diff --git a/openvidu-node-client/src/OpenViduRole.ts b/openvidu-node-client/src/OpenViduRole.ts index 25f2bd74..e4827b7d 100644 --- a/openvidu-node-client/src/OpenViduRole.ts +++ b/openvidu-node-client/src/OpenViduRole.ts @@ -19,7 +19,6 @@ * See {@link TokenOptions.role} */ export enum OpenViduRole { - /** * Can subscribe to published Streams of other users */ @@ -35,4 +34,4 @@ export enum OpenViduRole { * (call `Session.forceUnpublish()` and `Session.forceDisconnect()`) */ MODERATOR = 'MODERATOR' -} \ No newline at end of file +} diff --git a/openvidu-node-client/src/Publisher.ts b/openvidu-node-client/src/Publisher.ts index 4f876604..b6a8d993 100644 --- a/openvidu-node-client/src/Publisher.ts +++ b/openvidu-node-client/src/Publisher.ts @@ -15,14 +15,12 @@ * */ - /** * See {@link Connection.publishers} * * This is a backend representation of a published media stream (see [OpenVidu Browser Stream class](/en/stable/api/openvidu-browser/classes/Stream.html)) */ export class Publisher { - /** * Unique identifier of the [Stream](/en/stable/api/openvidu-browser/classes/Stream.html) associated to this Publisher. * Each Publisher is paired with only one Stream, so you can identify each Publisher by its @@ -109,5 +107,4 @@ export class Publisher { this.videoDimensions === other.videoDimensions ); } - -} \ No newline at end of file +} diff --git a/openvidu-node-client/src/Recording.ts b/openvidu-node-client/src/Recording.ts index 5c84c2c1..097aa7b6 100644 --- a/openvidu-node-client/src/Recording.ts +++ b/openvidu-node-client/src/Recording.ts @@ -22,7 +22,6 @@ import { RecordingLayout } from './RecordingLayout'; * See {@link OpenVidu.startRecording} */ export class Recording { - /** * Recording unique identifier */ @@ -63,7 +62,6 @@ export class Recording { */ properties: RecordingProperties; - /* tslint:disable:no-string-literal */ /** * @hidden @@ -77,37 +75,44 @@ export class Recording { this.url = json['url']; this.status = json['status']; this.properties = { - name: (json['name'] != null) ? json['name'] : this.id, - hasAudio: (json['hasAudio'] != null) ? !!json['hasAudio'] : Recording.DefaultRecordingPropertiesValues.hasAudio, - hasVideo: (json['hasVideo'] != null) ? !!json['hasVideo'] : Recording.DefaultRecordingPropertiesValues.hasVideo, - outputMode: (json['outputMode'] != null) ? json['outputMode'] : Recording.DefaultRecordingPropertiesValues.outputMode, + name: json['name'] != null ? json['name'] : this.id, + hasAudio: json['hasAudio'] != null ? !!json['hasAudio'] : Recording.DefaultRecordingPropertiesValues.hasAudio, + hasVideo: json['hasVideo'] != null ? !!json['hasVideo'] : Recording.DefaultRecordingPropertiesValues.hasVideo, + outputMode: json['outputMode'] != null ? json['outputMode'] : Recording.DefaultRecordingPropertiesValues.outputMode, mediaNode: json['mediaNode'] }; - if ((this.properties.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED] - || this.properties.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED_QUICK_START]) - && this.properties.hasVideo) { - this.properties.recordingLayout = (json['recordingLayout'] != null) ? json['recordingLayout'] : Recording.DefaultRecordingPropertiesValues.recordingLayout; - this.properties.resolution = (json['resolution'] != null) ? json['resolution'] : Recording.DefaultRecordingPropertiesValues.resolution; - this.properties.frameRate = (json['frameRate'] != null) ? Number(json['frameRate']) : Recording.DefaultRecordingPropertiesValues.frameRate; - this.properties.shmSize = (json['shmSize'] != null) ? Number(json['shmSize']) : Recording.DefaultRecordingPropertiesValues.shmSize; + if ( + (this.properties.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED] || + this.properties.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED_QUICK_START]) && + this.properties.hasVideo + ) { + this.properties.recordingLayout = + json['recordingLayout'] != null ? json['recordingLayout'] : Recording.DefaultRecordingPropertiesValues.recordingLayout; + this.properties.resolution = + json['resolution'] != null ? json['resolution'] : Recording.DefaultRecordingPropertiesValues.resolution; + this.properties.frameRate = + json['frameRate'] != null ? Number(json['frameRate']) : Recording.DefaultRecordingPropertiesValues.frameRate; + this.properties.shmSize = + json['shmSize'] != null ? Number(json['shmSize']) : Recording.DefaultRecordingPropertiesValues.shmSize; if (this.properties.recordingLayout.toString() === RecordingLayout[RecordingLayout.CUSTOM]) { - this.properties.customLayout = (json['customLayout'] != null) ? json['customLayout'] : ''; + this.properties.customLayout = json['customLayout'] != null ? json['customLayout'] : ''; } } if (this.properties.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.INDIVIDUAL]) { - this.properties.ignoreFailedStreams = (json['ignoreFailedStreams'] != null) ? !!json['ignoreFailedStreams'] : Recording.DefaultRecordingPropertiesValues.ignoreFailedStreams; + this.properties.ignoreFailedStreams = + json['ignoreFailedStreams'] != null + ? !!json['ignoreFailedStreams'] + : Recording.DefaultRecordingPropertiesValues.ignoreFailedStreams; } } /* tslint:enable:no-string-literal */ } export namespace Recording { - /** * See {@link Recording.status} */ export enum Status { - /** * The recording is starting (cannot be stopped). Some recording may not go * through this status and directly reach "started" status @@ -142,7 +147,6 @@ export namespace Recording { * See {@link RecordingProperties.outputMode} */ export enum OutputMode { - /** * Record all streams in a grid layout in a single archive */ @@ -153,7 +157,7 @@ export namespace Recording { * service module will start some time in advance and won't be terminated * once a specific session recording has ended. This module will remain * up and running as long as the session remains active. - * + * * - **Pros vs COMPOSED**: the process of starting the recording will be noticeably * faster. This can be very useful in use cases where a session needs to be * recorded multiple times over time, when a better response time is usually @@ -181,10 +185,9 @@ export namespace Recording { static readonly hasVideo: boolean = true; static readonly outputMode: Recording.OutputMode = Recording.OutputMode.COMPOSED; static readonly recordingLayout: RecordingLayout = RecordingLayout.BEST_FIT; - static readonly resolution: string = "1280x720"; + static readonly resolution: string = '1280x720'; static readonly frameRate: number = 25; static readonly shmSize: number = 536870912; static readonly ignoreFailedStreams: boolean = false; } - -} \ No newline at end of file +} diff --git a/openvidu-node-client/src/RecordingLayout.ts b/openvidu-node-client/src/RecordingLayout.ts index 007a3273..e250366d 100644 --- a/openvidu-node-client/src/RecordingLayout.ts +++ b/openvidu-node-client/src/RecordingLayout.ts @@ -19,7 +19,6 @@ * See {@link RecordingProperties.recordingLayout} */ export enum RecordingLayout { - /** * All the videos are evenly distributed, taking up as much space as possible */ diff --git a/openvidu-node-client/src/RecordingMode.ts b/openvidu-node-client/src/RecordingMode.ts index 18747443..6ab32ce4 100644 --- a/openvidu-node-client/src/RecordingMode.ts +++ b/openvidu-node-client/src/RecordingMode.ts @@ -19,7 +19,6 @@ * See {@link SessionProperties.recordingMode} */ export enum RecordingMode { - /** * The session is recorded automatically as soon as the first client publishes a stream to the session. It is automatically stopped * after last user leaves the session (or until you call {@link OpenVidu.stopRecording}). diff --git a/openvidu-node-client/src/RecordingProperties.ts b/openvidu-node-client/src/RecordingProperties.ts index 9356016a..e12b3207 100644 --- a/openvidu-node-client/src/RecordingProperties.ts +++ b/openvidu-node-client/src/RecordingProperties.ts @@ -22,7 +22,6 @@ import { RecordingLayout } from './RecordingLayout'; * See {@link OpenVidu.startRecording} */ export interface RecordingProperties { - /** * Name of the Recording. The video file will be named after this property. * You can access this same value in your clients on recording events @@ -122,6 +121,5 @@ export interface RecordingProperties { */ mediaNode?: { id: string; - } - + }; } diff --git a/openvidu-node-client/src/Session.ts b/openvidu-node-client/src/Session.ts index 4a8b247c..12ad55c3 100644 --- a/openvidu-node-client/src/Session.ts +++ b/openvidu-node-client/src/Session.ts @@ -30,7 +30,6 @@ import { RecordingProperties } from './RecordingProperties'; import { IceServerProperties } from './IceServerProperties'; export class Session { - /** * Unique identifier of the Session */ @@ -110,21 +109,18 @@ export class Session { return new Promise((resolve, reject) => { const data = JSON.stringify({ session: this.sessionId, - role: (!!tokenOptions && !!tokenOptions.role) ? tokenOptions.role : null, - data: (!!tokenOptions && !!tokenOptions.data) ? tokenOptions.data : null, - kurentoOptions: (!!tokenOptions && !!tokenOptions.kurentoOptions) ? tokenOptions.kurentoOptions : null + role: !!tokenOptions && !!tokenOptions.role ? tokenOptions.role : null, + data: !!tokenOptions && !!tokenOptions.data ? tokenOptions.data : null, + kurentoOptions: !!tokenOptions && !!tokenOptions.kurentoOptions ? tokenOptions.kurentoOptions : null }); - axios.post( - this.ov.host + OpenVidu.API_TOKENS, - data, - { + axios + .post(this.ov.host + OpenVidu.API_TOKENS, data, { headers: { - 'Authorization': this.ov.basicAuth, + Authorization: this.ov.basicAuth, 'Content-Type': 'application/json' } - } - ) - .then(res => { + }) + .then((res) => { if (res.status === 200) { // SUCCESS response from openvidu-server. Resolve token resolve(res.data.token); @@ -132,7 +128,8 @@ export class Session { // ERROR response from openvidu-server. Resolve HTTP status reject(new Error(res.status.toString())); } - }).catch(error => { + }) + .catch((error) => { this.ov.handleError(error, reject); }); }); @@ -149,28 +146,28 @@ export class Session { public createConnection(connectionProperties?: ConnectionProperties): Promise { return new Promise((resolve, reject) => { const data = JSON.stringify({ - type: (!!connectionProperties && !!connectionProperties.type) ? connectionProperties.type : null, - data: (!!connectionProperties && !!connectionProperties.data) ? connectionProperties.data : null, + type: !!connectionProperties && !!connectionProperties.type ? connectionProperties.type : null, + data: !!connectionProperties && !!connectionProperties.data ? connectionProperties.data : null, record: !!connectionProperties ? connectionProperties.record : null, - role: (!!connectionProperties && !!connectionProperties.role) ? connectionProperties.role : null, - kurentoOptions: (!!connectionProperties && !!connectionProperties.kurentoOptions) ? connectionProperties.kurentoOptions : null, - rtspUri: (!!connectionProperties && !!connectionProperties.rtspUri) ? connectionProperties.rtspUri : null, + role: !!connectionProperties && !!connectionProperties.role ? connectionProperties.role : null, + kurentoOptions: + !!connectionProperties && !!connectionProperties.kurentoOptions ? connectionProperties.kurentoOptions : null, + rtspUri: !!connectionProperties && !!connectionProperties.rtspUri ? connectionProperties.rtspUri : null, adaptativeBitrate: !!connectionProperties ? connectionProperties.adaptativeBitrate : null, onlyPlayWithSubscribers: !!connectionProperties ? connectionProperties.onlyPlayWithSubscribers : null, - networkCache: (!!connectionProperties && (connectionProperties.networkCache != null)) ? connectionProperties.networkCache : null, - customIceServers: (!!connectionProperties && (!!connectionProperties.customIceServers != null)) ? connectionProperties.customIceServers : null + networkCache: + !!connectionProperties && connectionProperties.networkCache != null ? connectionProperties.networkCache : null, + customIceServers: + !!connectionProperties && !!connectionProperties.customIceServers != null ? connectionProperties.customIceServers : null }); - axios.post( - this.ov.host + OpenVidu.API_SESSIONS + '/' + this.sessionId + '/connection', - data, - { + axios + .post(this.ov.host + OpenVidu.API_SESSIONS + '/' + this.sessionId + '/connection', data, { headers: { - 'Authorization': this.ov.basicAuth, + Authorization: this.ov.basicAuth, 'Content-Type': 'application/json' } - } - ) - .then(res => { + }) + .then((res) => { if (res.status === 200) { // SUCCESS response from openvidu-server. Store and resolve Connection const connection = new Connection(res.data); @@ -183,7 +180,8 @@ export class Session { // ERROR response from openvidu-server. Resolve HTTP status reject(new Error(res.status.toString())); } - }).catch(error => { + }) + .catch((error) => { this.ov.handleError(error, reject); }); }); @@ -197,26 +195,25 @@ export class Session { */ public close(): Promise { return new Promise((resolve, reject) => { - axios.delete( - this.ov.host + OpenVidu.API_SESSIONS + '/' + this.sessionId, - { + axios + .delete(this.ov.host + OpenVidu.API_SESSIONS + '/' + this.sessionId, { headers: { - 'Authorization': this.ov.basicAuth, + Authorization: this.ov.basicAuth, 'Content-Type': 'application/x-www-form-urlencoded' } - } - ) - .then(res => { + }) + .then((res) => { if (res.status === 204) { // SUCCESS response from openvidu-server - const indexToRemove: number = this.ov.activeSessions.findIndex(s => s.sessionId === this.sessionId); + const indexToRemove: number = this.ov.activeSessions.findIndex((s) => s.sessionId === this.sessionId); this.ov.activeSessions.splice(indexToRemove, 1); resolve(); } else { // ERROR response from openvidu-server. Resolve HTTP status reject(new Error(res.status.toString())); } - }).catch(error => { + }) + .catch((error) => { this.ov.handleError(error, reject); }); }); @@ -235,16 +232,14 @@ export class Session { public fetch(): Promise { return new Promise((resolve, reject) => { const beforeJSON: string = JSON.stringify(this, this.removeCircularOpenViduReference); - axios.get( - this.ov.host + OpenVidu.API_SESSIONS + '/' + this.sessionId + '?pendingConnections=true', - { + axios + .get(this.ov.host + OpenVidu.API_SESSIONS + '/' + this.sessionId + '?pendingConnections=true', { headers: { - 'Authorization': this.ov.basicAuth, + Authorization: this.ov.basicAuth, 'Content-Type': 'application/x-www-form-urlencoded' } - } - ) - .then(res => { + }) + .then((res) => { if (res.status === 200) { // SUCCESS response from openvidu-server this.resetWithJson(res.data); @@ -256,7 +251,8 @@ export class Session { // ERROR response from openvidu-server. Resolve HTTP status reject(new Error(res.status.toString())); } - }).catch(error => { + }) + .catch((error) => { this.ov.handleError(error, reject); }); }); @@ -282,20 +278,19 @@ export class Session { public forceDisconnect(connection: string | Connection): Promise { return new Promise((resolve, reject) => { const connectionId: string = typeof connection === 'string' ? connection : (connection).connectionId; - axios.delete( - this.ov.host + OpenVidu.API_SESSIONS + '/' + this.sessionId + '/connection/' + connectionId, - { + axios + .delete(this.ov.host + OpenVidu.API_SESSIONS + '/' + this.sessionId + '/connection/' + connectionId, { headers: { - 'Authorization': this.ov.basicAuth, + Authorization: this.ov.basicAuth, 'Content-Type': 'application/x-www-form-urlencoded' } }) - .then(res => { + .then((res) => { if (res.status === 204) { // SUCCESS response from openvidu-server // Remove connection from connections array let connectionClosed; - this.connections = this.connections.filter(con => { + this.connections = this.connections.filter((con) => { if (con.connectionId !== connectionId) { return true; } else { @@ -305,13 +300,13 @@ export class Session { }); // Remove every Publisher of the closed connection from every subscriber list of other connections if (!!connectionClosed) { - connectionClosed.publishers.forEach(publisher => { - this.connections.forEach(con => { - con.subscribers = con.subscribers.filter(subscriber => { + connectionClosed.publishers.forEach((publisher) => { + this.connections.forEach((con) => { + con.subscribers = con.subscribers.filter((subscriber) => { // tslint:disable:no-string-literal if (!!subscriber['streamId']) { // Subscriber with advanced webRtc configuration properties - return (subscriber['streamId'] !== publisher.streamId); + return subscriber['streamId'] !== publisher.streamId; // tslint:enable:no-string-literal } else { // Regular string subscribers @@ -321,7 +316,9 @@ export class Session { }); }); } else { - console.warn("The closed connection wasn't fetched in OpenVidu Node Client. No changes in the collection of active connections of the Session"); + console.warn( + "The closed connection wasn't fetched in OpenVidu Node Client. No changes in the collection of active connections of the Session" + ); } this.updateActiveConnectionsArray(); console.log("Connection '" + connectionId + "' closed"); @@ -331,7 +328,7 @@ export class Session { reject(new Error(res.status.toString())); } }) - .catch(error => { + .catch((error) => { this.ov.handleError(error, reject); }); }); @@ -355,31 +352,29 @@ export class Session { public forceUnpublish(publisher: string | Publisher): Promise { return new Promise((resolve, reject) => { const streamId: string = typeof publisher === 'string' ? publisher : (publisher).streamId; - axios.delete( - this.ov.host + OpenVidu.API_SESSIONS + '/' + this.sessionId + '/stream/' + streamId, - { + axios + .delete(this.ov.host + OpenVidu.API_SESSIONS + '/' + this.sessionId + '/stream/' + streamId, { headers: { - 'Authorization': this.ov.basicAuth, + Authorization: this.ov.basicAuth, 'Content-Type': 'application/x-www-form-urlencoded' } - } - ) - .then(res => { + }) + .then((res) => { if (res.status === 204) { // SUCCESS response from openvidu-server - this.connections.forEach(connection => { + this.connections.forEach((connection) => { // Try to remove the Publisher from the Connection publishers collection - connection.publishers = connection.publishers.filter(pub => pub.streamId !== streamId); + connection.publishers = connection.publishers.filter((pub) => pub.streamId !== streamId); // Try to remove the Publisher from the Connection subscribers collection if (!!connection.subscribers && connection.subscribers.length > 0) { // tslint:disable:no-string-literal if (!!connection.subscribers[0]['streamId']) { // Subscriber with advanced webRtc configuration properties - connection.subscribers = connection.subscribers.filter(sub => sub['streamId'] !== streamId); + connection.subscribers = connection.subscribers.filter((sub) => sub['streamId'] !== streamId); // tslint:enable:no-string-literal } else { // Regular string subscribers - connection.subscribers = connection.subscribers.filter(sub => sub !== streamId); + connection.subscribers = connection.subscribers.filter((sub) => sub !== streamId); } } }); @@ -390,7 +385,8 @@ export class Session { // ERROR response from openvidu-server. Resolve HTTP status reject(new Error(res.status.toString())); } - }).catch(error => { + }) + .catch((error) => { this.ov.handleError(error, reject); }); }); @@ -423,17 +419,14 @@ export class Session { */ public updateConnection(connectionId: string, connectionProperties: ConnectionProperties): Promise { return new Promise((resolve, reject) => { - axios.patch( - this.ov.host + OpenVidu.API_SESSIONS + "/" + this.sessionId + "/connection/" + connectionId, - connectionProperties, - { + axios + .patch(this.ov.host + OpenVidu.API_SESSIONS + '/' + this.sessionId + '/connection/' + connectionId, connectionProperties, { headers: { - 'Authorization': this.ov.basicAuth, + Authorization: this.ov.basicAuth, 'Content-Type': 'application/json' } - } - ) - .then(res => { + }) + .then((res) => { if (res.status === 200) { console.log('Connection ' + connectionId + ' updated'); } else { @@ -442,7 +435,7 @@ export class Session { return; } // Update the actual Connection object with the new options - const existingConnection: Connection = this.connections.find(con => con.connectionId === connectionId); + const existingConnection: Connection = this.connections.find((con) => con.connectionId === connectionId); if (!existingConnection) { // The updated Connection is not available in local map const newConnection: Connection = new Connection(res.data); @@ -455,7 +448,8 @@ export class Session { this.updateActiveConnectionsArray(); resolve(existingConnection); } - }).catch(error => { + }) + .catch((error) => { this.ov.handleError(error, reject); }); }); @@ -473,28 +467,22 @@ export class Session { */ public getSessionHttp(): Promise { return new Promise((resolve, reject) => { - if (!!this.sessionId) { resolve(this.sessionId); } this.sanitizeDefaultSessionProperties(this.properties); - const data = JSON.stringify( - this.properties - ); + const data = JSON.stringify(this.properties); - axios.post( - this.ov.host + OpenVidu.API_SESSIONS, - data, - { + axios + .post(this.ov.host + OpenVidu.API_SESSIONS, data, { headers: { - 'Authorization': this.ov.basicAuth, + Authorization: this.ov.basicAuth, 'Content-Type': 'application/json' } - } - ) - .then(res => { + }) + .then((res) => { if (res.status === 200) { // SUCCESS response from openvidu-server. Resolve token this.sessionId = res.data.id; @@ -512,7 +500,8 @@ export class Session { // ERROR response from openvidu-server. Resolve HTTP status reject(new Error(res.status.toString())); } - }).catch(error => { + }) + .catch((error) => { if (!!error.response && error.response.status === 409) { // 'customSessionId' already existed this.sessionId = this.properties.customSessionId; @@ -559,10 +548,10 @@ export class Session { // 1. Array to store fetched connections and later remove closed ones const fetchedConnectionIds: string[] = []; - json.connections.content.forEach(jsonConnection => { + json.connections.content.forEach((jsonConnection) => { const connectionObj: Connection = new Connection(jsonConnection); fetchedConnectionIds.push(connectionObj.connectionId); - let storedConnection = this.connections.find(c => c.connectionId === connectionObj.connectionId); + let storedConnection = this.connections.find((c) => c.connectionId === connectionObj.connectionId); if (!!storedConnection) { // 2. Update existing Connection @@ -581,14 +570,13 @@ export class Session { } // Order connections by time of creation - this.connections.sort((c1, c2) => (c1.createdAt > c2.createdAt) ? 1 : ((c2.createdAt > c1.createdAt) ? -1 : 0)); + this.connections.sort((c1, c2) => (c1.createdAt > c2.createdAt ? 1 : c2.createdAt > c1.createdAt ? -1 : 0)); // Order Ice candidates in connection properties - this.connections.forEach(connection => { - if (connection.connectionProperties.customIceServers != null && - connection.connectionProperties.customIceServers.length > 0) { + this.connections.forEach((connection) => { + if (connection.connectionProperties.customIceServers != null && connection.connectionProperties.customIceServers.length > 0) { // Order alphabetically Ice servers using url just to keep the same list order. - const simpleIceComparator = (a: IceServerProperties, b: IceServerProperties) => (a.url > b.url) ? 1 : -1 + const simpleIceComparator = (a: IceServerProperties, b: IceServerProperties) => (a.url > b.url ? 1 : -1); connection.connectionProperties.customIceServers.sort(simpleIceComparator); } }); @@ -601,14 +589,13 @@ export class Session { * @hidden */ equalTo(other: Session): boolean { - let equals: boolean = ( + let equals: boolean = this.sessionId === other.sessionId && this.createdAt === other.createdAt && this.recording === other.recording && this.broadcasting === other.broadcasting && this.connections.length === other.connections.length && - JSON.stringify(this.properties) === JSON.stringify(other.properties) - ); + JSON.stringify(this.properties) === JSON.stringify(other.properties); if (equals) { let i = 0; while (equals && i < this.connections.length) { @@ -637,7 +624,7 @@ export class Session { */ private updateActiveConnectionsArray() { this.activeConnections = []; - this.connections.forEach(con => { + this.connections.forEach((con) => { if (con.status === 'active') { this.activeConnections.push(con); } @@ -648,10 +635,9 @@ export class Session { * @hidden */ private sanitizeDefaultSessionProperties(props: SessionProperties) { - - props.mediaMode = (props.mediaMode != null) ? props.mediaMode : MediaMode.ROUTED; - props.recordingMode = (props.recordingMode != null) ? props.recordingMode : RecordingMode.MANUAL; - props.customSessionId = (props.customSessionId != null) ? props.customSessionId : ''; + props.mediaMode = props.mediaMode != null ? props.mediaMode : MediaMode.ROUTED; + props.recordingMode = props.recordingMode != null ? props.recordingMode : RecordingMode.MANUAL; + props.customSessionId = props.customSessionId != null ? props.customSessionId : ''; // Remove null values: either set, or undefined props.mediaNode = props.mediaNode ?? undefined; @@ -661,22 +647,51 @@ export class Session { if (!props.defaultRecordingProperties) { props.defaultRecordingProperties = {}; } - props.defaultRecordingProperties.name = (props.defaultRecordingProperties?.name != null) ? props.defaultRecordingProperties.name : ''; - props.defaultRecordingProperties.hasAudio = (props.defaultRecordingProperties?.hasAudio != null) ? props.defaultRecordingProperties.hasAudio : Recording.DefaultRecordingPropertiesValues.hasAudio; - props.defaultRecordingProperties.hasVideo = (props.defaultRecordingProperties?.hasVideo != null) ? props.defaultRecordingProperties.hasVideo : Recording.DefaultRecordingPropertiesValues.hasVideo; - props.defaultRecordingProperties.outputMode = (props.defaultRecordingProperties?.outputMode != null) ? props.defaultRecordingProperties.outputMode : Recording.DefaultRecordingPropertiesValues.outputMode; + props.defaultRecordingProperties.name = props.defaultRecordingProperties?.name != null ? props.defaultRecordingProperties.name : ''; + props.defaultRecordingProperties.hasAudio = + props.defaultRecordingProperties?.hasAudio != null + ? props.defaultRecordingProperties.hasAudio + : Recording.DefaultRecordingPropertiesValues.hasAudio; + props.defaultRecordingProperties.hasVideo = + props.defaultRecordingProperties?.hasVideo != null + ? props.defaultRecordingProperties.hasVideo + : Recording.DefaultRecordingPropertiesValues.hasVideo; + props.defaultRecordingProperties.outputMode = + props.defaultRecordingProperties?.outputMode != null + ? props.defaultRecordingProperties.outputMode + : Recording.DefaultRecordingPropertiesValues.outputMode; props.defaultRecordingProperties.mediaNode = props.defaultRecordingProperties?.mediaNode; - if ((props.defaultRecordingProperties.outputMode === Recording.OutputMode.COMPOSED || props.defaultRecordingProperties.outputMode == Recording.OutputMode.COMPOSED_QUICK_START) && props.defaultRecordingProperties.hasVideo) { - props.defaultRecordingProperties.recordingLayout = (props.defaultRecordingProperties.recordingLayout != null) ? props.defaultRecordingProperties.recordingLayout : Recording.DefaultRecordingPropertiesValues.recordingLayout; - props.defaultRecordingProperties.resolution = (props.defaultRecordingProperties.resolution != null) ? props.defaultRecordingProperties.resolution : Recording.DefaultRecordingPropertiesValues.resolution; - props.defaultRecordingProperties.frameRate = (props.defaultRecordingProperties.frameRate != null) ? props.defaultRecordingProperties.frameRate : Recording.DefaultRecordingPropertiesValues.frameRate; - props.defaultRecordingProperties.shmSize = (props.defaultRecordingProperties.shmSize != null) ? props.defaultRecordingProperties.shmSize : Recording.DefaultRecordingPropertiesValues.shmSize; + if ( + (props.defaultRecordingProperties.outputMode === Recording.OutputMode.COMPOSED || + props.defaultRecordingProperties.outputMode == Recording.OutputMode.COMPOSED_QUICK_START) && + props.defaultRecordingProperties.hasVideo + ) { + props.defaultRecordingProperties.recordingLayout = + props.defaultRecordingProperties.recordingLayout != null + ? props.defaultRecordingProperties.recordingLayout + : Recording.DefaultRecordingPropertiesValues.recordingLayout; + props.defaultRecordingProperties.resolution = + props.defaultRecordingProperties.resolution != null + ? props.defaultRecordingProperties.resolution + : Recording.DefaultRecordingPropertiesValues.resolution; + props.defaultRecordingProperties.frameRate = + props.defaultRecordingProperties.frameRate != null + ? props.defaultRecordingProperties.frameRate + : Recording.DefaultRecordingPropertiesValues.frameRate; + props.defaultRecordingProperties.shmSize = + props.defaultRecordingProperties.shmSize != null + ? props.defaultRecordingProperties.shmSize + : Recording.DefaultRecordingPropertiesValues.shmSize; if (props.defaultRecordingProperties.recordingLayout === RecordingLayout.CUSTOM) { - props.defaultRecordingProperties.customLayout = (props.defaultRecordingProperties.customLayout != null) ? props.defaultRecordingProperties.customLayout : ''; + props.defaultRecordingProperties.customLayout = + props.defaultRecordingProperties.customLayout != null ? props.defaultRecordingProperties.customLayout : ''; } } if (props.defaultRecordingProperties.outputMode === Recording.OutputMode.INDIVIDUAL) { - props.defaultRecordingProperties.ignoreFailedStreams = (props.defaultRecordingProperties?.ignoreFailedStreams != null) ? props.defaultRecordingProperties.ignoreFailedStreams : Recording.DefaultRecordingPropertiesValues.ignoreFailedStreams; + props.defaultRecordingProperties.ignoreFailedStreams = + props.defaultRecordingProperties?.ignoreFailedStreams != null + ? props.defaultRecordingProperties.ignoreFailedStreams + : Recording.DefaultRecordingPropertiesValues.ignoreFailedStreams; } this.formatMediaNodeObjectIfNecessary(props.defaultRecordingProperties); @@ -693,5 +708,4 @@ export class Session { } } } - } diff --git a/openvidu-node-client/src/SessionProperties.ts b/openvidu-node-client/src/SessionProperties.ts index 30840cf1..06da3654 100644 --- a/openvidu-node-client/src/SessionProperties.ts +++ b/openvidu-node-client/src/SessionProperties.ts @@ -24,7 +24,6 @@ import { VideoCodec } from './VideoCodec'; * See {@link OpenVidu.createSession} */ export interface SessionProperties { - /** * How the media streams will be sent and received by your clients: routed through OpenVidu Media Node * (`MediaMode.ROUTED`) or attempting direct p2p connections (`MediaMode.RELAYED`, _not available yet_) @@ -68,7 +67,7 @@ export interface SessionProperties { */ mediaNode?: { id: string; - } + }; /** * Define which video codec will be forcibly used for this session. diff --git a/openvidu-node-client/src/TokenOptions.ts b/openvidu-node-client/src/TokenOptions.ts index 704bdda6..347a509c 100644 --- a/openvidu-node-client/src/TokenOptions.ts +++ b/openvidu-node-client/src/TokenOptions.ts @@ -21,10 +21,9 @@ import { OpenViduRole } from './OpenViduRole'; * @deprecated Use {@link ConnectionProperties} instead */ export interface TokenOptions { - /** * The role assigned to this token - * + * * @default PUBLISHER */ role?: OpenViduRole; @@ -59,10 +58,10 @@ export interface TokenOptions { * - `allowedFilters`: names of the filters the user owning the token will be able to apply. See [Voice and video filters](/en/stable/advanced-features/filters/) */ kurentoOptions?: { - videoMaxRecvBandwidth?: number, - videoMinRecvBandwidth?: number, - videoMaxSendBandwidth?: number, - videoMinSendBandwidth?: number, - allowedFilters?: string[] + videoMaxRecvBandwidth?: number; + videoMinRecvBandwidth?: number; + videoMaxSendBandwidth?: number; + videoMinSendBandwidth?: number; + allowedFilters?: string[]; }; -} \ No newline at end of file +} diff --git a/openvidu-node-client/src/VideoCodec.ts b/openvidu-node-client/src/VideoCodec.ts index 8fda28a0..f2b99885 100644 --- a/openvidu-node-client/src/VideoCodec.ts +++ b/openvidu-node-client/src/VideoCodec.ts @@ -6,5 +6,5 @@ export enum VideoCodec { NONE = 'NONE', VP8 = 'VP8', VP9 = 'VP9', - H264 = 'H264', + H264 = 'H264' } diff --git a/openvidu-node-client/src/index.ts b/openvidu-node-client/src/index.ts index 80b7989c..9b3f7c98 100644 --- a/openvidu-node-client/src/index.ts +++ b/openvidu-node-client/src/index.ts @@ -13,4 +13,4 @@ export * from './RecordingProperties'; export * from './Connection'; export * from './Publisher'; export * from './VideoCodec'; -export * from './IceServerProperties'; \ No newline at end of file +export * from './IceServerProperties';