Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

middleware.js 9.3KB

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