mediasfu-shared 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +163 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +163 -46
- package/dist/index.js.map +1 -1
- package/package.json +15 -4
- package/src/consumers/connectIps.ts +3 -1
- package/src/consumers/connectLocalIps.ts +3 -1
- package/src/consumers/connectRecvTransport.ts +75 -21
- package/src/consumers/processConsumerTransportsAudio.ts +94 -2
- package/src/consumers/translationConsumerSwitch.ts +38 -16
- package/src/methods/requests/hostRequestResponse.ts +12 -7
- package/src/methods/socketReceive/translationReceiveMethods.ts +2 -2
- package/src/methods/stream/clickVideo.ts +6 -1
- package/src/methods/utils/resolveMediaSFURoomApi.ts +13 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mediasfu-shared",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Framework-agnostic MediaSFU runtime for room creation
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "Framework-agnostic MediaSFU WebRTC runtime for room creation, joining, mediasoup signaling, sockets, media state, translation, and reusable SDK types",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"module": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -44,16 +44,27 @@
|
|
|
44
44
|
"keywords": [
|
|
45
45
|
"mediasfu",
|
|
46
46
|
"webrtc",
|
|
47
|
+
"webRTC",
|
|
47
48
|
"mediasoup",
|
|
48
|
-
"
|
|
49
|
+
"sfu",
|
|
49
50
|
"video-conferencing",
|
|
51
|
+
"video-call",
|
|
52
|
+
"audio-call",
|
|
53
|
+
"socket.io",
|
|
50
54
|
"real-time-communication",
|
|
55
|
+
"real-time-media",
|
|
51
56
|
"room-management",
|
|
52
57
|
"screen-sharing",
|
|
53
58
|
"recording",
|
|
59
|
+
"live-streaming",
|
|
60
|
+
"webinar",
|
|
61
|
+
"broadcasting",
|
|
62
|
+
"whiteboard",
|
|
63
|
+
"live-subtitles",
|
|
54
64
|
"translation",
|
|
55
65
|
"framework-agnostic",
|
|
56
|
-
"typescript"
|
|
66
|
+
"typescript",
|
|
67
|
+
"mediasfu-shared"
|
|
57
68
|
],
|
|
58
69
|
"author": "MediaSFU",
|
|
59
70
|
"license": "MIT",
|
|
@@ -125,13 +125,15 @@ export const connectIps = async ({
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
// Handle new pipe producer event
|
|
128
|
-
remote_sock.on("new-pipe-producer", async ({ producerId, islevel }: { producerId: string; islevel: string }) => {
|
|
128
|
+
remote_sock.on("new-pipe-producer", async ({ producerId, islevel, isTranslation, translationMeta }: { producerId: string; islevel: string; isTranslation?: boolean; translationMeta?: { speakerId: string; speakerName: string; language: string; originalProducerId?: string; isSpeakerControlled?: boolean } }) => {
|
|
129
129
|
if (newProducerMethod) {
|
|
130
130
|
await newProducerMethod({
|
|
131
131
|
producerId,
|
|
132
132
|
islevel,
|
|
133
133
|
nsock: remote_sock,
|
|
134
134
|
parameters,
|
|
135
|
+
isTranslation,
|
|
136
|
+
translationMeta,
|
|
135
137
|
});
|
|
136
138
|
}
|
|
137
139
|
});
|
|
@@ -76,13 +76,15 @@ export const connectLocalIps = async ({
|
|
|
76
76
|
|
|
77
77
|
// Connect to the remote socket using socket.io-client
|
|
78
78
|
// Handle new pipe producer event
|
|
79
|
-
socket.on("new-producer", async ({ producerId, islevel }: { producerId: string; islevel: string }) => {
|
|
79
|
+
socket.on("new-producer", async ({ producerId, islevel, isTranslation, translationMeta }: { producerId: string; islevel: string; isTranslation?: boolean; translationMeta?: { speakerId: string; speakerName: string; language: string; originalProducerId?: string } }) => {
|
|
80
80
|
if (newProducerMethod) {
|
|
81
81
|
await newProducerMethod({
|
|
82
82
|
producerId,
|
|
83
83
|
islevel,
|
|
84
84
|
nsock: socket,
|
|
85
85
|
parameters,
|
|
86
|
+
isTranslation,
|
|
87
|
+
translationMeta,
|
|
86
88
|
});
|
|
87
89
|
}
|
|
88
90
|
});
|
|
@@ -11,6 +11,74 @@ interface SpeakerTranslationState {
|
|
|
11
11
|
enabled: boolean;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
const getSpeakerNameForProducerId = (
|
|
15
|
+
producerId: string,
|
|
16
|
+
parameters: Record<string, any>,
|
|
17
|
+
): string | undefined => {
|
|
18
|
+
const participants = parameters.participants as Participant[] | undefined;
|
|
19
|
+
const participant = participants?.find((candidate) => candidate.audioID === producerId);
|
|
20
|
+
|
|
21
|
+
if (participant?.name) {
|
|
22
|
+
return participant.name;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const audStreamName = (parameters.audStreamNames as Array<{ producerId?: string; name?: string }> | undefined)
|
|
26
|
+
?.find((stream) => stream.producerId === producerId && typeof stream.name === 'string');
|
|
27
|
+
if (audStreamName?.name) {
|
|
28
|
+
return audStreamName.name;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (parameters.allAudioStreams as Array<{ producerId?: string; name?: string }> | undefined)
|
|
32
|
+
?.find((stream) => stream.producerId === producerId && typeof stream.name === 'string')
|
|
33
|
+
?.name;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const isOriginalAudioSuppressedByTranslation = (
|
|
37
|
+
producerId: string,
|
|
38
|
+
parameters: Record<string, any>,
|
|
39
|
+
): boolean => {
|
|
40
|
+
const activeTranslationProducerIds = parameters.activeTranslationProducerIds as Set<string> | undefined;
|
|
41
|
+
if (activeTranslationProducerIds?.has(producerId)) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const speakerTranslationStates = parameters.speakerTranslationStates as
|
|
46
|
+
| Map<string, SpeakerTranslationState>
|
|
47
|
+
| undefined;
|
|
48
|
+
|
|
49
|
+
if (!speakerTranslationStates?.size) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const speakerName = getSpeakerNameForProducerId(producerId, parameters);
|
|
54
|
+
|
|
55
|
+
if (speakerName) {
|
|
56
|
+
const speakerState = speakerTranslationStates.get(speakerName);
|
|
57
|
+
if (speakerState?.enabled) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return Array.from(speakerTranslationStates.values()).some((speakerState) => {
|
|
63
|
+
if (!speakerState?.enabled) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (speakerState.originalProducerId === producerId) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!speakerName) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
speakerState.speakerId === speakerName ||
|
|
77
|
+
speakerState.speakerName === speakerName
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
|
|
14
82
|
interface Params {
|
|
15
83
|
id: string;
|
|
16
84
|
producerId: string;
|
|
@@ -152,28 +220,14 @@ export const connectRecvTransport = async ({
|
|
|
152
220
|
if (params.kind === 'audio') {
|
|
153
221
|
try {
|
|
154
222
|
const updatedParams = parameters.getUpdatedAllParams();
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const participant = participants.find(
|
|
162
|
-
(candidate) => candidate.audioID === remoteProducerId,
|
|
223
|
+
if (isOriginalAudioSuppressedByTranslation(remoteProducerId, updatedParams)) {
|
|
224
|
+
consumer.pause();
|
|
225
|
+
nsock.emit(
|
|
226
|
+
'consumer-pause',
|
|
227
|
+
{ serverConsumerId: params.serverConsumerId },
|
|
228
|
+
() => {},
|
|
163
229
|
);
|
|
164
|
-
|
|
165
|
-
if (participant?.name) {
|
|
166
|
-
const speakerState = speakerTranslationStates.get(participant.name);
|
|
167
|
-
|
|
168
|
-
if (speakerState?.enabled && speakerState.originalProducerId === remoteProducerId) {
|
|
169
|
-
consumer.pause();
|
|
170
|
-
nsock.emit(
|
|
171
|
-
'consumer-pause',
|
|
172
|
-
{ serverConsumerId: params.serverConsumerId },
|
|
173
|
-
() => {},
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
230
|
+
return;
|
|
177
231
|
}
|
|
178
232
|
} catch {
|
|
179
233
|
}
|
|
@@ -4,6 +4,18 @@ interface ProducerIdCarrier {
|
|
|
4
4
|
producerId?: string | null;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
interface SpeakerTranslationStateLike {
|
|
8
|
+
enabled?: boolean;
|
|
9
|
+
speakerId?: string;
|
|
10
|
+
speakerName?: string;
|
|
11
|
+
originalProducerId?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface ParticipantLike {
|
|
15
|
+
name?: string | null;
|
|
16
|
+
audioID?: string | null;
|
|
17
|
+
}
|
|
18
|
+
|
|
7
19
|
const getProducerId = (value: unknown): string | null | undefined => {
|
|
8
20
|
return (value as ProducerIdCarrier | null | undefined)?.producerId;
|
|
9
21
|
};
|
|
@@ -30,6 +42,78 @@ interface TransportLike {
|
|
|
30
42
|
serverConsumerTransportId: string;
|
|
31
43
|
}
|
|
32
44
|
|
|
45
|
+
const getSpeakerNameForProducerId = (
|
|
46
|
+
producerId: string,
|
|
47
|
+
parameters: Record<string, any>,
|
|
48
|
+
): string | undefined => {
|
|
49
|
+
const participants = parameters.participants as ParticipantLike[] | undefined;
|
|
50
|
+
const participant = participants?.find((candidate) => candidate.audioID === producerId);
|
|
51
|
+
|
|
52
|
+
if (participant?.name) {
|
|
53
|
+
return participant.name;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const audStreamName = (parameters.audStreamNames as Array<{ producerId?: string; name?: string }> | undefined)
|
|
57
|
+
?.find((stream) => stream.producerId === producerId && typeof stream.name === 'string');
|
|
58
|
+
if (audStreamName?.name) {
|
|
59
|
+
return audStreamName.name;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return (parameters.allAudioStreams as Array<{ producerId?: string; name?: string }> | undefined)
|
|
63
|
+
?.find((stream) => stream.producerId === producerId && typeof stream.name === 'string')
|
|
64
|
+
?.name;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const isOriginalAudioSuppressedByTranslation = (
|
|
68
|
+
producerId: string | null | undefined,
|
|
69
|
+
parameters: Record<string, any>,
|
|
70
|
+
): boolean => {
|
|
71
|
+
if (!producerId) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const activeTranslationProducerIds = parameters.activeTranslationProducerIds as Set<string> | undefined;
|
|
76
|
+
if (activeTranslationProducerIds?.has(producerId)) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const speakerTranslationStates = parameters.speakerTranslationStates as
|
|
81
|
+
| Map<string, SpeakerTranslationStateLike>
|
|
82
|
+
| undefined;
|
|
83
|
+
|
|
84
|
+
if (!speakerTranslationStates?.size) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const speakerName = getSpeakerNameForProducerId(producerId, parameters);
|
|
89
|
+
|
|
90
|
+
if (speakerName) {
|
|
91
|
+
const speakerState = speakerTranslationStates.get(speakerName);
|
|
92
|
+
if (speakerState?.enabled) {
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return Array.from(speakerTranslationStates.values()).some((speakerState) => {
|
|
98
|
+
if (!speakerState?.enabled) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (speakerState.originalProducerId === producerId) {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!speakerName) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
speakerState.speakerId === speakerName ||
|
|
112
|
+
speakerState.speakerName === speakerName
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
|
|
33
117
|
export interface ProcessConsumerTransportsAudioParameters {
|
|
34
118
|
|
|
35
119
|
// mediasfu functions
|
|
@@ -106,6 +190,7 @@ export const processConsumerTransportsAudio = async <
|
|
|
106
190
|
const consumerTransportsToResume = consumerTransports.filter(
|
|
107
191
|
(transport) =>
|
|
108
192
|
isValidProducerId(transport.producerId, lStreams) &&
|
|
193
|
+
!isOriginalAudioSuppressedByTranslation(transport.producerId, parameters) &&
|
|
109
194
|
transport.consumer?.paused === true &&
|
|
110
195
|
transport.consumer?.kind === "audio"
|
|
111
196
|
);
|
|
@@ -116,8 +201,11 @@ export const processConsumerTransportsAudio = async <
|
|
|
116
201
|
transport.producerId &&
|
|
117
202
|
transport.producerId !== null &&
|
|
118
203
|
transport.producerId !== "" &&
|
|
119
|
-
|
|
120
|
-
(
|
|
204
|
+
(
|
|
205
|
+
isOriginalAudioSuppressedByTranslation(transport.producerId, parameters) ||
|
|
206
|
+
!lStreams.some(
|
|
207
|
+
(stream) => getProducerId(stream) === transport.producerId
|
|
208
|
+
)
|
|
121
209
|
) &&
|
|
122
210
|
transport.consumer &&
|
|
123
211
|
transport.consumer?.kind &&
|
|
@@ -141,6 +229,10 @@ export const processConsumerTransportsAudio = async <
|
|
|
141
229
|
|
|
142
230
|
// Emit consumer.resume() for each transport to resume
|
|
143
231
|
for (const transport of consumerTransportsToResume) {
|
|
232
|
+
if (isOriginalAudioSuppressedByTranslation(transport.producerId, parameters)) {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
|
|
144
236
|
transport.socket_.emit(
|
|
145
237
|
"consumer-resume",
|
|
146
238
|
{ serverConsumerId: transport.serverConsumerTransportId },
|
|
@@ -107,15 +107,25 @@ export const pauseOriginalProducer = async ({
|
|
|
107
107
|
(t) => t.producerId === originalProducerId && t.consumer?.kind === 'audio'
|
|
108
108
|
);
|
|
109
109
|
|
|
110
|
-
if (
|
|
111
|
-
|
|
110
|
+
if (!transport?.consumer) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
112
113
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
{ serverConsumerId: transport.serverConsumerTransportId },
|
|
116
|
-
async () => {}
|
|
117
|
-
);
|
|
114
|
+
if (transport.consumer.track) {
|
|
115
|
+
transport.consumer.track.enabled = false;
|
|
118
116
|
}
|
|
117
|
+
|
|
118
|
+
if (transport.consumer.paused) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
transport.consumer.pause();
|
|
123
|
+
|
|
124
|
+
transport.socket_?.emit(
|
|
125
|
+
'consumer-pause',
|
|
126
|
+
{ serverConsumerId: transport.serverConsumerTransportId },
|
|
127
|
+
async () => {}
|
|
128
|
+
);
|
|
119
129
|
} catch (error) {
|
|
120
130
|
console.error('[TranslationSwitch] Error pausing original producer:', error);
|
|
121
131
|
}
|
|
@@ -137,17 +147,29 @@ export const resumeOriginalProducer = async ({
|
|
|
137
147
|
(t) => t.producerId === originalProducerId && t.consumer?.kind === 'audio'
|
|
138
148
|
);
|
|
139
149
|
|
|
140
|
-
if (transport
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
150
|
+
if (!transport?.consumer) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (!transport.consumer.paused) {
|
|
155
|
+
if (transport.consumer.track) {
|
|
156
|
+
transport.consumer.track.enabled = true;
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
transport.socket_?.emit(
|
|
162
|
+
'consumer-resume',
|
|
163
|
+
{ serverConsumerId: transport.serverConsumerTransportId },
|
|
164
|
+
async ({ resumed }: { resumed: boolean }) => {
|
|
165
|
+
if (resumed) {
|
|
166
|
+
if (transport.consumer.track) {
|
|
167
|
+
transport.consumer.track.enabled = true;
|
|
147
168
|
}
|
|
169
|
+
transport.consumer.resume();
|
|
148
170
|
}
|
|
149
|
-
|
|
150
|
-
|
|
171
|
+
}
|
|
172
|
+
);
|
|
151
173
|
} catch (error) {
|
|
152
174
|
console.error('[TranslationSwitch] Error resuming original producer:', error);
|
|
153
175
|
}
|
|
@@ -51,17 +51,22 @@ export const hostRequestResponse = async ({
|
|
|
51
51
|
updateChatRequestTime,
|
|
52
52
|
updateRequestIntervalSeconds,
|
|
53
53
|
}: HostRequestResponseOptions): Promise<void> => {
|
|
54
|
+
const requestType = requestResponse.type ?? requestResponse.icon;
|
|
55
|
+
|
|
56
|
+
// Remove only the specific request that received a host response.
|
|
54
57
|
const filteredRequests = requestList.filter(
|
|
55
|
-
(request) =>
|
|
56
|
-
request.id
|
|
57
|
-
request.icon
|
|
58
|
-
request.name
|
|
59
|
-
|
|
58
|
+
(request) => {
|
|
59
|
+
const matchesId = request.id === requestResponse.id;
|
|
60
|
+
const matchesType = requestType == null || request.icon === requestType;
|
|
61
|
+
const matchesName = requestResponse.name == null || request.name === requestResponse.name;
|
|
62
|
+
const matchesUsername =
|
|
63
|
+
requestResponse.username == null || request.username === requestResponse.username;
|
|
64
|
+
|
|
65
|
+
return !(matchesId && matchesType && matchesName && matchesUsername);
|
|
66
|
+
}
|
|
60
67
|
);
|
|
61
68
|
updateRequestList(filteredRequests);
|
|
62
69
|
|
|
63
|
-
const requestType = requestResponse.type;
|
|
64
|
-
|
|
65
70
|
if (requestResponse.action === 'accepted') {
|
|
66
71
|
switch (requestType) {
|
|
67
72
|
case 'fa-microphone':
|
|
@@ -164,7 +164,7 @@ export interface TranslationSubscribedOptions {
|
|
|
164
164
|
data: TranslationSubscribedData;
|
|
165
165
|
updateListenPreferences?: (updater: (prev: Map<string, string>) => Map<string, string>) => void;
|
|
166
166
|
updateTranslationProducerMap?: (updater: (prev: TranslationProducerMap) => TranslationProducerMap) => void;
|
|
167
|
-
startConsumingTranslation?: (producerId: string, speakerId: string, language: string) => Promise<void>;
|
|
167
|
+
startConsumingTranslation?: (producerId: string, speakerId: string, language: string, originalProducerId?: string) => Promise<void>;
|
|
168
168
|
showAlert?: ShowAlert;
|
|
169
169
|
}
|
|
170
170
|
|
|
@@ -323,7 +323,7 @@ export const translationSubscribed: TranslationSubscribedType = async ({
|
|
|
323
323
|
}
|
|
324
324
|
|
|
325
325
|
if (producerId && startConsumingTranslation) {
|
|
326
|
-
await startConsumingTranslation(producerId, speakerId, language);
|
|
326
|
+
await startConsumingTranslation(producerId, speakerId, language, originalProducerId);
|
|
327
327
|
}
|
|
328
328
|
|
|
329
329
|
if (showAlert && channelCreated) {
|
|
@@ -208,7 +208,12 @@ export const clickVideo = async ({ parameters }: ClickVideoOptions): Promise<voi
|
|
|
208
208
|
if (!videoAction && islevel !== "2" && !youAreCoHost) {
|
|
209
209
|
response = await checkPermission({
|
|
210
210
|
permissionType: "videoSetting",
|
|
211
|
-
audioSetting,
|
|
211
|
+
audioSetting,
|
|
212
|
+
videoSetting,
|
|
213
|
+
screenshareSetting,
|
|
214
|
+
chatSetting,
|
|
215
|
+
permissionConfig: parameters.permissionConfig,
|
|
216
|
+
participantLevel: islevel,
|
|
212
217
|
});
|
|
213
218
|
} else {
|
|
214
219
|
response = 0;
|
|
@@ -2,15 +2,27 @@ const DEFAULT_MEDIA_SFU_ROOM_API_URL = 'https://mediasfu.com/v1/rooms/';
|
|
|
2
2
|
|
|
3
3
|
type MediaSFURoomApiAction = 'createRoom' | 'joinRoom';
|
|
4
4
|
|
|
5
|
+
const normalizeManagedRoomApi = (normalizedLink: string): string => {
|
|
6
|
+
if (normalizedLink.includes('/v1/rooms')) {
|
|
7
|
+
return `${normalizedLink.replace(/\/$/, '')}/`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return `${normalizedLink.replace(/\/$/, '')}/v1/rooms/`;
|
|
11
|
+
};
|
|
12
|
+
|
|
5
13
|
export const resolveMediaSFURoomApi = (
|
|
6
14
|
localLink: string | undefined,
|
|
7
15
|
action: MediaSFURoomApiAction,
|
|
8
16
|
): string => {
|
|
9
17
|
const normalizedLink = localLink?.trim();
|
|
10
18
|
|
|
11
|
-
if (!normalizedLink
|
|
19
|
+
if (!normalizedLink) {
|
|
12
20
|
return DEFAULT_MEDIA_SFU_ROOM_API_URL;
|
|
13
21
|
}
|
|
14
22
|
|
|
23
|
+
if (normalizedLink.includes('mediasfu.com')) {
|
|
24
|
+
return normalizeManagedRoomApi(normalizedLink);
|
|
25
|
+
}
|
|
26
|
+
|
|
15
27
|
return `${normalizedLink.replace(/\/$/, '')}/${action}`;
|
|
16
28
|
};
|