@whereby.com/core 1.2.9 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import nodeBtoa from 'btoa';
2
- import { assert, fromLocation, ServerSocket, getDeviceData, getStream, getUpdatedDevices, RtcManagerDispatcher, setClientProvider, subscribeIssues } from '@whereby.com/media';
2
+ import { assert, fromLocation, ServerSocket, getDeviceData, getStream, getUpdatedDevices, replaceTracksInStream, RtcManagerDispatcher, setClientProvider, subscribeIssues } from '@whereby.com/media';
3
3
  import axios from 'axios';
4
4
  import { EventEmitter } from 'events';
5
5
  import { createListenerMiddleware, createSlice, createAction, createSelector, createAsyncThunk, isAnyOf, combineReducers, configureStore, addListener } from '@reduxjs/toolkit';
@@ -1145,9 +1145,9 @@ const createReactor = (selectors, callback) => {
1145
1145
  });
1146
1146
  };
1147
1147
 
1148
- const coreVersion = "1.2.9";
1148
+ const coreVersion = "1.3.1";
1149
1149
 
1150
- const initialState = {
1150
+ const initialState$1 = {
1151
1151
  isNodeSdk: false,
1152
1152
  isActive: false,
1153
1153
  isDialIn: false,
@@ -1161,7 +1161,7 @@ const initialState = {
1161
1161
  };
1162
1162
  const appSlice = createSlice({
1163
1163
  name: "app",
1164
- initialState,
1164
+ initialState: initialState$1,
1165
1165
  reducers: {
1166
1166
  doAppStart: (state, action) => {
1167
1167
  const url = new URL(action.payload.roomUrl);
@@ -1688,6 +1688,12 @@ const localMediaSlice = createSlice({
1688
1688
  builder.addCase(doSwitchLocalStream.rejected, (state) => {
1689
1689
  return Object.assign(Object.assign({}, state), { isSwitchingStream: false });
1690
1690
  });
1691
+ builder.addCase(doLocalStreamEffect.fulfilled, (state, { payload }) => {
1692
+ if (!payload) {
1693
+ return state;
1694
+ }
1695
+ return Object.assign(Object.assign({}, state), { beforeEffectTracks: Object.assign(Object.assign({}, state.beforeEffectTracks), payload.beforeEffectTracks) });
1696
+ });
1691
1697
  },
1692
1698
  });
1693
1699
  const { deviceBusy, setCurrentCameraDeviceId, setCurrentMicrophoneDeviceId, setCurrentSpeakerDeviceId, toggleCameraEnabled, toggleMicrophoneEnabled, toggleLowDataModeEnabled, setLocalMediaOptions, setLocalMediaStream, localMediaStopped, localStreamMetadataUpdated, } = localMediaSlice.actions;
@@ -1817,6 +1823,15 @@ const doSwitchLocalStream = createAppAsyncThunk("localMedia/doSwitchLocalStream"
1817
1823
  if (!replaceStream) {
1818
1824
  return;
1819
1825
  }
1826
+ const beforeEffectTracks = selectLocalMediaBeforeEffectTracks(state);
1827
+ if (audioId !== undefined && (beforeEffectTracks === null || beforeEffectTracks === void 0 ? void 0 : beforeEffectTracks.audio)) {
1828
+ beforeEffectTracks.audio.stop();
1829
+ beforeEffectTracks.audio = undefined;
1830
+ }
1831
+ if (videoId !== undefined && (beforeEffectTracks === null || beforeEffectTracks === void 0 ? void 0 : beforeEffectTracks.video)) {
1832
+ beforeEffectTracks.video.stop();
1833
+ beforeEffectTracks.video = undefined;
1834
+ }
1820
1835
  try {
1821
1836
  const { replacedTracks } = yield getStream(Object.assign(Object.assign({}, constraintsOptions), { audioId: audioId === undefined ? false : audioId, videoId: videoId === undefined ? false : videoId, type: "exact" }), { replaceStream });
1822
1837
  const deviceId = audioId || videoId;
@@ -1825,7 +1840,7 @@ const doSwitchLocalStream = createAppAsyncThunk("localMedia/doSwitchLocalStream"
1825
1840
  deviceId,
1826
1841
  }));
1827
1842
  }
1828
- return { replacedTracks };
1843
+ return { replacedTracks, beforeEffectTracks };
1829
1844
  }
1830
1845
  catch (error) {
1831
1846
  console.error(error);
@@ -1876,6 +1891,46 @@ const doStopLocalMedia = createAppThunk(() => (dispatch, getState) => {
1876
1891
  }
1877
1892
  dispatch(localMediaStopped());
1878
1893
  });
1894
+ const doLocalStreamEffect = createAppAsyncThunk("localMedia/doLocalStreamEffect", (_a, _b) => __awaiter(void 0, [_a, _b], void 0, function* ({ effectStream, only, stopBeforeTrack, }, { getState }) {
1895
+ var _c;
1896
+ const state = getState();
1897
+ let beforeEffectTracks = selectLocalMediaBeforeEffectTracks(state);
1898
+ const beforeTrack = beforeEffectTracks === null || beforeEffectTracks === void 0 ? void 0 : beforeEffectTracks[only];
1899
+ if (!effectStream && !beforeTrack)
1900
+ return;
1901
+ try {
1902
+ const stream = selectLocalMediaStream(state);
1903
+ let replacedTracks = null;
1904
+ if (!stream) {
1905
+ throw new Error("No local media stream");
1906
+ }
1907
+ if (effectStream) {
1908
+ if (!beforeTrack) {
1909
+ beforeEffectTracks = {
1910
+ [only]: (_c = (only === "audio" ? stream === null || stream === void 0 ? void 0 : stream.getAudioTracks() : stream === null || stream === void 0 ? void 0 : stream.getVideoTracks())) === null || _c === void 0 ? void 0 : _c[0],
1911
+ };
1912
+ }
1913
+ replacedTracks = replaceTracksInStream(stream, effectStream, only);
1914
+ }
1915
+ else if (!stopBeforeTrack) {
1916
+ replacedTracks = replaceTracksInStream(stream, beforeTrack ? new MediaStream([beforeTrack]) : new MediaStream(), only);
1917
+ beforeEffectTracks = {
1918
+ [only]: undefined,
1919
+ };
1920
+ }
1921
+ else if (beforeTrack) {
1922
+ beforeEffectTracks = {
1923
+ [only]: undefined,
1924
+ };
1925
+ beforeTrack.stop();
1926
+ }
1927
+ return { effectStream, beforeEffectTracks, replacedTracks };
1928
+ }
1929
+ catch (error) {
1930
+ console.error("Error applying local stream effect", error);
1931
+ return;
1932
+ }
1933
+ }));
1879
1934
  const selectBusyDeviceIds = (state) => state.localMedia.busyDeviceIds;
1880
1935
  const selectCameraDeviceError = (state) => state.localMedia.cameraDeviceError;
1881
1936
  const selectCurrentCameraDeviceId = (state) => state.localMedia.currentCameraDeviceId;
@@ -1896,6 +1951,7 @@ const selectLocalMediaStream = (state) => state.localMedia.stream;
1896
1951
  const selectMicrophoneDeviceError = (state) => state.localMedia.microphoneDeviceError;
1897
1952
  const selectLocalMediaStartError = (state) => state.localMedia.startError;
1898
1953
  const selectLocalMediaIsSwitchingStream = (state) => state.localMedia.isSwitchingStream;
1954
+ const selectLocalMediaBeforeEffectTracks = (state) => state.localMedia.beforeEffectTracks;
1899
1955
  const selectLocalMediaConstraintsOptions = createSelector(selectLocalMediaDevices, selectCurrentCameraDeviceId, selectCurrentMicrophoneDeviceId, selectIsLowDataModeEnabled, (devices, videoId, audioId, lowDataMode) => ({
1900
1956
  devices,
1901
1957
  videoId,
@@ -1992,7 +2048,7 @@ startAppListening({
1992
2048
  },
1993
2049
  });
1994
2050
  startAppListening({
1995
- matcher: isAnyOf(doStartLocalMedia.fulfilled, doUpdateDeviceList.fulfilled, doSwitchLocalStream.fulfilled, doSwitchLocalStream.rejected),
2051
+ matcher: isAnyOf(doStartLocalMedia.fulfilled, doUpdateDeviceList.fulfilled, doSwitchLocalStream.fulfilled, doSwitchLocalStream.rejected, doLocalStreamEffect.fulfilled),
1996
2052
  effect: (_action, { dispatch, getState }) => {
1997
2053
  const state = getState();
1998
2054
  const stream = selectLocalMediaStream(state);
@@ -3043,7 +3099,7 @@ startAppListening({
3043
3099
  },
3044
3100
  });
3045
3101
  startAppListening({
3046
- actionCreator: doSwitchLocalStream.fulfilled,
3102
+ matcher: isAnyOf(doSwitchLocalStream.fulfilled, doLocalStreamEffect.fulfilled),
3047
3103
  effect: ({ payload }, { getState }) => {
3048
3104
  var _a;
3049
3105
  const stream = selectLocalMediaStream(getState());
@@ -4059,11 +4115,107 @@ const doRejectWaitingParticipant = createRoomConnectedThunk((payload) => (dispat
4059
4115
  });
4060
4116
  const selectWaitingParticipants = (state) => state.waitingParticipants.waitingParticipants;
4061
4117
 
4118
+ const initialState = {
4119
+ isSwitching: false,
4120
+ raw: {},
4121
+ };
4122
+ const cameraEffectsSlice = createSlice({
4123
+ name: "cameraEffects",
4124
+ initialState,
4125
+ reducers: {
4126
+ cameraEffectsSwitching(state, action) {
4127
+ state.isSwitching = action.payload.isSwitching;
4128
+ },
4129
+ cameraEffectsCleared(state) {
4130
+ state.currentEffectId = null;
4131
+ state.setup = undefined;
4132
+ state.params = undefined;
4133
+ state.raw = {};
4134
+ state.error = undefined;
4135
+ state.isSwitching = false;
4136
+ },
4137
+ cameraEffectsUpdated(state, action) {
4138
+ const { effectId, setup, params, raw } = action.payload;
4139
+ state.currentEffectId = effectId;
4140
+ state.setup = setup;
4141
+ state.params = params;
4142
+ if (raw)
4143
+ state.raw = raw;
4144
+ state.error = undefined;
4145
+ state.isSwitching = false;
4146
+ },
4147
+ cameraEffectsError(state, action) {
4148
+ state.error = action.payload.error;
4149
+ state.isSwitching = false;
4150
+ },
4151
+ },
4152
+ });
4153
+ const { cameraEffectsSwitching, cameraEffectsCleared, cameraEffectsUpdated, cameraEffectsError } = cameraEffectsSlice.actions;
4154
+ const selectCameraEffectsRaw = (state) => state.cameraEffects.raw;
4155
+ const doCameraEffectsSwitchPreset = createAppAsyncThunk("cameraEffects/switchPreset", (_a, _b) => __awaiter(void 0, [_a, _b], void 0, function* ({ effectId, setup, params, allowSafari, }, { getState, dispatch, rejectWithValue }) {
4156
+ var _c, _d, _e;
4157
+ const state = getState();
4158
+ if (selectLocalMediaIsSwitchingStream(state)) {
4159
+ return;
4160
+ }
4161
+ dispatch(cameraEffectsSwitching({ isSwitching: true }));
4162
+ try {
4163
+ const raw = selectCameraEffectsRaw(state);
4164
+ const localStream = selectLocalMediaStream(state);
4165
+ if (!((_c = localStream === null || localStream === void 0 ? void 0 : localStream.getVideoTracks()) === null || _c === void 0 ? void 0 : _c[0])) {
4166
+ dispatch(cameraEffectsCleared());
4167
+ return;
4168
+ }
4169
+ if (!effectId) {
4170
+ if (raw.effectStream) {
4171
+ yield dispatch(doLocalStreamEffect({ effectStream: undefined, only: "video" }));
4172
+ }
4173
+ (_d = raw.stop) === null || _d === void 0 ? void 0 : _d.call(raw);
4174
+ dispatch(cameraEffectsCleared());
4175
+ return;
4176
+ }
4177
+ if (raw.tryUpdate) {
4178
+ const ok = yield raw.tryUpdate(effectId, Object.assign({}, (setup || {})), Object.assign({}, (params || {})));
4179
+ if (ok) {
4180
+ dispatch(cameraEffectsUpdated({ effectId, setup, params }));
4181
+ return;
4182
+ }
4183
+ }
4184
+ if (raw.effectStream) {
4185
+ yield dispatch(doLocalStreamEffect({ effectStream: undefined, only: "video" }));
4186
+ (_e = raw.stop) === null || _e === void 0 ? void 0 : _e.call(raw);
4187
+ }
4188
+ let mod;
4189
+ try {
4190
+ mod = yield import('@whereby.com/camera-effects');
4191
+ }
4192
+ catch (_f) {
4193
+ throw new Error("@whereby.com/camera-effects is not installed. Add it as a dependency to enable camera effects.");
4194
+ }
4195
+ const { createEffectStream, getUsablePresets } = mod;
4196
+ const usable = getUsablePresets({ filter: () => true, options: { allowSafari } });
4197
+ if (!usable.includes(effectId)) {
4198
+ throw new Error(`Unknown or unsupported effect preset: ${effectId}`);
4199
+ }
4200
+ const { stream: effectStream, stop, tryUpdate, } = yield createEffectStream(localStream, effectId, setup, params);
4201
+ yield dispatch(doLocalStreamEffect({ effectStream, only: "video" }));
4202
+ dispatch(cameraEffectsUpdated({ effectId, setup, params, raw: { stop, tryUpdate, effectStream } }));
4203
+ }
4204
+ catch (error) {
4205
+ dispatch(cameraEffectsError({ error }));
4206
+ return rejectWithValue(error);
4207
+ }
4208
+ }));
4209
+ createAppAsyncThunk("cameraEffects/clear", (_1, _a) => __awaiter(void 0, [_1, _a], void 0, function* (_, { dispatch }) {
4210
+ yield dispatch(doCameraEffectsSwitchPreset({ effectId: null }));
4211
+ }));
4212
+
4062
4213
  const IS_DEV = undefined === "true";
4063
4214
  const appReducer = combineReducers({
4064
4215
  app: appSlice.reducer,
4065
4216
  authorization: authorizationSlice.reducer,
4066
4217
  breakout: breakoutSlice.reducer,
4218
+ cameraEffects: cameraEffectsSlice.reducer,
4067
4219
  chat: chatSlice.reducer,
4068
4220
  cloudRecording: cloudRecordingSlice.reducer,
4069
4221
  connectionMonitor: connectionMonitorSlice.reducer,
@@ -4733,6 +4885,28 @@ class RoomConnectionClient extends BaseClient {
4733
4885
  reportStreamResolution(streamId, width, height) {
4734
4886
  this.store.dispatch(doRtcReportStreamResolution({ streamId, width, height }));
4735
4887
  }
4888
+ switchCameraEffect(effectId) {
4889
+ return __awaiter(this, void 0, void 0, function* () {
4890
+ try {
4891
+ yield this.store.dispatch(doCameraEffectsSwitchPreset({ effectId }));
4892
+ }
4893
+ catch (error) {
4894
+ return Promise.reject(error);
4895
+ }
4896
+ return Promise.resolve();
4897
+ });
4898
+ }
4899
+ clearCameraEffect() {
4900
+ return __awaiter(this, void 0, void 0, function* () {
4901
+ try {
4902
+ yield this.store.dispatch(doCameraEffectsSwitchPreset({ effectId: null }));
4903
+ }
4904
+ catch (error) {
4905
+ return Promise.reject(error);
4906
+ }
4907
+ return Promise.resolve();
4908
+ });
4909
+ }
4736
4910
  destroy() {
4737
4911
  super.destroy();
4738
4912
  this.store.dispatch(doAppStop());
@@ -1,5 +1,5 @@
1
1
  import nodeBtoa from 'btoa';
2
- import { assert, fromLocation, ServerSocket, getDeviceData, getStream, getUpdatedDevices, RtcManagerDispatcher, setClientProvider, subscribeIssues } from '@whereby.com/media';
2
+ import { assert, fromLocation, ServerSocket, getDeviceData, getStream, getUpdatedDevices, replaceTracksInStream, RtcManagerDispatcher, setClientProvider, subscribeIssues } from '@whereby.com/media';
3
3
  import axios from 'axios';
4
4
  import { EventEmitter } from 'events';
5
5
  import { createListenerMiddleware, createSlice, createAction, createSelector, createAsyncThunk, isAnyOf, combineReducers, configureStore, addListener } from '@reduxjs/toolkit';
@@ -1145,9 +1145,9 @@ const createReactor = (selectors, callback) => {
1145
1145
  });
1146
1146
  };
1147
1147
 
1148
- const coreVersion = "1.2.9";
1148
+ const coreVersion = "1.3.1";
1149
1149
 
1150
- const initialState = {
1150
+ const initialState$1 = {
1151
1151
  isNodeSdk: false,
1152
1152
  isActive: false,
1153
1153
  isDialIn: false,
@@ -1161,7 +1161,7 @@ const initialState = {
1161
1161
  };
1162
1162
  const appSlice = createSlice({
1163
1163
  name: "app",
1164
- initialState,
1164
+ initialState: initialState$1,
1165
1165
  reducers: {
1166
1166
  doAppStart: (state, action) => {
1167
1167
  const url = new URL(action.payload.roomUrl);
@@ -1688,6 +1688,12 @@ const localMediaSlice = createSlice({
1688
1688
  builder.addCase(doSwitchLocalStream.rejected, (state) => {
1689
1689
  return Object.assign(Object.assign({}, state), { isSwitchingStream: false });
1690
1690
  });
1691
+ builder.addCase(doLocalStreamEffect.fulfilled, (state, { payload }) => {
1692
+ if (!payload) {
1693
+ return state;
1694
+ }
1695
+ return Object.assign(Object.assign({}, state), { beforeEffectTracks: Object.assign(Object.assign({}, state.beforeEffectTracks), payload.beforeEffectTracks) });
1696
+ });
1691
1697
  },
1692
1698
  });
1693
1699
  const { deviceBusy, setCurrentCameraDeviceId, setCurrentMicrophoneDeviceId, setCurrentSpeakerDeviceId, toggleCameraEnabled, toggleMicrophoneEnabled, toggleLowDataModeEnabled, setLocalMediaOptions, setLocalMediaStream, localMediaStopped, localStreamMetadataUpdated, } = localMediaSlice.actions;
@@ -1817,6 +1823,15 @@ const doSwitchLocalStream = createAppAsyncThunk("localMedia/doSwitchLocalStream"
1817
1823
  if (!replaceStream) {
1818
1824
  return;
1819
1825
  }
1826
+ const beforeEffectTracks = selectLocalMediaBeforeEffectTracks(state);
1827
+ if (audioId !== undefined && (beforeEffectTracks === null || beforeEffectTracks === void 0 ? void 0 : beforeEffectTracks.audio)) {
1828
+ beforeEffectTracks.audio.stop();
1829
+ beforeEffectTracks.audio = undefined;
1830
+ }
1831
+ if (videoId !== undefined && (beforeEffectTracks === null || beforeEffectTracks === void 0 ? void 0 : beforeEffectTracks.video)) {
1832
+ beforeEffectTracks.video.stop();
1833
+ beforeEffectTracks.video = undefined;
1834
+ }
1820
1835
  try {
1821
1836
  const { replacedTracks } = yield getStream(Object.assign(Object.assign({}, constraintsOptions), { audioId: audioId === undefined ? false : audioId, videoId: videoId === undefined ? false : videoId, type: "exact" }), { replaceStream });
1822
1837
  const deviceId = audioId || videoId;
@@ -1825,7 +1840,7 @@ const doSwitchLocalStream = createAppAsyncThunk("localMedia/doSwitchLocalStream"
1825
1840
  deviceId,
1826
1841
  }));
1827
1842
  }
1828
- return { replacedTracks };
1843
+ return { replacedTracks, beforeEffectTracks };
1829
1844
  }
1830
1845
  catch (error) {
1831
1846
  console.error(error);
@@ -1876,6 +1891,46 @@ const doStopLocalMedia = createAppThunk(() => (dispatch, getState) => {
1876
1891
  }
1877
1892
  dispatch(localMediaStopped());
1878
1893
  });
1894
+ const doLocalStreamEffect = createAppAsyncThunk("localMedia/doLocalStreamEffect", (_a, _b) => __awaiter(void 0, [_a, _b], void 0, function* ({ effectStream, only, stopBeforeTrack, }, { getState }) {
1895
+ var _c;
1896
+ const state = getState();
1897
+ let beforeEffectTracks = selectLocalMediaBeforeEffectTracks(state);
1898
+ const beforeTrack = beforeEffectTracks === null || beforeEffectTracks === void 0 ? void 0 : beforeEffectTracks[only];
1899
+ if (!effectStream && !beforeTrack)
1900
+ return;
1901
+ try {
1902
+ const stream = selectLocalMediaStream(state);
1903
+ let replacedTracks = null;
1904
+ if (!stream) {
1905
+ throw new Error("No local media stream");
1906
+ }
1907
+ if (effectStream) {
1908
+ if (!beforeTrack) {
1909
+ beforeEffectTracks = {
1910
+ [only]: (_c = (only === "audio" ? stream === null || stream === void 0 ? void 0 : stream.getAudioTracks() : stream === null || stream === void 0 ? void 0 : stream.getVideoTracks())) === null || _c === void 0 ? void 0 : _c[0],
1911
+ };
1912
+ }
1913
+ replacedTracks = replaceTracksInStream(stream, effectStream, only);
1914
+ }
1915
+ else if (!stopBeforeTrack) {
1916
+ replacedTracks = replaceTracksInStream(stream, beforeTrack ? new MediaStream([beforeTrack]) : new MediaStream(), only);
1917
+ beforeEffectTracks = {
1918
+ [only]: undefined,
1919
+ };
1920
+ }
1921
+ else if (beforeTrack) {
1922
+ beforeEffectTracks = {
1923
+ [only]: undefined,
1924
+ };
1925
+ beforeTrack.stop();
1926
+ }
1927
+ return { effectStream, beforeEffectTracks, replacedTracks };
1928
+ }
1929
+ catch (error) {
1930
+ console.error("Error applying local stream effect", error);
1931
+ return;
1932
+ }
1933
+ }));
1879
1934
  const selectBusyDeviceIds = (state) => state.localMedia.busyDeviceIds;
1880
1935
  const selectCameraDeviceError = (state) => state.localMedia.cameraDeviceError;
1881
1936
  const selectCurrentCameraDeviceId = (state) => state.localMedia.currentCameraDeviceId;
@@ -1896,6 +1951,7 @@ const selectLocalMediaStream = (state) => state.localMedia.stream;
1896
1951
  const selectMicrophoneDeviceError = (state) => state.localMedia.microphoneDeviceError;
1897
1952
  const selectLocalMediaStartError = (state) => state.localMedia.startError;
1898
1953
  const selectLocalMediaIsSwitchingStream = (state) => state.localMedia.isSwitchingStream;
1954
+ const selectLocalMediaBeforeEffectTracks = (state) => state.localMedia.beforeEffectTracks;
1899
1955
  const selectLocalMediaConstraintsOptions = createSelector(selectLocalMediaDevices, selectCurrentCameraDeviceId, selectCurrentMicrophoneDeviceId, selectIsLowDataModeEnabled, (devices, videoId, audioId, lowDataMode) => ({
1900
1956
  devices,
1901
1957
  videoId,
@@ -1992,7 +2048,7 @@ startAppListening({
1992
2048
  },
1993
2049
  });
1994
2050
  startAppListening({
1995
- matcher: isAnyOf(doStartLocalMedia.fulfilled, doUpdateDeviceList.fulfilled, doSwitchLocalStream.fulfilled, doSwitchLocalStream.rejected),
2051
+ matcher: isAnyOf(doStartLocalMedia.fulfilled, doUpdateDeviceList.fulfilled, doSwitchLocalStream.fulfilled, doSwitchLocalStream.rejected, doLocalStreamEffect.fulfilled),
1996
2052
  effect: (_action, { dispatch, getState }) => {
1997
2053
  const state = getState();
1998
2054
  const stream = selectLocalMediaStream(state);
@@ -3043,7 +3099,7 @@ startAppListening({
3043
3099
  },
3044
3100
  });
3045
3101
  startAppListening({
3046
- actionCreator: doSwitchLocalStream.fulfilled,
3102
+ matcher: isAnyOf(doSwitchLocalStream.fulfilled, doLocalStreamEffect.fulfilled),
3047
3103
  effect: ({ payload }, { getState }) => {
3048
3104
  var _a;
3049
3105
  const stream = selectLocalMediaStream(getState());
@@ -4059,11 +4115,107 @@ const doRejectWaitingParticipant = createRoomConnectedThunk((payload) => (dispat
4059
4115
  });
4060
4116
  const selectWaitingParticipants = (state) => state.waitingParticipants.waitingParticipants;
4061
4117
 
4118
+ const initialState = {
4119
+ isSwitching: false,
4120
+ raw: {},
4121
+ };
4122
+ const cameraEffectsSlice = createSlice({
4123
+ name: "cameraEffects",
4124
+ initialState,
4125
+ reducers: {
4126
+ cameraEffectsSwitching(state, action) {
4127
+ state.isSwitching = action.payload.isSwitching;
4128
+ },
4129
+ cameraEffectsCleared(state) {
4130
+ state.currentEffectId = null;
4131
+ state.setup = undefined;
4132
+ state.params = undefined;
4133
+ state.raw = {};
4134
+ state.error = undefined;
4135
+ state.isSwitching = false;
4136
+ },
4137
+ cameraEffectsUpdated(state, action) {
4138
+ const { effectId, setup, params, raw } = action.payload;
4139
+ state.currentEffectId = effectId;
4140
+ state.setup = setup;
4141
+ state.params = params;
4142
+ if (raw)
4143
+ state.raw = raw;
4144
+ state.error = undefined;
4145
+ state.isSwitching = false;
4146
+ },
4147
+ cameraEffectsError(state, action) {
4148
+ state.error = action.payload.error;
4149
+ state.isSwitching = false;
4150
+ },
4151
+ },
4152
+ });
4153
+ const { cameraEffectsSwitching, cameraEffectsCleared, cameraEffectsUpdated, cameraEffectsError } = cameraEffectsSlice.actions;
4154
+ const selectCameraEffectsRaw = (state) => state.cameraEffects.raw;
4155
+ const doCameraEffectsSwitchPreset = createAppAsyncThunk("cameraEffects/switchPreset", (_a, _b) => __awaiter(void 0, [_a, _b], void 0, function* ({ effectId, setup, params, allowSafari, }, { getState, dispatch, rejectWithValue }) {
4156
+ var _c, _d, _e;
4157
+ const state = getState();
4158
+ if (selectLocalMediaIsSwitchingStream(state)) {
4159
+ return;
4160
+ }
4161
+ dispatch(cameraEffectsSwitching({ isSwitching: true }));
4162
+ try {
4163
+ const raw = selectCameraEffectsRaw(state);
4164
+ const localStream = selectLocalMediaStream(state);
4165
+ if (!((_c = localStream === null || localStream === void 0 ? void 0 : localStream.getVideoTracks()) === null || _c === void 0 ? void 0 : _c[0])) {
4166
+ dispatch(cameraEffectsCleared());
4167
+ return;
4168
+ }
4169
+ if (!effectId) {
4170
+ if (raw.effectStream) {
4171
+ yield dispatch(doLocalStreamEffect({ effectStream: undefined, only: "video" }));
4172
+ }
4173
+ (_d = raw.stop) === null || _d === void 0 ? void 0 : _d.call(raw);
4174
+ dispatch(cameraEffectsCleared());
4175
+ return;
4176
+ }
4177
+ if (raw.tryUpdate) {
4178
+ const ok = yield raw.tryUpdate(effectId, Object.assign({}, (setup || {})), Object.assign({}, (params || {})));
4179
+ if (ok) {
4180
+ dispatch(cameraEffectsUpdated({ effectId, setup, params }));
4181
+ return;
4182
+ }
4183
+ }
4184
+ if (raw.effectStream) {
4185
+ yield dispatch(doLocalStreamEffect({ effectStream: undefined, only: "video" }));
4186
+ (_e = raw.stop) === null || _e === void 0 ? void 0 : _e.call(raw);
4187
+ }
4188
+ let mod;
4189
+ try {
4190
+ mod = yield import('@whereby.com/camera-effects');
4191
+ }
4192
+ catch (_f) {
4193
+ throw new Error("@whereby.com/camera-effects is not installed. Add it as a dependency to enable camera effects.");
4194
+ }
4195
+ const { createEffectStream, getUsablePresets } = mod;
4196
+ const usable = getUsablePresets({ filter: () => true, options: { allowSafari } });
4197
+ if (!usable.includes(effectId)) {
4198
+ throw new Error(`Unknown or unsupported effect preset: ${effectId}`);
4199
+ }
4200
+ const { stream: effectStream, stop, tryUpdate, } = yield createEffectStream(localStream, effectId, setup, params);
4201
+ yield dispatch(doLocalStreamEffect({ effectStream, only: "video" }));
4202
+ dispatch(cameraEffectsUpdated({ effectId, setup, params, raw: { stop, tryUpdate, effectStream } }));
4203
+ }
4204
+ catch (error) {
4205
+ dispatch(cameraEffectsError({ error }));
4206
+ return rejectWithValue(error);
4207
+ }
4208
+ }));
4209
+ createAppAsyncThunk("cameraEffects/clear", (_1, _a) => __awaiter(void 0, [_1, _a], void 0, function* (_, { dispatch }) {
4210
+ yield dispatch(doCameraEffectsSwitchPreset({ effectId: null }));
4211
+ }));
4212
+
4062
4213
  const IS_DEV = undefined === "true";
4063
4214
  const appReducer = combineReducers({
4064
4215
  app: appSlice.reducer,
4065
4216
  authorization: authorizationSlice.reducer,
4066
4217
  breakout: breakoutSlice.reducer,
4218
+ cameraEffects: cameraEffectsSlice.reducer,
4067
4219
  chat: chatSlice.reducer,
4068
4220
  cloudRecording: cloudRecordingSlice.reducer,
4069
4221
  connectionMonitor: connectionMonitorSlice.reducer,
@@ -4733,6 +4885,28 @@ class RoomConnectionClient extends BaseClient {
4733
4885
  reportStreamResolution(streamId, width, height) {
4734
4886
  this.store.dispatch(doRtcReportStreamResolution({ streamId, width, height }));
4735
4887
  }
4888
+ switchCameraEffect(effectId) {
4889
+ return __awaiter(this, void 0, void 0, function* () {
4890
+ try {
4891
+ yield this.store.dispatch(doCameraEffectsSwitchPreset({ effectId }));
4892
+ }
4893
+ catch (error) {
4894
+ return Promise.reject(error);
4895
+ }
4896
+ return Promise.resolve();
4897
+ });
4898
+ }
4899
+ clearCameraEffect() {
4900
+ return __awaiter(this, void 0, void 0, function* () {
4901
+ try {
4902
+ yield this.store.dispatch(doCameraEffectsSwitchPreset({ effectId: null }));
4903
+ }
4904
+ catch (error) {
4905
+ return Promise.reject(error);
4906
+ }
4907
+ return Promise.resolve();
4908
+ });
4909
+ }
4736
4910
  destroy() {
4737
4911
  super.destroy();
4738
4912
  this.store.dispatch(doAppStop());