mediasfu-shared 1.2.0 → 1.2.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 (31) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +145 -131
  3. package/dist/consumers/connectIps.d.ts.map +1 -1
  4. package/dist/consumers/index.cjs +1 -1
  5. package/dist/consumers/index.js +1 -1
  6. package/dist/{getParticipantMedia-CdcYqcbD.cjs → getParticipantMedia-BH3d_-Q1.cjs} +4 -4
  7. package/dist/getParticipantMedia-BH3d_-Q1.cjs.map +1 -0
  8. package/dist/{getParticipantMedia-B0JS6d2b.js → getParticipantMedia-CGqnSb93.js} +4 -4
  9. package/dist/getParticipantMedia-CGqnSb93.js.map +1 -0
  10. package/dist/index.cjs +3 -3
  11. package/dist/index.js +4 -4
  12. package/dist/index.native.cjs +3 -3
  13. package/dist/index.native.js +3 -3
  14. package/dist/{joinLocalRoom-CvteNe4R.js → joinLocalRoom-7ibqv7zZ.js} +2 -2
  15. package/dist/{joinLocalRoom-CvteNe4R.js.map → joinLocalRoom-7ibqv7zZ.js.map} +1 -1
  16. package/dist/{joinLocalRoom-BdM1msQt.cjs → joinLocalRoom-BJBDvTYh.cjs} +2 -2
  17. package/dist/{joinLocalRoom-BdM1msQt.cjs.map → joinLocalRoom-BJBDvTYh.cjs.map} +1 -1
  18. package/dist/methods/recording/startRecording.d.ts.map +1 -1
  19. package/dist/types/index.cjs +2 -2
  20. package/dist/types/index.js +2 -2
  21. package/dist/{updateParticipantAudioDecibels-DLd8Y-Fh.js → updateParticipantAudioDecibels-C_MygF6q.js} +45 -5
  22. package/dist/updateParticipantAudioDecibels-C_MygF6q.js.map +1 -0
  23. package/dist/{updateParticipantAudioDecibels-DPmHiy3K.cjs → updateParticipantAudioDecibels-bZ6R8WHj.cjs} +45 -5
  24. package/dist/updateParticipantAudioDecibels-bZ6R8WHj.cjs.map +1 -0
  25. package/package.json +2 -1
  26. package/src/consumers/connectIps.ts +68 -8
  27. package/src/methods/recording/startRecording.ts +7 -1
  28. package/dist/getParticipantMedia-B0JS6d2b.js.map +0 -1
  29. package/dist/getParticipantMedia-CdcYqcbD.cjs.map +0 -1
  30. package/dist/updateParticipantAudioDecibels-DLd8Y-Fh.js.map +0 -1
  31. package/dist/updateParticipantAudioDecibels-DPmHiy3K.cjs.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mediasfu-shared",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "mediasfu-shared – framework-agnostic WebRTC runtime for MediaSFU. Room helpers, mediasoup signaling, socket management, media state, and TypeScript types for React, Vue, Angular, Svelte, and plain TS.",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",
@@ -37,6 +37,7 @@
37
37
  "src",
38
38
  "README.md",
39
39
  "HEADLESS_GUIDE.md",
40
+ "CHANGELOG.md",
40
41
  "LICENSE"
41
42
  ],
