您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

middleware.js 8.6KB

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