@reactoo/watchtogether-sdk-js 2.6.50 → 2.6.60

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.
@@ -189,7 +189,7 @@
189
189
  participantData.session = session;
190
190
  return Promise.all([session, session.connect()])
191
191
  })
192
- .then(([session, _]) => session.publishLocal(participantData.canvas.captureStream(), {getStreamIfEmpty: false}))
192
+ .then(([session, _]) => session.publishLocal(participantData.canvas.captureStream(), 'camera0'))
193
193
  .then(() => document.getElementById('joined-participants-count').value = ++joinedParticipantsCount);
194
194
  }
195
195
 
@@ -431,7 +431,7 @@
431
431
  .then(audioStream => {
432
432
  participant.stream = participant.canvas.captureStream();
433
433
  participant.stream.addTrack(audioStream.getAudioTracks()[0]);
434
- return session.publishLocal(participant.stream, {getStreamIfEmpty: false});
434
+ return session.publishLocal(participant.stream, 'camera0');
435
435
  });
436
436
  });
437
437
  });
@@ -62,7 +62,7 @@
62
62
 
63
63
  function createParticipant(data, isRemote = false) {
64
64
 
65
- console.log('participantData', isRemote, data);
65
+ console.log('creating participant - participantData', isRemote, data);
66
66
 
67
67
  this.removeParticipant({id: data.id})
68
68
 
@@ -126,7 +126,7 @@
126
126
 
127
127
  function publishBlank() {
128
128
  let sess = Instance.room.getSessionByConstructId(constructId);
129
- sess.publishLocal(null)
129
+ sess.publishLocal(null, 'screen')
130
130
  }
131
131
 
132
132
  function publish(vDeviceId) {
@@ -149,6 +149,14 @@
149
149
  Instance.room.getSessionByConstructId(constructId).toggleAudio()
150
150
  }
151
151
 
152
+ function setListenIntercomGroups(groups) {
153
+ Instance.room.getSessionByConstructId(constructId).setListenIntercomGroups(groups)
154
+ }
155
+
156
+ function setTalkIntercomGroups(groups) {
157
+ Instance.room.getSessionByConstructId(constructId).setTalkIntercomGroups(groups)
158
+ }
159
+
152
160
  let Instance = WatchTogetherSDK({debug:true, apiUrl: 'https://api.reactoo.com/stefan/swagger.json'})({instanceType:'reactooDemo'});
153
161
 
154
162
  Instance.auth.$on('login', () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reactoo/watchtogether-sdk-js",
3
- "version": "2.6.50",
3
+ "version": "2.6.60",
4
4
  "description": "Javascript SDK for Reactoo",
5
5
  "main": "src/index.js",
6
6
  "unpkg": "dist/watchtogether-sdk.min.js",
@@ -85,10 +85,6 @@ class RoomSession {
85
85
  return randomString;
86
86
  }
87
87
 
88
- //TODO: solve
89
- // #eventList = ['error', 'kicked', 'addLocalParticipant', ,'addRemoteInstructor','addRemoteParticipant','addRemoteTalkback', 'addRemoteObserver', 'removeRemoteInstructor', 'removeLocalParticipant', 'removeRemoteParticipant', 'removeRemoteTalkback', 'removeRemoteObserver', 'localMuted', 'localHasVideo', 'localHasAudio', 'data', 'iceState', 'connectionState', 'joined', 'joining', 'dataChannel', 'disconnect', 'observerIds', 'talkbackIds', 'instructorId', 'published', 'publishing', 'remoteTrackMuted', 'streamingStatus', 'streaming', 'streamStarting'];
90
- //
91
-
92
88
  static sessionTypes = {
93
89
  'reactooroom': 'janus.plugin.reactooroom',
94
90
  'streaming': 'janus.plugin.streaming'
@@ -198,19 +194,19 @@ class RoomSession {
198
194
  this.isPublished = false;
199
195
  this.isReclaiming = false;
200
196
  this.isStreaming = false;
201
-
197
+ this.isMuted = [];
198
+ this.isVideoEnabled = false;
199
+ this.isAudioEnabed = false;
200
+ this._sendMessageTimout = 5000;
202
201
  this._retries = 0;
203
202
  this._maxRetries = 5;
204
203
  this._keepAliveId = null;
205
204
  this._participants = [];
206
- this._talkIntercomGroups = ['all'];
207
- this._listenIntercomGroups = ['all'];
205
+ this._talkIntercomGroups = ['participants'];
206
+ this._listenIntercomGroups = ['participants'];
208
207
  this._roomType = 'watchparty';
209
208
  this._isDataChannelOpen = false;
210
209
  this._abortController = null;
211
- this.isMuted = [];
212
- this.isVideoEnabled = false;
213
- this.isAudioEnabed = false;
214
210
 
215
211
  this.userRoleSubscriptionRules = {
216
212
  ...RoomSession.userRoleSubscriptionRules,
@@ -231,6 +227,14 @@ class RoomSession {
231
227
  return this.userRoleSubscriptionRules[localUserRole][(this._roomType || 'watchparty')].indexOf(remoteUserRole) > -1;
232
228
  }
233
229
 
230
+ _intercomSubscribe = (stream) => {
231
+ if(stream.type === 'data') {
232
+ return true;
233
+ }
234
+ const intercomGroups = JSON.parse(stream.description || "[]")?.intercomGroups || [];
235
+ return intercomGroups.some(g => this._listenIntercomGroups.indexOf(g) > -1);
236
+ };
237
+
234
238
  _getAddParticipantEventName(handleId) {
235
239
 
236
240
  let handle = this._getHandle(handleId);
@@ -376,7 +380,7 @@ class RoomSession {
376
380
  this.ws.removeEventListener('message', parseResponse);
377
381
  this._abortController.signal.removeEventListener('abort', abortResponse);
378
382
  reject({type: 'warning', id: 3, message: 'send timeout', data: requestData});
379
- }, 5000);
383
+ }, this._sendMessageTimout);
380
384
  this._abortController.signal.addEventListener('abort', abortResponse);
381
385
  this.ws.send(JSON.stringify(requestData));
382
386
  } else {
@@ -390,7 +394,7 @@ class RoomSession {
390
394
  return Promise.reject(e);
391
395
  }
392
396
  else if(e.id === 29 && retry > 0) {
393
- return wait(5000).then(() => this._send(request, ignoreResponse, dontResolveOnAck, retry - 1));
397
+ return wait(this._sendMessageTimout).then(() => this._send(request, ignoreResponse, dontResolveOnAck, retry - 1));
394
398
  }
395
399
  else if(retry > 0) {
396
400
  return this._send(request, ignoreResponse, dontResolveOnAck, retry - 1);
@@ -499,7 +503,7 @@ class RoomSession {
499
503
  } else if (type === "event") {
500
504
  //none universal
501
505
  } else if (type === 'timeout') {
502
- this._log('ws timeout', json);
506
+ this._log('WebSockets Gateway timeout', json);
503
507
  this.ws.close(3504, "Gateway timeout");
504
508
  } else if (type === 'success' || type === 'error') {
505
509
  // we're capturing those elsewhere
@@ -560,14 +564,14 @@ class RoomSession {
560
564
  this._log('Creating user: ', userId);
561
565
  this._createParticipant(userId, id)
562
566
  .then(handle => {
567
+ this._updateParticipantsTrackData(handle.handleId, streams);
563
568
  return this.sendMessage(handle.handleId, {
564
569
  body: {
565
570
  "request": "join",
566
571
  "room": this.roomId,
567
572
  "ptype": "subscriber",
568
573
  "private_id": this.privateId,
569
- //TODO: check which groups we want to subscribe to
570
- streams: streams.filter(s => !s.disabled).map(stream => ({feed: stream.id, mid: stream.mid})),
574
+ streams: streams.filter(s => !s.disabled && this._intercomSubscribe(s)).map(stream => ({feed: stream.id, mid: stream.mid})),
571
575
  //"feed": id,
572
576
  ...(this.webrtcVersion > 1000 ? {id: this.userId} : {}),
573
577
  pin: this.pin
@@ -601,25 +605,17 @@ class RoomSession {
601
605
  let handle = this._getHandle(null, id);
602
606
  if(handle) {
603
607
 
604
- //TODO: check which groups we want to subscribe to
605
- const _sfn = (stream) => {
606
- if(stream.type === 'data') {
607
- return true;
608
- }
609
- // if stream.description is not set, we assume it's a data stream, and we shouldn't get here at all
610
- const source = stream.description ? JSON.parse(stream.description)?.source : undefined;
611
- if(!this.options?.restrictSources?.length) {
612
- return true
613
- }
614
- else if(this.options?.restrictSources?.includes(source)) {
615
- return true;
616
- }
617
- return false;
618
- }
608
+ this._updateParticipantsTrackData(handle.handleId, streams)
609
+
610
+ let subscribe = streams
611
+ .filter(stream => !stream.disabled && this._intercomSubscribe(stream) && !this._isAlreadySubscribed(handle.handleId, stream))
612
+ .map(s => ({feed: s.id, mid: s.mid}));
613
+
614
+ let unsubscribe = streams.filter(stream => stream.disabled || !this._intercomSubscribe(stream))
615
+ .map(s => ({feed: s.id, mid: s.mid}));
619
616
 
620
- let subscribe = streams.filter(stream => !stream.disabled && _sfn(stream) && handle.webrtcStuff.tracksMap.filter(t => t.active).findIndex(t => t.mid === stream.mid) === -1).map(s => ({feed: s.id, mid: s.mid}));
621
- let unsubscribe = streams.filter(stream => stream.disabled).map(s => ({feed: s.id, mid: s.mid}));
622
617
  this._log('Already subscribed to user: ', userId, 'Update streams', subscribe, unsubscribe);
618
+
623
619
  if(subscribe.length || unsubscribe.length) {
624
620
  this.sendMessage(handle.handleId, {
625
621
  body: {
@@ -634,14 +630,14 @@ class RoomSession {
634
630
  this._log('Creating user: ', userId, streams);
635
631
  this._createParticipant(userId, id)
636
632
  .then(handle => {
633
+ this._updateParticipantsTrackData(handle.handleId, streams);
637
634
  return this.sendMessage(handle.handleId, {
638
635
  body: {
639
636
  "request": "join",
640
637
  "room": this.roomId,
641
638
  "ptype": "subscriber",
642
639
  "private_id": this.privateId,
643
- //TODO: check which groups we want to subscribe to
644
- streams: streams.filter(s => !s.disabled).map(stream => ({feed: stream.id, mid: stream.mid})),
640
+ streams: streams.filter(s => !s.disabled && this._intercomSubscribe(s)).map(stream => ({feed: stream.id, mid: stream.mid})),
645
641
  ...(this.webrtcVersion > 1000 ? {id: this.userId} : {}),
646
642
  pin: this.pin
647
643
  }
@@ -706,8 +702,6 @@ class RoomSession {
706
702
  }
707
703
 
708
704
  if (jsep !== undefined && jsep !== null) {
709
-
710
- console.log(msg)
711
705
  if (this.sessiontype === 'reactooroom') {
712
706
  this._webrtcPeer(this.handleId, jsep)
713
707
  .catch(err => {
@@ -752,12 +746,12 @@ class RoomSession {
752
746
 
753
747
  if (event === "updated") {
754
748
  this._log('Remote has updated tracks', msg);
755
- this._updateParticipantsTrackData(handle.handleId, msg["streams"] || []);
749
+ this._updateCurrentSubscriptions(handle.handleId, msg["streams"] || []);
756
750
  }
757
751
 
758
752
  if (event === "attached") {
759
753
  this._log('Remote have successfully joined Room', msg);
760
- this._updateParticipantsTrackData(handle.handleId, msg["streams"] || []);
754
+ this._updateCurrentSubscriptions(handle.handleId, msg["streams"] || []);
761
755
  this.emit(this._getAddParticipantEventName(handle.handleId), {
762
756
  tid: generateUUID(),
763
757
  id: handle.handleId,
@@ -898,6 +892,7 @@ class RoomSession {
898
892
  stream: null,
899
893
  streamMap: {},
900
894
  tracksMap: [],
895
+ currentSubscriptions: [],
901
896
  mySdp: null,
902
897
  mediaConstraints: null,
903
898
  pc: null,
@@ -949,6 +944,7 @@ class RoomSession {
949
944
  stream: null,
950
945
  streamMap: {},
951
946
  tracksMap: [],
947
+ currentSubscriptions: [],
952
948
  mySdp: null,
953
949
  mediaConstraints: null,
954
950
  pc: null,
@@ -965,6 +961,36 @@ class RoomSession {
965
961
  })
966
962
  }
967
963
 
964
+ _isAlreadySubscribed(handleId, stream) {
965
+ let handle = this._getHandle(handleId);
966
+ if (!handle) {
967
+ this.emit('error', {
968
+ type: 'warning',
969
+ id: 15,
970
+ message: 'id non-existent',
971
+ data: [handleId, 'isAlreadySubscribed']
972
+ });
973
+ return;
974
+ }
975
+ return handle.webrtcStuff.currentSubscriptions.filter(t => t.active).findIndex(t => t.mid === stream.mid) > -1
976
+ }
977
+
978
+ _updateCurrentSubscriptions(handleId, streams) {
979
+ this._log('Updating current subscription data', handleId, streams);
980
+ let handle = this._getHandle(handleId);
981
+ if (!handle) {
982
+ this.emit('error', {
983
+ type: 'warning',
984
+ id: 15,
985
+ message: 'id non-existent',
986
+ data: [handleId, 'updateCurrentSubscriptions']
987
+ });
988
+ return;
989
+ }
990
+ let config = handle.webrtcStuff;
991
+ config.currentSubscriptions = structuredClone(streams);
992
+ }
993
+
968
994
  _updateParticipantsTrackData(handleId, streams) {
969
995
  this._log('Updating participants track data', handleId, streams);
970
996
  let handle = this._getHandle(handleId);
@@ -978,7 +1004,16 @@ class RoomSession {
978
1004
  return;
979
1005
  }
980
1006
  let config = handle.webrtcStuff;
981
- config.tracksMap = structuredClone(streams);
1007
+ config.tracksMap = structuredClone(streams.map(s => ({
1008
+ active: !s.disabled,
1009
+ description: s.description,
1010
+ display: s.display,
1011
+ id: s.id,
1012
+ mid: s.mid,
1013
+ mindex: s.mindex,
1014
+ codec: s.codec,
1015
+ type: s.type,
1016
+ })));
982
1017
  }
983
1018
 
984
1019
  _updateRemoteParticipantStreamMap(handleId) {
@@ -1002,9 +1037,7 @@ class RoomSession {
1002
1037
  if(tItem.active === false) {
1003
1038
  return;
1004
1039
  }
1005
-
1006
- const source = JSON.parse(tItem.feed_description)?.source;
1007
-
1040
+ const source = JSON.parse(tItem.description)?.source;
1008
1041
  if(!config.streamMap[source]) {
1009
1042
  config.streamMap[source] = [];
1010
1043
  }
@@ -1498,12 +1531,18 @@ class RoomSession {
1498
1531
  if(!event.streams?.[0]?.onremovetrack) {
1499
1532
  event.streams[0].onremovetrack = (ev) => {
1500
1533
  this._log('Remote track removed', ev);
1534
+ config.stream?.removeTrack(ev.track);
1535
+
1536
+ // check if handle still exists
1537
+ if(!this._getHandle(handle.handleId)) {
1538
+ return;
1539
+ }
1540
+
1501
1541
  let transceiver = config.pc?.getTransceivers()?.find(
1502
1542
  t => t.receiver.track === ev.track);
1503
1543
 
1504
1544
  let mid = transceiver?.mid || ev.track.id;
1505
1545
  let source = Object.keys(config.streamMap).find(key => config.streamMap[key].includes(ev.track.id));
1506
- config.stream?.removeTrack(ev.track);
1507
1546
  this._updateRemoteParticipantStreamMap(handle.handleId);
1508
1547
  this.emit(this._getAddParticipantEventName(handle.handleId), {
1509
1548
  tid: generateUUID(),
@@ -1525,9 +1564,9 @@ class RoomSession {
1525
1564
  }
1526
1565
 
1527
1566
  if (event.track) {
1567
+ config.stream?.addTrack(event.track);
1528
1568
  let mid = event.transceiver ? event.transceiver.mid : event.track.id;
1529
1569
  let source = Object.keys(config.streamMap).find(key => config.streamMap[key].includes(event.track.id));
1530
- config.stream?.addTrack(event.track);
1531
1570
  this._updateRemoteParticipantStreamMap(handle.handleId);
1532
1571
  this.emit(this._getAddParticipantEventName(handle.handleId), {
1533
1572
  tid: generateUUID(),
@@ -1551,13 +1590,15 @@ class RoomSession {
1551
1590
 
1552
1591
  event.track.onended = (ev) => {
1553
1592
  this._log('Remote track ended');
1554
-
1593
+ config.stream?.removeTrack(ev.target);
1594
+ // check if handle still exists
1595
+ if(!this._getHandle(handle.handleId)) {
1596
+ return;
1597
+ }
1555
1598
  let transceiver = config.pc?.getTransceivers()?.find(
1556
1599
  t => t.receiver.track === ev.target);
1557
-
1558
1600
  let mid = transceiver?.mid || ev.target.id;
1559
1601
  let source = Object.keys(config.streamMap).find(key => config.streamMap[key].includes(ev.target.id));
1560
- config.stream?.removeTrack(ev.target);
1561
1602
  this._updateRemoteParticipantStreamMap(handle.handleId);
1562
1603
  this.emit(this._getAddParticipantEventName(handle.handleId), {
1563
1604
  tid: generateUUID(),
@@ -1682,10 +1723,6 @@ class RoomSession {
1682
1723
  return Promise.resolve(null);
1683
1724
  }
1684
1725
 
1685
- if(config.pc.signalingState === 'stable') {
1686
- //TODO
1687
- }
1688
-
1689
1726
  return config.pc.setRemoteDescription(jsep)
1690
1727
  .then(() => {
1691
1728
  config.remoteSdp = jsep.sdp;
@@ -2105,7 +2142,7 @@ class RoomSession {
2105
2142
  videoTransceiver = transceiver;
2106
2143
  }
2107
2144
 
2108
- // Reusing existing transceivers
2145
+ // Reusing existing transceivers
2109
2146
  // TODO: if we start using different codecs for different sources, we need to check for that here
2110
2147
 
2111
2148
  else if(transceiver.currentDirection === 'inactive' && transceiver.sender?.getParameters()?.codecs?.find(c => c.mimeType.indexOf('audio') > -1) && replaceAudio && !audioTransceiver) {
@@ -2459,7 +2496,13 @@ class RoomSession {
2459
2496
  }
2460
2497
 
2461
2498
 
2462
- setTalkIntercomGroups(groups = ['all']) {
2499
+ setTalkIntercomGroups(groups = ['participants']) {
2500
+
2501
+ if(typeof groups !== 'object' || !("length" in groups)) {
2502
+ this._log('setTalkIntercomGroups: groups must be an array');
2503
+ groups = [groups];
2504
+ }
2505
+
2463
2506
  this._talkIntercomGroups = structuredClone(groups);
2464
2507
  let handle = this._getHandle(this.handleId);
2465
2508
  if (!handle) {
@@ -2479,13 +2522,68 @@ class RoomSession {
2479
2522
  return this.sendMessage(this.handleId, {
2480
2523
  body: {
2481
2524
  "request": "configure",
2482
- descriptions: descriptions
2525
+ descriptions: descriptions,
2483
2526
  }
2484
2527
  })
2485
2528
  }
2486
2529
 
2487
- setListenIntercomGroups(groups = ['all']) {
2488
- this._listenIntercomGroups = groups;
2530
+ setListenIntercomGroups(groups = ['participants']) {
2531
+
2532
+ if(typeof groups !== 'object' || !("length" in groups)) {
2533
+ this._log('setListenIntercomGroups: groups must be an array');
2534
+ groups = [groups];
2535
+ }
2536
+
2537
+ this._listenIntercomGroups = structuredClone(groups);
2538
+ let handle = this._getHandle(this.handleId);
2539
+ if (!handle) {
2540
+ return Promise.resolve();
2541
+ }
2542
+
2543
+ let promises = [];
2544
+ this._participants.forEach(participant => {
2545
+
2546
+ if(participant.handleId === this.handleId) {
2547
+ return;
2548
+ }
2549
+
2550
+
2551
+ let handle = this._getHandle(participant.handleId);
2552
+ if(handle) {
2553
+
2554
+
2555
+ const tracksMap = handle.webrtcStuff.tracksMap.filter(t => t.active);
2556
+ const subscribe = [];
2557
+ const unsubscribe = [];
2558
+
2559
+ tracksMap.forEach(track => {
2560
+ if(track.type === 'data') {
2561
+ //subscribe.push({feed: track.id, mid: track.mid})
2562
+ return;
2563
+ }
2564
+ const description = JSON.parse(track.description);
2565
+ const intercomGroups = description?.intercomGroups || [];
2566
+
2567
+ if(this._listenIntercomGroups.some(g => intercomGroups.includes(g))) {
2568
+ subscribe.push({feed: track.id, mid: track.mid})
2569
+ }
2570
+ else {
2571
+ unsubscribe.push({feed: track.id, mid: track.mid})
2572
+ }
2573
+ });
2574
+
2575
+ if(subscribe.length || unsubscribe.length) {
2576
+ promises.push(this.sendMessage(handle.handleId, {
2577
+ body: {
2578
+ "request": "update",
2579
+ ...(subscribe.length ? {subscribe}: {}),
2580
+ ...(unsubscribe.length ? {unsubscribe}: {})
2581
+ }
2582
+ }));
2583
+ }
2584
+ }
2585
+ });
2586
+ return Promise.all(promises);
2489
2587
  }
2490
2588
 
2491
2589
  setRoomType(type = 'watchparty') {