You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

middleware.js 7.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. // @flow
  2. import { MiddlewareRegistry } from '../base/redux';
  3. import {
  4. ENDPOINT_MESSAGE_RECEIVED,
  5. TOGGLE_REQUESTING_SUBTITLES,
  6. SET_REQUESTING_SUBTITLES
  7. } from './actionTypes';
  8. import {
  9. removeTranscriptMessage,
  10. updateTranscriptMessage
  11. } from './actions';
  12. import logger from './logger';
  13. /**
  14. * The type of json-message which indicates that json carries a
  15. * transcription result.
  16. */
  17. const JSON_TYPE_TRANSCRIPTION_RESULT = 'transcription-result';
  18. /**
  19. * The type of json-message which indicates that json carries a
  20. * translation result.
  21. */
  22. const JSON_TYPE_TRANSLATION_RESULT = 'translation-result';
  23. /**
  24. * The local participant property which is used to set whether the local
  25. * participant wants to have a transcriber in the room.
  26. */
  27. const P_NAME_REQUESTING_TRANSCRIPTION = 'requestingTranscription';
  28. /**
  29. * The local participant property which is used to store the language
  30. * preference for translation for a participant.
  31. */
  32. const P_NAME_TRANSLATION_LANGUAGE = 'translation_language';
  33. /**
  34. * Time after which the rendered subtitles will be removed.
  35. */
  36. const REMOVE_AFTER_MS = 3000;
  37. /**
  38. * Middleware that catches actions related to transcript messages to be rendered
  39. * in {@link Captions}.
  40. *
  41. * @param {Store} store - The redux store.
  42. * @returns {Function}
  43. */
  44. MiddlewareRegistry.register(store => next => action => {
  45. switch (action.type) {
  46. case ENDPOINT_MESSAGE_RECEIVED:
  47. return _endpointMessageReceived(store, next, action);
  48. case TOGGLE_REQUESTING_SUBTITLES:
  49. _requestingSubtitlesToggled(store);
  50. break;
  51. case SET_REQUESTING_SUBTITLES:
  52. _requestingSubtitlesSet(store, action.enabled);
  53. break;
  54. }
  55. return next(action);
  56. });
  57. /**
  58. * Notifies the feature transcription that the action
  59. * {@code ENDPOINT_MESSAGE_RECEIVED} is being dispatched within a specific redux
  60. * store.
  61. *
  62. * @param {Store} store - The redux store in which the specified {@code action}
  63. * is being dispatched.
  64. * @param {Dispatch} next - The redux {@code dispatch} function to
  65. * dispatch the specified {@code action} to the specified {@code store}.
  66. * @param {Action} action - The redux action {@code ENDPOINT_MESSAGE_RECEIVED}
  67. * which is being dispatched in the specified {@code store}.
  68. * @private
  69. * @returns {Object} The value returned by {@code next(action)}.
  70. */
  71. function _endpointMessageReceived({ dispatch, getState }, next, action) {
  72. const { json } = action;
  73. if (!(json
  74. && (json.type === JSON_TYPE_TRANSCRIPTION_RESULT
  75. || json.type === JSON_TYPE_TRANSLATION_RESULT))) {
  76. return next(action);
  77. }
  78. const state = getState();
  79. const translationLanguage
  80. = state['features/base/conference'].conference
  81. .getLocalParticipantProperty(P_NAME_TRANSLATION_LANGUAGE);
  82. try {
  83. const transcriptMessageID = json.message_id;
  84. const participantName = json.participant.name;
  85. if (json.type === JSON_TYPE_TRANSLATION_RESULT
  86. && json.language === translationLanguage) {
  87. // Displays final results in the target language if translation is
  88. // enabled.
  89. const newTranscriptMessage = {
  90. clearTimeOut: undefined,
  91. final: json.text,
  92. participantName
  93. };
  94. _setClearerOnTranscriptMessage(dispatch,
  95. transcriptMessageID, newTranscriptMessage);
  96. dispatch(updateTranscriptMessage(transcriptMessageID,
  97. newTranscriptMessage));
  98. } else if (json.type === JSON_TYPE_TRANSCRIPTION_RESULT
  99. && !translationLanguage) {
  100. // Displays interim and final results without any translation if
  101. // translations are disabled.
  102. const { text } = json.transcript[0];
  103. // We update the previous transcript message with the same
  104. // message ID or adds a new transcript message if it does not
  105. // exist in the map.
  106. const newTranscriptMessage = {
  107. ...state['features/subtitles']._transcriptMessages
  108. .get(transcriptMessageID)
  109. || { participantName }
  110. };
  111. _setClearerOnTranscriptMessage(dispatch,
  112. transcriptMessageID, newTranscriptMessage);
  113. // If this is final result, update the state as a final result
  114. // and start a count down to remove the subtitle from the state
  115. if (!json.is_interim) {
  116. newTranscriptMessage.final = text;
  117. } else if (json.stability > 0.85) {
  118. // If the message has a high stability, we can update the
  119. // stable field of the state and remove the previously
  120. // unstable results
  121. newTranscriptMessage.stable = text;
  122. newTranscriptMessage.unstable = undefined;
  123. } else {
  124. // Otherwise, this result has an unstable result, which we
  125. // add to the state. The unstable result will be appended
  126. // after the stable part.
  127. newTranscriptMessage.unstable = text;
  128. }
  129. dispatch(
  130. updateTranscriptMessage(
  131. transcriptMessageID,
  132. newTranscriptMessage));
  133. }
  134. } catch (error) {
  135. logger.error('Error occurred while updating transcriptions\n', error);
  136. }
  137. return next(action);
  138. }
  139. /**
  140. * Toggle the local property 'requestingTranscription'. This will cause Jicofo
  141. * and Jigasi to decide whether the transcriber needs to be in the room.
  142. *
  143. * @param {Store} store - The redux store.
  144. * @private
  145. * @returns {void}
  146. */
  147. function _requestingSubtitlesToggled({ getState }) {
  148. const state = getState();
  149. const { _requestingSubtitles } = state['features/subtitles'];
  150. const { conference } = state['features/base/conference'];
  151. conference.setLocalParticipantProperty(
  152. P_NAME_REQUESTING_TRANSCRIPTION,
  153. !_requestingSubtitles);
  154. }
  155. /**
  156. * Set the local property 'requestingTranscription'. This will cause Jicofo
  157. * and Jigasi to decide whether the transcriber needs to be in the room.
  158. *
  159. * @param {Store} store - The redux store.
  160. * @param {boolean} enabled - The new state of the subtitles.
  161. * @private
  162. * @returns {void}
  163. */
  164. function _requestingSubtitlesSet({ getState }, enabled: boolean) {
  165. const state = getState();
  166. const { conference } = state['features/base/conference'];
  167. conference.setLocalParticipantProperty(
  168. P_NAME_REQUESTING_TRANSCRIPTION,
  169. enabled);
  170. }
  171. /**
  172. * Set a timeout on a TranscriptMessage object so it clears itself when it's not
  173. * updated.
  174. *
  175. * @param {Function} dispatch - Dispatch remove action to store.
  176. * @param {string} transcriptMessageID - The id of the message to remove.
  177. * @param {Object} transcriptMessage - The message to remove.
  178. * @returns {void}
  179. */
  180. function _setClearerOnTranscriptMessage(
  181. dispatch,
  182. transcriptMessageID,
  183. transcriptMessage) {
  184. if (transcriptMessage.clearTimeOut) {
  185. clearTimeout(transcriptMessage.clearTimeOut);
  186. }
  187. transcriptMessage.clearTimeOut
  188. = setTimeout(
  189. () => dispatch(removeTranscriptMessage(transcriptMessageID)),
  190. REMOVE_AFTER_MS);
  191. }