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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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. // When in recorder mode no notifications are shown
  91. // or extra sounds are also not desired
  92. if (getState()['features/base/config'].iAmRecorder) {
  93. break;
  94. }
  95. const updatedSessionData
  96. = getSessionById(getState(), action.sessionData.id);
  97. const { PENDING, OFF, ON } = JitsiRecordingConstants.status;
  98. if (updatedSessionData.status === PENDING
  99. && (!oldSessionData || oldSessionData.status !== PENDING)) {
  100. dispatch(
  101. showPendingRecordingNotification(updatedSessionData.mode));
  102. } else if (updatedSessionData.status !== PENDING) {
  103. dispatch(
  104. hidePendingRecordingNotification(updatedSessionData.mode));
  105. if (updatedSessionData.status === ON
  106. && (!oldSessionData || oldSessionData.status !== ON)
  107. && updatedSessionData.mode
  108. === JitsiRecordingConstants.mode.FILE) {
  109. sendAnalytics(createRecordingEvent('start', 'file'));
  110. dispatch(playSound(RECORDING_ON_SOUND_ID));
  111. } else if (updatedSessionData.status === OFF
  112. && (!oldSessionData || oldSessionData.status !== OFF)) {
  113. dispatch(
  114. showStoppedRecordingNotification(
  115. updatedSessionData.mode));
  116. if (updatedSessionData.mode
  117. === JitsiRecordingConstants.mode.FILE) {
  118. let duration = 0;
  119. // eslint-disable-next-line max-depth
  120. if (oldSessionData && oldSessionData.timestamp) {
  121. duration
  122. = (Date.now() / 1000) - oldSessionData.timestamp;
  123. }
  124. sendAnalytics(
  125. createRecordingEvent('stop', 'file', duration));
  126. dispatch(stopSound(RECORDING_ON_SOUND_ID));
  127. dispatch(playSound(RECORDING_OFF_SOUND_ID));
  128. }
  129. }
  130. }
  131. break;
  132. }
  133. }
  134. return result;
  135. });
  136. /**
  137. * Shows a notification about an error in the recording session. A
  138. * default notification will display if no error is specified in the passed
  139. * in recording session.
  140. *
  141. * @private
  142. * @param {Object} recorderSession - The recorder session model from the
  143. * lib.
  144. * @param {Dispatch} dispatch - The Redux Dispatch function.
  145. * @returns {void}
  146. */
  147. function _showRecordingErrorNotification(recorderSession, dispatch) {
  148. const isStreamMode
  149. = recorderSession.getMode()
  150. === JitsiMeetJS.constants.recording.mode.STREAM;
  151. switch (recorderSession.getError()) {
  152. case JitsiMeetJS.constants.recording.error.SERVICE_UNAVAILABLE:
  153. dispatch(showRecordingError({
  154. descriptionKey: 'recording.unavailable',
  155. descriptionArguments: {
  156. serviceName: isStreamMode
  157. ? 'Live Streaming service'
  158. : 'Recording service'
  159. },
  160. titleKey: isStreamMode
  161. ? 'liveStreaming.unavailableTitle'
  162. : 'recording.unavailableTitle'
  163. }));
  164. break;
  165. case JitsiMeetJS.constants.recording.error.RESOURCE_CONSTRAINT:
  166. dispatch(showRecordingError({
  167. descriptionKey: isStreamMode
  168. ? 'liveStreaming.busy'
  169. : 'recording.busy',
  170. titleKey: isStreamMode
  171. ? 'liveStreaming.busyTitle'
  172. : 'recording.busyTitle'
  173. }));
  174. break;
  175. default:
  176. dispatch(showRecordingError({
  177. descriptionKey: isStreamMode
  178. ? 'liveStreaming.error'
  179. : 'recording.error',
  180. titleKey: isStreamMode
  181. ? 'liveStreaming.failedToStart'
  182. : 'recording.failedToStart'
  183. }));
  184. break;
  185. }
  186. }