@fishjam-cloud/ts-client 0.7.1 → 0.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.
@@ -2,10 +2,10 @@ import type { Deferred } from './deferred';
2
2
  import type { LocalTrackManager } from './tracks/LocalTrackManager';
3
3
  import type { ConnectionManager } from './ConnectionManager';
4
4
  export type Command = {
5
- handler: () => void;
5
+ handler: () => Promise<void>;
6
6
  parse?: () => void;
7
7
  resolutionNotifier: Deferred<void>;
8
- resolve: 'after-renegotiation' | 'immediately';
8
+ resolve: 'after-renegotiation' | 'on-handler-resolve';
9
9
  };
10
10
  export declare class CommandsQueue<EndpointMetadata, TrackMetadata> {
11
11
  private readonly localTrackManager;
@@ -56,11 +56,12 @@ export class CommandsQueue {
56
56
  this.commandResolutionNotifier = command.resolutionNotifier;
57
57
  this.handleCommand(command);
58
58
  };
59
- handleCommand = (command) => {
59
+ handleCommand = async (command) => {
60
60
  try {
61
61
  command.parse?.();
62
- command.handler();
63
- if (command.resolve === 'immediately') {
62
+ const promise = command.handler();
63
+ if (command.resolve === 'on-handler-resolve') {
64
+ await promise;
64
65
  this.resolvePreviousCommand();
65
66
  this.processNextCommand();
66
67
  }
@@ -1,7 +1,8 @@
1
- import { generateCustomEvent, generateMediaEvent } from '../mediaEvent';
1
+ import { generateCustomEvent } from '../mediaEvent';
2
2
  import { defaultBitrates, defaultSimulcastBitrates, UNLIMITED_BANDWIDTH } from '../bitrate';
3
3
  import { getEncodingParameters } from './encodings';
4
4
  import { createTransceiverConfig } from './transceivers';
5
+ import { emitMutableEvents, getActionType } from './muteTrackUtils';
5
6
  /**
6
7
  * This is a wrapper over `TrackContext` that adds additional properties such as:
7
8
  * - `MLineId`: required to generate sdpOffer
@@ -99,6 +100,7 @@ export class LocalTrack {
99
100
  replaceTrack = async (newTrack, webrtc) => {
100
101
  const trackId = this.id;
101
102
  const stream = this.trackContext.stream;
103
+ const oldTrack = this.trackContext.track;
102
104
  if (!this.sender)
103
105
  throw Error('There is no RTCRtpSender for this track id!');
104
106
  stream?.getTracks().forEach((track) => {
@@ -107,25 +109,25 @@ export class LocalTrack {
107
109
  if (newTrack) {
108
110
  stream?.addTrack(newTrack);
109
111
  }
110
- if (this.trackContext.track && !newTrack) {
111
- const mediaEvent = generateMediaEvent('muteTrack', { trackId: trackId });
112
- webrtc.sendMediaEvent(mediaEvent);
113
- webrtc.emit('localTrackMuted', { trackId: trackId });
114
- }
115
- else if (!this.trackContext.track && newTrack) {
116
- const mediaEvent = generateMediaEvent('unmuteTrack', {
117
- trackId: trackId,
118
- });
119
- webrtc.sendMediaEvent(mediaEvent);
120
- webrtc.emit('localTrackUnmuted', { trackId: trackId });
121
- }
112
+ this.trackContext.track = newTrack;
122
113
  this.mediaStreamTrackId = newTrack?.id ?? null;
114
+ const action = getActionType(this.trackContext.track, newTrack);
115
+ if (action === 'mute' || action === 'unmute') {
116
+ emitMutableEvents(action, webrtc, trackId);
117
+ }
123
118
  try {
124
119
  await this.sender.replaceTrack(newTrack);
125
- this.trackContext.track = newTrack;
126
120
  }
127
121
  catch (_error) {
128
- // ignore
122
+ // rollback: emit opposite events and revert internal state
123
+ if (action === 'mute') {
124
+ emitMutableEvents('unmute', webrtc, trackId);
125
+ }
126
+ else if (action === 'unmute') {
127
+ emitMutableEvents('mute', webrtc, trackId);
128
+ }
129
+ this.trackContext.track = oldTrack;
130
+ this.mediaStreamTrackId = oldTrack?.id ?? null;
129
131
  }
130
132
  };
131
133
  setTrackBandwidth = (bandwidth) => {
@@ -0,0 +1,3 @@
1
+ import type { WebRTCEndpoint } from '../webRTCEndpoint';
2
+ export declare function emitMutableEvents<EndpointMetadata, TrackMetadata>(action: 'mute' | 'unmute', webrtc: WebRTCEndpoint<EndpointMetadata, TrackMetadata>, trackId: string): void;
3
+ export declare function getActionType(currentTrack: MediaStreamTrack | null, newTrack: MediaStreamTrack | null): 'mute' | 'unmute' | 'replace';
@@ -0,0 +1,19 @@
1
+ import { generateMediaEvent } from '../mediaEvent';
2
+ export function emitMutableEvents(action, webrtc, trackId) {
3
+ const mediaEventType = action === 'mute' ? `muteTrack` : `unmuteTrack`;
4
+ const localEventType = action === 'mute' ? `localTrackMuted` : `localTrackUnmuted`;
5
+ const mediaEvent = generateMediaEvent(mediaEventType, { trackId: trackId });
6
+ webrtc.sendMediaEvent(mediaEvent);
7
+ webrtc.emit(localEventType, { trackId: trackId });
8
+ }
9
+ export function getActionType(currentTrack, newTrack) {
10
+ if (currentTrack && !newTrack) {
11
+ return 'mute';
12
+ }
13
+ else if (!currentTrack && newTrack) {
14
+ return 'unmute';
15
+ }
16
+ else {
17
+ return 'replace';
18
+ }
19
+ }
@@ -327,9 +327,7 @@ export class WebRTCEndpoint extends EventEmitter {
327
327
  metadata = parsedMetadata;
328
328
  stream.addTrack(track);
329
329
  this.commandsQueue.pushCommand({
330
- handler: () => {
331
- this.localTrackManager.addTrackHandler(trackId, track, stream, parsedMetadata, simulcastConfig, maxBandwidth);
332
- },
330
+ handler: async () => this.localTrackManager.addTrackHandler(trackId, track, stream, parsedMetadata, simulcastConfig, maxBandwidth),
333
331
  parse: () => this.localTrackManager.parseAddTrack(track, simulcastConfig, maxBandwidth),
334
332
  resolve: 'after-renegotiation',
335
333
  resolutionNotifier,
@@ -397,11 +395,9 @@ export class WebRTCEndpoint extends EventEmitter {
397
395
  async replaceTrack(trackId, newTrack) {
398
396
  const resolutionNotifier = new Deferred();
399
397
  this.commandsQueue.pushCommand({
400
- handler: () => {
401
- this.localTrackManager.replaceTrackHandler(this, trackId, newTrack);
402
- },
398
+ handler: () => this.localTrackManager.replaceTrackHandler(this, trackId, newTrack),
403
399
  resolutionNotifier,
404
- resolve: 'immediately',
400
+ resolve: 'on-handler-resolve',
405
401
  });
406
402
  return resolutionNotifier.promise.then(() => {
407
403
  this.emit('localTrackReplaced', { trackId, track: newTrack });
@@ -466,9 +462,7 @@ export class WebRTCEndpoint extends EventEmitter {
466
462
  async removeTrack(trackId) {
467
463
  const resolutionNotifier = new Deferred();
468
464
  this.commandsQueue.pushCommand({
469
- handler: () => {
470
- this.localTrackManager.removeTrackHandler(trackId);
471
- },
465
+ handler: async () => this.localTrackManager.removeTrackHandler(trackId),
472
466
  resolutionNotifier,
473
467
  resolve: 'after-renegotiation',
474
468
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fishjam-cloud/ts-client",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "description": "Typescript client library for Fishjam Cloud",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Fishjam Cloud Team",
@@ -53,20 +53,20 @@
53
53
  "devDependencies": {
54
54
  "@playwright/test": "^1.47.2",
55
55
  "@types/events": "^3.0.3",
56
- "@types/node": "^22.7.4",
56
+ "@types/node": "^22.7.5",
57
57
  "@types/uuid": "^10.0.0",
58
- "@vitest/coverage-v8": "^2.1.1",
58
+ "@vitest/coverage-v8": "^2.1.2",
59
59
  "fake-mediastreamtrack": "^1.2.0",
60
60
  "husky": "^9.1.6",
61
61
  "lint-staged": "^15.2.10",
62
62
  "react": "^18.2.0",
63
- "ts-proto": "^2.2.1",
63
+ "ts-proto": "^2.2.3",
64
64
  "typed-emitter": "^2.1.0",
65
- "typedoc": "^0.26.7",
65
+ "typedoc": "^0.26.8",
66
66
  "typedoc-plugin-external-resolver": "^1.0.3",
67
67
  "typedoc-plugin-mdn-links": "^3.3.2",
68
68
  "typescript": "^5.6.2",
69
- "vitest": "^2.1.1",
69
+ "vitest": "^2.1.2",
70
70
  "zod": "^3.23.6"
71
71
  },
72
72
  "lint-staged": {