diff --git a/lib/captions.js b/lib/captions.js new file mode 100644 index 00000000..c9a7c4f4 --- /dev/null +++ b/lib/captions.js @@ -0,0 +1,131 @@ +const fetch = require('node-fetch'); +const errors = require('./errors'); +const pkg = require('../package.json'); +const generateJwt = require('./generateJwt'); +const generateHeaders = (config) => { + return { + 'User-Agent': 'OpenTok-Node-SDK/' + pkg.version, + 'X-OPENTOK-AUTH': generateJwt(config), + Accept: 'application/json', + }; +}; +const api = (config, method, path, body, callback) => { + const rurl = config.apiEndpoint + '/v2/project/' + config.apiKey + path; + + const headers = generateHeaders(config); + + if (body && ['POST', 'PATCH', 'PUT'].includes(method)) { + headers['Content-Type'] = 'application/json'; + } + + Promise.resolve(fetch( + rurl, + { + method: method, + body: body ? JSON.stringify(body) : null, + headers: headers, + } + )) + .then(async (response) => { + const otResponse = { + statusCode: response.status, + } + + const body = await response.text(); + + callback(null, otResponse, body) + }) + .catch(async (error) => { + callback(error); + }); +}; + +exports.startCaptions = ( + config, + sessionId, + token, + { + languageCode = 'en-US', + maxDuration = 1800, + partialCaptions = true + }, + callback, + ) => { + if (typeof callback !== 'function') { + throw new errors.ArgumentError('No callback given to startCaptions'); + } + + api( + config, + 'POST', + '/captions', + { + sessionId: sessionId, + token: token, + languageCode: languageCode, + maxDuration: maxDuration, + partialCaptions: partialCaptions, + }, + (err, response, body) => { + if (err) { + callback(err); + return; + } + + let responseJson = {}; + try { + responseJson = JSON.parse(body); + } catch { + + } + switch (response?.statusCode) { + case 200: + const { captionsId } = responseJson + callback(null, captionsId); + break; + case 400: + callback(new errors.RequestError(body)); + break; + case 409: + callback(new errors.CaptionsError()); + break; + default: + callback(new errors.RequestError('Unexpected response from OpenTok: ' + JSON.stringify({ statusCode: response.statusCode, statusMessage: response.statusMessage }))); + } + } + ); +}; + +exports.stopCaptions = ( + config, + captionsId, + callback, +) => { + if (typeof callback !== 'function') { + throw new errors.ArgumentError('No callback given to stopArchive'); + } + + api( + config, + 'POST', + `/captions/${captionsId}/stop`, + {}, + (err, response, body) => { + if (err) { + callback(err); + return; + } + + switch (response?.statusCode) { + case 202: + callback(null, true); + break; + case 400: + callback(new errors.NotFoundError(`No matching captions found for ${captionsId}`)); + break; + default: + callback(new errors.RequestError('Unexpected response from OpenTok: ' + JSON.stringify({ statusCode: response.statusCode, statusMessage: response.statusMessage }))); + } + } + ); +}; diff --git a/lib/errors.js b/lib/errors.js index 63e8841c..e8017177 100644 --- a/lib/errors.js +++ b/lib/errors.js @@ -18,6 +18,9 @@ exports.ArchiveError = function (message) { exports.ArchiveError.prototype = Object.create(Error.prototype); +exports.CaptionsError = function (message = 'Live captions have already started for this OpenTok Session') { + this.message = message; +}; exports.SipError = function (message) { this.message = message; diff --git a/lib/opentok.js b/lib/opentok.js index fb935aab..d6cd1b3c 100644 --- a/lib/opentok.js +++ b/lib/opentok.js @@ -18,6 +18,7 @@ var errors = require('./errors'); var callbacks = require('./callbacks'); var generateJwt = require('./generateJwt'); var render = require('./render.js'); +var captions = require('./captions.js'); var OpenTok; var key; @@ -486,6 +487,100 @@ OpenTok = function (apiKey, apiSecret, env) { ); }; + + /** + * Starts live captions for an OpenTok Session + *
+ * The maximum allowed duration is 4 hours, after which the audio captioning will stop without + * any effect on the ongoing OpenTok Session. An event will be posted to your callback URL if + * provided when starting the captions. + *
+ * Each OpenTok Session supports only one audio captioning session. + * + * @param sessionId The session ID of the OpenTok session to archive. + * + * @param token A valid OpenTok token with role set to Moderator. + * + * @param options {Object} An optional options object with the following properties (each + * of which is optional): + *
+ *
languageCode
(String) — The BCP-47 code for a spoken language used on
+ * this call. The default value is "en-US". The following language codes are supported:
+ * "en-AU" (English, Australia), "en-GB" (Englsh, UK), "es-US" (English, US),
+ * "zh-CN” (Chinese, Simplified), "fr-FR" (French), "fr-CA" (French, Canadian),
+ * "de-DE" (German), "hi-IN" (Hindi, Indian), "it-IT" (Italian), "ja-JP" (Japanese),
+ * "ko-KR" (Korean), "pt-BR" (Portuguese, Brazilian), "th-TH" (Thai).
+ * maxDuration
(Integer) — The maximum duration for the audio captioning,
+ * in seconds. The default value is 14,400 seconds (4 hours), the maximum duration allowed.
+ * The minimum value for maxDuration is 300 (300 seconds, or 5 minutes).
+ * partialCaptions
(Boolean) — Whether to enable this to faster captioning
+ * at the cost of some degree of inaccuracies. The default value is true.
+ * error
— An error object (if the call to the method fails).
+ * captionID
— The id of the captions
+ * error
— An error object (if the call to the method fails).
+ * success
— True always
+ *