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 6.5KB

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