mediasfu-shared 1.0.0
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/LICENSE +21 -0
- package/README.md +228 -0
- package/dist/index.cjs +7707 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +7285 -0
- package/dist/index.js +7690 -0
- package/dist/index.js.map +1 -0
- package/package.json +78 -0
- package/src/ProducerClient/producerClientEmits/createDeviceClient.ts +68 -0
- package/src/consumers/addVideosGrid.ts +18 -0
- package/src/consumers/autoAdjust.ts +100 -0
- package/src/consumers/calculateRowsAndColumns.ts +51 -0
- package/src/consumers/changeVids.ts +753 -0
- package/src/consumers/checkGrid.ts +100 -0
- package/src/consumers/checkPermission.ts +89 -0
- package/src/consumers/checkScreenShare.ts +114 -0
- package/src/consumers/closeAndResize.ts +401 -0
- package/src/consumers/compareActiveNames.ts +122 -0
- package/src/consumers/compareScreenStates.ts +117 -0
- package/src/consumers/connectIps.ts +175 -0
- package/src/consumers/connectLocalIps.ts +103 -0
- package/src/consumers/connectRecvTransport.ts +158 -0
- package/src/consumers/connectSendTransport.ts +150 -0
- package/src/consumers/connectSendTransportAudio.ts +161 -0
- package/src/consumers/connectSendTransportScreen.ts +169 -0
- package/src/consumers/connectSendTransportVideo.ts +149 -0
- package/src/consumers/consumerResume.ts +25 -0
- package/src/consumers/controlMedia.ts +118 -0
- package/src/consumers/createSendTransport.ts +312 -0
- package/src/consumers/disconnectSendTransportAudio.ts +170 -0
- package/src/consumers/disconnectSendTransportScreen.ts +130 -0
- package/src/consumers/disconnectSendTransportVideo.ts +161 -0
- package/src/consumers/dispStreams.ts +694 -0
- package/src/consumers/generatePageContent.ts +118 -0
- package/src/consumers/getEstimate.ts +124 -0
- package/src/consumers/getPipedProducersAlt.ts +96 -0
- package/src/consumers/getProducersPiped.ts +89 -0
- package/src/consumers/getVideos.ts +107 -0
- package/src/consumers/mixStreams.ts +97 -0
- package/src/consumers/onScreenChanges.ts +106 -0
- package/src/consumers/prepopulateUserMedia.ts +18 -0
- package/src/consumers/processConsumerTransports.ts +157 -0
- package/src/consumers/processConsumerTransportsAudio.ts +121 -0
- package/src/consumers/rePort.ts +123 -0
- package/src/consumers/reUpdateInter.ts +289 -0
- package/src/consumers/readjust.ts +170 -0
- package/src/consumers/receiveAllPipedTransports.ts +77 -0
- package/src/consumers/receiveRoomMessages.ts +55 -0
- package/src/consumers/reorderStreams.ts +246 -0
- package/src/consumers/requestScreenShare.ts +103 -0
- package/src/consumers/resumePauseAudioStreams.ts +174 -0
- package/src/consumers/resumePauseStreams.ts +110 -0
- package/src/consumers/resumeSendTransportAudio.ts +143 -0
- package/src/consumers/signalNewConsumerTransport.ts +179 -0
- package/src/consumers/socketReceiveMethods/joinConsumeRoom.ts +130 -0
- package/src/consumers/socketReceiveMethods/newPipeProducer.ts +138 -0
- package/src/consumers/socketReceiveMethods/producerClosed.ts +102 -0
- package/src/consumers/startShareScreen.ts +124 -0
- package/src/consumers/stopShareScreen.ts +241 -0
- package/src/consumers/streamSuccessAudio.ts +297 -0
- package/src/consumers/streamSuccessAudioSwitch.ts +315 -0
- package/src/consumers/streamSuccessScreen.ts +255 -0
- package/src/consumers/streamSuccessVideo.ts +373 -0
- package/src/consumers/switchUserAudio.ts +140 -0
- package/src/consumers/switchUserVideo.ts +201 -0
- package/src/consumers/switchUserVideoAlt.ts +331 -0
- package/src/consumers/trigger.ts +250 -0
- package/src/consumers/updateMiniCardsGrid.ts +150 -0
- package/src/consumers/updateParticipantAudioDecibels.ts +56 -0
- package/src/index.ts +119 -0
- package/src/methods/background/launchBackground.ts +16 -0
- package/src/methods/breakoutRooms/breakoutRoomUpdated.ts +161 -0
- package/src/methods/breakoutRooms/handleStartBreakout.ts +96 -0
- package/src/methods/breakoutRooms/handleStopBreakout.ts +72 -0
- package/src/methods/breakoutRooms/index.ts +4 -0
- package/src/methods/breakoutRooms/launchBreakoutRooms.ts +31 -0
- package/src/methods/coHost/launchCoHost.ts +28 -0
- package/src/methods/coHostMethods/index.ts +2 -0
- package/src/methods/coHostMethods/modifyCoHostSettings.ts +94 -0
- package/src/methods/displaySettings/index.ts +1 -0
- package/src/methods/displaySettings/launchDisplaySettings.ts +31 -0
- package/src/methods/displaySettings/modifyDisplaySettings.ts +242 -0
- package/src/methods/exit/confirmExit.ts +60 -0
- package/src/methods/exit/index.ts +2 -0
- package/src/methods/exit/launchConfirmExit.ts +29 -0
- package/src/methods/index.ts +5 -0
- package/src/methods/mediaSettings/launchMediaSettings.ts +61 -0
- package/src/methods/menu/launchMenuModal.ts +28 -0
- package/src/methods/message/index.ts +1 -0
- package/src/methods/message/launchMessages.ts +27 -0
- package/src/methods/message/sendMessage.ts +175 -0
- package/src/methods/participants/index.ts +3 -0
- package/src/methods/participants/launchParticipants.ts +25 -0
- package/src/methods/participants/messageParticipants.ts +78 -0
- package/src/methods/participants/muteParticipants.ts +79 -0
- package/src/methods/participants/removeParticipants.ts +97 -0
- package/src/methods/polls/handleCreatePoll.ts +66 -0
- package/src/methods/polls/handleEndPoll.ts +64 -0
- package/src/methods/polls/handleVotePoll.ts +76 -0
- package/src/methods/polls/index.ts +3 -0
- package/src/methods/polls/launchPoll.ts +25 -0
- package/src/methods/prejoin/handleCreateRoom.ts +441 -0
- package/src/methods/prejoin/handleJoinRoom.ts +153 -0
- package/src/methods/prejoin/index.ts +14 -0
- package/src/methods/recording/checkPauseState.ts +57 -0
- package/src/methods/recording/checkResumeState.ts +42 -0
- package/src/methods/recording/confirmRecording.ts +241 -0
- package/src/methods/recording/launchRecording.ts +114 -0
- package/src/methods/recording/recordPauseTimer.ts +52 -0
- package/src/methods/recording/recordResumeTimer.ts +92 -0
- package/src/methods/recording/recordStartTimer.ts +106 -0
- package/src/methods/recording/recordUpdateTimer.ts +49 -0
- package/src/methods/recording/startRecording.ts +268 -0
- package/src/methods/recording/stopRecording.ts +124 -0
- package/src/methods/recording/updateRecording.ts +245 -0
- package/src/methods/requests/index.ts +1 -0
- package/src/methods/requests/launchRequests.ts +25 -0
- package/src/methods/requests/respondToRequests.ts +108 -0
- package/src/methods/settings/index.ts +2 -0
- package/src/methods/settings/launchSettings.ts +25 -0
- package/src/methods/settings/modifySettings.ts +99 -0
- package/src/methods/stream/clickVideo.ts +337 -0
- package/src/methods/stream/index.ts +3 -0
- package/src/methods/stream/switchAudio.ts +73 -0
- package/src/methods/stream/switchVideo.ts +148 -0
- package/src/methods/stream/switchVideoAlt.ts +152 -0
- package/src/methods/utils/checkLimitsAndMakeRequest.ts +103 -0
- package/src/methods/utils/validateAlphanumeric.ts +41 -0
- package/src/methods/waiting/index.ts +1 -0
- package/src/methods/waiting/launchWaiting.ts +26 -0
- package/src/methods/waiting/respondToWaiting.ts +82 -0
- package/src/methods/welcome/handleWelcomeRequest.ts +189 -0
- package/src/methods/welcome/index.ts +5 -0
- package/src/methods/whiteboard/handleStartWhiteboard.ts +65 -0
- package/src/methods/whiteboard/handleStopWhiteboard.ts +48 -0
- package/src/methods/whiteboard/index.ts +4 -0
- package/src/methods/whiteboard/launchConfigureWhiteboard.ts +29 -0
- package/src/producers/producerEmits/joinConRoom.ts +153 -0
- package/src/sockets/SocketManager.ts +232 -0
- package/src/types/shared-base-types.ts +752 -0
- package/src/types/types.ts +84 -0
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
import { Socket } from 'socket.io-client';
|
|
2
|
+
import type {
|
|
3
|
+
CreateMediaSFURoomOptions,
|
|
4
|
+
CreateRoomOnMediaSFUType,
|
|
5
|
+
RecordingParams,
|
|
6
|
+
MeetingRoomParams,
|
|
7
|
+
ResponseLocalConnectionData,
|
|
8
|
+
PreJoinPageParameters,
|
|
9
|
+
} from '../../types/types';
|
|
10
|
+
import { checkLimitsAndMakeRequest } from '../utils/checkLimitsAndMakeRequest';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Parameters for creating a local room
|
|
14
|
+
*/
|
|
15
|
+
export interface CreateLocalRoomParameters {
|
|
16
|
+
eventID: string;
|
|
17
|
+
duration: number;
|
|
18
|
+
capacity: number;
|
|
19
|
+
userName: string;
|
|
20
|
+
scheduledDate: Date;
|
|
21
|
+
secureCode: string;
|
|
22
|
+
waitRoom?: boolean;
|
|
23
|
+
recordingParams?: RecordingParams;
|
|
24
|
+
eventRoomParams?: MeetingRoomParams;
|
|
25
|
+
videoPreference?: string | null;
|
|
26
|
+
audioPreference?: string | null;
|
|
27
|
+
audioOutputPreference?: string | null;
|
|
28
|
+
mediasfuURL?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Options for creating a local room
|
|
33
|
+
*/
|
|
34
|
+
export interface CreateLocalRoomOptions {
|
|
35
|
+
createData: CreateLocalRoomParameters;
|
|
36
|
+
link?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Response from creating/joining a local room
|
|
41
|
+
*/
|
|
42
|
+
export interface CreateJoinLocalRoomResponse {
|
|
43
|
+
success: boolean;
|
|
44
|
+
secret: string;
|
|
45
|
+
reason?: string;
|
|
46
|
+
url?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Options for handleCreateRoom function
|
|
51
|
+
*/
|
|
52
|
+
export interface HandleCreateRoomOptions {
|
|
53
|
+
payload: CreateMediaSFURoomOptions;
|
|
54
|
+
localLink?: string;
|
|
55
|
+
connectMediaSFU?: boolean;
|
|
56
|
+
apiUserName: string;
|
|
57
|
+
apiKey: string;
|
|
58
|
+
validate?: boolean;
|
|
59
|
+
parameters: PreJoinPageParameters;
|
|
60
|
+
initSocket?: Socket;
|
|
61
|
+
localData?: ResponseLocalConnectionData;
|
|
62
|
+
createMediaSFURoom: CreateRoomOnMediaSFUType;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Handles room creation with MediaSFU and/or local server
|
|
67
|
+
*
|
|
68
|
+
* @param {HandleCreateRoomOptions} options - Configuration for creating a room
|
|
69
|
+
* @returns {Promise<void>}
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* await handleCreateRoom({
|
|
74
|
+
* payload: {
|
|
75
|
+
* action: 'create',
|
|
76
|
+
* duration: 30,
|
|
77
|
+
* capacity: 10,
|
|
78
|
+
* eventType: 'conference',
|
|
79
|
+
* userName: 'John Doe',
|
|
80
|
+
* recordOnly: false,
|
|
81
|
+
* },
|
|
82
|
+
* apiUserName: 'user123',
|
|
83
|
+
* apiKey: 'key123',
|
|
84
|
+
* parameters,
|
|
85
|
+
* createMediaSFURoom,
|
|
86
|
+
* });
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export async function handleCreateRoom({
|
|
90
|
+
payload,
|
|
91
|
+
localLink = '',
|
|
92
|
+
connectMediaSFU = true,
|
|
93
|
+
apiUserName,
|
|
94
|
+
apiKey,
|
|
95
|
+
validate = true,
|
|
96
|
+
parameters,
|
|
97
|
+
initSocket,
|
|
98
|
+
localData,
|
|
99
|
+
createMediaSFURoom,
|
|
100
|
+
}: HandleCreateRoomOptions): Promise<void> {
|
|
101
|
+
const {
|
|
102
|
+
updateIsLoadingModalVisible,
|
|
103
|
+
updateSocket,
|
|
104
|
+
updateApiUserName,
|
|
105
|
+
updateApiToken,
|
|
106
|
+
updateLink,
|
|
107
|
+
updateRoomName,
|
|
108
|
+
updateMember,
|
|
109
|
+
updateValidated,
|
|
110
|
+
showAlert,
|
|
111
|
+
} = parameters;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Creates a local room by emitting to the socket
|
|
115
|
+
*/
|
|
116
|
+
const createLocalRoom = async ({
|
|
117
|
+
createData,
|
|
118
|
+
link = localLink,
|
|
119
|
+
}: CreateLocalRoomOptions) => {
|
|
120
|
+
initSocket?.emit('createRoom', createData, (response: CreateJoinLocalRoomResponse) => {
|
|
121
|
+
if (response.success) {
|
|
122
|
+
updateSocket(initSocket!);
|
|
123
|
+
updateApiUserName(localData?.apiUserName || '');
|
|
124
|
+
updateApiToken(response.secret);
|
|
125
|
+
updateLink(link);
|
|
126
|
+
updateRoomName(createData.eventID);
|
|
127
|
+
// local needs islevel updated from here
|
|
128
|
+
// we update member as `userName` + "_2" and split it in the room
|
|
129
|
+
updateMember(createData.userName + '_2');
|
|
130
|
+
updateIsLoadingModalVisible(false);
|
|
131
|
+
updateValidated(true);
|
|
132
|
+
} else {
|
|
133
|
+
updateIsLoadingModalVisible(false);
|
|
134
|
+
showAlert?.({
|
|
135
|
+
message: `Unable to create room. ${response.reason}`,
|
|
136
|
+
type: 'danger',
|
|
137
|
+
duration: 3000,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Creates a room on MediaSFU
|
|
145
|
+
*/
|
|
146
|
+
const roomCreator = async ({
|
|
147
|
+
payload: createPayload,
|
|
148
|
+
apiUserName: userName,
|
|
149
|
+
apiKey: key,
|
|
150
|
+
validate: shouldValidate = true,
|
|
151
|
+
}: {
|
|
152
|
+
payload: CreateMediaSFURoomOptions;
|
|
153
|
+
apiUserName: string;
|
|
154
|
+
apiKey: string;
|
|
155
|
+
validate?: boolean;
|
|
156
|
+
}) => {
|
|
157
|
+
const response = await createMediaSFURoom({
|
|
158
|
+
payload: createPayload,
|
|
159
|
+
apiUserName: userName,
|
|
160
|
+
apiKey: key,
|
|
161
|
+
localLink: localLink,
|
|
162
|
+
});
|
|
163
|
+
if (response.success && response.data && 'roomName' in response.data) {
|
|
164
|
+
await checkLimitsAndMakeRequest({
|
|
165
|
+
apiUserName: response.data.roomName,
|
|
166
|
+
apiToken: response.data.secret,
|
|
167
|
+
link: response!.data.link,
|
|
168
|
+
userName: createPayload.userName,
|
|
169
|
+
parameters: parameters,
|
|
170
|
+
validate: shouldValidate,
|
|
171
|
+
});
|
|
172
|
+
return response;
|
|
173
|
+
} else {
|
|
174
|
+
updateIsLoadingModalVisible(false);
|
|
175
|
+
showAlert?.({
|
|
176
|
+
message: `Unable to create room. ${
|
|
177
|
+
response.data ? ('error' in response.data ? response.data.error : '') : ''
|
|
178
|
+
}`,
|
|
179
|
+
type: 'danger',
|
|
180
|
+
duration: 3000,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
updateIsLoadingModalVisible(true);
|
|
186
|
+
|
|
187
|
+
if (localLink.length > 0) {
|
|
188
|
+
const secureCode =
|
|
189
|
+
Math.random().toString(30).substring(2, 14) +
|
|
190
|
+
Math.random().toString(30).substring(2, 14);
|
|
191
|
+
let eventID =
|
|
192
|
+
new Date().getTime().toString(30) +
|
|
193
|
+
new Date().getUTCMilliseconds() +
|
|
194
|
+
Math.floor(10 + Math.random() * 99).toString();
|
|
195
|
+
eventID = 'm' + eventID;
|
|
196
|
+
const eventRoomParams = localData?.meetingRoomParams_;
|
|
197
|
+
if (eventRoomParams && payload.eventType) {
|
|
198
|
+
eventRoomParams.type = payload.eventType;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const createData: CreateLocalRoomParameters = {
|
|
202
|
+
eventID: eventID,
|
|
203
|
+
duration: payload.duration,
|
|
204
|
+
capacity: payload.capacity,
|
|
205
|
+
userName: payload.userName,
|
|
206
|
+
scheduledDate: new Date(),
|
|
207
|
+
secureCode: secureCode,
|
|
208
|
+
waitRoom: false,
|
|
209
|
+
recordingParams: localData?.recordingParams_,
|
|
210
|
+
eventRoomParams: eventRoomParams,
|
|
211
|
+
videoPreference: null,
|
|
212
|
+
audioPreference: null,
|
|
213
|
+
audioOutputPreference: null,
|
|
214
|
+
mediasfuURL: '',
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
if (connectMediaSFU && initSocket && localData && localData.apiUserName && localData.apiKey) {
|
|
218
|
+
// Store references to prevent race conditions
|
|
219
|
+
const localApiUserName = localData.apiUserName;
|
|
220
|
+
const localApiKey = localData.apiKey;
|
|
221
|
+
|
|
222
|
+
// Build a unique identifier for this create request
|
|
223
|
+
const roomIdentifier = `local_create_${payload.userName}_${payload.duration}_${payload.capacity}`;
|
|
224
|
+
const pendingKey = `prejoin_pending_${roomIdentifier}`;
|
|
225
|
+
const PENDING_TIMEOUT = 30 * 1000; // 30 seconds
|
|
226
|
+
|
|
227
|
+
// Check pending status to prevent duplicate requests
|
|
228
|
+
try {
|
|
229
|
+
const pendingRequest = localStorage.getItem(pendingKey);
|
|
230
|
+
if (pendingRequest) {
|
|
231
|
+
const pendingData = JSON.parse(pendingRequest);
|
|
232
|
+
const timeSincePending = Date.now() - (pendingData?.timestamp ?? 0);
|
|
233
|
+
if (timeSincePending < PENDING_TIMEOUT) {
|
|
234
|
+
updateIsLoadingModalVisible(false);
|
|
235
|
+
showAlert?.({
|
|
236
|
+
message: 'Room creation already in progress',
|
|
237
|
+
type: 'danger',
|
|
238
|
+
duration: 3000,
|
|
239
|
+
});
|
|
240
|
+
return;
|
|
241
|
+
} else {
|
|
242
|
+
// Stale lock, clear it
|
|
243
|
+
try {
|
|
244
|
+
localStorage.removeItem(pendingKey);
|
|
245
|
+
} catch {
|
|
246
|
+
// Ignore localStorage errors
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
} catch {
|
|
251
|
+
// Ignore localStorage read/JSON errors
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Mark request as pending
|
|
255
|
+
try {
|
|
256
|
+
localStorage.setItem(
|
|
257
|
+
pendingKey,
|
|
258
|
+
JSON.stringify({
|
|
259
|
+
timestamp: Date.now(),
|
|
260
|
+
payload: {
|
|
261
|
+
action: 'create',
|
|
262
|
+
userName: payload.userName,
|
|
263
|
+
duration: payload.duration,
|
|
264
|
+
capacity: payload.capacity,
|
|
265
|
+
},
|
|
266
|
+
})
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
// Auto-clear the pending flag after timeout to avoid stale locks
|
|
270
|
+
setTimeout(() => {
|
|
271
|
+
try {
|
|
272
|
+
localStorage.removeItem(pendingKey);
|
|
273
|
+
} catch {
|
|
274
|
+
// Ignore localStorage errors
|
|
275
|
+
}
|
|
276
|
+
}, PENDING_TIMEOUT);
|
|
277
|
+
} catch {
|
|
278
|
+
// Ignore localStorage write errors
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
payload.recordOnly = true; // allow production to mediasfu only; no consumption
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
const response = await roomCreator({
|
|
285
|
+
payload,
|
|
286
|
+
apiUserName: localApiUserName,
|
|
287
|
+
apiKey: localApiKey,
|
|
288
|
+
validate: false,
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// Clear pending status on completion
|
|
292
|
+
try {
|
|
293
|
+
localStorage.removeItem(pendingKey);
|
|
294
|
+
} catch {
|
|
295
|
+
/* ignore */
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (response && response.success && response.data && 'roomName' in response.data) {
|
|
299
|
+
createData.eventID = response.data.roomName;
|
|
300
|
+
createData.secureCode = response.data.secureCode || '';
|
|
301
|
+
createData.mediasfuURL = response.data.publicURL;
|
|
302
|
+
await createLocalRoom({
|
|
303
|
+
createData: createData,
|
|
304
|
+
link: response.data.link,
|
|
305
|
+
});
|
|
306
|
+
} else {
|
|
307
|
+
updateIsLoadingModalVisible(false);
|
|
308
|
+
showAlert?.({
|
|
309
|
+
message: `Unable to create room on MediaSFU.`,
|
|
310
|
+
type: 'danger',
|
|
311
|
+
duration: 3000,
|
|
312
|
+
});
|
|
313
|
+
try {
|
|
314
|
+
updateSocket(initSocket);
|
|
315
|
+
await createLocalRoom({ createData: createData });
|
|
316
|
+
} catch (error) {
|
|
317
|
+
updateIsLoadingModalVisible(false);
|
|
318
|
+
showAlert?.({
|
|
319
|
+
message: `Unable to create room. ${error}`,
|
|
320
|
+
type: 'danger',
|
|
321
|
+
duration: 3000,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
} catch (error) {
|
|
326
|
+
// Clear pending status on error
|
|
327
|
+
try {
|
|
328
|
+
localStorage.removeItem(pendingKey);
|
|
329
|
+
} catch {
|
|
330
|
+
/* ignore */
|
|
331
|
+
}
|
|
332
|
+
updateIsLoadingModalVisible(false);
|
|
333
|
+
showAlert?.({
|
|
334
|
+
message: `Unable to create room on MediaSFU. ${error}`,
|
|
335
|
+
type: 'danger',
|
|
336
|
+
duration: 3000,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
} else {
|
|
340
|
+
try {
|
|
341
|
+
updateSocket(initSocket!);
|
|
342
|
+
await createLocalRoom({ createData: createData });
|
|
343
|
+
} catch (error) {
|
|
344
|
+
updateIsLoadingModalVisible(false);
|
|
345
|
+
showAlert?.({
|
|
346
|
+
message: `Unable to create room. ${error}`,
|
|
347
|
+
type: 'danger',
|
|
348
|
+
duration: 3000,
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
} else {
|
|
353
|
+
// Build a unique identifier for this create request (non-local)
|
|
354
|
+
const roomIdentifier = `mediasfu_create_${payload.userName}_${payload.duration}_${payload.capacity}`;
|
|
355
|
+
const pendingKey = `prejoin_pending_${roomIdentifier}`;
|
|
356
|
+
const PENDING_TIMEOUT = 30 * 1000; // 30 seconds
|
|
357
|
+
|
|
358
|
+
// Check pending status to prevent duplicate requests
|
|
359
|
+
try {
|
|
360
|
+
const pendingRequest = localStorage.getItem(pendingKey);
|
|
361
|
+
if (pendingRequest) {
|
|
362
|
+
const pendingData = JSON.parse(pendingRequest);
|
|
363
|
+
const timeSincePending = Date.now() - (pendingData?.timestamp ?? 0);
|
|
364
|
+
if (timeSincePending < PENDING_TIMEOUT) {
|
|
365
|
+
updateIsLoadingModalVisible(false);
|
|
366
|
+
showAlert?.({
|
|
367
|
+
message: 'Room creation already in progress',
|
|
368
|
+
type: 'danger',
|
|
369
|
+
duration: 3000,
|
|
370
|
+
});
|
|
371
|
+
return;
|
|
372
|
+
} else {
|
|
373
|
+
// Stale lock, clear it
|
|
374
|
+
try {
|
|
375
|
+
localStorage.removeItem(pendingKey);
|
|
376
|
+
} catch {
|
|
377
|
+
// Ignore localStorage errors
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
} catch {
|
|
382
|
+
// Ignore localStorage read/JSON errors
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Mark request as pending
|
|
386
|
+
try {
|
|
387
|
+
localStorage.setItem(
|
|
388
|
+
pendingKey,
|
|
389
|
+
JSON.stringify({
|
|
390
|
+
timestamp: Date.now(),
|
|
391
|
+
payload: {
|
|
392
|
+
action: 'create',
|
|
393
|
+
userName: payload.userName,
|
|
394
|
+
duration: payload.duration,
|
|
395
|
+
capacity: payload.capacity,
|
|
396
|
+
},
|
|
397
|
+
})
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
// Auto-clear the pending flag after timeout to avoid stale locks
|
|
401
|
+
setTimeout(() => {
|
|
402
|
+
try {
|
|
403
|
+
localStorage.removeItem(pendingKey);
|
|
404
|
+
} catch {
|
|
405
|
+
// Ignore localStorage errors
|
|
406
|
+
}
|
|
407
|
+
}, PENDING_TIMEOUT);
|
|
408
|
+
} catch {
|
|
409
|
+
// Ignore localStorage write errors
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
try {
|
|
413
|
+
await roomCreator({
|
|
414
|
+
payload,
|
|
415
|
+
apiUserName: apiUserName,
|
|
416
|
+
apiKey: apiKey,
|
|
417
|
+
validate: validate,
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// Clear pending status on completion
|
|
421
|
+
try {
|
|
422
|
+
localStorage.removeItem(pendingKey);
|
|
423
|
+
} catch {
|
|
424
|
+
/* ignore */
|
|
425
|
+
}
|
|
426
|
+
} catch (error) {
|
|
427
|
+
// Clear pending status on error
|
|
428
|
+
try {
|
|
429
|
+
localStorage.removeItem(pendingKey);
|
|
430
|
+
} catch {
|
|
431
|
+
/* ignore */
|
|
432
|
+
}
|
|
433
|
+
updateIsLoadingModalVisible(false);
|
|
434
|
+
showAlert?.({
|
|
435
|
+
message: `Unable to create room. ${error}`,
|
|
436
|
+
type: 'danger',
|
|
437
|
+
duration: 3000,
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { Socket } from 'socket.io-client';
|
|
2
|
+
import type {
|
|
3
|
+
JoinMediaSFURoomOptions,
|
|
4
|
+
JoinRoomOnMediaSFUType,
|
|
5
|
+
PreJoinPageParameters,
|
|
6
|
+
} from '../../types/types';
|
|
7
|
+
import { checkLimitsAndMakeRequest } from '../utils/checkLimitsAndMakeRequest';
|
|
8
|
+
import type { CreateJoinLocalRoomResponse } from './handleCreateRoom';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Parameters for joining a local event room
|
|
12
|
+
*/
|
|
13
|
+
export interface JoinLocalEventRoomParameters {
|
|
14
|
+
eventID: string;
|
|
15
|
+
userName: string;
|
|
16
|
+
secureCode?: string;
|
|
17
|
+
videoPreference?: string | null;
|
|
18
|
+
audioPreference?: string | null;
|
|
19
|
+
audioOutputPreference?: string | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Options for joining a local event room
|
|
24
|
+
*/
|
|
25
|
+
export interface JoinLocalEventRoomOptions {
|
|
26
|
+
joinData: JoinLocalEventRoomParameters;
|
|
27
|
+
link?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Options for handleJoinRoom function
|
|
32
|
+
*/
|
|
33
|
+
export interface HandleJoinRoomOptions {
|
|
34
|
+
payload: JoinMediaSFURoomOptions;
|
|
35
|
+
localLink?: string;
|
|
36
|
+
apiUserName: string;
|
|
37
|
+
apiKey: string;
|
|
38
|
+
parameters: PreJoinPageParameters;
|
|
39
|
+
initSocket?: Socket;
|
|
40
|
+
localData?: any;
|
|
41
|
+
joinMediaSFURoom: JoinRoomOnMediaSFUType;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Handles joining an existing room on MediaSFU or local server
|
|
46
|
+
*
|
|
47
|
+
* @param {HandleJoinRoomOptions} options - Configuration for joining a room
|
|
48
|
+
* @returns {Promise<void>}
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* await handleJoinRoom({
|
|
53
|
+
* payload: {
|
|
54
|
+
* action: 'join',
|
|
55
|
+
* meetingID: 'room123',
|
|
56
|
+
* userName: 'John Doe',
|
|
57
|
+
* },
|
|
58
|
+
* apiUserName: 'user123',
|
|
59
|
+
* apiKey: 'key123',
|
|
60
|
+
* parameters,
|
|
61
|
+
* joinMediaSFURoom,
|
|
62
|
+
* });
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export async function handleJoinRoom({
|
|
66
|
+
payload,
|
|
67
|
+
localLink = '',
|
|
68
|
+
apiUserName,
|
|
69
|
+
apiKey,
|
|
70
|
+
parameters,
|
|
71
|
+
initSocket,
|
|
72
|
+
localData,
|
|
73
|
+
joinMediaSFURoom,
|
|
74
|
+
}: HandleJoinRoomOptions): Promise<void> {
|
|
75
|
+
const {
|
|
76
|
+
updateIsLoadingModalVisible,
|
|
77
|
+
updateSocket,
|
|
78
|
+
updateApiUserName,
|
|
79
|
+
updateApiToken,
|
|
80
|
+
updateLink,
|
|
81
|
+
updateRoomName,
|
|
82
|
+
updateMember,
|
|
83
|
+
updateValidated,
|
|
84
|
+
showAlert,
|
|
85
|
+
} = parameters;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Joins a local room by emitting to the socket
|
|
89
|
+
*/
|
|
90
|
+
const joinLocalRoom = async ({ joinData, link = localLink }: JoinLocalEventRoomOptions) => {
|
|
91
|
+
initSocket?.emit('joinEventRoom', joinData, (response: CreateJoinLocalRoomResponse) => {
|
|
92
|
+
if (response.success) {
|
|
93
|
+
updateSocket(initSocket!);
|
|
94
|
+
updateApiUserName(localData?.apiUserName || '');
|
|
95
|
+
updateApiToken(response.secret);
|
|
96
|
+
updateLink(link);
|
|
97
|
+
updateRoomName(joinData.eventID);
|
|
98
|
+
updateMember(joinData.userName);
|
|
99
|
+
updateIsLoadingModalVisible(false);
|
|
100
|
+
updateValidated(true);
|
|
101
|
+
} else {
|
|
102
|
+
updateIsLoadingModalVisible(false);
|
|
103
|
+
showAlert?.({
|
|
104
|
+
message: `Unable to join room. ${response.reason}`,
|
|
105
|
+
type: 'danger',
|
|
106
|
+
duration: 3000,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
if (localLink.length > 0 && !localLink.includes('mediasfu.com')) {
|
|
113
|
+
const joinData: JoinLocalEventRoomParameters = {
|
|
114
|
+
eventID: payload.meetingID,
|
|
115
|
+
userName: payload.userName,
|
|
116
|
+
secureCode: '',
|
|
117
|
+
videoPreference: null,
|
|
118
|
+
audioPreference: null,
|
|
119
|
+
audioOutputPreference: null,
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
await joinLocalRoom({ joinData: joinData });
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
updateIsLoadingModalVisible(true);
|
|
127
|
+
|
|
128
|
+
const response = await joinMediaSFURoom({
|
|
129
|
+
payload,
|
|
130
|
+
apiUserName: apiUserName,
|
|
131
|
+
apiKey: apiKey,
|
|
132
|
+
localLink: localLink,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
if (response.success && response.data && 'roomName' in response.data) {
|
|
136
|
+
await checkLimitsAndMakeRequest({
|
|
137
|
+
apiUserName: response.data.roomName,
|
|
138
|
+
apiToken: response.data.secret,
|
|
139
|
+
link: response.data.link,
|
|
140
|
+
userName: payload.userName,
|
|
141
|
+
parameters: parameters,
|
|
142
|
+
});
|
|
143
|
+
} else {
|
|
144
|
+
updateIsLoadingModalVisible(false);
|
|
145
|
+
showAlert?.({
|
|
146
|
+
message: `Unable to join room. ${
|
|
147
|
+
response.data ? ('error' in response.data ? response.data.error : '') : ''
|
|
148
|
+
}`,
|
|
149
|
+
type: 'danger',
|
|
150
|
+
duration: 3000,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { handleCreateRoom } from './handleCreateRoom';
|
|
2
|
+
export type {
|
|
3
|
+
HandleCreateRoomOptions,
|
|
4
|
+
CreateLocalRoomParameters,
|
|
5
|
+
CreateLocalRoomOptions,
|
|
6
|
+
CreateJoinLocalRoomResponse,
|
|
7
|
+
} from './handleCreateRoom';
|
|
8
|
+
|
|
9
|
+
export { handleJoinRoom } from './handleJoinRoom';
|
|
10
|
+
export type {
|
|
11
|
+
HandleJoinRoomOptions,
|
|
12
|
+
JoinLocalEventRoomParameters,
|
|
13
|
+
JoinLocalEventRoomOptions,
|
|
14
|
+
} from './handleJoinRoom';
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ShowAlert } from '../../types/types'
|
|
2
|
+
|
|
3
|
+
export interface CheckPauseStateOptions {
|
|
4
|
+
recordingMediaOptions: string
|
|
5
|
+
recordingVideoPausesLimit: number
|
|
6
|
+
recordingAudioPausesLimit: number
|
|
7
|
+
pauseRecordCount: number
|
|
8
|
+
showAlert?: ShowAlert
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type CheckPauseStateType = (options: CheckPauseStateOptions) => Promise<boolean>
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Checks if the recording can be paused based on the current pause count and the allowed pause limits.
|
|
15
|
+
*
|
|
16
|
+
* @param {Object} options - The options for checking the pause state.
|
|
17
|
+
* @param {string} options.recordingMediaOptions - The type of media being recorded ("video" or "audio").
|
|
18
|
+
* @param {number} options.recordingVideoPausesLimit - The maximum number of pauses allowed for video recordings.
|
|
19
|
+
* @param {number} options.recordingAudioPausesLimit - The maximum number of pauses allowed for audio recordings.
|
|
20
|
+
* @param {number} options.pauseRecordCount - The current count of pauses that have been made.
|
|
21
|
+
* @param {Function} options.showAlert - Function to show an alert message if the pause limit is reached.
|
|
22
|
+
*
|
|
23
|
+
* @returns {Promise<boolean>} - Resolves to `true` if the recording can be paused, otherwise `false`.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* const canPause = await checkPauseState({
|
|
28
|
+
* recordingMediaOptions: 'audio',
|
|
29
|
+
* recordingVideoPausesLimit: 3,
|
|
30
|
+
* recordingAudioPausesLimit: 5,
|
|
31
|
+
* pauseRecordCount: 4,
|
|
32
|
+
* showAlert: ({ message }) => console.log(message),
|
|
33
|
+
* })
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export const checkPauseState: CheckPauseStateType = async ({
|
|
37
|
+
recordingMediaOptions,
|
|
38
|
+
recordingVideoPausesLimit,
|
|
39
|
+
recordingAudioPausesLimit,
|
|
40
|
+
pauseRecordCount,
|
|
41
|
+
showAlert,
|
|
42
|
+
}) => {
|
|
43
|
+
const refLimit = recordingMediaOptions === 'video'
|
|
44
|
+
? recordingVideoPausesLimit
|
|
45
|
+
: recordingAudioPausesLimit
|
|
46
|
+
|
|
47
|
+
if (pauseRecordCount < refLimit) {
|
|
48
|
+
return true
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
showAlert?.({
|
|
52
|
+
message: 'You have reached the limit of pauses - you can choose to stop recording.',
|
|
53
|
+
type: 'danger',
|
|
54
|
+
duration: 3000,
|
|
55
|
+
})
|
|
56
|
+
return false
|
|
57
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface CheckResumeStateOptions {
|
|
2
|
+
recordingMediaOptions: string
|
|
3
|
+
recordingVideoPausesLimit: number
|
|
4
|
+
recordingAudioPausesLimit: number
|
|
5
|
+
pauseRecordCount: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type CheckResumeStateType = (options: CheckResumeStateOptions) => Promise<boolean>
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Checks if the recording can be resumed based on the media type and pause limits.
|
|
12
|
+
*
|
|
13
|
+
* @param {Object} options - The options for checking resume state.
|
|
14
|
+
* @param {string} options.recordingMediaOptions - The type of media being recorded ("video" or "audio").
|
|
15
|
+
* @param {number} options.recordingVideoPausesLimit - The maximum number of pauses allowed for video recording.
|
|
16
|
+
* @param {number} options.recordingAudioPausesLimit - The maximum number of pauses allowed for audio recording.
|
|
17
|
+
* @param {number} options.pauseRecordCount - The current number of pauses that have occurred.
|
|
18
|
+
*
|
|
19
|
+
* @returns {Promise<boolean>} - Resolves to `true` if the recording can be resumed, otherwise `false`.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* const canResume = await checkResumeState({
|
|
24
|
+
* recordingMediaOptions: 'video',
|
|
25
|
+
* recordingVideoPausesLimit: 3,
|
|
26
|
+
* recordingAudioPausesLimit: 5,
|
|
27
|
+
* pauseRecordCount: 2,
|
|
28
|
+
* })
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export const checkResumeState: CheckResumeStateType = async ({
|
|
32
|
+
recordingMediaOptions,
|
|
33
|
+
recordingVideoPausesLimit,
|
|
34
|
+
recordingAudioPausesLimit,
|
|
35
|
+
pauseRecordCount,
|
|
36
|
+
}) => {
|
|
37
|
+
const refLimit = recordingMediaOptions === 'video'
|
|
38
|
+
? recordingVideoPausesLimit
|
|
39
|
+
: recordingAudioPausesLimit
|
|
40
|
+
|
|
41
|
+
return pauseRecordCount <= refLimit
|
|
42
|
+
}
|