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,72 @@
1
+ export interface StartMeetingProgressTimerParameters {
2
+ updateMeetingProgressTime: (formattedTime: string) => void;
3
+ validated: boolean;
4
+ roomName: string;
5
+ getUpdatedAllParams: () => StartMeetingProgressTimerParameters;
6
+ [key: string]: any;
7
+ }
8
+
9
+ export interface StartMeetingProgressTimerOptions {
10
+ startTime: number;
11
+ parameters: StartMeetingProgressTimerParameters;
12
+ }
13
+
14
+ export type StartMeetingProgressTimerType = (options: StartMeetingProgressTimerOptions) => void;
15
+
16
+ /**
17
+ * Starts a one-second meeting progress timer and updates a formatted elapsed-time string.
18
+ *
19
+ * The timer automatically stops when the current runtime parameters indicate the
20
+ * meeting is no longer validated or no room is active.
21
+ *
22
+ * @param options Timer start options.
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * startMeetingProgressTimer({
27
+ * startTime: Math.floor(Date.now() / 1000),
28
+ * parameters: {
29
+ * validated: true,
30
+ * roomName: 'room123',
31
+ * updateMeetingProgressTime: setMeetingTime,
32
+ * getUpdatedAllParams: () => currentParams,
33
+ * },
34
+ * });
35
+ * ```
36
+ */
37
+ export function startMeetingProgressTimer({
38
+ startTime,
39
+ parameters,
40
+ }: StartMeetingProgressTimerOptions): void {
41
+ let { updateMeetingProgressTime, getUpdatedAllParams } = parameters;
42
+
43
+ function calculateElapsedTime(currentStartTime: number): number {
44
+ const currentTime = Math.floor(new Date().getTime() / 1000);
45
+ return currentTime - currentStartTime;
46
+ }
47
+
48
+ function padNumber(value: number): string {
49
+ return value.toString().padStart(2, '0');
50
+ }
51
+
52
+ function formatTime(time: number): string {
53
+ const hours = Math.floor(time / 3600);
54
+ const minutes = Math.floor((time % 3600) / 60);
55
+ const seconds = Math.floor(time % 60);
56
+ return `${padNumber(hours)}:${padNumber(minutes)}:${padNumber(seconds)}`;
57
+ }
58
+
59
+ let elapsedTime = calculateElapsedTime(startTime);
60
+
61
+ let timeProgress: ReturnType<typeof setInterval> | null = setInterval(() => {
62
+ elapsedTime++;
63
+ updateMeetingProgressTime(formatTime(elapsedTime));
64
+
65
+ parameters = getUpdatedAllParams();
66
+
67
+ if (!parameters.validated || !parameters.roomName) {
68
+ clearInterval(timeProgress!);
69
+ timeProgress = null;
70
+ }
71
+ }, 1000);
72
+ }
@@ -0,0 +1,10 @@
1
+ import type { AParamsType } from '../../../types/types';
2
+
3
+ export const aParams: AParamsType = {
4
+ encodings: [
5
+ {
6
+ rid: 'r0',
7
+ maxBitrate: 64000,
8
+ },
9
+ ],
10
+ };
@@ -0,0 +1,26 @@
1
+ import type { HParamsType } from '../../../types/types';
2
+
3
+ export const hParams: HParamsType = {
4
+ encodings: [
5
+ {
6
+ rid: 'r8',
7
+ maxBitrate: 240000,
8
+ scalabilityMode: 'L1T3',
9
+ scaleResolutionDownBy: 4.0,
10
+ },
11
+ {
12
+ rid: 'r9',
13
+ maxBitrate: 480000,
14
+ scalabilityMode: 'L1T3',
15
+ scaleResolutionDownBy: 2.0,
16
+ },
17
+ {
18
+ rid: 'r10',
19
+ maxBitrate: 960000,
20
+ scalabilityMode: 'L1T3',
21
+ },
22
+ ],
23
+ codecOptions: {
24
+ videoGoogleStartBitrate: 384,
25
+ },
26
+ };
@@ -0,0 +1,13 @@
1
+ import type { ScreenParamsType } from '../../../types/types';
2
+
3
+ export const screenParams: ScreenParamsType = {
4
+ encodings: [
5
+ {
6
+ rid: 'r7',
7
+ maxBitrate: 3000000,
8
+ },
9
+ ],
10
+ codecOptions: {
11
+ videoGoogleStartBitrate: 1000,
12
+ },
13
+ };
@@ -0,0 +1,26 @@
1
+ import type { VParamsType } from '../../../types/types';
2
+
3
+ export const vParams: VParamsType = {
4
+ encodings: [
5
+ {
6
+ rid: 'r3',
7
+ maxBitrate: 200000,
8
+ scalabilityMode: 'L1T3',
9
+ scaleResolutionDownBy: 4.0,
10
+ },
11
+ {
12
+ rid: 'r4',
13
+ maxBitrate: 400000,
14
+ scalabilityMode: 'L1T3',
15
+ scaleResolutionDownBy: 2.0,
16
+ },
17
+ {
18
+ rid: 'r5',
19
+ maxBitrate: 800000,
20
+ scalabilityMode: 'L1T3',
21
+ },
22
+ ],
23
+ codecOptions: {
24
+ videoGoogleStartBitrate: 320,
25
+ },
26
+ };
@@ -0,0 +1,65 @@
1
+ const QnHDCons = { width: { ideal: 320 }, height: { ideal: 180 } };
2
+ const sdCons = { width: { ideal: 640 }, height: { ideal: 360 } };
3
+ const hdCons = { width: { ideal: 1280 }, height: { ideal: 720 } };
4
+ const fhdCons = { width: { ideal: 1920 }, height: { ideal: 1080 } };
5
+ const qhdCons = { width: { ideal: 2560 }, height: { ideal: 1440 } };
6
+
7
+ const QnHDConsPort = { width: { ideal: 180 }, height: { ideal: 320 } };
8
+ const sdConsPort = { width: { ideal: 360 }, height: { ideal: 640 } };
9
+ const hdConsPort = { width: { ideal: 720 }, height: { ideal: 1280 } };
10
+ const fhdConsPort = { width: { ideal: 1080 }, height: { ideal: 1920 } };
11
+ const qhdConsPort = { width: { ideal: 1440 }, height: { ideal: 2560 } };
12
+
13
+ const QnHDConsNeu = { width: { ideal: 240 }, height: { ideal: 240 } };
14
+ const sdConsNeu = { width: { ideal: 480 }, height: { ideal: 480 } };
15
+ const hdConsNeu = { width: { ideal: 960 }, height: { ideal: 960 } };
16
+ const fhdConsNeu = { width: { ideal: 1440 }, height: { ideal: 1440 } };
17
+ const qhdConsNeu = { width: { ideal: 1920 }, height: { ideal: 1920 } };
18
+
19
+ const QnHDFrameRate = 5;
20
+ const sdFrameRate = 10;
21
+ const hdFrameRate = 15;
22
+ const fhdFrameRate = 20;
23
+ const qhdFrameRate = 30;
24
+ const screenFrameRate = 30;
25
+
26
+ /**
27
+ * Preset video capture constraints and frame-rate defaults used by MediaSFU.
28
+ *
29
+ * These exports provide standard landscape, portrait, and neutral constraint
30
+ * presets along with recommended frame-rate values for each quality tier.
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * const constraints = {
35
+ * video: {
36
+ * ...hdCons,
37
+ * frameRate: { ideal: hdFrameRate },
38
+ * },
39
+ * audio: false,
40
+ * };
41
+ * ```
42
+ */
43
+ export {
44
+ QnHDCons,
45
+ sdCons,
46
+ hdCons,
47
+ fhdCons,
48
+ qhdCons,
49
+ QnHDConsPort,
50
+ sdConsPort,
51
+ hdConsPort,
52
+ fhdConsPort,
53
+ qhdConsPort,
54
+ QnHDConsNeu,
55
+ sdConsNeu,
56
+ hdConsNeu,
57
+ fhdConsNeu,
58
+ qhdConsNeu,
59
+ QnHDFrameRate,
60
+ sdFrameRate,
61
+ hdFrameRate,
62
+ fhdFrameRate,
63
+ qhdFrameRate,
64
+ screenFrameRate,
65
+ };
@@ -0,0 +1,16 @@
1
+ const DEFAULT_MEDIA_SFU_ROOM_API_URL = 'https://mediasfu.com/v1/rooms/';
2
+
3
+ type MediaSFURoomApiAction = 'createRoom' | 'joinRoom';
4
+
5
+ export const resolveMediaSFURoomApi = (
6
+ localLink: string | undefined,
7
+ action: MediaSFURoomApiAction,
8
+ ): string => {
9
+ const normalizedLink = localLink?.trim();
10
+
11
+ if (!normalizedLink || normalizedLink.includes('mediasfu.com')) {
12
+ return DEFAULT_MEDIA_SFU_ROOM_API_URL;
13
+ }
14
+
15
+ return `${normalizedLink.replace(/\/$/, '')}/${action}`;
16
+ };
@@ -0,0 +1,24 @@
1
+ export interface SleepOptions {
2
+ ms: number;
3
+ }
4
+
5
+ export type SleepType = (options: SleepOptions) => Promise<void>;
6
+
7
+ /**
8
+ * Delays execution for a specified number of milliseconds.
9
+ *
10
+ * This helper is used throughout MediaSFU to wait for short transport or media
11
+ * lifecycle transitions before continuing work.
12
+ *
13
+ * @param options Delay configuration.
14
+ * @returns A promise that resolves after the requested delay.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * await sleep({ ms: 500 });
19
+ * // Continue after a half-second pause.
20
+ * ```
21
+ */
22
+ export function sleep({ ms }: SleepOptions): Promise<void> {
23
+ return new Promise((resolve) => setTimeout(resolve, ms));
24
+ }
@@ -0,0 +1,308 @@
1
+ export type VoiceGender = 'male' | 'female' | 'neutral';
2
+
3
+ export type TTSSupport = 'excellent' | 'good' | 'moderate' | 'limited' | 'unknown' | 'n/a';
4
+
5
+ export type LanguageRegion =
6
+ | 'global'
7
+ | 'europe'
8
+ | 'asia'
9
+ | 'south-asia'
10
+ | 'mena'
11
+ | 'africa'
12
+ | 'caucasus'
13
+ | 'central-asia'
14
+ | 'constructed'
15
+ | 'special'
16
+ | 'other';
17
+
18
+ export interface LanguageMetadata {
19
+ name: string;
20
+ nativeName: string;
21
+ region: LanguageRegion;
22
+ ttsSupport: TTSSupport;
23
+ }
24
+
25
+ export interface LanguageOption {
26
+ code: string;
27
+ name: string;
28
+ nativeName: string;
29
+ region: LanguageRegion;
30
+ ttsSupport: TTSSupport;
31
+ }
32
+
33
+ export interface VoiceOption {
34
+ id: string;
35
+ name: string;
36
+ gender: VoiceGender;
37
+ provider: string;
38
+ language: string;
39
+ style?: string;
40
+ }
41
+
42
+ export interface VoiceSelectionPreference {
43
+ gender: VoiceGender;
44
+ voiceId?: string;
45
+ style?: string;
46
+ }
47
+
48
+ export interface TranslationVoiceConfig {
49
+ voiceGender?: VoiceGender;
50
+ voiceId?: string;
51
+ voiceClone?: {
52
+ provider: 'elevenlabs' | 'playht' | 'coqui' | 'cartesia';
53
+ voiceId: string;
54
+ stability?: number;
55
+ similarity?: number;
56
+ };
57
+ sttNickName?: string;
58
+ llmNickName?: string;
59
+ ttsNickName?: string;
60
+ ttsProvider?: string;
61
+ sttParams?: Record<string, string | number | boolean>;
62
+ llmParams?: Record<string, string | number | boolean>;
63
+ ttsParams?: Record<string, string | number | boolean>;
64
+ }
65
+
66
+ export interface TranslationLanguageEntry {
67
+ code: string;
68
+ nickname?: string;
69
+ voiceConfig?: TranslationVoiceConfig;
70
+ }
71
+
72
+ export const SUPPORTED_LANGUAGE_CODES: string[] = [
73
+ 'en', 'es', 'fr', 'de', 'it', 'pt', 'ru', 'zh', 'ja', 'ko', 'ar',
74
+ 'hi', 'bn', 'pa', 'te', 'mr', 'ta', 'ur', 'gu', 'kn', 'ml', 'ne', 'si',
75
+ 'nl', 'pl', 'tr', 'cs', 'el', 'hu', 'ro', 'sv', 'da', 'fi', 'no', 'sk',
76
+ 'uk', 'bg', 'hr', 'et', 'lt', 'lv', 'sl', 'sr', 'bs', 'mk', 'is',
77
+ 'ga', 'cy', 'mt', 'lb', 'sq', 'be',
78
+ 'he', 'fa', 'ps', 'ku',
79
+ 'vi', 'th', 'id', 'ms', 'tl', 'km', 'lo', 'my',
80
+ 'sw', 'yo', 'ig', 'ha', 'zu', 'xh', 'af', 'st', 'tn', 'sn', 'am', 'so', 'rw', 'mg', 'ny',
81
+ 'ee', 'tw', 'gaa',
82
+ 'ka', 'hy', 'az',
83
+ 'eu', 'gl', 'ca', 'la', 'eo',
84
+ 'kk', 'uz', 'tg', 'ky', 'tk', 'mn',
85
+ 'auto',
86
+ ];
87
+
88
+ const REGION_BY_LANGUAGE: Record<string, LanguageRegion> = {
89
+ en: 'global', es: 'global', fr: 'europe', de: 'europe', it: 'europe', pt: 'global', ru: 'europe', zh: 'asia', ja: 'asia', ko: 'asia', ar: 'mena',
90
+ hi: 'south-asia', bn: 'south-asia', pa: 'south-asia', te: 'south-asia', mr: 'south-asia', ta: 'south-asia', ur: 'south-asia', gu: 'south-asia', kn: 'south-asia', ml: 'south-asia', ne: 'south-asia', si: 'south-asia',
91
+ nl: 'europe', pl: 'europe', tr: 'europe', cs: 'europe', el: 'europe', hu: 'europe', ro: 'europe', sv: 'europe', da: 'europe', fi: 'europe', no: 'europe', sk: 'europe', uk: 'europe', bg: 'europe', hr: 'europe', et: 'europe', lt: 'europe', lv: 'europe', sl: 'europe', sr: 'europe', bs: 'europe', mk: 'europe', is: 'europe', ga: 'europe', cy: 'europe', mt: 'europe', lb: 'europe', sq: 'europe', be: 'europe',
92
+ he: 'mena', fa: 'mena', ps: 'mena', ku: 'mena',
93
+ vi: 'asia', th: 'asia', id: 'asia', ms: 'asia', tl: 'asia', km: 'asia', lo: 'asia', my: 'asia',
94
+ sw: 'africa', yo: 'africa', ig: 'africa', ha: 'africa', zu: 'africa', xh: 'africa', af: 'africa', st: 'africa', tn: 'africa', sn: 'africa', am: 'africa', so: 'africa', rw: 'africa', mg: 'africa', ny: 'africa', ee: 'africa', tw: 'africa', gaa: 'africa',
95
+ ka: 'caucasus', hy: 'caucasus', az: 'caucasus',
96
+ eu: 'europe', gl: 'europe', ca: 'europe', la: 'europe', eo: 'constructed',
97
+ kk: 'central-asia', uz: 'central-asia', tg: 'central-asia', ky: 'central-asia', tk: 'central-asia', mn: 'central-asia',
98
+ auto: 'special',
99
+ };
100
+
101
+ const TTS_SUPPORT_BY_LANGUAGE: Record<string, TTSSupport> = {
102
+ en: 'excellent', es: 'excellent', fr: 'excellent', de: 'excellent', it: 'excellent', pt: 'excellent', ru: 'excellent', zh: 'excellent', ja: 'excellent', ko: 'excellent', ar: 'excellent',
103
+ hi: 'good', bn: 'good', te: 'good', mr: 'good', ta: 'good', ur: 'good', nl: 'excellent', pl: 'excellent', tr: 'excellent', cs: 'good', el: 'good', hu: 'good', ro: 'good', sv: 'excellent',
104
+ da: 'good', fi: 'good', no: 'good', uk: 'good', he: 'good', vi: 'good', th: 'good', id: 'good', ms: 'good', af: 'good', ca: 'good',
105
+ auto: 'n/a',
106
+ };
107
+
108
+ const getDisplayName = (code: string, locale: string, fallback: string) => {
109
+ try {
110
+ const displayNames = new Intl.DisplayNames([locale], { type: 'language' });
111
+ return displayNames.of(code) || fallback;
112
+ } catch {
113
+ return fallback;
114
+ }
115
+ };
116
+
117
+ export const normalizeLanguageCode = (code: string): string => {
118
+ if (!code || typeof code !== 'string') {
119
+ return 'en';
120
+ }
121
+
122
+ return code.trim().toLowerCase().split('-')[0];
123
+ };
124
+
125
+ export const isLanguageSupported = (code: string): boolean => {
126
+ const normalized = normalizeLanguageCode(code);
127
+ return SUPPORTED_LANGUAGE_CODES.includes(normalized);
128
+ };
129
+
130
+ export const getLanguageName = (code: string, displayLocale = 'en'): string => {
131
+ const normalized = normalizeLanguageCode(code);
132
+ if (normalized === 'auto') {
133
+ return 'Auto-detect';
134
+ }
135
+
136
+ return getDisplayName(normalized, displayLocale, normalized.toUpperCase());
137
+ };
138
+
139
+ export const getLanguageNativeName = (code: string): string => {
140
+ const normalized = normalizeLanguageCode(code);
141
+ if (normalized === 'auto') {
142
+ return 'Auto';
143
+ }
144
+
145
+ return getDisplayName(normalized, normalized, getLanguageName(normalized));
146
+ };
147
+
148
+ export const getLanguageMetadata = (code: string): LanguageMetadata => {
149
+ const normalized = normalizeLanguageCode(code);
150
+
151
+ return {
152
+ name: getLanguageName(normalized),
153
+ nativeName: getLanguageNativeName(normalized),
154
+ region: REGION_BY_LANGUAGE[normalized] || 'other',
155
+ ttsSupport: TTS_SUPPORT_BY_LANGUAGE[normalized] || 'unknown',
156
+ };
157
+ };
158
+
159
+ export const getSupportedLanguages = (displayLocale = 'en'): LanguageOption[] => {
160
+ return SUPPORTED_LANGUAGE_CODES.map((code) => {
161
+ const metadata = getLanguageMetadata(code);
162
+ return {
163
+ code,
164
+ name: getLanguageName(code, displayLocale),
165
+ nativeName: metadata.nativeName,
166
+ region: metadata.region,
167
+ ttsSupport: metadata.ttsSupport,
168
+ };
169
+ }).sort((left, right) => left.name.localeCompare(right.name));
170
+ };
171
+
172
+ export type TTSProvider =
173
+ | 'deepgram'
174
+ | 'openai'
175
+ | 'azure'
176
+ | 'google'
177
+ | 'aws'
178
+ | 'elevenlabs'
179
+ | 'playht'
180
+ | 'cartesia'
181
+ | 'rime'
182
+ | 'kokoro'
183
+ | 'gemini'
184
+ | 'assemblyai';
185
+
186
+ export const TTS_PROVIDERS: Record<TTSProvider, { name: string; supportsSSML: boolean; multilingual?: boolean; isDefault?: boolean }> = {
187
+ deepgram: { name: 'Deepgram Aura', supportsSSML: false, isDefault: true },
188
+ openai: { name: 'OpenAI TTS', supportsSSML: false, multilingual: true },
189
+ azure: { name: 'Azure Neural TTS', supportsSSML: true },
190
+ google: { name: 'Google Cloud TTS', supportsSSML: true },
191
+ aws: { name: 'AWS Polly', supportsSSML: true },
192
+ elevenlabs: { name: 'ElevenLabs', supportsSSML: false, multilingual: true },
193
+ playht: { name: 'PlayHT', supportsSSML: false, multilingual: true },
194
+ cartesia: { name: 'Cartesia Sonic', supportsSSML: false, multilingual: true },
195
+ rime: { name: 'Rime AI', supportsSSML: false, multilingual: true },
196
+ kokoro: { name: 'Kokoro', supportsSSML: false, multilingual: true },
197
+ gemini: { name: 'Google Gemini TTS', supportsSSML: false, multilingual: true },
198
+ assemblyai: { name: 'AssemblyAI', supportsSSML: false },
199
+ };
200
+
201
+ export interface SocketVoiceResponse {
202
+ provider: string;
203
+ language: string;
204
+ voices: { male: VoiceOption[]; female: VoiceOption[] };
205
+ providers?: Record<string, unknown>;
206
+ error?: string;
207
+ }
208
+
209
+ const buildFallbackVoices = (langCode: string, provider: string): { male: VoiceOption[]; female: VoiceOption[] } => {
210
+ const normalizedLanguage = normalizeLanguageCode(langCode === 'auto' ? 'en' : langCode);
211
+ const providerKey = provider.toLowerCase();
212
+ const providerName = TTS_PROVIDERS[providerKey as TTSProvider]?.name || provider;
213
+ const languageName = getLanguageName(normalizedLanguage);
214
+
215
+ return {
216
+ female: [
217
+ {
218
+ id: `${providerKey}-${normalizedLanguage}-female-1`,
219
+ name: `${providerName} ${languageName} Female`,
220
+ gender: 'female',
221
+ provider: providerKey,
222
+ language: normalizedLanguage,
223
+ },
224
+ {
225
+ id: `${providerKey}-${normalizedLanguage}-female-2`,
226
+ name: `${providerName} ${languageName} Female 2`,
227
+ gender: 'female',
228
+ provider: providerKey,
229
+ language: normalizedLanguage,
230
+ },
231
+ ],
232
+ male: [
233
+ {
234
+ id: `${providerKey}-${normalizedLanguage}-male-1`,
235
+ name: `${providerName} ${languageName} Male`,
236
+ gender: 'male',
237
+ provider: providerKey,
238
+ language: normalizedLanguage,
239
+ },
240
+ {
241
+ id: `${providerKey}-${normalizedLanguage}-male-2`,
242
+ name: `${providerName} ${languageName} Male 2`,
243
+ gender: 'male',
244
+ provider: providerKey,
245
+ language: normalizedLanguage,
246
+ },
247
+ ],
248
+ };
249
+ };
250
+
251
+ export const getAvailableVoices = (
252
+ langCode: string,
253
+ provider: TTSProvider | string = 'deepgram'
254
+ ): { male: VoiceOption[]; female: VoiceOption[] } => {
255
+ return buildFallbackVoices(langCode, provider.toString());
256
+ };
257
+
258
+ export const fetchVoicesViaSocket = (
259
+ socket: { emit: (event: string, data: unknown, callback?: (response: SocketVoiceResponse) => void) => void },
260
+ provider: TTSProvider | string = 'deepgram',
261
+ language: string = 'en'
262
+ ): Promise<SocketVoiceResponse> => {
263
+ return new Promise((resolve) => {
264
+ const providerName = provider.toString();
265
+ const fallbackVoices = getAvailableVoices(language, providerName);
266
+
267
+ const timeout = setTimeout(() => {
268
+ resolve({
269
+ provider: providerName,
270
+ language,
271
+ voices: fallbackVoices,
272
+ error: 'Request timed out, using shared fallback voices',
273
+ });
274
+ }, 5000);
275
+
276
+ socket.emit('translation:getVoices', { provider, language }, (response: SocketVoiceResponse) => {
277
+ clearTimeout(timeout);
278
+
279
+ if (response?.voices) {
280
+ resolve(response);
281
+ return;
282
+ }
283
+
284
+ resolve({
285
+ provider: response?.provider || providerName,
286
+ language: response?.language || language,
287
+ voices: fallbackVoices,
288
+ error: response?.error || 'No voices returned, using shared fallback voices',
289
+ });
290
+ });
291
+ });
292
+ };
293
+
294
+ export const fetchLanguagesViaSocket = (
295
+ socket: { emit: (event: string, data: unknown, callback?: (response: { languages?: LanguageOption[] }) => void) => void },
296
+ displayLocale: string = 'en'
297
+ ): Promise<LanguageOption[]> => {
298
+ return new Promise((resolve) => {
299
+ const timeout = setTimeout(() => {
300
+ resolve(getSupportedLanguages(displayLocale));
301
+ }, 5000);
302
+
303
+ socket.emit('translation:getLanguages', { displayLocale }, (response: { languages?: LanguageOption[] }) => {
304
+ clearTimeout(timeout);
305
+ resolve(response?.languages?.length ? response.languages : getSupportedLanguages(displayLocale));
306
+ });
307
+ });
308
+ };
@@ -0,0 +1,44 @@
1
+ // Default fallback
2
+ // Fallback for mediaDevices with support for getUserMedia
3
+ export const mediaDevices = {
4
+ getUserMedia: async (_constraints: MediaStreamConstraints): Promise<MediaStream> => {
5
+ // Fallback logic or mock implementation for getUserMedia
6
+ console.warn('getUserMedia is not implemented in this environment.');
7
+ return new MediaStream(); // Return an empty MediaStream as a fallback
8
+ },
9
+
10
+ getDisplayMedia: async (_constraints: MediaStreamConstraints): Promise<MediaStream> => {
11
+ // Fallback logic or mock implementation for getDisplayMedia
12
+ console.warn('getDisplayMedia is not implemented in this environment.');
13
+ return new MediaStream(); // Return an empty MediaStream as a fallback
14
+ },
15
+
16
+ enumerateDevices: async (): Promise<MediaDeviceInfo[]> => {
17
+ // Fallback logic or mock implementation for enumerateDevices
18
+ console.warn('enumerateDevices is not implemented in this environment.');
19
+ return []; // Return an empty array as a fallback
20
+ },
21
+ };
22
+
23
+ export function registerGlobals() {}
24
+
25
+ export class MediaStream {
26
+ tracks: any[];
27
+
28
+ constructor(tracks: any[] = []) {
29
+ this.tracks = tracks; // Assign tracks to the MediaStream instance
30
+ }
31
+
32
+ // You can add additional methods or properties if needed
33
+ [key: string]: any;
34
+ }
35
+
36
+ export class MediaStreamTrack {
37
+ [key: string]: any;
38
+ }
39
+
40
+ export class RTCView {
41
+ render(): null {
42
+ return null; // Empty RTCView implementation
43
+ }
44
+ }
@@ -1,4 +1,4 @@
1
- import { Socket } from 'socket.io-client';
1
+ import type { Socket } from 'socket.io-client';
2
2
  import Cookies from 'universal-cookie';
