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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  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 { getParticipantDisplayName } from '../base/participants';
  13. import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
  14. import {
  15. playSound,
  16. registerSound,
  17. stopSound,
  18. unregisterSound
  19. } from '../base/sounds';
  20. import {
  21. clearRecordingSessions,
  22. hidePendingRecordingNotification,
  23. showPendingRecordingNotification,
  24. showRecordingError,
  25. showStartedRecordingNotification,
  26. showStoppedRecordingNotification,
  27. updateRecordingSessionData
  28. } from './actions';
  29. import { RECORDING_SESSION_UPDATED } from './actionTypes';
  30. import {
  31. LIVE_STREAMING_OFF_SOUND_ID,
  32. LIVE_STREAMING_ON_SOUND_ID,
  33. RECORDING_OFF_SOUND_ID,
  34. RECORDING_ON_SOUND_ID
  35. } from './constants';
  36. import { getSessionById } from './functions';
  37. import {
  38. LIVE_STREAMING_OFF_SOUND_FILE,
  39. LIVE_STREAMING_ON_SOUND_FILE,
  40. RECORDING_OFF_SOUND_FILE,
  41. RECORDING_ON_SOUND_FILE
  42. } from './sounds';
  43. /**
  44. * StateListenerRegistry provides a reliable way to detect the leaving of a
  45. * conference, where we need to clean up the recording sessions.
  46. */
  47. StateListenerRegistry.register(
  48. /* selector */ state => getCurrentConference(state),
  49. /* listener */ (conference, { dispatch }) => {
  50. if (!conference) {
  51. dispatch(clearRecordingSessions());
  52. }
  53. }
  54. );
  55. /**
  56. * The redux middleware to handle the recorder updates in a React way.
  57. *
  58. * @param {Store} store - The redux store.
  59. * @returns {Function}
  60. */
  61. MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
  62. let oldSessionData;
  63. if (action.type === RECORDING_SESSION_UPDATED) {
  64. oldSessionData
  65. = getSessionById(getState(), action.sessionData.id);
  66. }
  67. const result = next(action);
  68. switch (action.type) {
  69. case APP_WILL_MOUNT:
  70. dispatch(registerSound(
  71. LIVE_STREAMING_OFF_SOUND_ID,
  72. LIVE_STREAMING_OFF_SOUND_FILE));
  73. dispatch(registerSound(
  74. LIVE_STREAMING_ON_SOUND_ID,
  75. LIVE_STREAMING_ON_SOUND_FILE));
  76. dispatch(registerSound(
  77. RECORDING_OFF_SOUND_ID,
  78. RECORDING_OFF_SOUND_FILE));
  79. dispatch(registerSound(
  80. RECORDING_ON_SOUND_ID,
  81. RECORDING_ON_SOUND_FILE));
  82. break;
  83. case APP_WILL_UNMOUNT:
  84. dispatch(unregisterSound(LIVE_STREAMING_OFF_SOUND_ID));
  85. dispatch(unregisterSound(LIVE_STREAMING_ON_SOUND_ID));
  86. dispatch(unregisterSound(RECORDING_OFF_SOUND_ID));
  87. dispatch(unregisterSound(RECORDING_ON_SOUND_ID));
  88. break;
  89. case CONFERENCE_WILL_JOIN: {
  90. const { conference } = action;
  91. conference.on(
  92. JitsiConferenceEvents.RECORDER_STATE_CHANGED,
  93. recorderSession => {
  94. if (recorderSession) {
  95. recorderSession.getID()
  96. && dispatch(
  97. updateRecordingSessionData(recorderSession));
  98. recorderSession.getError()
  99. && _showRecordingErrorNotification(
  100. recorderSession, dispatch);
  101. }
  102. return;
  103. });
  104. break;
  105. }
  106. case RECORDING_SESSION_UPDATED: {
  107. // When in recorder mode no notifications are shown
  108. // or extra sounds are also not desired
  109. // but we want to indicate those in case of sip gateway
  110. const {
  111. iAmRecorder,
  112. iAmSipGateway,
  113. disableRecordAudioNotification
  114. } = getState()['features/base/config'];
  115. if (iAmRecorder && !iAmSipGateway) {
  116. break;
  117. }
  118. const updatedSessionData
  119. = getSessionById(getState(), action.sessionData.id);
  120. const { initiator, mode, terminator } = updatedSessionData;
  121. const { PENDING, OFF, ON } = JitsiRecordingConstants.status;
  122. if (updatedSessionData.status === PENDING
  123. && (!oldSessionData || oldSessionData.status !== PENDING)) {
  124. dispatch(showPendingRecordingNotification(mode));
  125. } else if (updatedSessionData.status !== PENDING) {
  126. dispatch(hidePendingRecordingNotification(mode));
  127. if (updatedSessionData.status === ON
  128. && (!oldSessionData || oldSessionData.status !== ON)) {
  129. const initiatorName = initiator && getParticipantDisplayName(getState, initiator.getId());
  130. initiatorName && dispatch(showStartedRecordingNotification(mode, initiatorName));
  131. sendAnalytics(createRecordingEvent('start', mode));
  132. if (disableRecordAudioNotification) {
  133. break;
  134. }
  135. let soundID;
  136. if (mode === JitsiRecordingConstants.mode.FILE) {
  137. soundID = RECORDING_ON_SOUND_ID;
  138. } else if (mode === JitsiRecordingConstants.mode.STREAM) {
  139. soundID = LIVE_STREAMING_ON_SOUND_ID;
  140. }
  141. if (soundID) {
  142. dispatch(playSound(soundID));
  143. }
  144. } else if (updatedSessionData.status === OFF
  145. && (!oldSessionData || oldSessionData.status !== OFF)) {
  146. dispatch(showStoppedRecordingNotification(
  147. mode, terminator && getParticipantDisplayName(getState, terminator.getId())));
  148. let duration = 0, soundOff, soundOn;
  149. if (oldSessionData && oldSessionData.timestamp) {
  150. duration
  151. = (Date.now() / 1000) - oldSessionData.timestamp;
  152. }
  153. sendAnalytics(createRecordingEvent('stop', mode, duration));
  154. if (disableRecordAudioNotification) {
  155. break;
  156. }
  157. if (mode === JitsiRecordingConstants.mode.FILE) {
  158. soundOff = RECORDING_OFF_SOUND_ID;
  159. soundOn = RECORDING_ON_SOUND_ID;
  160. } else if (mode === JitsiRecordingConstants.mode.STREAM) {
  161. soundOff = LIVE_STREAMING_OFF_SOUND_ID;
  162. soundOn = LIVE_STREAMING_ON_SOUND_ID;
  163. }
  164. if (soundOff && soundOn) {
  165. dispatch(stopSound(soundOn));
  166. dispatch(playSound(soundOff));
  167. }
  168. }
  169. }
  170. break;
  171. }
  172. }
  173. return result;
  174. });
  175. /**
  176. * Shows a notification about an error in the recording session. A
  177. * default notification will display if no error is specified in the passed
  178. * in recording session.
  179. *
  180. * @private
  181. * @param {Object} recorderSession - The recorder session model from the
  182. * lib.
  183. * @param {Dispatch} dispatch - The Redux Dispatch function.
  184. * @returns {void}
  185. */
  186. function _showRecordingErrorNotification(recorderSession, dispatch) {
  187. const isStreamMode
  188. = recorderSession.getMode()
  189. === JitsiMeetJS.constants.recording.mode.STREAM;
  190. switch (recorderSession.getError()) {
  191. case JitsiMeetJS.constants.recording.error.SERVICE_UNAVAILABLE:
  192. dispatch(showRecordingError({
  193. descriptionKey: 'recording.unavailable',
  194. descriptionArguments: {
  195. serviceName: isStreamMode
  196. ? '$t(liveStreaming.serviceName)'
  197. : '$t(recording.serviceName)'
  198. },
  199. titleKey: isStreamMode
  200. ? 'liveStreaming.unavailableTitle'
  201. : 'recording.unavailableTitle'
  202. }));
  203. break;
  204. case JitsiMeetJS.constants.recording.error.RESOURCE_CONSTRAINT:
  205. dispatch(showRecordingError({
  206. descriptionKey: isStreamMode
  207. ? 'liveStreaming.busy'
  208. : 'recording.busy',
  209. titleKey: isStreamMode
  210. ? 'liveStreaming.busyTitle'
  211. : 'recording.busyTitle'
  212. }));
  213. break;
  214. default:
  215. dispatch(showRecordingError({
  216. descriptionKey: isStreamMode
  217. ? 'liveStreaming.error'
  218. : 'recording.error',
  219. titleKey: isStreamMode
  220. ? 'liveStreaming.failedToStart'
  221. : 'recording.failedToStart'
  222. }));
  223. break;
  224. }
  225. }