UNPKG

opentok

Version:
1,300 lines (1,250 loc) 76.4 kB
/* * OpenTok server-side SDK */ // Dependencies var net = require('net'); var _ = require('lodash'); var encodeToken = require('opentok-token'); var Client = require('./client'); var Session = require('./session'); var Stream = require('./stream'); var archiving = require('./archiving'); var Broadcast = require('./broadcast'); var SipInterconnect = require('./sipInterconnect'); var moderation = require('./moderation'); var signaling = require('./signaling'); var errors = require('./errors'); var callbacks = require('./callbacks'); var generateJwt = require('./generateJwt'); var render = require('./render.js'); var OpenTok; var key; /* * decodes a sessionId into the metadata that it contains * @param {string} sessionId * @returns {?SessionInfo} sessionInfo */ function decodeSessionId(sessionId) { var fields; // remove sentinal (e.g. '1_', '2_') sessionId = sessionId.substring(2); // replace invalid base64 chars sessionId = sessionId.replace(/-/g, '+').replace(/_/g, '/'); // base64 decode if (typeof Buffer.from === 'function') { sessionId = Buffer.from(sessionId, 'base64').toString('ascii'); } else { sessionId = Buffer.from(sessionId, 'base64').toString('ascii'); } // separate fields fields = sessionId.split('~'); return { apiKey: fields[1], location: fields[2], create_time: new Date(fields[3]) }; } /** * Contains methods for creating OpenTok sessions, generating tokens, and working with archives. * <p> * To create a new OpenTok object, call the OpenTok constructor with your OpenTok API key * and the API secret for your <a href="https://tokbox.com/account">TokBox account</a>. * Do not publicly share your API secret. You will use it with the OpenTok constructor * (only on your web server) to create OpenTok sessions. * <p> * Be sure to include the entire OpenTok Node.js SDK on your web server. * * @class OpenTok * * @param apiKey {String} Your OpenTok API key. (See your * <a href="https://tokbox.com/account">TokBox account page</a>.) * @param apiSecret {String} Your OpenTok API secret. (See your * <a href="https://tokbox.com/account">TokBox account page</a>.) */ // eslint-disable-next-line consistent-return OpenTok = function (apiKey, apiSecret, env) { var apiConfig; var clientConfig; var config; // we're loose about calling this constructor with `new`, we got your back if (!(this instanceof OpenTok)) return new OpenTok(apiKey, apiSecret, env); // validate arguments: apiKey := Number|String, apiSecret := String if (!(_.isNumber(apiKey) || _.isString(apiKey)) || !_.isString(apiSecret)) { throw new Error('Invalid arguments when initializing OpenTok: apiKey=' + apiKey + ', apiSecret=' + apiSecret); } // apiKey argument can be a Number, but we will internally store it as a String if (_.isNumber(apiKey)) apiKey = apiKey.toString(); this.client = new Client({ apiKey: apiKey, apiSecret: apiSecret }); this.apiKey = apiKey; this.apiSecret = apiSecret; // TODO: this is a pretty obvious seam, the integration could be more smooth apiConfig = { apiEndpoint: 'https://api.opentok.com', apiKey: apiKey, apiSecret: apiSecret, auth: { expire: 300 } }; // env can be either an object with a bunch of DI options, or a simple string for the apiUrl clientConfig = { request: {} }; if (_.isString(env)) { clientConfig.apiUrl = env; apiConfig.apiEndpoint = env; } else if (_.isObject(env) && !_.isFunction(env) && !_.isArray(env)) { if (_.isString(env.apiUrl)) { clientConfig.apiUrl = env.apiUrl; apiConfig.apiEndpoint = env.apiUrl; } if (_.isString(env.proxy)) { clientConfig.request.proxy = env.proxy; apiConfig.proxy = env.proxy; } if (_.isString(env.uaAddendum)) { clientConfig.uaAddendum = env.uaAddendum; apiConfig.uaAddendum = env.uaAddendum; } if (parseInt(env.timeout, 10)) { clientConfig.request.timeout = parseInt(env.timeout, 10); } } config = this.client.config(clientConfig); this.apiUrl = config.apiUrl; /** * Starts archiving an OpenTok session. * <p> * Clients must be actively connected to the OpenTok session for you to successfully start * recording an archive. * <p> * You can only record one archive at a time for a given session. You can only record archives * of sessions that uses the OpenTok Media Router (sessions with the media mode set to routed); * you cannot archive sessions with the media mode set to relayed. * * @param sessionId The session ID of the OpenTok session to archive. * * @param options {Object} An optional options object with the following properties (each * of which is optional): * <p> * <ul> * <li> * <code>name</code> (String) &mdash; the name of the archive, which you can use to identify * the archive. The name is set as a property of the Archive object, and it is a property of * archive-related events in the OpenTok client libraries. * </li> * <li> * <code>hasAudio</code> (Boolean) &mdash; Whether the archive will include an audio track * (<code>true</code>) or not (<code>false</code>). The default value is <code>true</code> * (an audio track is included). If you set both <code>hasAudio</code> and * <code>hasVideo</code> to <code>false</code>, the call to the <code>startArchive()</code> * method results in an error. * </li> * <li> * <code>hasVideo</code> (Boolean) &mdash; Whether the archive will include a video track * (<code>true</code>) or (not <code>false</code>). The default value is <code>true</code> * (a video track is included). If you set both <code>hasAudio</code> and * <code>hasVideo</code> to <code>false</code>, the call to the <code>startArchive()</code> * method results in an error. * </li> * <li> * <code>outputMode</code> (String) &mdash; Whether all streams in the archive are recorded * to a single file ("composed", the default) or to individual files ("individual"). * </li> * <li> * <code>layout</code> (Object) &mdash; An object defining the initial layout options * for a composed archive. This object has three properties: <code>type</code>, * <code>stylesheet</code>, and <code>screenshareType</code>, which are each strings. * Set <code>type</code> to "bestFit", * "pip", "verticalPresentation", "horizontalPresentation", or "custom". Set the * <code>stylesheet</code> property if <code>type</code> is set to "custom", and * set it to the stylesheet defining the custom layout. For example, set the * <code>layout</code> object to <code>{ type: "pip" }</code> to set the initial layout * of the archive to picture-in-picture. Set the <code>screenshareType</code> property * to the layout type to use when there is a screen-sharing stream in the session * (This property is optional.) Note if you set the <code>screenshareType</code> property, * you must set the <code>type</code> property to "bestFit" and leave * the <code>stylesheet</code> property unset. For details, see * <a href="https://tokbox.com/developer/guides/archiving/layout-control.html">Customizing * the video layout for composed archives</a>. * </li> * <li> * <code>resolution</code> (String) &mdash; For a composed archive, set this to the * resolution of the archive. Valid values are "1280x720" or "640x480" (the default). * </li> * <li> * <code>streamMode</code> (optional) &mdash; The stream mode for the archive. This can be * set to one of the the following: * * <ul> * <li> "auto" &mdash; Streams included in the archive are selected automatically * (the default).</li> * * <li> "manual" &mdash; Specify streams to be included based on calls to the * {@link OpenTok#addArchivetStream OpenTok.addArchiveStream()} and * {@link OpenTok#removeArchiveStream OpenTok.removeArchiveStream()} methods.</li> * </ul> * </li> * <li> * <code>multiArchiveTag</code> (String) &mdash; Set this to support recording * multiple archives for the same session simultaneously. Set this to a unique string * for each simultaneous archive of an ongoing session. You must also set this option * when manually starting an archive that is automatically archived. Note that the * <code>multiArchiveTag</code> value is not included in the response for * the methods to list archives and retrieve archive information. If you do not specify a * unique <code>multiArchiveTag</code> you can only record one archive at a time for a * given session. * </li> * </ul> * * For more information on archiving and the archive file formats, see the * <a href="https://tokbox.com/opentok/tutorials/archiving/">OpenTok archiving</a> * programming guide. * * @param callback {Function} The function to call upon completing the operation. Two arguments * are passed to the function: * * <ul> * * <li> * <code>error</code> &mdash; An error object (if the call to the method fails). * </li> * * <li> * <code>archive</code> &mdash; The {@link Archive} object. This object includes properties * defining the archive, including the archive ID. * </li> * * </ul> * * @method #startArchive * @memberof OpenTok */ this.startArchive = archiving.startArchive.bind(null, this, apiConfig); /** * Stops an OpenTok archive that is being recorded. * <p> * Archives automatically stop recording after 120 minutes or when all clients have disconnected * from the session being archived. * <p> * You cannot stop an archive that is not being recorded. * * @param archiveId {String} The archive ID of the archive you want to stop recording. * @return The {@link Archive} object corresponding to the archive being STOPPED. * * @param callback {Function} The function to call upon completing the operation. Two arguments * are passed to the function: * * <ul> * * <li> * <code>error</code> &mdash; An error object (if the call to the method fails). * </li> * * <li> * <code>archive</code> &mdash; The {@link Archive} object. * </li> * * </ul> * * @method #stopArchive * @memberof OpenTok */ this.stopArchive = archiving.stopArchive.bind(null, apiConfig); /** * Gets an {@link Archive} object for the given archive ID. * * @param archiveId {String} The archive ID. * * @param callback {Function} The function to call upon completing the operation. Two arguments * are passed to the function: * <ul> * <li><code>error</code> &mdash; An error object (if the call to the method fails). </li> * <li><code>archive</code> &mdash; The {@link Archive} object.</li> * </ul> * * @method #getArchive * @memberof OpenTok */ this.getArchive = archiving.getArchive.bind(null, apiConfig); /** * Deletes an OpenTok archive. * <p> * You can only delete an archive which has a status of "available" or "uploaded". Deleting an * archive removes its record from the list of archives. For an "available" archive, it also * removes the archive file, making it unavailable for download. * * @param {String} archiveId The archive ID of the archive you want to delete. * * @param callback {Function} The function to call upon completing the operation. On successfully * deleting the archive, the function is called with no arguments passed in. On failure, an error * object is passed into the function. * * @method #deleteArchive * @memberof OpenTok */ this.deleteArchive = archiving.deleteArchive.bind(null, apiConfig); /** * Retrieves a List of {@link Archive} objects, representing archives that are both * completed and in-progress, for your API key. * * @param options {Object} An options parameter with three properties: * * <ul> * * <li> * <code>count</code> &mdash; The maximum number of archives to return. The default number of * archives returned is 50 (or fewer, if there are fewer than 50 archives). The method returns * a maximum of 1000 archives. * </li> * * <li> * <code>offset</code> &mdash; The offset for the first archive to list (starting with the * first archive recorded as offset 0). 1 is the offset of the archive that started prior * to the most recent archive. This property is optional; the default is 0. * </li> * * <li> * <code>sessionId</code> &mdash; Specify the ID of a session in order to retrieve archives * specifically for that session. This property is optional. When no session ID is specified, * then the method will return archives from any session created with your API key. * </li> * * </ul> * * <p>If you don't pass in an <code>options</code> argument, * the method returns up to 1000 archives * starting with the first archive recorded. * * @param callback {Function} The function to call upon completing the operation. Two arguments * are passed to the function: * * <ul> * * <li> * <code>error</code> &mdash; An error object (if the call to the method fails). * </li> * * <li> * <code>archives</code> &mdash; An array of {@link Archive} objects. * </li> * * </ul> * * @method #listArchives * @memberof OpenTok */ this.listArchives = archiving.listArchives.bind(null, apiConfig); /** * Adds a stream to an archive that has the streamMode set to manual. * You can call the method repeatedly with the same stream ID, to toggle * the stream's audio or video in the archive. * * @param archiveId {String} The archive ID. * * @param streamId {String} The stream ID to add to archive. * * @param archiveOptions {Object} An object that has these properties: * * <ul> * * <li> * <code>hasAudio</code> &mdash; Whether the composed archive should include the stream's audio * (true, the default) or not (false). * </li> * * <li> * <code>hasVideo</code> &mdash; Whether the composed archive should include the stream's video * (true, the default) or not (false). * </li> * * </ul> * * @param callback {Function} The function to call upon completing the operation. One argument is * passed to the function * * <ul> * * <li> * <code>error</code> &mdash; An error object (if the call to the method fails). * </li> * * </ul> * * @method #addArchiveStream * @memberof OpenTok */ this.addArchiveStream = archiving.addArchiveStream.bind(null, apiConfig); /** * Removes a stream from a composed archive that has the streamMode set to manual. * * @param archiveId {String} The archive ID. * * @param streamId {String} The stream ID to remove from the archive. * * @param callback {Function} The function to call upon completing the operation. An error is * passed into the function if the call fails. * * @method #removeArchiveStream * @memberof OpenTok */ this.removeArchiveStream = archiving.removeArchiveStream.bind(null, apiConfig); /** * Sets the layout type for a composed archive. For a description of layout types, see * <a href="https://tokbox.com/developer/guides/archiving/layout-control.html">Customizing * the video layout for composed archives</a>. * * @param archiveId {String} The archive ID. * * @param type {String} The layout type. Set this to "bestFit", "pip", "verticalPresentation", * "horizontalPresentation", "focus", or "custom". For a description of these layout types, see * <a href="https://tokbox.com/developer/guides/archiving/layout-control.html">Customizing * the video layout for composed archives</a>. * * @param stylesheet {String} (Optional) The stylesheet for a custom layout. Set this parameter * if you set <code>type</code> to <code>"custom"</code>. Otherwise, leave it undefined or set * to null. * * @param screenshareType {String} (Optional) The layout type to use when * there is a screen-sharing * stream in the session. Note that to use this parameter, you must set the <code>type</code> * parameter to "bestFit" and set the <code>stylesheet</code> parameter to <code>null</code>. * * @param callback {Function} The function to call upon completing the operation. Upon error, * an <code>error</code> object is passed into the function. Upon success, the function is called * with no error object passed in. * * @method #setArchiveLayout * @memberof OpenTok */ this.setArchiveLayout = function setArchiveLayout( archiveId, type, stylesheet, screenshareType, callback ) { if (typeof archiveId !== 'string') { return callback(new Error('Invalid arguments -- must provide an archiveId string.')); } if (typeof type !== 'string') { return callback(new Error('Invalid arguments -- must provide a type string.')); } if (typeof stylesheet === 'function') { if (callback) { return callback(new Error('Invalid arguments -- stylesheet cannot be a function.')); } callback = stylesheet; // eslint-disable-line no-param-reassign } else if (stylesheet && typeof stylesheet !== 'string') { return callback(new Error('Invalid arguments -- stylesheet must be a string.')); } if (typeof screenshareType === 'function') { if (callback) { return callback(new Error('Invalid arguments -- screenshareType cannot be a function.')); } callback = screenshareType; // eslint-disable-line no-param-reassign } else if (screenshareType && typeof screenshareType !== 'string') { return callback(new Error('Invalid arguments -- screenshareType must be a string.')); } else if (screenshareType && type !== 'bestFit') { return callback(new Error('Invalid arguments -- type must be set to "bestFit" if you set screenshareType.')); } if (typeof callback !== 'function') { return callback(new Error('Invalid arguments -- must provide a callback function.')); } return this.client.setArchiveLayout( { archiveId: archiveId, type: type, stylesheet: stylesheet, screenshareType: screenshareType }, callback ); }; /** * Retrieves a List of {@link Render} objects, representing any renders in the starting, * started, stopped or failed state, for your API key. * * @param options {Object} An options parameter with three properties: * * <ul> * * <li> * <code>count</code> &mdash; The maximum number of renders to return. The default number of * renders returned is 50 (or fewer, if there are fewer than 50 renders). The method returns * a maximum of 1000 renders. * </li> * * <li> * <code>offset</code> &mdash; The offset for the first render to list (starting with the * first render recorded as offset 0). 1 is the offset of the render that started prior * to the most recent render. This property is optional; the default is 0. * </li> * * <li> * <code>sessionId</code> &mdash; Specify the ID of a session in order to retrieve renders * specifically for that session. This property is optional. When no session ID is specified, * then the method will return renders from any session created with your API key. * </li> * * </ul> * * <p>If you don't pass in an <code>options</code> argument, * the method returns up to 1000 renders, starting with the first render recorded. * * @param callback {Function} The function to call upon completing the operation. Two arguments * are passed to the function: * * <ul> * * <li> * <code>error</code> &mdash; An error object (if the call to the method fails). * </li> * * <li> * <code>renders</code> &mdash; An array of {@link Render} objects. * </li> * * </ul> * * @method #listRenders * @memberof OpenTok */ this.listRenders = render.listRenders.bind(null, apiConfig); /** * Gets an {@link Render} object for the given render ID. * * @param renderId {String} The render ID. * * @param callback {Function} The function to call upon completing the operation. Two arguments * are passed to the function: * <ul> * <li><code>error</code> &mdash; An error object (if the call to the method fails). </li> * <li><code>render</code> &mdash; The {@link Render} object.</li> * </ul> * * @method #getArchive * @memberof OpenTok */ this.getRender = render.getRender.bind(null, apiConfig); /** * Starts an Experience Composer for an OpenTok session. The Experience Composer * instance is represented as a Render object. * For more information, see the * <a href="https://tokbox.com/developer/guides/experience-composer/">Experience Composer developer guide</a>. * <p> * Clients must be actively connected to the OpenTok session for you to successfully start * rendering a session. * <p> * * @param options {Object} An optional options object with the following properties (each * of which is optional): * <p> * <ul> * <li> * <code>sessionId</code> (String) &mdash; The ID of a session (generated with the same * `APIKEY`as specified in the URL) which you wish to start rendering into * </li> * <li> * <code>token</code> (String) &mdash; A valid OpenTok token with a Publisher role and * (optionally) connection data to be associated with the output stream. * </li> * * <li> * <code>url</code> (String) &mdash; A publically reachable URL controlled by the * customer and capable of generating the content to be rendered without user intervention. * </li> * * <li> * <code>properties</code> (Object) &mdash; Initial configuration of Publisher properties for * the composed output stream. * </li> * * <li> * <code>maxDuration</code> (Number) &mdash; The maximum time allowed for the Render, in * seconds. After this time, the Render will be stopped automatically, if it is still running. * </li> * * <li> * <code>resolution</code> (String) &mdash; Resolution of the display area for the composition. * </li> * * </ul> * * @param callback {Function} The function to call upon completing the operation. Two arguments * are passed to the function: * * <ul> * * <li> * <code>error</code> &mdash; An error object (if the call to the method fails). * </li> * * <li> * <code>render</code> &mdash; The {@link Render} object. This object includes properties * defining the render, including the render ID. * </li> * * </ul> * * @method #startRender * @memberof OpenTok */ this.startRender = render.startRender.bind(null, apiConfig); /** * Stops an Experience Composer that is being rendered. * * @param renderId {String} The ID of the render you want to stop rendering. * @return The {@link Archive} object corresponding to the archive being STOPPED. * * @param callback {Function} The function to call upon completing the operation. Two arguments * are passed to the function: * * <ul> * * <li> * <code>error</code> &mdash; An error object (if the call to the method fails). * </li> * * <li> * <code>render</code> &mdash; The {@link Render} object. * </li> * * </ul> * * @method #stopRender * @memberof OpenTok */ this.stopRender = render.stopRender.bind(null, apiConfig); /** * Starts a <a href="https://tokbox.com/developer/guides/broadcast/live-streaming/">live * streaming broadcast</a>. * * @param {String} sessionId The ID of the session to broadcast. * * @param {Object} options An object with the following form: * * <pre><code>{ * outputs: { * hls: { * dvr: false, * lowLatency: false, * }, * rtmp: [{ * id: "foo", * serverUrl: "rtmp://myfooserver/myfooapp", * streamName: "myfoostream" * }, * { * id: "bar", * serverUrl: "rtmp://mybarserver/mybarapp", * streamName: "mybarstream" * }] * }, * maxDuration: 5400, * resolution: "640x480", * layout: { * type: "custom", * stylesheet: "the layout stylesheet (only used with type == custom)", * screenshareType: "the layout type to use when there is a screen-sharing stream (optional)" * }, * streamMode: "manual" * } * </code></pre> * * <p> * The <code>options</code> object includes the following properties: * * <ul> * <li> * <p> * <code>outputs</code> (required) &mdash; This object defines the types of * broadcast streams you want to start. You can include HLS, RTMP, or both * as broadcast streams. If you include RTMP streaming, you can specify up to five * target RTMP streams (or just one). * </p> * <p> * For HLS, include a single <code>hls</code> property in the outputs object. This object * includes the following optional properties: * <ul> * <li> * <code>dvr</code> (Boolean) — Whether to enable <a href="https://tokbox.com/developer/guides/broadcast/live-streaming/#dvr">DVR functionality</a> — rewinding, pausing, * and resuming — in players that support it (<code>true</code>), or not * (<code>false</code>, the default). With DVR enabled, the HLS URL will include a * <code>?DVR</code> query string appended to the end. * </li> * <li> * <code>lowLatency</code> (Boolean) — Whether to enable <a href="https://tokbox.com/developer/guides/broadcast/live-streaming/#low-latency">low-latency mode</a> for the HLS * stream. Some HLS players do not support low-latency mode. This feature is incompatible * with DVR mode HLS broadcasts. * </li> * </ul> * The HLS URL is returned in the <code>broadcastUrls</code> as the <code>hls</code> * property in the {@link Broadcast} object passed into the callback methods of the * {@link OpenTok#getBroadcast} and {@link OpenTok#listBroadcast} methods. * </p> * <p> * For each RTMP stream, specify <code>serverUrl</code> (the RTMP server URL), * <code>streamName</code> (the stream name, such as the YouTube Live stream name or * the Facebook stream key), and (optionally) <code>id</code> (a unique ID for the stream). * If you specify an ID, it will be included as the <code>id</code> property of the * {@link Broadcast} object passed into the callback methods of the * <code>startBroadcast()</code> method and the * {@link OpenTok#getBroadcast OpenTok.getBroadcast()} method. OpenTok streams * the session to each RTMP URL you specify. Note that OpenTok live streaming * supports RTMP and RTMPS. * </p> * </li> * <li> * <code>maxDuration</code> (optional) &mdash; The maximum duration for the broadcast, in * seconds. The broadcast will automatically stop when the maximum duration is reached. * You can set the maximum duration to a value from 60 (60 seconds) to 36000 (10 hours). * The default maximum duration is 4 hours (14,400 seconds). * </li> * <li> * <code>resolution</code> (optional) &mdash; The resolution of the broadcast: either * <code>"640x480"</code> (SD, the default) or <code>"1280x720"</code> (HD). * </li> * </li> * <code>layout</code> (optional) &mdash; Specify this to assign the initial layout type for * the broadcast. This object has three properties: <code>type</code>, * <code>stylesheet</code>, and <code>screenshareType</code>, which are each strings. * Valid values for the <code>type</code> property are <code>"bestFit"</code> * (best fit), <code>"custom"</code> (custom), <code>"horizontalPresentation"</code> * (horizontal presentation), <code>"pip"</code> (picture-in-picture), and * <code>"verticalPresentation"</code> (vertical presentation)). If you specify * a <code>"custom"</code> layout type, set the <code>stylesheet</code> property of * the <code>layout</code> object to the stylesheet. (For other layout types, do not set * a <code>stylesheet</code> property.) If you do not specify an initial layout type, * the broadcast stream uses the Best Fit layout type. Set the <code>screenshareType</code> * property to the layout type to use when there is a screen-sharing stream in the session. * (This property is optional.) Note if you set the <code>screenshareType</code> property, * you must set the <code>type</code> property to "bestFit" and leave the * <code>stylesheet</code> property unset. For more information, see * <a href="https://tokbox.com/developer/guides/broadcast/live-streaming/#configuring-video-layout-for-opentok-live-streaming-broadcasts)">Configuring * video layout for OpenTok live streaming broadcasts</a>. * </li> * <li> * <code>streamMode</code> (optional) &mdash; The stream mode for the broadcast. This can be * set to one of the the following: * * <ul> * <li> "auto" &mdash; Streams included in the broadcast are selected automatically * (the default).</li> * * <li> "manual" &mdash; Specify streams to be included based on calls to the * {@link OpenTok#addBroadcastStream OpenTok.addBroadcastStream()} and * {@link OpenTok#removeBroadcastStream OpenTok.removeBroadcastStream()} methods.</li> * </ul> * </li> * <li> * <code>multiBroadcastTag</code> (optional) &mdash; Set this to support multiple * broadcasts for the same session simultaneously. Set this to a unique string for * each simultaneous broadcast of an ongoing session. * Note that the <code>multiBroadcastTag</code> value is not included in the response * for the methods to list live streaming broadcasts and get information about a live * streaming broadcast. * </li> * </ul> * * @param {Function} callback A callback method that takes two parameters: * <code>error</code>, which is set to an Error object on error, and * <code>broadcast</code>, which is set to a {@link Broadcast} object on success. * * @method #startBroadcast * @memberof OpenTok */ OpenTok.prototype.startBroadcast = function (sessionId, options, callback) { var client = this.client; if (typeof callback !== 'function') { throw new errors.ArgumentError('No callback given to startBroadcast'); } if (sessionId == null || sessionId.length === 0) { callback(new errors.ArgumentError('No sessionId given to startBroadcast')); } else if (!options || typeof options !== 'object') { callback(new errors.ArgumentError('No options given to startBroadcast')); } else { options.sessionId = sessionId; if (!options.streamMode) { options.streamMode = 'auto'; } if (options.outputs && options.outputs.hls) { if (options.outputs.hls.dvr && options.outputs.hls.lowLatency) { callback(new errors.ArgumentError('Cannot set both dvr and lowLatency on HLS')); return; } } client.startBroadcast(options, function (err, json) { if (err) { return callback(new Error('Failed to start broadcast. ' + err)); } return callback(null, new Broadcast(client, json)); }); } }; /** * Adds a stream to a broadcast that has the streamMode set to manual. * You can call the method repeatedly with the same stream ID, to toggle * the stream's audio or video in the broadcast. * * @param broadcastId {String} The broadcast ID. * * @param streamId {String} The stream ID to add to broadcast. * * @param broadcastOptions {Object} An object that has these properties: * * <ul> * * <li> * <code>hasAudio</code> &mdash; Whether the broadcast should include the stream's audio * (true, the default) or not (false). * </li> * * <li> * <code>hasVideo</code> &mdash; Whether the broadcast should include the stream's video * (true, the default) or not (false). * </li> * * </ul> * * @method #addBroadcastStream * @memberof OpenTok */ this.addBroadcastStream = function addBroadcastStream( broadcastId, streamId, broadcastOptions, callback ) { var client = this.client; if (typeof broadcastOptions === 'function') { callback = broadcastOptions; broadcastOptions = {}; } if (typeof callback !== 'function') { throw (new errors.ArgumentError('No callback given to addBroadcastStream')); } if (broadcastId == null || broadcastId.length === 0) { callback(new errors.ArgumentError('No broadcastId given to addBroadcastStream')); } if (streamId == null || streamId.length === 0) { callback(new errors.ArgumentError('No streamId given to addBroadcastStream')); } broadcastOptions = { addStream: streamId, hasAudio: broadcastOptions.hasAudio || true, hasVideo: broadcastOptions.hasVideo || true }; client.patchBroadcast(broadcastId, broadcastOptions, callback); }; /** * Removes a stream from a broadcast that has the streamMode set to manual. * * @param broadcastId {String} The broadcast ID. * * @param streamId {String} The stream ID to remove from the broadcast. * * @param callback {Function} The function to call upon completing the operation. An error is * passed into the function if the call fails. * * @method #removeBroadcastStream * @memberof OpenTok */ this.removeBroadcastStream = function removeBroadcastStream(broadcastId, streamId, callback) { var client = this.client; if (typeof callback !== 'function') { throw new errors.ArgumentError('No callback given to removeBroadcastStream'); } if (broadcastId == null || broadcastId.length === 0) { callback(new errors.ArgumentError('No archiveId provided')); return; } if (streamId == null || streamId.length === 0) { callback(new errors.ArgumentError('No streamId provided')); return; } client.patchBroadcast(broadcastId, { removeStream: streamId }, callback); }; /** * Stops a live streaming broadcast. * * @param {String} broadcastId The ID of the broadcast. * * @param {Function} callback A callback method that takes two parameters: * <code>error</code>, which is set to an Error object on error, and * <code>broadcast</code>, which is set to a {@link Broadcast} object on success. * * @method #stopBroadcast * @memberof OpenTok */ this.stopBroadcast = function stopBroadcast(broadcastId, callback) { var client = this.client; if (broadcastId === null || broadcastId.length === 0) { callback(new errors.ArgumentError('No broadcast ID given')); return; } if (typeof callback !== 'function') { throw new errors.ArgumentError('No callback given to stopBroadcast'); } client.stopBroadcast(broadcastId, function (err, json) { if (err) return callback(new Error('Failed to stop broadcast. ' + err)); return callback(null, new Broadcast(client, json)); }); }; /** * Returns information about a live streaming broadcast. * * @param {String} broadcastId The ID of the broadcast. * * @param {Function} callback A callback method that takes two parameters: * <code>error</code>, which is set to an Error object on error, and * <code>broadcast</code>, which is set to a {@link Broadcast} object on success. * * @method #getBroadcast * @memberof OpenTok */ this.getBroadcast = function getBroadcast(broadcastId, callback) { var client = this.client; if (broadcastId === null || broadcastId.length === 0) { callback(new errors.ArgumentError('No broadcast ID given')); return; } if (typeof callback !== 'function') { throw new errors.ArgumentError('No callback given to getBroadcast'); } client.getBroadcast(broadcastId, function (err, json) { if (err) return callback(new Error('Failed to get broadcast. ' + err)); return callback(null, new Broadcast(client, json)); }); }; /** * Retrieves a List of {@link Broadcast} objects, representing broadcasts that are both * completed and in-progress, for your API key. * * @param options {Object} An options parameter with three properties: * * <ul> * * <li> * <code>count</code> &mdash; The maximum number of broadcasts to return. * The default number of * broadcasts returned is 50 (or fewer, if there are fewer than 50 broadcasts). * The method returns a maximum of 1000 broadcasts. * </li> * * <li> * <code>offset</code> &mdash; The offset for the first broadcast to list (starting with the * first broadcast recorded as offset 0). 1 is the offset of the broadcast that started prior * to the most recent broadcast. This property is optional; the default is 0. * </li> * * <li> * <code>sessionId</code> &mdash; Specify the ID of a session in order to retrieve broadcasts * specifically for that session. This property is optional. When no session ID is specified, * then the method will return broadcasts from any session created with your API key. * </li> * * </ul> * * <p>If you don't pass in an <code>options</code> argument, * the method returns up to 1000 broadcasts starting with the first broadcast recorded. * * @param callback {Function} The function to call upon completing the operation. Two arguments * are passed to the function: * * <ul> * * <li> * <code>error</code> &mdash; An error object (if the call to the method fails). * </li> * * <li> * <code>broadcasts</code> &mdash; An array of {@link Broadcast} objects. * </li> * * </ul> * * @method #listBroadcasts * @memberof OpenTok */ this.listBroadcasts = function listBroadcasts(options, callback) { var query = []; var queryString = null; if (typeof options === 'function') { callback = options; options = {}; } if (typeof callback !== 'function') { throw new errors.ArgumentError('No callback given to listBroadcasts'); } if (options.offset) { query.push('offset=' + parseInt(options.offset, 10)); } if (options.count) { query.push('count=' + parseInt(options.count, 10)); } if (options.sessionId) { query.push('sessionId=' + options.sessionId); } queryString = query.join('&'); return this.client.listBroadcasts( queryString, function cb(err, json, totalCount) { if (err) { return callback(err); } return callback(null, json, totalCount); } ); }; /** * Sets (or updates) the layout of the broadcast. See * <a href="https://tokbox.com/developer/guides/broadcast/live-streaming/#configuring-video-layout-for-opentok-live-streaming-broadcasts"> * Configuring video layout for OpenTok live streaming broadcasts</a>. * * @param {String} broadcastId The ID of the broadcast. * * @param type {String} The layout type. Set this to "bestFit", "pip", "verticalPresentation", * "horizontalPresentation", "focus", or "custom". For a description of these layout types, see * <a href=https://tokbox.com/developer/guides/broadcast/live-streaming/#configuring-video-layout-for-opentok-live-streaming-broadcasts> Configuring * layout for OpenTok live streaming broadcasts</a>. * * @param stylesheet {String} (Optional) The stylesheet for a custom layout. Set this * parameter if you set <code>type</code> to "custom". Otherwise, leave it undefined or * set to <code>null</code>. * * @param screenshareType {String} (Optional) The layout type to use when there is * a screen-sharing stream in the session. Note that to use this parameter, you must set * the <code>type</code> parameter to "bestFit" and set the <code>stylesheet</code> * parameter to <code>null</code>. * * @param callback {Function} The function to call upon completing the operation. Upon error, * an Error object is passed into the function. Upon success, the function is called * with no Error object passed in. * * @method #setBroadcastLayout * @memberof OpenTok */ this.setBroadcastLayout = function setBroadcastLayout( broadcastId, type, stylesheet, screenshareType, callback ) { if (typeof broadcastId !== 'string') { return callback(new Error('Invalid arguments -- must provide an broadcastId string.')); } if (typeof type !== 'string') { return callback(new Error('Invalid arguments -- must provide a type string.')); } if (typeof stylesheet === 'function') { if (callback) { return callback(new Error('Invalid arguments -- stylesheet cannot be a function.')); } callback = stylesheet; // eslint-disable-line no-param-reassign } else if (stylesheet && typeof stylesheet !== 'string') { return callback(new Error('Invalid arguments -- stylesheet must be a string.')); } if (typeof screenshareType === 'function') { if (callback) { return callback(new Error('Invalid arguments -- screenshareType cannot be a function.')); } callback = screenshareType; // eslint-disable-line no-param-reassign } else if (screenshareType && typeof screenshareType !== 'string') { return callback(new Error('Invalid arguments -- screenshareType must be a string.')); } else if (screenshareType && type !== 'bestFit') { return callback(new Error('Invalid arguments -- type must be set to "bestFit" if you set screenshareType.')); } if (typeof callback !== 'function') { return callback(new Error('Invalid arguments -- must provide a callback function.')); } return this.client.setBroadcastLayout( { broadcastId: broadcastId, type: type, stylesheet: stylesheet, screenshareType: screenshareType }, callback ); }; /** * Sets the layout class list for streams in a session. Layout classes are used in * the layout for composed archives and live streaming broadcasts. For more information, see * <a href="https://tokbox.com/developer/guides/archiving/layout-control.html">Customizing * the video layout for composed archives</a> and * <a href="https://tokbox.com/developer/guides/broadcast/live-streaming/#configuring-video-layout-for-opentok-live-streaming-broadcasts">Configuring * video layout for OpenTok live streaming broadcasts</a>. * * <p> * You can set the initial layout class list for streams published by a client when you generate * used by the client. See the {@link OpenTok#generateToken OpenTok.generateToken()} method. * * @param sessionId {String} The session ID of the session the streams belong to. * * @param classListArray {Array} (Optional) An array defining the class lists to apply to * streams. Each element in the array is an object with two properties: <code>id</code> and * <code>layoutClassList</code>. The <code>id</code> property is the stream ID (a String), * and the <code>layoutClassList</code> is an array of class names (Strings) to apply to the * stream. Set <code>layoutClassList</code> to an empty array to clear the layout class list for * a stream. For example, this <code>streamClassArray</code> array sets the layout class list for * three streams: * <p> * <pre> * const classListArray = [ * { id: '7b09ec3c-26f9-43d7-8197-f608f13d4fb6', layoutClassList: ['focus'] }, * { id: '567bc941-6ea0-4c69-97fc-70a740b68976', layoutClassList: ['top'] }, * { id: '307dc941-0450-4c09-975c-705740d08970', layoutClassList: ['bottom'] } * ]; * </pre> * * @param callback {Function} The function to call upon completing the operation. Upon error, * an <code>error</code> object is passed into the function. Upon success, the function is called * with no error object passed in. * * @method #setStreamClassLists * @memberof OpenTok */ this.setStreamClassLists = function setStreamClassLists( sessionId, classListArray, callback ) { var i; var j; var layoutObj; if (typeof sessionId !== 'string') { return callback(new Error('Invalid arguments -- must provide an sessionId string.')); } if (!Array.isArray(classListArray)) { return callback(new Error('Invalid arguments -- must provide a streamClassArray array.')); } for (i = 0; i < classListArray.length; i += 1) { layoutObj = classListArray[i]; if (typeof layoutObj.id !== 'string') { return callback(new Error('Invalid arguments -- each element in the streamClassArray ' + 'must have an id string.')); } if (!Array.isArray(layoutObj.layoutClassList)) { return callback(new Error('Invalid arguments -- each element in the streamClassArray ' + 'must have a layoutClassList array.')); } for (j = 0; j < layoutObj.layoutClassList.length; j += 1) { if (typeof layoutObj.layoutClassList[j] !== 'string') { return callback(new Error('Invalid arguments -- each element in the layoutClassList ' + 'array must be a string (defining class names).')); } } } if (typeof callback !== 'function') { return callback(new Error('Invalid arguments -- must provide a callback function.')); } return this.client.setStreamClassLists(sessionId, classListArray, callback); }; /** * Sends a signal to all the connections in a session or to a specific one. * <p> * Clients must be actively connected to the OpenTok session for you to successfully send * a signal to them. * <p> * For more information, see the * <a href="https://www.tokbox.com/developer/guides/signaling">OpenTok signaling</a> * programming guide. * * @param sessionId The session ID of the OpenTok session where you want to send the signal. * * @param connectionId The connection ID of a client connected to the session. Leave * this empty if you want to send a signal to all connections in the session. * * @param payload An object with optional <code>data</code> and <code>type</code> properties: * * <p> * * <ul> * <li> * <code>data</code> &mdash; The data to send. The limit to the length of data string * is 8kB. Do not set the data string to null or undefined. * </li> * * <li> * <code>type</code> &mdash; The type of the signal. Clients can use the type to filter * signals. The maximum length of the type string is 128 characters. * </li> * </ul> * * @param callback {Function} The callback function invoked when the call to the method * succeeds or fails. If the call fails, an error object is passed into the callback function. * * @method #signal * @memberof OpenTok */ this.signal = signaling.signal.bind(null, apiConfig); /** * Disconnects a participant from an OpenTok session. * * This is the server-side equivalent to the * <a href="https://www.tokbox.com/developer/guides/moderation/js/#force_disconnect"> * forceDisconnect() method in OpenTok.js</a> * * @param sessionId The session ID for the OpenTok session that the client you want * to disconnect is connected to. * * @param connectionId The connection ID of the client you want to disconnect. * * @param callback {Function} The function to call upon completing the operation. Two arguments * are passed to the function: * * <ul> * * <li> * <code>error</code> &mdash; An error object (if the call to the method fails). * </li> * * </ul> * * @method #forceDisconnect * @memberof OpenTok */ this.forceDisconnect = moderation.forceDisconnect.bind(null, apiConfig); /** * Gets info about a stream. The stream must be an active stream in an OpenTok session. * * @param sessionId {String} The session ID of the OpenTok session containing the stream. * * @param options {String} The stream ID. * * @param callback {Function} The function to call upon completing the operation. Two arguments * are passed to the function: * * <ul> * * <li> * <code>error</code> &mdash; An error object (if the call to the method fails). This is * set to null if there is no error. Calling this method results in an error if you pass in * an invalid stream ID or an invalid session ID. * </li> * * <li> * <code>stream</code> &mdash; The {@link Stream} object. This object includes properties * defining the stream. This is undefined if there is an error. * </li> * * </ul> * * @method #getStream * @memberof OpenTok */ this.getStream = function getStream(sessionId, streamId, callback) { if (!sessionId || typeof sessionId !== 'string')