Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

Recording.js 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. /* global APP, $, config, interfaceConfig */
  2. /*
  3. * Copyright @ 2015 Atlassian Pty Ltd
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. import UIEvents from "../../../service/UI/UIEvents";
  18. import UIUtil from '../util/UIUtil';
  19. /**
  20. * Indicates if the recording button should be enabled.
  21. *
  22. * @returns {boolean} {true} if the
  23. * @private
  24. */
  25. function _isRecordingButtonEnabled() {
  26. return interfaceConfig.TOOLBAR_BUTTONS.indexOf("recording") !== -1
  27. && config.enableRecording;
  28. }
  29. /**
  30. * Request live stream token from the user.
  31. * @returns {Promise}
  32. */
  33. function _requestLiveStreamId() {
  34. const msg = APP.translation.generateTranslationHTML("dialog.liveStreaming");
  35. const token = APP.translation.translateString("dialog.streamKey");
  36. const cancelButton
  37. = APP.translation.generateTranslationHTML("dialog.Cancel");
  38. const backButton = APP.translation.generateTranslationHTML("dialog.Back");
  39. const startStreamingButton
  40. = APP.translation.generateTranslationHTML("dialog.startLiveStreaming");
  41. const streamIdRequired
  42. = APP.translation.generateTranslationHTML(
  43. "liveStreaming.streamIdRequired");
  44. return new Promise(function (resolve, reject) {
  45. let dialog = APP.UI.messageHandler.openDialogWithStates({
  46. state0: {
  47. html:
  48. `<h2>${msg}</h2>
  49. <input name="streamId" type="text"
  50. data-i18n="[placeholder]dialog.streamKey"
  51. placeholder="${token}" autofocus>`,
  52. persistent: false,
  53. buttons: [
  54. {title: cancelButton, value: false},
  55. {title: startStreamingButton, value: true}
  56. ],
  57. focus: ':input:first',
  58. defaultButton: 1,
  59. submit: function (e, v, m, f) {
  60. e.preventDefault();
  61. if (v) {
  62. if (f.streamId && f.streamId.length > 0) {
  63. resolve(UIUtil.escapeHtml(f.streamId));
  64. dialog.close();
  65. return;
  66. }
  67. else {
  68. dialog.goToState('state1');
  69. return false;
  70. }
  71. } else {
  72. reject();
  73. dialog.close();
  74. return false;
  75. }
  76. }
  77. },
  78. state1: {
  79. html: `<h2>${msg}</h2> ${streamIdRequired}`,
  80. persistent: false,
  81. buttons: [
  82. {title: cancelButton, value: false},
  83. {title: backButton, value: true}
  84. ],
  85. focus: ':input:first',
  86. defaultButton: 1,
  87. submit: function (e, v, m, f) {
  88. e.preventDefault();
  89. if (v === 0) {
  90. reject();
  91. dialog.close();
  92. } else {
  93. dialog.goToState('state0');
  94. }
  95. }
  96. }
  97. });
  98. });
  99. }
  100. /**
  101. * Request recording token from the user.
  102. * @returns {Promise}
  103. */
  104. function _requestRecordingToken () {
  105. let msg = APP.translation.generateTranslationHTML("dialog.recordingToken");
  106. let token = APP.translation.translateString("dialog.token");
  107. return new Promise(function (resolve, reject) {
  108. APP.UI.messageHandler.openTwoButtonDialog(
  109. null, null, null,
  110. `<h2>${msg}</h2>
  111. <input name="recordingToken" type="text"
  112. data-i18n="[placeholder]dialog.token"
  113. placeholder="${token}" autofocus>`,
  114. false, "dialog.Save",
  115. function (e, v, m, f) {
  116. if (v && f.recordingToken) {
  117. resolve(UIUtil.escapeHtml(f.recordingToken));
  118. } else {
  119. reject();
  120. }
  121. },
  122. null,
  123. function () { },
  124. ':input:first'
  125. );
  126. });
  127. }
  128. function _showStopRecordingPrompt (recordingType) {
  129. var title;
  130. var message;
  131. var buttonKey;
  132. if (recordingType === "jibri") {
  133. title = "dialog.liveStreaming";
  134. message = "dialog.stopStreamingWarning";
  135. buttonKey = "dialog.stopLiveStreaming";
  136. }
  137. else {
  138. title = "dialog.recording";
  139. message = "dialog.stopRecordingWarning";
  140. buttonKey = "dialog.stopRecording";
  141. }
  142. return new Promise(function (resolve, reject) {
  143. APP.UI.messageHandler.openTwoButtonDialog(
  144. title,
  145. null,
  146. message,
  147. null,
  148. false,
  149. buttonKey,
  150. function(e,v,m,f) {
  151. if (v) {
  152. resolve();
  153. } else {
  154. reject();
  155. }
  156. }
  157. );
  158. });
  159. }
  160. function moveToCorner(selector, move) {
  161. let moveToCornerClass = "moveToCorner";
  162. if (move && !selector.hasClass(moveToCornerClass))
  163. selector.addClass(moveToCornerClass);
  164. else
  165. selector.removeClass(moveToCornerClass);
  166. }
  167. var Status = {
  168. ON: "on",
  169. OFF: "off",
  170. AVAILABLE: "available",
  171. UNAVAILABLE: "unavailable",
  172. PENDING: "pending"
  173. };
  174. var Recording = {
  175. /**
  176. * Initializes the recording UI.
  177. */
  178. init (emitter, recordingType) {
  179. this.eventEmitter = emitter;
  180. // Use recorder states directly from the library.
  181. this.currentState = Status.UNAVAILABLE;
  182. this.initRecordingButton(recordingType);
  183. },
  184. initRecordingButton(recordingType) {
  185. let selector = $('#toolbar_button_record');
  186. if (recordingType === 'jibri') {
  187. this.baseClass = "fa fa-play-circle";
  188. this.recordingOnKey = "liveStreaming.on";
  189. this.recordingOffKey = "liveStreaming.off";
  190. this.recordingPendingKey = "liveStreaming.pending";
  191. this.failedToStartKey = "liveStreaming.failedToStart";
  192. this.recordingButtonTooltip = "liveStreaming.buttonTooltip";
  193. }
  194. else {
  195. this.baseClass = "icon-recEnable";
  196. this.recordingOnKey = "recording.on";
  197. this.recordingOffKey = "recording.off";
  198. this.recordingPendingKey = "recording.pending";
  199. this.failedToStartKey = "recording.failedToStart";
  200. this.recordingButtonTooltip = "recording.buttonTooltip";
  201. }
  202. selector.addClass(this.baseClass);
  203. selector.attr("data-i18n", "[content]" + this.recordingButtonTooltip);
  204. selector.attr("content",
  205. APP.translation.translateString(this.recordingButtonTooltip));
  206. var self = this;
  207. selector.click(function () {
  208. switch (self.currentState) {
  209. case Status.ON:
  210. case Status.PENDING: {
  211. _showStopRecordingPrompt(recordingType).then(() =>
  212. self.eventEmitter.emit(UIEvents.RECORDING_TOGGLED));
  213. break;
  214. }
  215. case Status.AVAILABLE:
  216. case Status.OFF: {
  217. if (recordingType === 'jibri')
  218. _requestLiveStreamId().then((streamId) => {
  219. self.eventEmitter.emit( UIEvents.RECORDING_TOGGLED,
  220. {streamId: streamId});
  221. });
  222. else {
  223. if (self.predefinedToken) {
  224. self.eventEmitter.emit( UIEvents.RECORDING_TOGGLED,
  225. {token: self.predefinedToken});
  226. return;
  227. }
  228. _requestRecordingToken().then((token) => {
  229. self.eventEmitter.emit( UIEvents.RECORDING_TOGGLED,
  230. {token: token});
  231. });
  232. }
  233. break;
  234. }
  235. default: {
  236. APP.UI.messageHandler.openMessageDialog(
  237. "dialog.liveStreaming",
  238. "liveStreaming.unavailable"
  239. );
  240. }
  241. }
  242. });
  243. },
  244. // Shows or hides the 'recording' button.
  245. showRecordingButton (show) {
  246. if (_isRecordingButtonEnabled() && show) {
  247. $('#toolbar_button_record').css({display: "inline-block"});
  248. } else {
  249. $('#toolbar_button_record').css({display: "none"});
  250. }
  251. },
  252. updateRecordingState(recordingState) {
  253. // I'm the recorder, so I don't want to see any UI related to states.
  254. if (config.iAmRecorder)
  255. return;
  256. // If there's no state change, we ignore the update.
  257. if (this.currentState === recordingState)
  258. return;
  259. this.setRecordingButtonState(recordingState);
  260. },
  261. // Sets the state of the recording button
  262. setRecordingButtonState (recordingState) {
  263. let buttonSelector = $('#toolbar_button_record');
  264. let labelSelector = $('#recordingLabel');
  265. // TODO: handle recording state=available
  266. if (recordingState === Status.ON) {
  267. buttonSelector.removeClass(this.baseClass);
  268. buttonSelector.addClass(this.baseClass + " active");
  269. labelSelector.attr("data-i18n", this.recordingOnKey);
  270. moveToCorner(labelSelector, true, 3000);
  271. labelSelector
  272. .text(APP.translation.translateString(this.recordingOnKey));
  273. } else if (recordingState === Status.OFF
  274. || recordingState === Status.UNAVAILABLE) {
  275. // We don't want to do any changes if this is
  276. // an availability change.
  277. if (this.currentState !== Status.ON
  278. && this.currentState !== Status.PENDING)
  279. return;
  280. buttonSelector.removeClass(this.baseClass + " active");
  281. buttonSelector.addClass(this.baseClass);
  282. moveToCorner(labelSelector, false);
  283. let messageKey;
  284. if (this.currentState === Status.PENDING)
  285. messageKey = this.failedToStartKey;
  286. else
  287. messageKey = this.recordingOffKey;
  288. labelSelector.attr("data-i18n", messageKey);
  289. labelSelector.text(APP.translation.translateString(messageKey));
  290. setTimeout(function(){
  291. $('#recordingLabel').css({display: "none"});
  292. }, 5000);
  293. }
  294. else if (recordingState === Status.PENDING) {
  295. buttonSelector.removeClass(this.baseClass + " active");
  296. buttonSelector.addClass(this.baseClass);
  297. moveToCorner(labelSelector, false);
  298. labelSelector
  299. .attr("data-i18n", this.recordingPendingKey);
  300. labelSelector
  301. .text(APP.translation.translateString(
  302. this.recordingPendingKey));
  303. }
  304. this.currentState = recordingState;
  305. // We don't show the label for available state.
  306. if (recordingState !== Status.AVAILABLE
  307. && !labelSelector.is(":visible"))
  308. labelSelector.css({display: "inline-block"});
  309. },
  310. // checks whether recording is enabled and whether we have params
  311. // to start automatically recording
  312. checkAutoRecord () {
  313. if (_isRecordingButtonEnabled && config.autoRecord) {
  314. this.predefinedToken = UIUtil.escapeHtml(config.autoRecordToken);
  315. this.eventEmitter.emit(UIEvents.RECORDING_TOGGLED,
  316. this.predefinedToken);
  317. }
  318. }
  319. };
  320. export default Recording;