@stream-io/video-client 0.6.7 → 0.6.9

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/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
4
4
 
5
+ ### [0.6.9](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-0.6.8...@stream-io/video-client-0.6.9) (2024-04-05)
6
+
7
+
8
+ ### Features
9
+
10
+ * add submit feedback method to Call ([#1307](https://github.com/GetStream/stream-video-js/issues/1307)) ([45fb9da](https://github.com/GetStream/stream-video-js/commit/45fb9da6eb52e4509c7b45b53cd62b0af6f7ec74))
11
+
12
+ ### [0.6.8](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-0.6.7...@stream-io/video-client-0.6.8) (2024-04-05)
13
+
14
+
15
+ ### Features
16
+
17
+ * **react:** Support for Background Filters and Background Blurring ([#1283](https://github.com/GetStream/stream-video-js/issues/1283)) ([f790ee7](https://github.com/GetStream/stream-video-js/commit/f790ee78c20fb0f5266e429a777d8bb7ef158c83)), closes [#1271](https://github.com/GetStream/stream-video-js/issues/1271) [#1276](https://github.com/GetStream/stream-video-js/issues/1276)
18
+
5
19
  ### [0.6.7](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-0.6.6...@stream-io/video-client-0.6.7) (2024-04-02)
6
20
 
7
21
 
@@ -5802,7 +5802,7 @@ class SignalServerClient {
5802
5802
  const defaultOptions = {
5803
5803
  baseUrl: '',
5804
5804
  sendJson: true,
5805
- timeout: 5 * 1000,
5805
+ timeout: 5 * 1000, // ms.
5806
5806
  jsonOptions: {
5807
5807
  ignoreUnknownFields: true,
5808
5808
  },
@@ -6208,7 +6208,7 @@ const findOptimalScreenSharingLayers = (videoTrack, preferences) => {
6208
6208
  return [
6209
6209
  {
6210
6210
  active: true,
6211
- rid: 'q',
6211
+ rid: 'q', // single track, start from 'q'
6212
6212
  width: settings.width || 0,
6213
6213
  height: settings.height || 0,
6214
6214
  scaleResolutionDownBy: 1,
@@ -7061,9 +7061,14 @@ class CallState {
7061
7061
  this.thumbnails$ = this.thumbnailsSubject.asObservable();
7062
7062
  this.eventHandlers = {
7063
7063
  // these events are not updating the call state:
7064
+ 'call.closed_caption': undefined,
7064
7065
  'call.permission_request': undefined,
7065
7066
  'call.recording_failed': undefined,
7066
7067
  'call.recording_ready': undefined,
7068
+ 'call.transcription_started': undefined,
7069
+ 'call.transcription_stopped': undefined,
7070
+ 'call.transcription_ready': undefined,
7071
+ 'call.transcription_failed': undefined,
7067
7072
  'call.user_muted': undefined,
7068
7073
  'connection.error': undefined,
7069
7074
  'connection.ok': undefined,
@@ -9896,7 +9901,6 @@ class DynascaleManager {
9896
9901
  // play audio through the system's default device
9897
9902
  const { selectedDevice } = this.call.speaker.state;
9898
9903
  if (selectedDevice && 'setSinkId' in audioElement) {
9899
- // @ts-expect-error setSinkId is not yet in the lib
9900
9904
  audioElement.setSinkId(selectedDevice);
9901
9905
  }
9902
9906
  }
@@ -9906,7 +9910,6 @@ class DynascaleManager {
9906
9910
  ? null
9907
9911
  : this.call.speaker.state.selectedDevice$.subscribe((deviceId) => {
9908
9912
  if (deviceId) {
9909
- // @ts-expect-error setSinkId is not yet in the lib
9910
9913
  audioElement.setSinkId(deviceId);
9911
9914
  }
9912
9915
  });
@@ -10306,8 +10309,14 @@ class InputMediaDeviceManager {
10306
10309
  this.stopOnLeave = true;
10307
10310
  this.subscriptions = [];
10308
10311
  this.isTrackStoppedDueToTrackEnd = false;
10309
- this.removeSubscriptions = () => {
10310
- this.subscriptions.forEach((s) => s.unsubscribe());
10312
+ this.filters = [];
10313
+ /**
10314
+ * Disposes the manager.
10315
+ *
10316
+ * @internal
10317
+ */
10318
+ this.dispose = () => {
10319
+ this.subscriptions.forEach((s) => s());
10311
10320
  };
10312
10321
  this.logger = getLogger([`${TrackType[trackType].toLowerCase()} manager`]);
10313
10322
  if (deviceIds$ &&
@@ -10384,6 +10393,23 @@ class InputMediaDeviceManager {
10384
10393
  return this.enable();
10385
10394
  }
10386
10395
  }
10396
+ /**
10397
+ * Registers a filter that will be applied to the stream.
10398
+ *
10399
+ * The registered filter will get the existing stream, and it should return
10400
+ * a new stream with the applied filter.
10401
+ *
10402
+ * @param filter the filter to register.
10403
+ * @returns a function that will unregister the filter.
10404
+ */
10405
+ async registerFilter(filter) {
10406
+ this.filters.push(filter);
10407
+ await this.applySettingsToStream();
10408
+ return async () => {
10409
+ this.filters = this.filters.filter((f) => f !== filter);
10410
+ await this.applySettingsToStream();
10411
+ };
10412
+ }
10387
10413
  /**
10388
10414
  * Will set the default constraints for the device.
10389
10415
  *
@@ -10479,7 +10505,62 @@ class InputMediaDeviceManager {
10479
10505
  ...defaultConstraints,
10480
10506
  deviceId: this.state.selectedDevice,
10481
10507
  };
10482
- stream = await this.getStream(constraints);
10508
+ /**
10509
+ * Chains two media streams together.
10510
+ *
10511
+ * In our case, filters MediaStreams are derived from their parent MediaStream.
10512
+ * However, once a child filter's track is stopped,
10513
+ * the tracks of the parent MediaStream aren't automatically stopped.
10514
+ * This leads to a situation where the camera indicator light is still on
10515
+ * even though the user stopped publishing video.
10516
+ *
10517
+ * This function works around this issue by stopping the parent MediaStream's tracks
10518
+ * as well once the child filter's tracks are stopped.
10519
+ *
10520
+ * It works by patching the stop() method of the child filter's tracks to also stop
10521
+ * the parent MediaStream's tracks of the same type. Here we assume that
10522
+ * the parent MediaStream has only one track of each type.
10523
+ *
10524
+ * @param parentStream the parent MediaStream. Omit for the root stream.
10525
+ */
10526
+ const chainWith = (parentStream) => async (filterStream) => {
10527
+ if (!parentStream)
10528
+ return filterStream;
10529
+ // TODO OL: take care of track.enabled property as well
10530
+ const parent = await parentStream;
10531
+ filterStream.getTracks().forEach((track) => {
10532
+ const originalStop = track.stop;
10533
+ track.stop = function stop() {
10534
+ originalStop.call(track);
10535
+ parent.getTracks().forEach((parentTrack) => {
10536
+ if (parentTrack.kind === track.kind) {
10537
+ parentTrack.stop();
10538
+ }
10539
+ });
10540
+ };
10541
+ });
10542
+ parent.getTracks().forEach((parentTrack) => {
10543
+ // When the parent stream abruptly ends, we propagate the event
10544
+ // to the filter stream.
10545
+ // This usually happens when the camera/microphone permissions
10546
+ // are revoked or when the device is disconnected.
10547
+ const handleParentTrackEnded = () => {
10548
+ filterStream.getTracks().forEach((track) => {
10549
+ if (parentTrack.kind !== track.kind)
10550
+ return;
10551
+ track.stop();
10552
+ track.dispatchEvent(new Event('ended')); // propagate the event
10553
+ });
10554
+ };
10555
+ parentTrack.addEventListener('ended', handleParentTrackEnded);
10556
+ this.subscriptions.push(() => {
10557
+ parentTrack.removeEventListener('ended', handleParentTrackEnded);
10558
+ });
10559
+ });
10560
+ return filterStream;
10561
+ };
10562
+ // we publish the last MediaStream of the chain
10563
+ stream = await this.filters.reduce((parent, filter) => parent.then(filter).then(chainWith(parent)), this.getStream(constraints));
10483
10564
  }
10484
10565
  if (this.call.state.callingState === CallingState.JOINED) {
10485
10566
  await this.publishStream(stream);
@@ -10515,10 +10596,10 @@ class InputMediaDeviceManager {
10515
10596
  return '';
10516
10597
  }
10517
10598
  handleDisconnectedOrReplacedDevices() {
10518
- this.subscriptions.push(combineLatest([
10599
+ this.subscriptions.push(createSubscription(combineLatest([
10519
10600
  deviceIds$.pipe(pairwise()),
10520
10601
  this.state.selectedDevice$,
10521
- ]).subscribe(async ([[prevDevices, currentDevices], deviceId]) => {
10602
+ ]), async ([[prevDevices, currentDevices], deviceId]) => {
10522
10603
  if (!deviceId) {
10523
10604
  return;
10524
10605
  }
@@ -11282,7 +11363,12 @@ class SpeakerManager {
11282
11363
  constructor(call) {
11283
11364
  this.state = new SpeakerState();
11284
11365
  this.subscriptions = [];
11285
- this.removeSubscriptions = () => {
11366
+ /**
11367
+ * Disposes the manager.
11368
+ *
11369
+ * @internal
11370
+ */
11371
+ this.dispose = () => {
11286
11372
  this.subscriptions.forEach((s) => s.unsubscribe());
11287
11373
  };
11288
11374
  this.call = call;
@@ -11477,10 +11563,10 @@ class Call {
11477
11563
  // Call all leave call hooks, e.g. to clean up global event handlers
11478
11564
  this.leaveCallHooks.forEach((hook) => hook());
11479
11565
  this.clientStore.unregisterCall(this);
11480
- this.camera.removeSubscriptions();
11481
- this.microphone.removeSubscriptions();
11482
- this.screenShare.removeSubscriptions();
11483
- this.speaker.removeSubscriptions();
11566
+ this.camera.dispose();
11567
+ this.microphone.dispose();
11568
+ this.screenShare.dispose();
11569
+ this.speaker.dispose();
11484
11570
  const stopOnLeavePromises = [];
11485
11571
  if (this.camera.stopOnLeave) {
11486
11572
  stopOnLeavePromises.push(this.camera.disable(true));
@@ -12282,6 +12368,20 @@ class Call {
12282
12368
  this.stopRecording = async () => {
12283
12369
  return this.streamClient.post(`${this.streamClientBasePath}/stop_recording`, {});
12284
12370
  };
12371
+ /**
12372
+ * Starts the transcription of the call.
12373
+ *
12374
+ * @param request the request data.
12375
+ */
12376
+ this.startTranscription = async (request) => {
12377
+ return this.streamClient.post(`${this.streamClientBasePath}/start_transcription`, request);
12378
+ };
12379
+ /**
12380
+ * Stops the transcription of the call.
12381
+ */
12382
+ this.stopTranscription = async () => {
12383
+ return this.streamClient.post(`${this.streamClientBasePath}/stop_transcription`);
12384
+ };
12285
12385
  /**
12286
12386
  * Sends a `call.permission_request` event to all users connected to the call. The call settings object contains infomration about which permissions can be requested during a call (for example a user might be allowed to request permission to publish audio, but not video).
12287
12387
  */
@@ -12490,6 +12590,14 @@ class Call {
12490
12590
  }
12491
12591
  return this.streamClient.get(`${endpoint}/recordings`);
12492
12592
  };
12593
+ /**
12594
+ * Retrieves the list of transcriptions for the current call.
12595
+ *
12596
+ * @returns the list of transcriptions.
12597
+ */
12598
+ this.queryTranscriptions = async () => {
12599
+ return this.streamClient.get(`${this.streamClientBasePath}/transcriptions`);
12600
+ };
12493
12601
  /**
12494
12602
  * Retrieve call statistics for a particular call session (historical).
12495
12603
  * Here `callSessionID` is mandatory.
@@ -12501,6 +12609,19 @@ class Call {
12501
12609
  const endpoint = `${this.streamClientBasePath}/stats/${callSessionID}`;
12502
12610
  return this.streamClient.get(endpoint);
12503
12611
  };
12612
+ /**
12613
+ * Submit feedback on call experience
12614
+ *
12615
+ * @returns
12616
+ */
12617
+ this.submitFeedback = async (feedback) => {
12618
+ const callSessionId = this.state.session?.id;
12619
+ if (!callSessionId) {
12620
+ return;
12621
+ }
12622
+ const endpoint = `${this.streamClientBasePath}/feedback/${callSessionId}`;
12623
+ return this.streamClient.post(endpoint, feedback);
12624
+ };
12504
12625
  /**
12505
12626
  * Sends a custom event to all call participants.
12506
12627
  *
@@ -13557,7 +13678,7 @@ function UserFromToken(token) {
13557
13678
  */
13558
13679
  function DevToken(userId) {
13559
13680
  return [
13560
- 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9',
13681
+ 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9', //{"alg": "HS256", "typ": "JWT"}
13561
13682
  encodeBase64(JSON.stringify({ user_id: userId })),
13562
13683
  'devtoken', // hardcoded signature
13563
13684
  ].join('.');
@@ -14369,7 +14490,7 @@ class StreamClient {
14369
14490
  });
14370
14491
  };
14371
14492
  this.getUserAgent = () => {
14372
- const version = "0.6.7" ;
14493
+ const version = "0.6.9" ;
14373
14494
  return (this.userAgent ||
14374
14495
  `stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${version}`);
14375
14496
  };
@@ -14484,7 +14605,7 @@ class StreamClient {
14484
14605
  }
14485
14606
  this.options = {
14486
14607
  timeout: 5000,
14487
- withCredentials: false,
14608
+ withCredentials: false, // making sure cookies are not sent
14488
14609
  warmUp: false,
14489
14610
  ...inputOptions,
14490
14611
  };