mediasfu-shared 1.0.1 → 1.0.2

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.
Files changed (125) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +103 -222
  3. package/dist/index.cjs +7500 -2163
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.ts +4203 -273
  6. package/dist/index.js +7521 -2184
  7. package/dist/index.js.map +1 -1
  8. package/package.json +85 -78
  9. package/src/ProducerClient/producerClientEmits/joinRoomClient.ts +57 -0
  10. package/src/ProducerClient/producerClientEmits/updateRoomParametersClient.ts +401 -0
  11. package/src/consumers/addVideosGrid.ts +3 -2
  12. package/src/consumers/changeVids.ts +111 -41
  13. package/src/consumers/checkPermission.ts +35 -1
  14. package/src/consumers/connectRecvTransport.ts +42 -1
  15. package/src/consumers/consumerResume.ts +2 -2
  16. package/src/consumers/dispStreams.ts +83 -37
  17. package/src/consumers/frameworkConsumerContract.ts +6 -0
  18. package/src/consumers/generatePageContent.ts +24 -10
  19. package/src/consumers/getPipedProducersAlt.ts +112 -16
  20. package/src/consumers/gridLayout/addVideosGrid.engine.ts +42 -0
  21. package/src/consumers/gridLayout/prepopulateUserMedia.engine.ts +444 -0
  22. package/src/consumers/mixStreams.ts +45 -14
  23. package/src/consumers/onScreenChanges.ts +25 -10
  24. package/src/consumers/prepopulateUserMedia.ts +3 -2
  25. package/src/consumers/processConsumerTransports.ts +68 -23
  26. package/src/consumers/processConsumerTransportsAudio.ts +53 -16
  27. package/src/consumers/reUpdateInter.ts +61 -21
  28. package/src/consumers/readjust.ts +30 -14
  29. package/src/consumers/reorderStreams.ts +76 -42
  30. package/src/consumers/resumePauseAudioStreams.ts +66 -17
  31. package/src/consumers/resumePauseStreams.ts +53 -10
  32. package/src/consumers/socketReceiveMethods/joinConsumeRoom.ts +8 -0
  33. package/src/consumers/socketReceiveMethods/newPipeProducer.ts +114 -0
  34. package/src/consumers/socketReceiveMethods/producerClosed.ts +13 -0
  35. package/src/consumers/streamSuccessScreen.ts +2 -2
  36. package/src/consumers/streamSuccessVideo.ts +5 -0
  37. package/src/consumers/translationConsumerSwitch.ts +299 -0
  38. package/src/index.ts +85 -1
  39. package/src/methods/coHostMethods/modifyCoHostSettings.ts +9 -9
  40. package/src/methods/displaySettings/modifyDisplaySettings.ts +5 -0
  41. package/src/methods/index.ts +66 -0
  42. package/src/methods/message/sendMessage.ts +12 -29
  43. package/src/methods/panelists/focusPanelists.ts +83 -0
  44. package/src/methods/panelists/index.ts +3 -0
  45. package/src/methods/panelists/launchPanelists.ts +13 -0
  46. package/src/methods/panelists/updatePanelists.ts +135 -0
  47. package/src/methods/permissions/index.ts +3 -0
  48. package/src/methods/permissions/launchPermissions.ts +13 -0
  49. package/src/methods/permissions/updateParticipantPermission.ts +127 -0
  50. package/src/methods/permissions/updatePermissionConfig.ts +52 -0
  51. package/src/methods/polls/pollUpdated.ts +88 -0
  52. package/src/methods/recording/confirmRecording.ts +15 -12
  53. package/src/methods/recording/recordResumeTimer.ts +2 -2
  54. package/src/methods/recording/recordStartTimer.ts +2 -2
  55. package/src/methods/recording/timeLeftRecording.ts +25 -0
  56. package/src/methods/requests/hostRequestResponse.ts +153 -0
  57. package/src/methods/settings/modifySettings.ts +17 -17
  58. package/src/methods/socketReceive/allMembers.ts +450 -0
  59. package/src/methods/socketReceive/allMembersRest.ts +480 -0
  60. package/src/methods/socketReceive/allWaitingRoomMembers.ts +35 -0
  61. package/src/methods/socketReceive/banParticipant.ts +73 -0
  62. package/src/methods/socketReceive/controlMediaHost.ts +280 -0
  63. package/src/methods/socketReceive/disconnect.ts +40 -0
  64. package/src/methods/socketReceive/disconnectUserSelf.ts +56 -0
  65. package/src/methods/socketReceive/getDomains.ts +112 -0
  66. package/src/methods/socketReceive/meetingEnded.ts +49 -0
  67. package/src/methods/socketReceive/meetingStillThere.ts +26 -0
  68. package/src/methods/socketReceive/panelistReceiveMethods.ts +195 -0
  69. package/src/methods/socketReceive/participantRequested.ts +48 -0
  70. package/src/methods/socketReceive/permissionReceiveMethods.ts +59 -0
  71. package/src/methods/socketReceive/personJoined.ts +35 -0
  72. package/src/methods/socketReceive/producerMediaClosed.ts +223 -0
  73. package/src/methods/socketReceive/producerMediaPaused.ts +267 -0
  74. package/src/methods/socketReceive/producerMediaResumed.ts +157 -0
  75. package/src/methods/socketReceive/reInitiateRecording.ts +53 -0
  76. package/src/methods/socketReceive/receiveMessage.ts +117 -0
  77. package/src/methods/socketReceive/recordingNotice.ts +286 -0
  78. package/src/methods/socketReceive/roomRecordParams.ts +122 -0
  79. package/src/methods/socketReceive/screenProducerId.ts +61 -0
  80. package/src/methods/socketReceive/startRecords.ts +46 -0
  81. package/src/methods/socketReceive/stoppedRecording.ts +44 -0
  82. package/src/methods/socketReceive/translationReceiveMethods.ts +581 -0
  83. package/src/methods/socketReceive/updateConsumingDomains.ts +128 -0
  84. package/src/methods/socketReceive/updateMediaSettings.ts +45 -0
  85. package/src/methods/socketReceive/updatedCoHost.ts +75 -0
  86. package/src/methods/socketReceive/userWaiting.ts +45 -0
  87. package/src/methods/stream/clickAudio.ts +380 -0
  88. package/src/methods/stream/clickChat.ts +36 -0
  89. package/src/methods/stream/clickScreenShare.ts +173 -0
  90. package/src/methods/stream/clickVideo.ts +22 -5
  91. package/src/methods/stream/index.ts +1 -0
  92. package/src/methods/utils/SoundPlayer.ts +31 -0
  93. package/src/methods/utils/checkLimitsAndMakeRequest.ts +156 -2
  94. package/src/methods/utils/createResponseJoinRoom.ts +47 -0
  95. package/src/methods/utils/createRoomOnMediaSFU.ts +160 -0
  96. package/src/methods/utils/formatNumber.ts +42 -0
  97. package/src/methods/utils/generateRandomMessages.ts +70 -0
  98. package/src/methods/utils/generateRandomParticipants.ts +100 -0
  99. package/src/methods/utils/generateRandomPolls.ts +43 -0
  100. package/src/methods/utils/generateRandomRequestList.ts +51 -0
  101. package/src/methods/utils/generateRandomWaitingRoomList.ts +17 -0
  102. package/src/methods/utils/getModalPosition.ts +23 -0
  103. package/src/methods/utils/getOverlayPosition.ts +37 -0
  104. package/src/methods/utils/initialValuesState.ts +405 -0
  105. package/src/methods/utils/joinRoomOnMediaSFU.ts +124 -0
  106. package/src/methods/utils/liveSubtitle.ts +107 -0
  107. package/src/methods/utils/meetingTimeRemaining.ts +33 -0
  108. package/src/methods/utils/meetingTimer/startMeetingProgressTimer.ts +72 -0
  109. package/src/methods/utils/producer/aParams.ts +10 -0
  110. package/src/methods/utils/producer/hParams.ts +26 -0
  111. package/src/methods/utils/producer/screenParams.ts +13 -0
  112. package/src/methods/utils/producer/vParams.ts +26 -0
  113. package/src/methods/utils/producer/videoCaptureConstraints.ts +65 -0
  114. package/src/methods/utils/resolveMediaSFURoomApi.ts +16 -0
  115. package/src/methods/utils/sleep.ts +24 -0
  116. package/src/methods/utils/translationLanguages.ts +308 -0
  117. package/src/methods/utils/webrtc.ts +44 -0
  118. package/src/methods/welcome/handleWelcomeRequest.ts +11 -2
  119. package/src/methods/welcome/index.ts +5 -1
  120. package/src/methods/whiteboard/captureCanvasStream.ts +128 -0
  121. package/src/producers/producerEmits/joinConRoom.ts +2 -2
  122. package/src/producers/producerEmits/joinLocalRoom.ts +240 -0
  123. package/src/producers/producerEmits/joinRoom.ts +129 -0
  124. package/src/types/shared-base-types.ts +14 -3
  125. package/src/types/types.ts +255 -0