42
43
  "scripts": {
@@ -36,6 +36,63 @@ export interface ConnectIpsOptions {
36
36
  // Export the type definition for the function
37
37
  export type ConnectIpsType = (options: ConnectIpsOptions) => Promise<[Record<string, any>[], string[]]>;
38
38
 
39
+ // Keep the reservation local to one room engine's socket collection. Separate
40
+ // rooms may legitimately consume from the same endpoint and must not share a
41
+ // socket, while overlapping updates for one room must serialize that endpoint.
42
+ const pendingConsumeConnections = new WeakMap<
43
+ object,
44
+ Map<string, Promise<boolean>>
45
+ >();
46
+
47
+ const normalizeConsumeEndpoint = (ip: string): string =>
48
+ ip.trim().toLowerCase().replace(/\.$/, "");
49
+
50
+ const hasConsumeEndpoint = (
51
+ consumeSockets: ConsumeSocket[],
52
+ endpoint: string
53
+ ): boolean => consumeSockets.some((socketObj) => {
54
+ const ip = Object.keys(socketObj)[0];
55
+ return Boolean(ip) && normalizeConsumeEndpoint(ip) === endpoint;
56
+ });
57
+
58
+ async function reserveConsumeEndpoint(
59
+ consumeSockets: ConsumeSocket[],
60
+ ip: string,
61
+ reservationOwner: object = consumeSockets
62
+ ): Promise<((connected?: boolean) => void) | null> {
63
+ const endpoint = normalizeConsumeEndpoint(ip);
64
+ if (!endpoint || endpoint === "none") return null;
65
+
66
+ let pendingForSockets = pendingConsumeConnections.get(reservationOwner);
67
+ if (!pendingForSockets) {
68
+ pendingForSockets = new Map<string, Promise<boolean>>();
69
+ pendingConsumeConnections.set(reservationOwner, pendingForSockets);
70
+ }
71
+
72
+ while (true) {
73
+ if (hasConsumeEndpoint(consumeSockets, endpoint)) return null;
74
+
75
+ const pending = pendingForSockets.get(endpoint);
76
+ if (pending) {
77
+ if (await pending) return null;
78
+ continue;
79
+ }
80
+
81
+ let resolvePending!: (connected: boolean) => void;
82
+ const reservation = new Promise<boolean>((resolve) => {
83
+ resolvePending = resolve;
84
+ });
85
+ pendingForSockets.set(endpoint, reservation);
86
+
87
+ return (didConnect = false) => {
88
+ if (pendingForSockets?.get(endpoint) === reservation) {
89
+ pendingForSockets.delete(endpoint);
90
+ }
91
+ resolvePending(didConnect === true);
92
+ };
93
+ }
94
+ }
95
+
39
96
  /**
40
97
  * Connects to remote IPs and manages socket connections.
41
98
  *
@@ -105,15 +162,15 @@ export const connectIps = async ({
105
162
  }
106
163
 
107
164
  for (const ip of remIP) {
108
- try {
109
- // Check if the IP is already connected
110
- const matching = consume_sockets.find((socketObj) => Object.keys(socketObj)[0] === ip);
111
-
112
- if (matching || !ip) {
113
- // Skip if the IP is already connected or invalid
114
- continue;
115
- }
165
+ const releaseReservation = await reserveConsumeEndpoint(
166
+ consume_sockets,
167
+ ip,
168
+ parameters?.socket || consume_sockets
169
+ );
170
+ if (!releaseReservation) continue;
116
171
 
172
+ let connected = false;
173
+ try {
117
174
  // Connect to the remote socket using socket.io-client
118
175
  const remote_sock = await connectSocket({ apiUserName, apiKey, apiToken, link: `https://${ip}.mediasfu.com` });
119
176
 
@@ -161,10 +218,13 @@ export const connectIps = async ({
161
218
  // Add the remote socket to the consume_sockets array
162
219
  consume_sockets.push({ [ip]: remote_sock });
163
220
  updateConsume_sockets(consume_sockets);
221
+ connected = true;
164
222
  }
165
223
  } catch (error) {
166
224
  // Handle the error
167
225
  console.log("connectIps error", error);
226
+ } finally {
227
+ releaseReservation(connected);
168
228
  }
169
229
  }
170
230
 
@@ -222,7 +222,13 @@ export const startRecording: StartRecordingType = async ({
222
222
  updateRecordPaused(recordPaused)
223
223
 
224
224
  if (action === 'startRecord') {
225
- await rePort({ parameters: parameters.getUpdatedAllParams() })
225
+ // restart: force an immediate updateScreenClient. Without it,
226
+ // rePort only emits when activeNames or screenStates differ from
227
+ // their previous values — and nothing has changed at the moment a
228
+ // recording starts, so the recorder got no layout until something
229
+ // later did (a speaker change, a join, a share). Resume already
230
+ // forced it; start now matches.
231
+ await rePort({ restart: true, parameters: parameters.getUpdatedAllParams() })
226
232
  await recordStartTimer({ parameters })
227
233
  } else {
228
234
  updateRecordResumed(true)