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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /* @flow */
  2. import {
  3. createRecordingEvent,
  4. sendAnalytics
  5. } from '../analytics';
  6. import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app';
  7. import { CONFERENCE_WILL_JOIN, getCurrentConference } from '../base/conference';
  8. import JitsiMeetJS, {
  9. JitsiConferenceEvents,
  10. JitsiRecordingConstants
  11. } from '../base/lib-jitsi-meet';
  12. import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
  13. import {
  14. playSound,
  15. registerSound,
  16. stopSound,
  17. unregisterSound
  18. } from '../base/sounds';
  19. import {
  20. clearRecordingSessions,
  21. hidePendingRecordingNotification,
  22. showPendingRecordingNotification,
  23. showRecordingError,
  24. showStoppedRecordingNotification,
  25. updateRecordingSessionData
  26. } from './actions';
  27. import { RECORDING_SESSION_UPDATED } from './actionTypes';
  28. import { RECORDING_OFF_SOUND_ID, RECORDING_ON_SOUND_ID } from './constants';
  29. import { getSessionById } from './functions';
  30. import {
  31. RECORDING_OFF_SOUND_FILE,
  32. RECORDING_ON_SOUND_FILE
  33. } from './sounds';
  34. /**
  35. * StateListenerRegistry provides a reliable way to detect the leaving of a
  36. * conference, where we need to clean up the recording sessions.
  37. */
  38. StateListenerRegistry.register(
  39. /* selector */ state => getCurrentConference(state),
  40. /* listener */ (conference, { dispatch }) => {
  41. if (!conference) {
  42. dispatch(clearRecordingSessions());
  43. }
  44. }
  45. );
  46. /**
  47. * The redux middleware to handle the recorder updates in a React way.
  48. *
  49. * @param {Store} store - The redux store.
  50. * @returns {Function}
  51. */
  52. MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
  53. let oldSessionData;
  54. if (action.type === RECORDING_SESSION_UPDATED) {
  55. oldSessionData
  56. = getSessionById(getState(), action.sessionData.id);
  57. }
  58. const result = next(action);
  59. switch (action.type) {
  60. case APP_WILL_MOUNT:
  61. dispatch(registerSound(
  62. RECORDING_OFF_SOUND_ID,
  63. RECORDING_OFF_SOUND_FILE));
  64. dispatch(registerSound(
  65. RECORDING_ON_SOUND_ID,
  66. RECORDING_ON_SOUND_FILE));
  67. break;
  68. case APP_WILL_UNMOUNT:
  69. dispatch(unregisterSound(RECORDING_OFF_SOUND_ID));
  70. dispatch(unregisterSound(RECORDING_ON_SOUND_ID));
  71. break;
  72. case CONFERENCE_WILL_JOIN: {
  73. const { conference } = action;
  74. conference.on(
  75. JitsiConferenceEvents.RECORDER_STATE_CHANGED,
  76. recorderSession => {
  77. if (recorderSession) {
  78. recorderSession.getID()
  79. && dispatch(
  80. updateRecordingSessionData(recorderSession));
  81. recorderSession.getError()
  82. && _showRecordingErrorNotification(
  83. recorderSession, dispatch);
  84. }
  85. return;
  86. });
  87. break;
  88. }
  89. case RECORDING_SESSION_UPDATED: {
  90. const updatedSessionData
  91. = getSessionById(getState(), action.sessionData.id);
  92. const { PENDING, OFF, ON } = JitsiRecordingConstants.status;
  93. if (updatedSessionData.status === PENDING
  94. && (!oldSessionData || oldSessionData.status !== PENDING)) {
  95. dispatch(
  96. showPendingRecordingNotification(updatedSessionData.mode));
  97. } else if (updatedSessionData.status !== PENDING) {
  98. dispatch(
  99. hidePendingRecordingNotification(updatedSessionData.mode));
  100. if (updatedSessionData.status === ON
  101. && (!oldSessionData || oldSessionData.status !== ON)
  102. && updatedSessionData.mode
  103. === JitsiRecordingConstants.mode.FILE) {
  104. sendAnalytics(createRecordingEvent('start', 'file'));
  105. dispatch(playSound(RECORDING_ON_SOUND_ID));
  106. } else if (updatedSessionData.status === OFF
  107. && (!oldSessionData || oldSessionData.status !== OFF)) {
  108. dispatch(
  109. showStoppedRecordingNotification(
  110. updatedSessionData.mode));
  111. if (updatedSessionData.mode
  112. === JitsiRecordingConstants.mode.FILE) {
  113. let duration = 0;
  114. // eslint-disable-next-line max-depth
  115. if (oldSessionData && oldSessionData.timestamp) {
  116. duration
  117. = (Date.now() / 1000) - oldSessionData.timestamp;
  118. }
  119. sendAnalytics(
  120. createRecordingEvent('stop', 'file', duration));
  121. dispatch(stopSound(RECORDING_ON_SOUND_ID));
  122. dispatch(playSound(RECORDING_OFF_SOUND_ID));
  123. }
  124. }
  125. }
  126. break;
  127. }
  128. }
  129. return result;
  130. });
  131. /**
  132. * Shows a notification about an error in the recording session. A
  133. * default notification will display if no error is specified in the passed
  134. * in recording session.
  135. *
  136. * @private
  137. * @param {Object} recorderSession - The recorder session model from the
  138. * lib.
  139. * @param {Dispatch} dispatch - The Redux Dispatch function.
  140. * @returns {void}
  141. */
  142. function _showRecordingErrorNotification(recorderSession, dispatch) {
  143. const isStreamMode
  144. = recorderSession.getMode()
  145. === JitsiMeetJS.constants.recording.mode.STREAM;
  146. switch (recorderSession.getError()) {
  147. case JitsiMeetJS.constants.recording.error.SERVICE_UNAVAILABLE:
  148. dispatch(showRecordingError({
  149. descriptionKey: 'recording.unavailable',
  150. descriptionArguments: {
  151. serviceName: isStreamMode
  152. ? 'Live Streaming service'
  153. : 'Recording service'
  154. },
  155. titleKey: isStreamMode
  156. ? 'liveStreaming.unavailableTitle'
  157. : 'recording.unavailableTitle'
  158. }));
  159. break;
  160. case JitsiMeetJS.constants.recording.error.RESOURCE_CONSTRAINT:
  161. dispatch(showRecordingError({
  162. descriptionKey: isStreamMode
  163. ? 'liveStreaming.busy'
  164. : 'recording.busy',
  165. titleKey: isStreamMode
  166. ? 'liveStreaming.busyTitle'
  167. : 'recording.busyTitle'
  168. }));
  169. break;
  170. default:
  171. dispatch(showRecordingError({
  172. descriptionKey: isStreamMode
  173. ? 'liveStreaming.error'
  174. : 'recording.error',
  175. titleKey: isStreamMode
  176. ? 'liveStreaming.failedToStart'
  177. : 'recording.failedToStart'
  178. }));
  179. break;
  180. }
  181. }