@@ -0,0 +1,581 @@
1
+ import type { ShowAlert } from '../../types/types';
2
+ import type {
3
+ TranslationTranscriptData,
4
+ } from '../utils/liveSubtitle';
5
+ import type { TranslationVoiceConfig } from '../utils/translationLanguages';
6
+ import { getLanguageName } from '../utils/translationLanguages';
7
+
8
+ export type LanguageMode = 'allowlist' | 'blocklist' | 'any';
9
+
10
+ export interface LanguageEntry {
11
+ code: string;
12
+ nickname?: string;
13
+ voiceConfig?: TranslationVoiceConfig;
14
+ }
15
+
16
+ export interface TranslationRoomConfig {
17
+ supportTranslation: boolean;
18
+ spokenLanguageMode: LanguageMode;
19
+ allowedSpokenLanguages?: LanguageEntry[];
20
+ blockedSpokenLanguages?: string[];
21
+ listenLanguageMode: LanguageMode;
22
+ allowedListenLanguages?: LanguageEntry[];
23
+ blockedListenLanguages?: string[];
24
+ maxActiveChannelsPerSpeaker: number;
25
+ autoDetectSpokenLanguage: boolean;
26
+ allowSpokenLanguageChange?: boolean;
27
+ allowListenLanguageChange?: boolean;
28
+ translationVoiceConfig?: TranslationVoiceConfig | null;
29
+ providerGroups?: {
30
+ groupA?: {
31
+ languages: string[];
32
+ sttNickName?: string;
33
+ llmNickName?: string;
34
+ ttsNickName?: string;
35
+ };
36
+ groupB?: {
37
+ languages: string[];
38
+ sttNickName?: string;
39
+ llmNickName?: string;
40
+ ttsNickName?: string;
41
+ };
42
+ default?: {
43
+ sttNickName?: string;
44
+ llmNickName?: string;
45
+ ttsNickName?: string;
46
+ };
47
+ } | null;
48
+ }
49
+
50
+ export interface TranslationRoomConfigData {
51
+ config: TranslationRoomConfig;
52
+ }
53
+
54
+ export interface TranslationConfigUpdatedData {
55
+ config: TranslationRoomConfig;
56
+ }
57
+
58
+ export interface TranslationLanguageSetData {
59
+ success: boolean;
60
+ language: string;
61
+ enabled: boolean;
62
+ error?: string;
63
+ }
64
+
65
+ export interface TranslationSubscribedData {
66
+ speakerId: string;
67
+ speakerName?: string;
68
+ language: string;
69
+ channelCreated: boolean;
70
+ producerId?: string;
71
+ originalProducerId?: string;
72
+ }
73
+
74
+ export interface TranslationUnsubscribedData {
75
+ speakerId: string;
76
+ language: string;
77
+ channelClosed: boolean;
78
+ }
79
+
80
+ export interface TranslationProducerReadyData {
81
+ speakerId: string;
82
+ speakerName?: string;
83
+ language: string;
84
+ producerId: string;
85
+ originalProducerId: string;
86
+ }
87
+
88
+ export interface TranslationProducerClosedData {
89
+ speakerId: string;
90
+ language: string;
91
+ producerId: string;
92
+ reason?: string;
93
+ }
94
+
95
+ export interface TranslationChannelsAvailableData {
96
+ speakerId: string;
97
+ speakerName?: string;
98
+ languages: string[];
99
+ originalProducerId: string;
100
+ }
101
+
102
+ export interface TranslationMemberStateData {
103
+ memberId: string;
104
+ memberName?: string;
105
+ state: {
106
+ speaking?: {
107
+ enabled: boolean;
108
+ inputLanguage: string;
109
+ originalProducerId: string;
110
+ };
111
+ listening?: {
112
+ [speakerId: string]: {
113
+ language: string;
114
+ producerId: string | null;
115
+ };
116
+ };
117
+ };
118
+ }
119
+
120
+ export interface TranslationErrorData {
121
+ error: string;
122
+ code?: string;
123
+ details?: unknown;
124
+ availableChannels?: string[];
125
+ maxChannels?: number;
126
+ message?: string;
127
+ }
128
+
129
+ export interface TranslationSpeakerOutputChangedData {
130
+ speakerId: string;
131
+ speakerName: string;
132
+ inputLanguage: string;
133
+ outputLanguage: string | null;
134
+ originalProducerId: string;
135
+ enabled: boolean;
136
+ }
137
+
138
+ export interface TranslationProducerMap {
139
+ [originalProducerId: string]: {
140
+ [languageCode: string]: string;
141
+ };
142
+ }
143
+
144
+ export interface TranslationRoomConfigOptions {
145
+ data: TranslationRoomConfigData;
146
+ updateTranslationConfig?: (config: TranslationRoomConfig) => void;
147
+ updateTranslationSupported?: (supported: boolean) => void;
148
+ }
149
+
150
+ export interface TranslationConfigUpdatedOptions {
151
+ data: TranslationConfigUpdatedData;
152
+ updateTranslationConfig?: (config: TranslationRoomConfig) => void;
153
+ showAlert?: ShowAlert;
154
+ }
155
+
156
+ export interface TranslationLanguageSetOptions {
157
+ data: TranslationLanguageSetData;
158
+ updateMySpokenLanguage?: (lang: string) => void;
159
+ updateMySpokenLanguageEnabled?: (enabled: boolean) => void;
160
+ showAlert?: ShowAlert;
161
+ }
162
+
163
+ export interface TranslationSubscribedOptions {
164
+ data: TranslationSubscribedData;
165
+ updateListenPreferences?: (updater: (prev: Map<string, string>) => Map<string, string>) => void;
166
+ updateTranslationProducerMap?: (updater: (prev: TranslationProducerMap) => TranslationProducerMap) => void;
167
+ startConsumingTranslation?: (producerId: string, speakerId: string, language: string) => Promise<void>;
168
+ showAlert?: ShowAlert;
169
+ }
170
+
171
+ export interface TranslationUnsubscribedOptions {
172
+ data: TranslationUnsubscribedData;
173
+ updateListenPreferences?: (updater: (prev: Map<string, string>) => Map<string, string>) => void;
174
+ stopConsumingTranslation?: (speakerId: string, language: string) => Promise<void>;
175
+ }
176
+
177
+ export interface TranslationProducerReadyOptions {
178
+ data: TranslationProducerReadyData;
179
+ updateTranslationProducerMap?: (updater: (prev: TranslationProducerMap) => TranslationProducerMap) => void;
180
+ startConsumingTranslation?: (producerId: string, speakerId: string, language: string, originalProducerId: string) => Promise<void>;
181
+ pauseOriginalProducer?: (originalProducerId: string) => Promise<void>;
182
+ showAlert?: ShowAlert;
183
+ }
184
+
185
+ export interface TranslationProducerClosedOptions {
186
+ data: TranslationProducerClosedData;
187
+ updateTranslationProducerMap?: (updater: (prev: TranslationProducerMap) => TranslationProducerMap) => void;
188
+ stopConsumingTranslation?: (producerId: string) => Promise<void>;
189
+ resumeOriginalProducer?: (speakerId: string) => Promise<void>;
190
+ showAlert?: ShowAlert;
191
+ }
192
+
193
+ export interface TranslationChannelsAvailableOptions {
194
+ data: TranslationChannelsAvailableData;
195
+ updateAvailableTranslationChannels?: (speakerId: string, languages: string[], originalProducerId: string) => void;
196
+ myDefaultListenLanguage?: string | null;
197
+ socket?: {
198
+ emit: (event: string, payload: Record<string, unknown>) => void;
199
+ } | null;
200
+ roomName?: string;
201
+ }
202
+
203
+ export interface TranslationMemberStateOptions {
204
+ data: TranslationMemberStateData;
205
+ updateParticipantTranslationState?: (memberId: string, state: TranslationMemberStateData['state']) => void;
206
+ }
207
+
208
+ export interface TranslationErrorOptions {
209
+ data: TranslationErrorData;
210
+ showAlert?: ShowAlert;
211
+ }
212
+
213
+ export interface TranslationTranscriptOptions {
214
+ data: TranslationTranscriptData;
215
+ updateTranscripts?: (updater: (prev: TranslationTranscriptData[]) => TranslationTranscriptData[]) => void;
216
+ onTranscriptReceived?: (transcript: TranslationTranscriptData) => void;
217
+ maxTranscripts?: number;
218
+ }
219
+
220
+ export interface TranslationSpeakerOutputChangedOptions {
221
+ data: TranslationSpeakerOutputChangedData;
222
+ pauseOriginalProducer?: (originalProducerId: string, speakerId: string) => Promise<void>;
223
+ resumeOriginalProducer?: (originalProducerId: string, speakerId: string) => Promise<void>;
224
+ stopConsumingTranslationForSpeaker?: (speakerId: string) => Promise<void>;
225
+ updateSpeakerTranslationState?: (speakerId: string, outputLanguage: string | null, originalProducerId: string) => void;
226
+ showAlert?: ShowAlert;
227
+ listenerOverride?: { speakerId: string; wantOriginal: boolean; preferredLanguage?: string } | null;
228
+ }
229
+
230
+ export type TranslationRoomConfigType = (options: TranslationRoomConfigOptions) => Promise<void>;
231
+ export type TranslationConfigUpdatedType = (options: TranslationConfigUpdatedOptions) => Promise<void>;
232
+ export type TranslationLanguageSetType = (options: TranslationLanguageSetOptions) => Promise<void>;
233
+ export type TranslationSubscribedType = (options: TranslationSubscribedOptions) => Promise<void>;
234
+ export type TranslationUnsubscribedType = (options: TranslationUnsubscribedOptions) => Promise<void>;
235
+ export type TranslationProducerReadyType = (options: TranslationProducerReadyOptions) => Promise<void>;
236
+ export type TranslationProducerClosedType = (options: TranslationProducerClosedOptions) => Promise<void>;
237
+ export type TranslationChannelsAvailableType = (options: TranslationChannelsAvailableOptions) => Promise<void>;
238
+ export type TranslationMemberStateType = (options: TranslationMemberStateOptions) => Promise<void>;
239
+ export type TranslationErrorType = (options: TranslationErrorOptions) => Promise<void>;
240
+ export type TranslationTranscriptType = (options: TranslationTranscriptOptions) => Promise<void>;
241
+ export type TranslationSpeakerOutputChangedType = (options: TranslationSpeakerOutputChangedOptions) => Promise<void>;
242
+
243
+ export const translationRoomConfig: TranslationRoomConfigType = async ({
244
+ data,
245
+ updateTranslationConfig,
246
+ updateTranslationSupported,
247
+ }): Promise<void> => {
248
+ try {
249
+ const { config } = data;
250
+ updateTranslationSupported?.(config.supportTranslation);
251
+
252
+ if (updateTranslationConfig && config.supportTranslation) {
253
+ updateTranslationConfig(config);
254
+ }
255
+ } catch (error) {
256
+ console.error('Error handling translation:roomConfig:', error);
257
+ }
258
+ };
259
+
260
+ export const translationConfigUpdated: TranslationConfigUpdatedType = async ({
261
+ data,
262
+ updateTranslationConfig,
263
+ showAlert,
264
+ }): Promise<void> => {
265
+ try {
266
+ updateTranslationConfig?.(data.config);
267
+ showAlert?.({
268
+ message: 'Translation settings updated by host',
269
+ type: 'info',
270
+ duration: 2000,
271
+ });
272
+ } catch (error) {
273
+ console.error('Error handling translation:configUpdated:', error);
274
+ }
275
+ };
276
+
277
+ export const translationLanguageSet: TranslationLanguageSetType = async ({
278
+ data,
279
+ updateMySpokenLanguage,
280
+ updateMySpokenLanguageEnabled,
281
+ showAlert,
282
+ }): Promise<void> => {
283
+ try {
284
+ if (data.success) {
285
+ updateMySpokenLanguage?.(data.language);
286
+ updateMySpokenLanguageEnabled?.(data.enabled);
287
+ } else if (showAlert && data.error) {
288
+ showAlert({
289
+ message: data.error,
290
+ type: 'danger',
291
+ duration: 3000,
292
+ });
293
+ }
294
+ } catch (error) {
295
+ console.error('Error handling translation:languageSet:', error);
296
+ }
297
+ };
298
+
299
+ export const translationSubscribed: TranslationSubscribedType = async ({
300
+ data,
301
+ updateListenPreferences,
302
+ updateTranslationProducerMap,
303
+ startConsumingTranslation,
304
+ showAlert,
305
+ }): Promise<void> => {
306
+ try {
307
+ const { speakerId, language, channelCreated, producerId, originalProducerId } = data;
308
+
309
+ updateListenPreferences?.((prev) => {
310
+ const next = new Map(prev);
311
+ next.set(speakerId, language);
312
+ return next;
313
+ });
314
+
315
+ if (producerId && originalProducerId && updateTranslationProducerMap) {
316
+ updateTranslationProducerMap((prev) => ({
317
+ ...prev,
318
+ [originalProducerId]: {
319
+ ...(prev[originalProducerId] || {}),
320
+ [language]: producerId,
321
+ },
322
+ }));
323
+ }
324
+
325
+ if (producerId && startConsumingTranslation) {
326
+ await startConsumingTranslation(producerId, speakerId, language);
327
+ }
328
+
329
+ if (showAlert && channelCreated) {
330
+ showAlert({
331
+ message: `Translation channel created for ${language}`,
332
+ type: 'success',
333
+ duration: 2000,
334
+ });
335
+ }
336
+ } catch (error) {
337
+ console.error('Error handling translation:subscribed:', error);
338
+ }
339
+ };
340
+
341
+ export const translationUnsubscribed: TranslationUnsubscribedType = async ({
342
+ data,
343
+ updateListenPreferences,
344
+ stopConsumingTranslation,
345
+ }): Promise<void> => {
346
+ try {
347
+ updateListenPreferences?.((prev) => {
348
+ const next = new Map(prev);
349
+ next.delete(data.speakerId);
350
+ return next;
351
+ });
352
+
353
+ if (stopConsumingTranslation) {
354
+ await stopConsumingTranslation(data.speakerId, data.language);
355
+ }
356
+ } catch (error) {
357
+ console.error('Error handling translation:unsubscribed:', error);
358
+ }
359
+ };
360
+
361
+ export const translationProducerReady: TranslationProducerReadyType = async ({
362
+ data,
363
+ updateTranslationProducerMap,
364
+ pauseOriginalProducer,
365
+ }): Promise<void> => {
366
+ try {
367
+ updateTranslationProducerMap?.((prev) => ({
368
+ ...prev,
369
+ [data.originalProducerId]: {
370
+ ...(prev[data.originalProducerId] || {}),
371
+ [data.language]: data.producerId,
372
+ },
373
+ }));
374
+
375
+ if (pauseOriginalProducer) {
376
+ await pauseOriginalProducer(data.originalProducerId);
377
+ }
378
+ } catch (error) {
379
+ console.error('Error handling translation:producerReady:', error);
380
+ }
381
+ };
382
+
383
+ export const translationProducerClosed: TranslationProducerClosedType = async ({
384
+ data,
385
+ updateTranslationProducerMap,
386
+ stopConsumingTranslation,
387
+ resumeOriginalProducer,
388
+ showAlert,
389
+ }): Promise<void> => {
390
+ try {
391
+ if (updateTranslationProducerMap) {
392
+ updateTranslationProducerMap((prev) => {
393
+ const next = { ...prev };
394
+ for (const [originalProducerId, languageMap] of Object.entries(next)) {
395
+ if (languageMap[data.language] === data.producerId) {
396
+ delete languageMap[data.language];
397
+ if (Object.keys(languageMap).length === 0) {
398
+ delete next[originalProducerId];
399
+ }
400
+ }
401
+ }
402
+ return next;
403
+ });
404
+ }
405
+
406
+ if (stopConsumingTranslation) {
407
+ await stopConsumingTranslation(data.producerId);
408
+ }
409
+
410
+ if (resumeOriginalProducer) {
411
+ await resumeOriginalProducer(data.speakerId);
412
+ }
413
+
414
+ if (showAlert && data.reason) {
415
+ showAlert({
416
+ message: `Translation stopped: ${data.reason}`,
417
+ type: 'info',
418
+ duration: 2000,
419
+ });
420
+ }
421
+ } catch (error) {
422
+ console.error('Error handling translation:producerClosed:', error);
423
+ }
424
+ };
425
+
426
+ export const translationChannelsAvailable: TranslationChannelsAvailableType = async ({
427
+ data,
428
+ updateAvailableTranslationChannels,
429
+ myDefaultListenLanguage,
430
+ socket,
431
+ roomName,
432
+ }): Promise<void> => {
433
+ try {
434
+ updateAvailableTranslationChannels?.(data.speakerId, data.languages, data.originalProducerId);
435
+
436
+ if (myDefaultListenLanguage && data.languages.includes(myDefaultListenLanguage) && socket && roomName) {
437
+ socket.emit('translation:subscribe', {
438
+ roomName,
439
+ speakerId: data.speakerId,
440
+ language: myDefaultListenLanguage,
441
+ originalProducerId: data.originalProducerId,
442
+ });
443
+ }
444
+ } catch (error) {
445
+ console.error('Error handling translation:channelsAvailable:', error);
446
+ }
447
+ };
448
+
449
+ export const translationMemberState: TranslationMemberStateType = async ({
450
+ data,
451
+ updateParticipantTranslationState,
452
+ }): Promise<void> => {
453
+ try {
454
+ updateParticipantTranslationState?.(data.memberId, data.state);
455
+ } catch (error) {
456
+ console.error('Error handling translation:memberState:', error);
457
+ }
458
+ };
459
+
460
+ export const translationError: TranslationErrorType = async ({
461
+ data,
462
+ showAlert,
463
+ }): Promise<void> => {
464
+ try {
465
+ if (showAlert) {
466
+ let message = data.error;
467
+
468
+ switch (data.code) {
469
+ case 'max_channels':
470
+ if (data.availableChannels && data.availableChannels.length > 0) {
471
+ message = `Maximum ${data.maxChannels || 5} translation channels reached. Available: ${data.availableChannels.join(', ')}`;
472
+ } else {
473
+ message = data.message || 'Maximum translation channels reached. Please wait for a slot to open.';
474
+ }
475
+ break;
476
+ case 'speaker_not_found':
477
+ message = 'Speaker not found or has left the meeting.';
478
+ break;
479
+ case 'language_not_allowed':
480
+ message = 'This language is not available for translation in this room.';
481
+ break;
482
+ default:
483
+ message = data.error || 'Translation error occurred';
484
+ }
485
+
486
+ showAlert({
487
+ message,
488
+ type: 'danger',
489
+ duration: 5000,
490
+ });
491
+ }
492
+ } catch (err) {
493
+ console.error('Error handling translation:error:', err);
494
+ }
495
+ };
496
+
497
+ export const translationTranscript: TranslationTranscriptType = async ({
498
+ data,
499
+ updateTranscripts,
500
+ onTranscriptReceived,
501
+ maxTranscripts = 100,
502
+ }): Promise<void> => {
503
+ try {
504
+ if (updateTranscripts) {
505
+ updateTranscripts((prev) => {
506
+ const next = [...prev, data];
507
+ return next.length > maxTranscripts ? next.slice(-maxTranscripts) : next;
508
+ });
509
+ }
510
+
511
+ onTranscriptReceived?.(data);
512
+ } catch (err) {
513
+ console.error('Error handling translation:transcript:', err);
514
+ }
515
+ };
516
+
517
+ export const translationSpeakerOutputChanged: TranslationSpeakerOutputChangedType = async ({
518
+ data,
519
+ pauseOriginalProducer,
520
+ resumeOriginalProducer,
521
+ stopConsumingTranslationForSpeaker,
522
+ updateSpeakerTranslationState,
523
+ showAlert,
524
+ listenerOverride,
525
+ }): Promise<void> => {
526
+ try {
527
+ updateSpeakerTranslationState?.(data.speakerId, data.outputLanguage, data.originalProducerId);
528
+
529
+ const listenerWantsOriginal = listenerOverride?.wantOriginal === true;
530
+ const listenerWantsDifferentLanguage = Boolean(
531
+ listenerOverride?.preferredLanguage &&
532
+ listenerOverride.preferredLanguage.toLowerCase() !== data.outputLanguage?.toLowerCase(),
533
+ );
534
+
535
+ if (listenerWantsOriginal) {
536
+ showAlert?.({
537
+ message: `${data.speakerName} is speaking in ${data.outputLanguage ? getLanguageName(data.outputLanguage) : 'translated'} but you're hearing original`,
538
+ type: 'info',
539
+ duration: 3000,
540
+ });
541
+ return;
542
+ }
543
+
544
+ if (listenerWantsDifferentLanguage) {
545
+ if (pauseOriginalProducer && data.originalProducerId) {
546
+ await pauseOriginalProducer(data.originalProducerId, data.speakerId);
547
+ }
548
+ return;
549
+ }
550
+
551
+ if (data.enabled && data.outputLanguage && data.originalProducerId) {
552
+ if (pauseOriginalProducer) {
553
+ await pauseOriginalProducer(data.originalProducerId, data.speakerId);
554
+ }
555
+
556
+ showAlert?.({
557
+ message: `${data.speakerName} is now speaking in ${getLanguageName(data.outputLanguage)}`,
558
+ type: 'info',
559
+ duration: 3000,
560
+ });
561
+ } else if (!data.enabled || !data.outputLanguage) {
562
+ if (stopConsumingTranslationForSpeaker) {
563
+ await stopConsumingTranslationForSpeaker(data.speakerId);
564
+ }
565
+
566
+ if (resumeOriginalProducer && data.originalProducerId) {
567
+ await resumeOriginalProducer(data.originalProducerId, data.speakerId);
568
+ }
569
+
570
+ if (showAlert && !data.enabled) {
571
+ showAlert({
572
+ message: `${data.speakerName} returned to original language`,
573
+ type: 'info',
574
+ duration: 3000,
575
+ });
576
+ }
577
+ }
578
+ } catch (err) {
579
+ console.error('Error handling translation:speakerOutputChanged:', err);
580
+ }
581
+ };
@@ -0,0 +1,128 @@
1
+ import type {
2
+ AltDomains,
3
+ ConsumeSocket,
4
+ Participant,
5
+ } from '../../types/types';
6
+ import type { ConnectIpsLikeType } from './getDomains';
7
+
8
+ export interface UpdateConsumingDomainsGetDomainsOptions<
9
+ TParameters = unknown,
10
+ TParticipant extends { name?: string | null } = Participant,
11
+ TConsumeSocket = ConsumeSocket,
12
+ > {
13
+ domains: string[];
14
+ alt_domains: AltDomains;
15
+ apiUserName: string;
16
+ apiKey: string;
17
+ apiToken: string;
18
+ parameters:
19
+ UpdateConsumingDomainsParameters<TParameters, TParticipant, TConsumeSocket> &
20
+ TParameters;
21
+ }
22
+
23
+ export type UpdateConsumingDomainsGetDomainsType<
24
+ TParameters = unknown,
25
+ TParticipant extends { name?: string | null } = Participant,
26
+ TConsumeSocket = ConsumeSocket,
27
+ > = (
28
+ options: UpdateConsumingDomainsGetDomainsOptions<TParameters, TParticipant, TConsumeSocket>,
29
+ ) => Promise<void>;
30
+
31
+ export interface UpdateConsumingDomainsParameters
32
+ <
33
+ TParameters = unknown,
34
+ TParticipant extends { name?: string | null } = Participant,
35
+ TConsumeSocket = ConsumeSocket,
36
+ > {
37
+ participants: TParticipant[];
38
+ consume_sockets: TConsumeSocket[];
39
+ connectIps: ConnectIpsLikeType<TParameters, TConsumeSocket>;
40
+ getDomains: UpdateConsumingDomainsGetDomainsType<TParameters, TParticipant, TConsumeSocket>;
41
+ getUpdatedAllParams: () =>
42
+ UpdateConsumingDomainsParameters<TParameters, TParticipant, TConsumeSocket> &
43
+ TParameters;
44
+ [key: string]: any;
45
+ }
46
+
47
+ export interface UpdateConsumingDomainsOptions<
48
+ TParameters = unknown,
49
+ TParticipant extends { name?: string | null } = Participant,
50
+ TConsumeSocket = ConsumeSocket,
51
+ > {
52
+ domains: string[];
53
+ alt_domains: AltDomains;
54
+ apiUserName: string;
55
+ apiKey: string;
56
+ apiToken: string;
57
+ parameters:
58
+ UpdateConsumingDomainsParameters<TParameters, TParticipant, TConsumeSocket> &
59
+ TParameters;
60
+ }
61
+
62
+ export type UpdateConsumingDomainsType<
63
+ TParameters = unknown,
64
+ TParticipant extends { name?: string | null } = Participant,
65
+ TConsumeSocket = ConsumeSocket,
66
+ > = (
67
+ options: UpdateConsumingDomainsOptions<TParameters, TParticipant, TConsumeSocket>,
68
+ ) => Promise<void>;
69
+
70
+ /**
71
+ * Connects newly announced consuming domains, routing through alternate-domain lookup when needed.
72
+ *
73
+ * @param {UpdateConsumingDomainsOptions} options - Domain payload and consume-domain connection helpers.
74
+ * @returns {Promise<void>} Resolves once the needed consume domains are connected.
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * await updateConsumingDomains({
79
+ * domains,
80
+ * alt_domains,
81
+ * apiUserName: 'api-user',
82
+ * apiKey: 'api-key',
83
+ * apiToken: 'api-token',
84
+ * parameters,
85
+ * });
86
+ * ```
87
+ */
88
+ export const updateConsumingDomains = async <
89
+ TParameters = unknown,
90
+ TParticipant extends { name?: string | null } = Participant,
91
+ TConsumeSocket = ConsumeSocket,
92
+ >({
93
+ domains,
94
+ alt_domains,
95
+ parameters,
96
+ apiUserName,
97
+ apiKey,
98
+ apiToken,
99
+ }: UpdateConsumingDomainsOptions<TParameters, TParticipant, TConsumeSocket>): Promise<void> => {
100
+ let { participants, getDomains, consume_sockets, connectIps } = parameters;
101
+ consume_sockets = parameters.getUpdatedAllParams().consume_sockets;
102
+
103
+ try {
104
+ if (participants.length > 0) {
105
+ if (Object.keys(alt_domains).length > 0) {
106
+ await getDomains({
107
+ domains,
108
+ alt_domains,
109
+ apiUserName,
110
+ apiKey,
111
+ apiToken,
112
+ parameters,
113
+ });
114
+ } else {
115
+ await connectIps({
116
+ consume_sockets,
117
+ remIP: domains,
118
+ parameters,
119
+ apiUserName,
120
+ apiKey,
121
+ apiToken,
122
+ });
123
+ }
124
+ }
125
+ } catch (error) {
126
+ console.log('Error in updateConsumingDomains: ', error);
127
+ }
128
+ };