@reactoo/watchtogether-sdk-js 2.6.99 → 2.7.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.
@@ -169,10 +169,14 @@
169
169
  Instance.room.getSessionByConstructId(constructId).setTalkIntercomChannels(groups)
170
170
  }
171
171
 
172
+ function setRestrictSubscribeToUserIds(userIds) {
173
+ Instance.room.getSessionByConstructId(constructId).setRestrictSubscribeToUserIds(userIds)
174
+ }
175
+
172
176
  let Instance = WatchTogetherSDK({debug:true})({instanceType:'reactooDemo'});
173
177
 
174
- Instance.auth.$on('login', () => {
175
- console.log('We are in');
178
+ Instance.auth.$on('login', (r) => {
179
+ console.log('We are in', r);
176
180
  });
177
181
 
178
182
  Instance.iot.$on('connect', () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reactoo/watchtogether-sdk-js",
3
- "version": "2.6.99",
3
+ "version": "2.7.2",
4
4
  "description": "Javascript SDK for Reactoo",
5
5
  "main": "src/index.js",
6
6
  "unpkg": "dist/watchtogether-sdk.min.js",
@@ -21,7 +21,7 @@ import syncModule from "../modules/sync-modules/sync-module";
21
21
  let roomSession = function ({roomId, pinHash, role, options = {}}, room, wt) {
22
22
 
23
23
  let primaryRoomId = roomId;
24
- let {simulcast = null, simulcastBitrates = null, simulcastMode = null, simulcastDefaultManualSubstream = null} = options;
24
+ let {simulcast = null, simulcastBitrates = null, simulcastMode = null, simulcastDefaultManualSubstream = null, enableDtx = null} = options;
25
25
  let publicCustomEvents = ['changePlayerSource', 'chatMessage', 'userUpdate', 'reconnecting', 'connecting', 'remoteMuted', 'scaling'];
26
26
 
27
27
  const addEvents = (events) => {
@@ -122,6 +122,7 @@ let roomSession = function ({roomId, pinHash, role, options = {}}, room, wt) {
122
122
  simulcastBitrates !== null ? simulcastBitrates : roomData?.data?.simulcast?.bitrates,
123
123
  simulcastMode !== null ? simulcastMode : roomData?.data?.simulcast?.mode,
124
124
  simulcastDefaultManualSubstream !== null ? simulcastDefaultManualSubstream : roomData?.data?.simulcast?.defaultManualSubstream,
125
+ enableDtx !== null ? enableDtx : roomData?.data?.enableDtx,
125
126
  )
126
127
  })
127
128
  .finally(() => {
@@ -339,6 +340,10 @@ let roomSession = function ({roomId, pinHash, role, options = {}}, room, wt) {
339
340
  return room.toggleVideo(value, source);
340
341
  },
341
342
 
343
+ setRestrictSubscribeToUserIds: (userIds = []) => {
344
+ return room.setRestrictSubscribeToUserIds(userIds);
345
+ },
346
+
342
347
  setTalkIntercomChannels: (groups) => {
343
348
  return room.setTalkIntercomChannels(groups);
344
349
  },
@@ -55,14 +55,16 @@ let room = function () {
55
55
  }))
56
56
  },
57
57
 
58
- createRoom: ({title, description, isPublic, isRouter, isStudioLayout, wtChannelId, isHd, disableSync, reduceRoomControls, hasStudioChat, maxParticipants, customAttributes, chatRoomId, linkedRoomId, type, dotAttribute} = {}) => {
58
+ createRoom: ({sourceRoomId, title, description, isPublic, isRouter, isStudioLayout, wtChannelId, isHd, disableSync, reduceRoomControls, hasStudioChat, maxParticipants, customAttributes, chatRoomId, linkedRoomId, type, dotAttribute} = {}) => {
59
59
  let _da = dotAttribute
60
60
  ? (Array.isArray(dotAttribute)
61
61
  ? dotAttribute.reduce((p, cv) => (p[cv.name] = cv.value) && p || p, {})
62
62
  : {[dotAttribute.name]: dotAttribute.value})
63
63
  : {};
64
64
  return this.__privates.auth.__client
65
- .then(client => client.apis.wt.createRoom({}, {
65
+ .then(client => client.apis.wt.createRoom({
66
+ sourceRoomId
67
+ }, {
66
68
  requestBody: {
67
69
  title,
68
70
  description,
@@ -182,6 +182,7 @@ class RoomSession {
182
182
  this.sessiontype = type;
183
183
  this.initialBitrate = 0;
184
184
  this.simulcast = false;
185
+ this.enableDtx = false;
185
186
  this.simulcastMode = 'controlled'; // controlled, manual, browserControlled
186
187
  this.simulcastDefaultManualSubstream = 0; // 0 = maximum quality
187
188
 
@@ -222,11 +223,13 @@ class RoomSession {
222
223
  this._maxRetries = 5;
223
224
  this._keepAliveId = null;
224
225
  this._participants = [];
226
+ this._restrictSubscribeToUserIds = []; // all if empty
225
227
  this._talkIntercomChannels = ['participants'];
226
228
  this._listenIntercomChannels = ['participants'];
227
229
  this._roomType = 'watchparty';
228
230
  this._isDataChannelOpen = false;
229
231
  this._abortController = null;
232
+ this._remoteUsersCache = [];
230
233
 
231
234
  this.userRoleSubscriptionRules = {
232
235
  ...RoomSession.userRoleSubscriptionRules,
@@ -239,12 +242,29 @@ class RoomSession {
239
242
  }
240
243
  }
241
244
 
245
+ _pushToRemoteUsersCache(userId, streams, id) {
246
+ const existingIndex = this._remoteUsersCache.findIndex(u => u.userId === userId);
247
+ if (existingIndex > -1) {
248
+ this._remoteUsersCache.splice(existingIndex, 1, {userId, streams, id})
249
+ } else {
250
+ this._remoteUsersCache.push({userId, streams, id});
251
+ }
252
+ }
253
+
254
+ _removeFromRemoteUsersCache(rfid) {
255
+ const existingIndex = this._remoteUsersCache.findIndex(u => u.id === rfid);
256
+ if (existingIndex > -1) {
257
+ this._remoteUsersCache.splice(existingIndex, 1);
258
+ }
259
+ }
260
+
242
261
  _participantShouldSubscribe(userId) {
243
262
  const myUser = decodeJanusDisplay(this.display);
244
263
  const remoteUser = decodeJanusDisplay(userId);
245
264
  let localUserRole = myUser?.role || 'participant';
246
265
  let remoteUserRole = remoteUser?.role || 'participant';
247
- return this.userRoleSubscriptionRules[localUserRole][(this._roomType || 'watchparty')].indexOf(remoteUserRole) > -1;
266
+ return this.userRoleSubscriptionRules[localUserRole][(this._roomType || 'watchparty')].indexOf(remoteUserRole) > -1
267
+ && (this._restrictSubscribeToUserIds.length === 0 || this._restrictSubscribeToUserIds.indexOf(remoteUser?.userId) > -1);
248
268
  }
249
269
 
250
270
  _intercomSubscribe = (stream) => {
@@ -582,6 +602,7 @@ class RoomSession {
582
602
  streams[i]["display"] = userId;
583
603
  }
584
604
  this._log('Remote userId: ', userId);
605
+ this._pushToRemoteUsersCache(userId, streams, id);
585
606
  if (this._participantShouldSubscribe(userId)) {
586
607
  this._log('Creating user: ', userId);
587
608
  this._createParticipant(userId, id)
@@ -632,6 +653,9 @@ class RoomSession {
632
653
  streams[i]["display"] = userId;
633
654
  }
634
655
  this._log('Remote userId: ', userId);
656
+
657
+ this._pushToRemoteUsersCache(userId, streams, id);
658
+
635
659
  if (this._participantShouldSubscribe(userId)) {
636
660
 
637
661
  let handle = this._getHandle(null, id);
@@ -694,8 +718,9 @@ class RoomSession {
694
718
  this.disconnect().catch(() => {});
695
719
  }
696
720
  } else if (leaving) {
697
- //TODO: in 1 PeerConnection case we only unsubscribe from streams
721
+ //TODO: in 1 PeerConnection case we only unsubscribe from streams, this may not be true, check janus docs
698
722
  this._log('leaving', leaving);
723
+ this._removeFromRemoteUsersCache(leaving);
699
724
  this._removeParticipant(null, leaving, true);
700
725
  }
701
726
 
@@ -705,6 +730,7 @@ class RoomSession {
705
730
  } else if (unpublished) {
706
731
  //TODO: in 1 PeerConnection case we only unsubscribe from streams
707
732
  this._log('unpublished', unpublished);
733
+ this._removeFromRemoteUsersCache(unpublished);
708
734
  this._removeParticipant(null, unpublished, true); // we do hangup and detach
709
735
  }
710
736
 
@@ -712,6 +738,7 @@ class RoomSession {
712
738
  // this case shouldn't exist
713
739
  } else if(kicked) {
714
740
  this._log('kicked', kicked);
741
+ this._removeFromRemoteUsersCache(kicked);
715
742
  this._removeParticipant(null, kicked, true); // we do hangup and detach
716
743
  }
717
744
 
@@ -1068,7 +1095,7 @@ class RoomSession {
1068
1095
  message: 'id non-existent',
1069
1096
  data: [handleId, 'isAlreadySubscribed']
1070
1097
  });
1071
- return;
1098
+ return false;
1072
1099
  }
1073
1100
  return handle.webrtcStuff.subscribeMap.findIndex(t => t.mid === mid && t.feed === feed) > -1
1074
1101
  }
@@ -1264,7 +1291,7 @@ class RoomSession {
1264
1291
  simulcastBitrates = this.simulcastBitrates,
1265
1292
  simulcastMode = this.simulcastMode,
1266
1293
  simulcastDefaultManualSubstream = this.simulcastDefaultManualSubstream,
1267
- simulcastInitialSubstream = null
1294
+ enableDtx = false
1268
1295
  ) {
1269
1296
 
1270
1297
  if (this.isConnecting) {
@@ -1289,6 +1316,7 @@ class RoomSession {
1289
1316
  this.initialBitrate = initialBitrate;
1290
1317
  this.recordingFilename = recordingFilename;
1291
1318
  this.isConnecting = true;
1319
+ this.enableDtx = enableDtx;
1292
1320
  this.simulcast = simulcast;
1293
1321
  this.simulcastMode = simulcastMode;
1294
1322
  this.simulcastDefaultManualSubstream = simulcastDefaultManualSubstream;
@@ -2343,6 +2371,13 @@ class RoomSession {
2343
2371
 
2344
2372
  return config.pc[methodName](mediaConstraints)
2345
2373
  .then( (response) => {
2374
+
2375
+ // if type offer and its me and we want dtx we mungle the sdp
2376
+ if(handleId === this.handleId && type === 'offer' && this.enableDtx) {
2377
+ // enable DTX
2378
+ response.sdp = response.sdp.replace("useinbandfec=1", "useinbandfec=1;usedtx=1")
2379
+ }
2380
+
2346
2381
  config.mySdp = response.sdp;
2347
2382
  let _p = config.pc.setLocalDescription(response)
2348
2383
  .catch((e) => {
@@ -3140,6 +3175,68 @@ class RoomSession {
3140
3175
  return this._roomType;
3141
3176
  }
3142
3177
 
3178
+ setRestrictSubscribeToUserIds(userIds = []) {
3179
+ this._restrictSubscribeToUserIds = structuredClone(userIds);
3180
+ if(!this.isConnected) {
3181
+ return Promise.resolve(this._restrictSubscribeToUserIds);
3182
+ }
3183
+ // sanity check by getting listparticipants and comparing it to _remoteUsersCache
3184
+ this.sendMessage(this.handleId, {body: {request: 'listparticipants', room: this.roomId}})
3185
+ .then(r => {
3186
+ let participants = r.participants;
3187
+ let remoteUsersCache = this._remoteUsersCache;
3188
+ let handle = this._getHandle(this.handleId);
3189
+
3190
+ // filter out my user id from response and compare it to remoteUsersCache
3191
+ participants = participants.filter(p => p.id !== handle.userId);
3192
+
3193
+ // get rid of participants that are in participants but not in remoteUsersCache
3194
+ remoteUsersCache = remoteUsersCache.filter(r => participants.find(p => p.id === r.id));
3195
+ remoteUsersCache.forEach(r => {
3196
+ const handle = this._getHandle(null, null, decodeJanusDisplay(r.userId)?.userId);
3197
+
3198
+ if(this._participantShouldSubscribe(r.userId)) {
3199
+ // todo: do a nicer flag to indicate inactive handle than just checking for pc
3200
+ if(!handle || !handle.webrtcStuff?.pc) {
3201
+ this._log('Subscribing to ', r.userId)
3202
+ this._createParticipant(r.userId, r.id)
3203
+ .then(handle => {
3204
+ this._updateParticipantsTrackData(handle.handleId, r.streams);
3205
+ const subscribe = r.streams.filter(s => !s.disabled && this._intercomSubscribe(s)).map(stream => ({feed: stream.id, mid: stream.mid}));
3206
+ handle.webrtcStuff.subscribeMap = structuredClone(subscribe);
3207
+ return this.sendMessage(handle.handleId, {
3208
+ body: {
3209
+ "request": "join",
3210
+ "room": this.roomId,
3211
+ "ptype": "subscriber",
3212
+ "private_id": this.privateId,
3213
+ streams: subscribe,
3214
+ //"feed": id,
3215
+ ...(this.webrtcVersion > 1000 ? {id: this.userId} : {}),
3216
+ pin: this.pin
3217
+ }
3218
+ })
3219
+ })
3220
+ .catch(err => {
3221
+ this.emit('error', err);
3222
+ })
3223
+
3224
+ }
3225
+
3226
+ else {
3227
+ this._log('Already subscribed to ', r.userId);
3228
+ }
3229
+
3230
+ } else if(handle) {
3231
+ this._log('Unsubscribing from ', r.userId)
3232
+ this._removeParticipant(handle.handleId);
3233
+ }
3234
+ })
3235
+ });
3236
+
3237
+
3238
+ }
3239
+
3143
3240
 
3144
3241
  }
3145
3242