3
3
  import type { PreJoinPageParameters } from '../../types/types';
4
4
 
@@ -6,6 +6,15 @@ const cookies = new Cookies();
6
6
  const MAX_ATTEMPTS = 10;
7
7
  const RATE_LIMIT_DURATION = 3 * 60 * 60 * 1000; // 3 hours
8
8
 
9
+ const hasConnectedSocketId = (socket: unknown): socket is Socket & { id: string } => {
10
+ if (!socket || typeof socket !== 'object') {
11
+ return false;
12
+ }
13
+
14
+ const candidate = socket as { id?: unknown };
15
+ return typeof candidate.id === 'string' && candidate.id.length > 0;
16
+ };
17
+
9
18
  /**
10
19
  * Options for handleWelcomeRequest function
11
20
  */
@@ -143,7 +152,7 @@ export async function handleWelcomeRequest({
143
152
 
144
153
  const socket = await Promise.race([socketPromise, timeoutPromise]);
145
154
 
146
- if (socket && socket instanceof Socket && socket.id) {
155
+ if (hasConnectedSocketId(socket)) {
147
156
  unsuccessfulAttempts = 0;
148
157
  cookies.set('unsuccessfulAttempts', unsuccessfulAttempts.toString());
149
158
  cookies.set('lastRequestTimestamp', Date.now().toString());
@@ -1,4 +1,8 @@
1
- export { handleWelcomeRequest, validateAlphanumeric, validateWelcomeInputs } from './handleWelcomeRequest';
1
+ export {
2
+ handleWelcomeRequest,
3
+ validateAlphanumeric as validateWelcomeAlphanumeric,
4
+ validateWelcomeInputs,
5
+ } from './handleWelcomeRequest';
2
6
  export type {
3
7
  HandleWelcomeRequestOptions,
4
8
  ValidateWelcomeInputsOptions,