@fishjam-cloud/ts-client 0.5.1 → 0.5.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.
package/README.md CHANGED
@@ -1,11 +1,20 @@
1
1
  [![NPM Version](https://img.shields.io/npm/v/@fishjam-cloud/ts-client)](https://www.npmjs.com/package/@fishjam-cloud/ts-client)
2
2
  [![TypeScript Strict](https://badgen.net/badge/TS/Strict)](https://www.typescriptlang.org)
3
- [![TypeDoc](https://img.shields.io/badge/TypeDoc-8A2BE2)](https://fishjam-cloud.github.io/web-client-sdk/)
3
+ [![TypeDoc](https://img.shields.io/badge/TypeDoc-8A2BE2)](https://fishjam-cloud.github.io/web-client-sdk/modules/_fishjam_dev_ts_client.html)
4
4
 
5
5
  # Fishjam TS Client
6
6
 
7
7
  TypeScript client library for [Fishjam Cloud](https://cloud.fishjam.stream).
8
8
 
9
+ ## Documentation
10
+
11
+ Documentation is available [here](https://fishjam-cloud.github.io/web-client-sdk/modules/_fishjam_dev_ts_client.html) or
12
+ you can generate it locally:
13
+
14
+ ```bash
15
+ yarn run docs
16
+ ```
17
+
9
18
  ## Installation
10
19
 
11
20
  You can install this package using `npm`:
@@ -17,27 +26,17 @@ npm install @fishjam-cloud/ts-client
17
26
  or `yarn`:
18
27
 
19
28
  ```bash
20
- yarn @fishjam-cloud/react-client
29
+ yarn @fishjam-cloud/ts-client
21
30
  ```
22
31
 
23
- ## Documentation
24
-
25
- Documentation is available
26
- [here](https://fishjam-cloud.github.io/web-client-sdk/modules/_fishjam_dev_ts_client.html/).
27
-
28
- For a more comprehensive tutorial on Fishjam, its capabilities and usage in
29
- production, refer to the [Fishjam Cloud Docs](https://cloud.fishjam.stream/).
30
-
31
32
  ## Usage
32
33
 
33
34
  Prerequisites:
34
35
 
35
- - Account on Fishjam Cloud with App configured.
36
- - Created room and token of peer in that room. You can use Room Manager to
37
- create room and peer token.
36
+ - Account on [Fishjam Cloud](https://cloud.fishjam.stream) with App configured.
37
+ - Created room and token of peer in that room. You can use Room Manager to create room and peer token.
38
38
 
39
- The following code snippet is based on the
40
- [minimal](../../examples/ts-client//minimal/) example.
39
+ The following code snippet is based on the [minimal](../../examples/ts-client/minimal/) example.
41
40
 
42
41
  ```ts
43
42
  import { FishjamClient, WebRTCEndpoint } from '@fishjam-cloud/ts-client';
@@ -65,11 +64,13 @@ const client = new FishjamClient<PeerMetadata, TrackMetadata>();
65
64
 
66
65
  const peerToken = prompt('Enter peer token') ?? 'YOUR_PEER_TOKEN';
67
66
 
67
+ const FISHJAM_URL = 'ws://localhost:5002';
68
+
68
69
  // Start the peer connection
69
70
  client.connect({
70
71
  peerMetadata: { name: 'peer' },
71
72
  token: peerToken,
72
- // if the 'signaling' field is missing, the client will connect to ws://localhost:5002/socket/peer/websocket
73
+ url: FISHJAM_URL,
73
74
  });
74
75
 
75
76
  // You can listen to events emitted by the client
@@ -109,14 +110,10 @@ client.on('trackRemoved', (ctx) => {
109
110
 
110
111
  async function startScreenSharing(webrtc: WebRTCEndpoint) {
111
112
  // Get screen sharing MediaStream
112
- const screenStream = await navigator.mediaDevices.getDisplayMedia(
113
- SCREEN_SHARING_MEDIA_CONSTRAINTS,
114
- );
113
+ const screenStream = await navigator.mediaDevices.getDisplayMedia(SCREEN_SHARING_MEDIA_CONSTRAINTS);
115
114
 
116
115
  // Add local MediaStream to webrtc
117
- screenStream
118
- .getTracks()
119
- .forEach((track) => webrtc.addTrack(track, { type: 'screen' }));
116
+ screenStream.getTracks().forEach((track) => webrtc.addTrack(track, { type: 'screen' }));
120
117
  }
121
118
  ```
122
119
 
@@ -126,8 +123,7 @@ For more examples, see the [examples](../../examples/ts-client/) folder.
126
123
 
127
124
  ## Copyright and License
128
125
 
129
- Copyright 2024,
130
- [Software Mansion](https://swmansion.com/?utm_source=git&utm_medium=readme&utm_campaign=fishjam-ts)
126
+ Copyright 2024, [Software Mansion](https://swmansion.com/?utm_source=git&utm_medium=readme&utm_campaign=fishjam-ts)
131
127
 
132
128
  [![Software Mansion](https://logo.swmansion.com/logo?color=white&variant=desktop&width=200&tag=fishjam-github)](https://swmansion.com/?utm_source=git&utm_medium=readme&utm_campaign=fishjam-ts)
133
129
 
@@ -1,4 +1,4 @@
1
- import type { BandwidthLimit, Endpoint, SimulcastConfig, TrackBandwidthLimit, TrackContext, Encoding, MetadataParser, WebRTCEndpointEvents } from './webrtc';
1
+ import type { BandwidthLimit, Encoding, Endpoint, MetadataParser, SimulcastConfig, TrackBandwidthLimit, TrackContext, WebRTCEndpointEvents } from './webrtc';
2
2
  import type TypedEmitter from 'typed-emitter';
3
3
  import type { ReconnectConfig } from './reconnection';
4
4
  import type { AuthErrorReason } from './auth';
@@ -134,30 +134,14 @@ export interface MessageEvents<PeerMetadata, TrackMetadata> {
134
134
  localTrackMetadataChanged: (event: Parameters<WebRTCEndpointEvents<PeerMetadata, TrackMetadata>['localTrackMetadataChanged']>[0]) => void;
135
135
  disconnectRequested: (event: Parameters<WebRTCEndpointEvents<PeerMetadata, TrackMetadata>['disconnectRequested']>[0]) => void;
136
136
  }
137
- export type SignalingUrl = {
138
- /**
139
- * Protocol of the websocket server
140
- * Default is `"ws"`
141
- */
142
- protocol?: string;
143
- /**
144
- * Host of the websocket server
145
- * Default is `"localhost:5002"`
146
- */
147
- host?: string;
148
- /**
149
- * Path of the websocket server
150
- * Default is `"/socket/peer/websocket"`
151
- */
152
- path?: string;
153
- };
154
137
  /** Configuration object for the client */
155
138
  export interface ConnectConfig<PeerMetadata> {
156
139
  /** Metadata for the peer */
157
140
  peerMetadata: PeerMetadata;
158
141
  /** Token for authentication */
159
142
  token: string;
160
- signaling?: SignalingUrl;
143
+ /** Fishjam url */
144
+ url: string;
161
145
  }
162
146
  export type CreateConfig<PeerMetadata, TrackMetadata> = {
163
147
  peerMetadataParser?: MetadataParser<PeerMetadata>;
@@ -228,6 +212,7 @@ export declare class FishjamClient<PeerMetadata, TrackMetadata> extends FishjamC
228
212
  */
229
213
  connect(config: ConnectConfig<PeerMetadata>): void;
230
214
  private initConnection;
215
+ private getUrl;
231
216
  private initWebsocket;
232
217
  /**
233
218
  * Retrieves statistics related to the RTCPeerConnection.
@@ -380,11 +365,9 @@ export declare class FishjamClient<PeerMetadata, TrackMetadata> extends FishjamC
380
365
  *
381
366
  * @param {string} trackId - Id of audio or video track to replace.
382
367
  * @param {MediaStreamTrack} newTrack - New audio or video track.
383
- * @param {TrackMetadata} [newTrackMetadata] - Optional track metadata to apply to the new track. If no track metadata is passed, the
384
- * old track metadata is retained.
385
368
  * @returns {Promise<boolean>} Success
386
369
  */
387
- replaceTrack(trackId: string, newTrack: MediaStreamTrack | null, newTrackMetadata?: TrackMetadata): Promise<void>;
370
+ replaceTrack(trackId: string, newTrack: MediaStreamTrack | null): Promise<void>;
388
371
  /**
389
372
  * Updates maximum bandwidth for the track identified by trackId. This value directly translates to quality of the
390
373
  * stream and, in case of video, to the amount of RTP packets being sent. In case trackId points at the simulcast
@@ -10,6 +10,7 @@ const isComponent = (endpoint) => endpoint.type === 'recording' ||
10
10
  endpoint.type === 'file' ||
11
11
  endpoint.type === 'rtsp' ||
12
12
  endpoint.type === 'sip';
13
+ const WEBSOCKET_PATH = 'socket/peer/websocket';
13
14
  /**
14
15
  * FishjamClient is the main class to interact with Fishjam.
15
16
  *
@@ -56,10 +57,8 @@ export class FishjamClient extends EventEmitter {
56
57
  trackMetadataParser;
57
58
  constructor(config) {
58
59
  super();
59
- this.peerMetadataParser =
60
- config?.peerMetadataParser ?? ((x) => x);
61
- this.trackMetadataParser =
62
- config?.trackMetadataParser ?? ((x) => x);
60
+ this.peerMetadataParser = config?.peerMetadataParser ?? ((x) => x);
61
+ this.trackMetadataParser = config?.trackMetadataParser ?? ((x) => x);
63
62
  this.reconnectManager = new ReconnectManager(this, (peerMetadata) => this.initConnection(peerMetadata), config?.reconnect);
64
63
  }
65
64
  /**
@@ -95,14 +94,16 @@ export class FishjamClient extends EventEmitter {
95
94
  this.setupCallbacks();
96
95
  this.status = 'initialized';
97
96
  }
97
+ getUrl(url) {
98
+ if (url.endsWith('/'))
99
+ return `${url}${WEBSOCKET_PATH}`;
100
+ return `${url}/${WEBSOCKET_PATH}`;
101
+ }
98
102
  initWebsocket(peerMetadata) {
99
103
  if (!this.connectConfig)
100
104
  throw Error('ConnectConfig is null');
101
- const { token, signaling } = this.connectConfig;
102
- const protocol = signaling?.protocol ?? 'ws';
103
- const host = signaling?.host ?? 'localhost:5002';
104
- const path = signaling?.path ?? '/socket/peer/websocket';
105
- const websocketUrl = protocol + '://' + host + path;
105
+ const { token, url } = this.connectConfig;
106
+ const websocketUrl = this.getUrl(url);
106
107
  this.websocket = new WebSocket(websocketUrl);
107
108
  this.websocket.binaryType = 'arraybuffer';
108
109
  const socketOpenHandler = (event) => {
@@ -180,7 +181,7 @@ export class FishjamClient extends EventEmitter {
180
181
  return Object.entries(this.webrtc?.getRemoteEndpoints() ?? {}).reduce((acc, [id, peer]) => (isPeer(peer) ? { ...acc, [id]: peer } : acc), {});
181
182
  }
182
183
  getRemoteComponents() {
183
- return Object.entries(this.webrtc?.getRemoteEndpoints() ?? {}).reduce((acc, [id, component]) => isComponent(component) ? { ...acc, [id]: component } : acc, {});
184
+ return Object.entries(this.webrtc?.getRemoteEndpoints() ?? {}).reduce((acc, [id, component]) => (isComponent(component) ? { ...acc, [id]: component } : acc), {});
184
185
  }
185
186
  getLocalPeer() {
186
187
  return this.webrtc?.getLocalEndpoint() || null;
@@ -458,14 +459,12 @@ export class FishjamClient extends EventEmitter {
458
459
  *
459
460
  * @param {string} trackId - Id of audio or video track to replace.
460
461
  * @param {MediaStreamTrack} newTrack - New audio or video track.
461
- * @param {TrackMetadata} [newTrackMetadata] - Optional track metadata to apply to the new track. If no track metadata is passed, the
462
- * old track metadata is retained.
463
462
  * @returns {Promise<boolean>} Success
464
463
  */
465
- async replaceTrack(trackId, newTrack, newTrackMetadata) {
464
+ async replaceTrack(trackId, newTrack) {
466
465
  if (!this.webrtc)
467
466
  throw this.handleWebRTCNotInitialized();
468
- return this.webrtc.replaceTrack(trackId, newTrack, newTrackMetadata);
467
+ return this.webrtc.replaceTrack(trackId, newTrack);
469
468
  }
470
469
  /**
471
470
  * Updates maximum bandwidth for the track identified by trackId. This value directly translates to quality of the
@@ -1,4 +1,4 @@
1
- export type { Peer, Component, ConnectConfig, CreateConfig, MessageEvents, SignalingUrl, } from './FishjamClient';
1
+ export type { Peer, Component, ConnectConfig, CreateConfig, MessageEvents } from './FishjamClient';
2
2
  export type { ReconnectConfig, ReconnectionStatus } from './reconnection';
3
3
  export type { AuthErrorReason } from './auth.js';
4
4
  export { isAuthError, AUTH_ERROR_REASONS } from './auth.js';
@@ -78,8 +78,7 @@ export class ReconnectManager {
78
78
  this.client.emit('reconnectionStarted');
79
79
  this.lastLocalEndpoint = this.client.getLocalPeer() || null;
80
80
  }
81
- const timeout = this.reconnectConfig.initialDelay +
82
- this.reconnectAttempt * this.reconnectConfig.delay;
81
+ const timeout = this.reconnectConfig.initialDelay + this.reconnectAttempt * this.reconnectConfig.delay;
83
82
  this.reconnectAttempt += 1;
84
83
  this.reconnectTimeoutId = setTimeout(() => {
85
84
  this.reconnectTimeoutId = null;
@@ -115,7 +114,6 @@ export const createReconnectConfig = (config) => {
115
114
  maxAttempts: config?.maxAttempts ?? DEFAULT_RECONNECT_CONFIG.maxAttempts,
116
115
  initialDelay: config?.initialDelay ?? DEFAULT_RECONNECT_CONFIG.initialDelay,
117
116
  delay: config?.delay ?? DEFAULT_RECONNECT_CONFIG.delay,
118
- addTracksOnReconnect: config?.addTracksOnReconnect ??
119
- DEFAULT_RECONNECT_CONFIG.addTracksOnReconnect,
117
+ addTracksOnReconnect: config?.addTracksOnReconnect ?? DEFAULT_RECONNECT_CONFIG.addTracksOnReconnect,
120
118
  };
121
119
  };
@@ -28,31 +28,15 @@ export class CommandsQueue {
28
28
  }
29
29
  };
30
30
  this.clearConnectionCallbacks = () => {
31
- connection
32
- .getConnection()
33
- .removeEventListener('signalingstatechange', onSignalingStateChange);
34
- connection
35
- .getConnection()
36
- .removeEventListener('icegatheringstatechange', onIceGatheringStateChange);
37
- connection
38
- .getConnection()
39
- .removeEventListener('connectionstatechange', onConnectionStateChange);
40
- connection
41
- .getConnection()
42
- .removeEventListener('iceconnectionstatechange', onIceConnectionStateChange);
31
+ connection.getConnection().removeEventListener('signalingstatechange', onSignalingStateChange);
32
+ connection.getConnection().removeEventListener('icegatheringstatechange', onIceGatheringStateChange);
33
+ connection.getConnection().removeEventListener('connectionstatechange', onConnectionStateChange);
34
+ connection.getConnection().removeEventListener('iceconnectionstatechange', onIceConnectionStateChange);
43
35
  };
44
- connection
45
- .getConnection()
46
- .addEventListener('icegatheringstatechange', onIceConnectionStateChange);
47
- connection
48
- .getConnection()
49
- .addEventListener('connectionstatechange', onConnectionStateChange);
50
- connection
51
- .getConnection()
52
- .addEventListener('iceconnectionstatechange', onIceConnectionStateChange);
53
- connection
54
- .getConnection()
55
- .addEventListener('signalingstatechange', onSignalingStateChange);
36
+ connection.getConnection().addEventListener('icegatheringstatechange', onIceConnectionStateChange);
37
+ connection.getConnection().addEventListener('connectionstatechange', onConnectionStateChange);
38
+ connection.getConnection().addEventListener('iceconnectionstatechange', onIceConnectionStateChange);
39
+ connection.getConnection().addEventListener('signalingstatechange', onSignalingStateChange);
56
40
  };
57
41
  commandsQueue = [];
58
42
  commandResolutionNotifier = null;
@@ -35,14 +35,10 @@ export class ConnectionManager {
35
35
  return this.connection;
36
36
  };
37
37
  setTransceiversToReadOnly = () => {
38
- this.connection
39
- .getTransceivers()
40
- .forEach((transceiver) => (transceiver.direction = 'sendonly'));
38
+ this.connection.getTransceivers().forEach((transceiver) => (transceiver.direction = 'sendonly'));
41
39
  };
42
40
  addTransceiversIfNeeded = (serverTracks) => {
43
- const recvTransceivers = this.connection
44
- .getTransceivers()
45
- .filter((elem) => elem.direction === 'recvonly');
41
+ const recvTransceivers = this.connection.getTransceivers().filter((elem) => elem.direction === 'recvonly');
46
42
  ['audio', 'video']
47
43
  .flatMap((type) => this.getNeededTransceiversTypes(type, recvTransceivers, serverTracks))
48
44
  .forEach((kind) => this.connection.addTransceiver(kind, { direction: 'recvonly' }));
@@ -73,9 +69,7 @@ export class ConnectionManager {
73
69
  removeTrack = (sender) => {
74
70
  this.connection.removeTrack(sender);
75
71
  };
76
- findSender = (mediaStreamTrackId) => this.connection
77
- .getSenders()
78
- .find((sender) => sender.track && sender.track.id === mediaStreamTrackId);
72
+ findSender = (mediaStreamTrackId) => this.connection.getSenders().find((sender) => sender.track && sender.track.id === mediaStreamTrackId);
79
73
  addIceCandidate = async (iceCandidate) => {
80
74
  await this.connection.addIceCandidate(iceCandidate);
81
75
  };
@@ -31,7 +31,7 @@ export declare class Local<EndpointMetadata, TrackMetadata> {
31
31
  disableAllLocalTrackEncodings: () => Promise<void>;
32
32
  getTrackByMidOrNull: (mid: string) => LocalTrack<EndpointMetadata, TrackMetadata> | null;
33
33
  removeTrack: (trackId: TrackId) => void;
34
- replaceTrack: (webrtc: WebRTCEndpoint<EndpointMetadata, TrackMetadata>, trackId: TrackId, newTrack: MediaStreamTrack | null, newTrackMetadata: TrackMetadata | undefined) => Promise<void>;
34
+ replaceTrack: (webrtc: WebRTCEndpoint<EndpointMetadata, TrackMetadata>, trackId: TrackId, newTrack: MediaStreamTrack | null) => Promise<void>;
35
35
  setEndpointMetadata: (metadata: EndpointMetadata) => void;
36
36
  getEndpoint: () => EndpointWithTrackContext<EndpointMetadata, TrackMetadata>;
37
37
  setTrackBandwidth: (trackId: string, bandwidth: BandwidthLimit) => Promise<void>;
@@ -85,8 +85,7 @@ export class Local {
85
85
  }
86
86
  };
87
87
  getTrackByMidOrNull = (mid) => {
88
- return (Object.values(this.localTracks).find((track) => track.mLineId === mid) ??
89
- null);
88
+ return Object.values(this.localTracks).find((track) => track.mLineId === mid) ?? null;
90
89
  };
91
90
  removeTrack = (trackId) => {
92
91
  const trackManager = this.localTracks[trackId];
@@ -98,16 +97,12 @@ export class Local {
98
97
  this.localEndpoint.tracks.delete(trackId);
99
98
  delete this.localTracks[trackId];
100
99
  };
101
- replaceTrack = async (
102
- // todo remove webrtc with newTrackMetadata
103
- webrtc, trackId, newTrack,
104
- // todo remove webrtc with newTrackMetadata
105
- newTrackMetadata) => {
100
+ replaceTrack = async (webrtc, trackId, newTrack) => {
106
101
  // TODO add validation to track.kind, you cannot replace video with audio
107
102
  const trackManager = this.localTracks[trackId];
108
103
  if (!trackManager)
109
104
  throw new Error(`Cannot find ${trackId}`);
110
- await trackManager.replaceTrack(newTrack, newTrackMetadata, webrtc);
105
+ await trackManager.replaceTrack(newTrack, webrtc);
111
106
  };
112
107
  setEndpointMetadata = (metadata) => {
113
108
  try {
@@ -49,7 +49,7 @@ export declare class LocalTrack<EndpointMetadata, TrackMetadata> implements Trac
49
49
  addTrackToConnection: () => void;
50
50
  private updateEncodings;
51
51
  removeFromConnection: () => void;
52
- replaceTrack: (newTrack: MediaStreamTrack | null, newTrackMetadata: TrackMetadata | undefined, webrtc: WebRTCEndpoint<EndpointMetadata, TrackMetadata>) => Promise<void>;
52
+ replaceTrack: (newTrack: MediaStreamTrack | null, webrtc: WebRTCEndpoint<EndpointMetadata, TrackMetadata>) => Promise<void>;
53
53
  setTrackBandwidth: (bandwidth: BandwidthLimit) => Promise<void>;
54
54
  setEncodingBandwidth(rid: Encoding, bandwidth: BandwidthLimit): Promise<void>;
55
55
  updateTrackMetadata: (metadata: unknown) => void;
@@ -1,5 +1,5 @@
1
1
  import { generateCustomEvent, generateMediaEvent } from '../mediaEvent';
2
- import { defaultBitrates, defaultSimulcastBitrates, UNLIMITED_BANDWIDTH, } from '../bitrate';
2
+ import { defaultBitrates, defaultSimulcastBitrates, UNLIMITED_BANDWIDTH } from '../bitrate';
3
3
  import { getEncodingParameters } from './encodings';
4
4
  import { createTransceiverConfig } from './transceivers';
5
5
  /**
@@ -82,8 +82,7 @@ export class LocalTrack {
82
82
  };
83
83
  // 1
84
84
  updateEncodings = () => {
85
- if (this.trackContext?.track?.kind === 'video' &&
86
- this.trackContext.simulcastConfig?.activeEncodings) {
85
+ if (this.trackContext?.track?.kind === 'video' && this.trackContext.simulcastConfig?.activeEncodings) {
87
86
  const activeEncodings = this.trackContext.simulcastConfig.activeEncodings;
88
87
  this.encodings.l = activeEncodings.some((e) => e === 'l');
89
88
  this.encodings.m = activeEncodings.some((e) => e === 'm');
@@ -97,14 +96,7 @@ export class LocalTrack {
97
96
  throw new Error(`There is no active RTCPeerConnection`);
98
97
  this.connection.removeTrack(this.sender);
99
98
  };
100
- // TODO: Remove `newTrackMetadata` parameter because this function should be an atomic operation.
101
- // Metadata are updated after `await this.sender.replaceTrack(newTrack)`,
102
- // so it could be chained by the user.
103
- replaceTrack = async (newTrack,
104
- // todo remove webrtc with newTrackMetadata
105
- newTrackMetadata,
106
- // todo remove webrtc with newTrackMetadata
107
- webrtc) => {
99
+ replaceTrack = async (newTrack, webrtc) => {
108
100
  const trackId = this.id;
109
101
  const stream = this.trackContext.stream;
110
102
  if (!this.sender)
@@ -131,11 +123,8 @@ export class LocalTrack {
131
123
  try {
132
124
  await this.sender.replaceTrack(newTrack);
133
125
  this.trackContext.track = newTrack;
134
- if (newTrackMetadata) {
135
- webrtc.updateTrackMetadata(trackId, newTrackMetadata);
136
- }
137
126
  }
138
- catch (error) {
127
+ catch (_error) {
139
128
  // ignore
140
129
  }
141
130
  };
@@ -209,8 +198,7 @@ export class LocalTrack {
209
198
  throw new Error(`RTCRtpSender for track ${this.id} not found`);
210
199
  const encodings = this.sender.getParameters().encodings;
211
200
  if (this.isNotSimulcastTrack(encodings)) {
212
- return (encodings[0].maxBitrate ||
213
- (kind ? defaultBitrates[kind] : UNLIMITED_BANDWIDTH));
201
+ return encodings[0].maxBitrate || (kind ? defaultBitrates[kind] : UNLIMITED_BANDWIDTH);
214
202
  }
215
203
  else if (kind === 'audio') {
216
204
  throw 'Audio track cannot have multiple encodings';
@@ -31,7 +31,7 @@ export declare class LocalTrackManager<EndpointMetadata, TrackMetadata> {
31
31
  parseAddTrack: (track: MediaStreamTrack, simulcastConfig: SimulcastConfig, maxBandwidth: TrackBandwidthLimit) => void;
32
32
  addTrackHandler: (trackId: string, track: MediaStreamTrack, stream: MediaStream, trackMetadata: TrackMetadata | undefined, simulcastConfig: SimulcastConfig, maxBandwidth: TrackBandwidthLimit) => void;
33
33
  removeTrackHandler: (trackId: string) => void;
34
- replaceTrackHandler: (webrtc: WebRTCEndpoint<EndpointMetadata, TrackMetadata>, trackId: string, newTrack: MediaStreamTrack | null, newTrackMetadata?: TrackMetadata) => Promise<void>;
34
+ replaceTrackHandler: (webrtc: WebRTCEndpoint<EndpointMetadata, TrackMetadata>, trackId: string, newTrack: MediaStreamTrack | null) => Promise<void>;
35
35
  getEndpointId: () => string;
36
36
  updateConnection: (connection: ConnectionManager) => void;
37
37
  updateSenders: () => void;
@@ -59,19 +59,14 @@ export class LocalTrackManager {
59
59
  this.ongoingRenegotiation = true;
60
60
  this.local.removeTrack(trackId);
61
61
  };
62
- replaceTrackHandler = async (
63
- // todo remove webrtc with newTrackMetadata
64
- webrtc, trackId, newTrack,
65
- // todo remove webrtc with newTrackMetadata
66
- newTrackMetadata) => {
62
+ replaceTrackHandler = async (webrtc, trackId, newTrack) => {
67
63
  this.ongoingTrackReplacement = true;
68
64
  try {
69
- await this.local.replaceTrack(webrtc, trackId, newTrack, newTrackMetadata);
65
+ await this.local.replaceTrack(webrtc, trackId, newTrack);
70
66
  }
71
- catch (e) {
67
+ finally {
72
68
  this.ongoingTrackReplacement = false;
73
69
  }
74
- this.ongoingTrackReplacement = false;
75
70
  };
76
71
  getEndpointId = () => this.local.getEndpoint().id;
77
72
  updateConnection = (connection) => {
@@ -1,7 +1,5 @@
1
1
  import { splitBandwidth } from './bandwidth';
2
2
  export const getEncodingParameters = (parameters, bandwidth) => {
3
3
  const unlimitedEncodings = [{}];
4
- return parameters.encodings.length === 0
5
- ? unlimitedEncodings
6
- : splitBandwidth(parameters.encodings, bandwidth);
4
+ return parameters.encodings.length === 0 ? unlimitedEncodings : splitBandwidth(parameters.encodings, bandwidth);
7
5
  };
@@ -16,8 +16,7 @@ const createAudioTransceiverConfig = (stream) => {
16
16
  const createVideoTransceiverConfig = (trackContext, maxBandwidth) => {
17
17
  if (!trackContext.simulcastConfig)
18
18
  throw new Error(`Simulcast config for track ${trackContext.trackId} not found.`);
19
- if (typeof maxBandwidth !== 'number' &&
20
- trackContext.simulcastConfig.enabled) {
19
+ if (typeof maxBandwidth !== 'number' && trackContext.simulcastConfig.enabled) {
21
20
  return createSimulcastTransceiverConfig(trackContext, maxBandwidth);
22
21
  }
23
22
  if (typeof maxBandwidth === 'number') {
@@ -243,7 +243,6 @@ export interface WebRTCEndpointEvents<EndpointMetadata, TrackMetadata> {
243
243
  localTrackReplaced: (event: {
244
244
  trackId: string;
245
245
  track: MediaStreamTrack | null;
246
- metadata?: TrackMetadata;
247
246
  }) => void;
248
247
  localTrackMuted: (event: {
249
248
  trackId: string;
@@ -127,8 +127,6 @@ export declare class WebRTCEndpoint<EndpointMetadata = any, TrackMetadata = any>
127
127
  * @param trackId - Audio or video track.
128
128
  * @param {string} trackId - Id of audio or video track to replace.
129
129
  * @param {MediaStreamTrack} newTrack
130
- * @param {any} [newTrackMetadata] - Optional track metadata to apply to the new track. If no
131
- * track metadata is passed, the old track metadata is retained.
132
130
  * @returns {Promise<boolean>} success
133
131
  * @example
134
132
  * ```ts
@@ -169,7 +167,7 @@ export declare class WebRTCEndpoint<EndpointMetadata = any, TrackMetadata = any>
169
167
  * })
170
168
  * ```
171
169
  */
172
- replaceTrack(trackId: string, newTrack: MediaStreamTrack | null, newTrackMetadata?: any): Promise<void>;
170
+ replaceTrack(trackId: string, newTrack: MediaStreamTrack | null): Promise<void>;
173
171
  /**
174
172
  * Updates maximum bandwidth for the track identified by trackId.
175
173
  * This value directly translates to quality of the stream and, in case of video, to the amount of RTP packets being sent.
@@ -1,4 +1,4 @@
1
- import { deserializeMediaEvent, generateCustomEvent, generateMediaEvent, serializeMediaEvent, } from './mediaEvent';
1
+ import { deserializeMediaEvent, generateCustomEvent, generateMediaEvent, serializeMediaEvent } from './mediaEvent';
2
2
  import { v4 as uuidv4 } from 'uuid';
3
3
  import EventEmitter from 'events';
4
4
  import { Deferred } from './deferred';
@@ -23,10 +23,8 @@ export class WebRTCEndpoint extends EventEmitter {
23
23
  clearConnectionCallbacks = null;
24
24
  constructor(config) {
25
25
  super();
26
- this.endpointMetadataParser =
27
- config?.endpointMetadataParser ?? ((x) => x);
28
- this.trackMetadataParser =
29
- config?.trackMetadataParser ?? ((x) => x);
26
+ this.endpointMetadataParser = config?.endpointMetadataParser ?? ((x) => x);
27
+ this.trackMetadataParser = config?.trackMetadataParser ?? ((x) => x);
30
28
  const sendEvent = (mediaEvent) => this.sendMediaEvent(mediaEvent);
31
29
  const emit = (events, ...args) => {
32
30
  this.emit(events, ...args);
@@ -77,8 +75,7 @@ export class WebRTCEndpoint extends EventEmitter {
77
75
  switch (deserializedMediaEvent.type) {
78
76
  case 'connected': {
79
77
  this.local.setLocalEndpointId(deserializedMediaEvent.data.id);
80
- const endpoints = deserializedMediaEvent.data
81
- .otherEndpoints;
78
+ const endpoints = deserializedMediaEvent.data.otherEndpoints;
82
79
  // todo implement track mapping (+ validate metadata)
83
80
  // todo implement endpoint metadata mapping
84
81
  endpoints.forEach((endpoint) => {
@@ -113,8 +110,7 @@ export class WebRTCEndpoint extends EventEmitter {
113
110
  * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/getStats | MDN Web Docs: RTCPeerConnection.getStats()}
114
111
  */
115
112
  async getStatistics(selector) {
116
- return ((await this.connectionManager?.getConnection().getStats(selector)) ??
117
- new Map());
113
+ return (await this.connectionManager?.getConnection().getStats(selector)) ?? new Map();
118
114
  }
119
115
  /**
120
116
  * Returns a snapshot of currently received remote tracks.
@@ -361,8 +357,6 @@ export class WebRTCEndpoint extends EventEmitter {
361
357
  * @param trackId - Audio or video track.
362
358
  * @param {string} trackId - Id of audio or video track to replace.
363
359
  * @param {MediaStreamTrack} newTrack
364
- * @param {any} [newTrackMetadata] - Optional track metadata to apply to the new track. If no
365
- * track metadata is passed, the old track metadata is retained.
366
360
  * @returns {Promise<boolean>} success
367
361
  * @example
368
362
  * ```ts
@@ -403,29 +397,17 @@ export class WebRTCEndpoint extends EventEmitter {
403
397
  * })
404
398
  * ```
405
399
  */
406
- async replaceTrack(trackId, newTrack, newTrackMetadata) {
400
+ async replaceTrack(trackId, newTrack) {
407
401
  const resolutionNotifier = new Deferred();
408
- try {
409
- const newMetadata = newTrackMetadata !== undefined
410
- ? this.trackMetadataParser(newTrackMetadata)
411
- : undefined;
412
- this.commandsQueue.pushCommand({
413
- handler: () => {
414
- this.localTrackManager.replaceTrackHandler(this, trackId, newTrack, newMetadata);
415
- },
416
- resolutionNotifier,
417
- resolve: 'immediately',
418
- });
419
- }
420
- catch (error) {
421
- resolutionNotifier.reject(error);
422
- }
402
+ this.commandsQueue.pushCommand({
403
+ handler: () => {
404
+ this.localTrackManager.replaceTrackHandler(this, trackId, newTrack);
405
+ },
406
+ resolutionNotifier,
407
+ resolve: 'immediately',
408
+ });
423
409
  return resolutionNotifier.promise.then(() => {
424
- this.emit('localTrackReplaced', {
425
- trackId,
426
- track: newTrack,
427
- metadata: newTrackMetadata,
428
- });
410
+ this.emit('localTrackReplaced', { trackId, track: newTrack });
429
411
  });
430
412
  }
431
413
  /**
@@ -635,31 +617,15 @@ export class WebRTCEndpoint extends EventEmitter {
635
617
  if (!connection)
636
618
  throw new Error(`There is no active RTCPeerConnection`);
637
619
  this.clearConnectionCallbacks = () => {
638
- connection
639
- ?.getConnection()
640
- ?.removeEventListener('icecandidate', onIceCandidate);
641
- connection
642
- ?.getConnection()
643
- ?.removeEventListener('icecandidateerror', onIceCandidateError);
644
- connection
645
- ?.getConnection()
646
- ?.removeEventListener('connectionstatechange', onConnectionStateChange);
647
- connection
648
- ?.getConnection()
649
- ?.removeEventListener('iceconnectionstatechange', onIceConnectionStateChange);
620
+ connection?.getConnection()?.removeEventListener('icecandidate', onIceCandidate);
621
+ connection?.getConnection()?.removeEventListener('icecandidateerror', onIceCandidateError);
622
+ connection?.getConnection()?.removeEventListener('connectionstatechange', onConnectionStateChange);
623
+ connection?.getConnection()?.removeEventListener('iceconnectionstatechange', onIceConnectionStateChange);
650
624
  };
651
- connection
652
- .getConnection()
653
- .addEventListener('icecandidate', onIceCandidate);
654
- connection
655
- .getConnection()
656
- .addEventListener('icecandidateerror', onIceCandidateError);
657
- connection
658
- .getConnection()
659
- .addEventListener('connectionstatechange', onConnectionStateChange);
660
- connection
661
- .getConnection()
662
- .addEventListener('iceconnectionstatechange', onIceConnectionStateChange);
625
+ connection.getConnection().addEventListener('icecandidate', onIceCandidate);
626
+ connection.getConnection().addEventListener('icecandidateerror', onIceCandidateError);
627
+ connection.getConnection().addEventListener('connectionstatechange', onConnectionStateChange);
628
+ connection.getConnection().addEventListener('iceconnectionstatechange', onIceConnectionStateChange);
663
629
  this.commandsQueue.initConnection(connection);
664
630
  this.local.addAllTracksToConnection();
665
631
  connection.setTransceiversToReadOnly();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fishjam-cloud/ts-client",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Typescript client library for Fishjam Cloud",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Fishjam Cloud Team",
@@ -52,22 +52,22 @@
52
52
  "uuid": "^10.0.0"
53
53
  },
54
54
  "devDependencies": {
55
- "@playwright/test": "^1.45.3",
55
+ "@playwright/test": "^1.46.1",
56
56
  "@types/events": "^3.0.3",
57
- "@types/node": "^22.0.0",
57
+ "@types/node": "^22.4.1",
58
58
  "@types/uuid": "^10.0.0",
59
- "@vitest/coverage-v8": "^2.0.4",
59
+ "@vitest/coverage-v8": "^2.0.5",
60
60
  "fake-mediastreamtrack": "^1.2.0",
61
61
  "husky": "^9.1.4",
62
- "lint-staged": "^15.2.5",
62
+ "lint-staged": "^15.2.9",
63
63
  "react": "^18.2.0",
64
- "ts-proto": "^1.181.1",
64
+ "ts-proto": "^2.0.2",
65
65
  "typed-emitter": "^2.1.0",
66
- "typedoc": "^0.26.5",
66
+ "typedoc": "^0.26.6",
67
67
  "typedoc-plugin-external-resolver": "^1.0.3",
68
- "typedoc-plugin-mdn-links": "^3.2.6",
68
+ "typedoc-plugin-mdn-links": "^3.2.9",
69
69
  "typescript": "^5.5.4",
70
- "vitest": "^2.0.4",
70
+ "vitest": "^2.0.5",
71
71
  "zod": "^3.23.6"
72
72
  },
73
73
  "lint-staged": {