@stream-io/video-client 1.17.0 → 1.18.0
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 +20 -0
- package/dist/index.browser.es.js +66 -46
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +65 -47
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +66 -46
- package/dist/index.es.js.map +1 -1
- package/dist/src/coordinator/connection/client.d.ts +1 -6
- package/dist/src/coordinator/connection/types.d.ts +12 -0
- package/dist/src/helpers/DynascaleManager.d.ts +0 -2
- package/dist/src/helpers/client-details.d.ts +1 -6
- package/dist/src/helpers/clientUtils.d.ts +1 -1
- package/dist/src/stats/SfuStatsReporter.d.ts +2 -3
- package/dist/src/stats/utils.d.ts +2 -6
- package/package.json +2 -2
- package/src/Call.ts +8 -6
- package/src/coordinator/connection/client.ts +23 -23
- package/src/coordinator/connection/connection.ts +1 -1
- package/src/coordinator/connection/types.ts +14 -0
- package/src/helpers/DynascaleManager.ts +0 -2
- package/src/helpers/__tests__/clientUtils.test.ts +29 -3
- package/src/helpers/client-details.ts +23 -19
- package/src/helpers/clientUtils.ts +22 -12
- package/src/rtc/BasePeerConnection.ts +1 -0
- package/src/stats/SfuStatsReporter.ts +3 -6
- package/src/stats/utils.ts +2 -3
|
@@ -21,7 +21,7 @@ export declare class StreamClient {
|
|
|
21
21
|
setUserPromise: ConnectAPIResponse | null;
|
|
22
22
|
tokenManager: TokenManager;
|
|
23
23
|
user?: UserWithId;
|
|
24
|
-
|
|
24
|
+
private cachedUserAgent?;
|
|
25
25
|
userID?: string;
|
|
26
26
|
wsBaseURL?: string;
|
|
27
27
|
wsConnection: StableWSConnection | null;
|
|
@@ -142,11 +142,6 @@ export declare class StreamClient {
|
|
|
142
142
|
*/
|
|
143
143
|
connect: () => Promise<ConnectedEvent | undefined>;
|
|
144
144
|
getUserAgent: () => string;
|
|
145
|
-
setUserAgent: (userAgent: string) => void;
|
|
146
|
-
/**
|
|
147
|
-
* _isUsingServerAuth - Returns true if we're using server side auth
|
|
148
|
-
*/
|
|
149
|
-
_isUsingServerAuth: () => boolean;
|
|
150
145
|
_enrichAxiosOptions: (options?: AxiosRequestConfig & {
|
|
151
146
|
config?: AxiosRequestConfig;
|
|
152
147
|
} & {
|
|
@@ -124,6 +124,18 @@ export type StreamClientOptions = Partial<AxiosRequestConfig> & {
|
|
|
124
124
|
* timer throttling issues in inactive browser tabs.
|
|
125
125
|
*/
|
|
126
126
|
enableTimerWorker?: boolean;
|
|
127
|
+
/**
|
|
128
|
+
* The client app identifier.
|
|
129
|
+
*/
|
|
130
|
+
clientAppIdentifier?: ClientAppIdentifier;
|
|
131
|
+
};
|
|
132
|
+
export type ClientAppIdentifier = {
|
|
133
|
+
sdkName?: 'react' | 'react-native' | 'plain-javascript' | (string & {});
|
|
134
|
+
sdkVersion?: string;
|
|
135
|
+
app?: string;
|
|
136
|
+
app_version?: string;
|
|
137
|
+
os?: string;
|
|
138
|
+
device_model?: string;
|
|
127
139
|
};
|
|
128
140
|
export type TokenProvider = () => Promise<string>;
|
|
129
141
|
export type TokenOrProvider = null | string | TokenProvider | undefined;
|
|
@@ -51,8 +51,6 @@ export declare class DynascaleManager {
|
|
|
51
51
|
}>;
|
|
52
52
|
/**
|
|
53
53
|
* Creates a new DynascaleManager instance.
|
|
54
|
-
*
|
|
55
|
-
* @param call the call to manage.
|
|
56
54
|
*/
|
|
57
55
|
constructor(callState: CallState, speaker: SpeakerManager);
|
|
58
56
|
setSfuClient(sfuClient: StreamSfuClient | undefined): void;
|
|
@@ -5,14 +5,9 @@ type WebRTCInfoType = {
|
|
|
5
5
|
export declare const setSdkInfo: (info: Sdk) => void;
|
|
6
6
|
export declare const getSdkInfo: () => Sdk | undefined;
|
|
7
7
|
export declare const setOSInfo: (info: OS) => void;
|
|
8
|
-
export declare const getOSInfo: () => OS | undefined;
|
|
9
8
|
export declare const setDeviceInfo: (info: Device) => void;
|
|
10
|
-
export declare const getDeviceInfo: () => Device | undefined;
|
|
11
9
|
export declare const getWebRTCInfo: () => WebRTCInfoType | undefined;
|
|
12
10
|
export declare const setWebRTCInfo: (info: WebRTCInfoType) => void;
|
|
13
|
-
export type LocalClientDetailsType = ClientDetails & {
|
|
14
|
-
webRTCInfo?: WebRTCInfoType;
|
|
15
|
-
};
|
|
16
11
|
export declare const setThermalState: (state: string) => void;
|
|
17
12
|
export declare const setPowerState: (powerMode: boolean) => void;
|
|
18
13
|
export declare const getDeviceState: () => {
|
|
@@ -24,5 +19,5 @@ export declare const getDeviceState: () => {
|
|
|
24
19
|
} | {
|
|
25
20
|
oneofKind: undefined;
|
|
26
21
|
};
|
|
27
|
-
export declare const getClientDetails: () =>
|
|
22
|
+
export declare const getClientDetails: () => Promise<ClientDetails>;
|
|
28
23
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StreamClientOptions, TokenOrProvider, User } from '../coordinator/connection/types';
|
|
1
|
+
import type { StreamClientOptions, TokenOrProvider, User } from '../coordinator/connection/types';
|
|
2
2
|
import { StreamClient } from '../coordinator/connection/client';
|
|
3
3
|
import type { StreamVideoClientOptions } from '../StreamVideoClient';
|
|
4
4
|
/**
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { StreamSfuClient } from '../StreamSfuClient';
|
|
2
2
|
import { StatsOptions } from '../gen/coordinator';
|
|
3
3
|
import { Publisher, Subscriber } from '../rtc';
|
|
4
|
-
import {
|
|
5
|
-
import { WebsocketReconnectStrategy } from '../gen/video/sfu/models/models';
|
|
4
|
+
import { ClientDetails, WebsocketReconnectStrategy } from '../gen/video/sfu/models/models';
|
|
6
5
|
import { CameraManager, MicrophoneManager } from '../devices';
|
|
7
6
|
import { CallState } from '../store';
|
|
8
7
|
export type SfuStatsReporterOptions = {
|
|
9
8
|
options: StatsOptions;
|
|
10
|
-
clientDetails:
|
|
9
|
+
clientDetails: ClientDetails;
|
|
11
10
|
subscriber: Subscriber;
|
|
12
11
|
publisher?: Publisher;
|
|
13
12
|
microphone: MicrophoneManager;
|
|
@@ -1,18 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Sdk } from '../gen/video/sfu/models/models';
|
|
1
|
+
import { ClientDetails, Sdk } from '../gen/video/sfu/models/models';
|
|
3
2
|
/**
|
|
4
3
|
* Flatten the stats report into an array of stats objects.
|
|
5
4
|
*
|
|
6
5
|
* @param report the report to flatten.
|
|
7
6
|
*/
|
|
8
7
|
export declare const flatten: (report: RTCStatsReport) => RTCStats[];
|
|
9
|
-
export declare const getSdkSignature: (clientDetails:
|
|
8
|
+
export declare const getSdkSignature: (clientDetails: ClientDetails) => {
|
|
10
9
|
os?: import("../gen/video/sfu/models/models").OS;
|
|
11
10
|
browser?: import("../gen/video/sfu/models/models").Browser;
|
|
12
11
|
device?: import("../gen/video/sfu/models/models").Device;
|
|
13
|
-
webRTCInfo?: {
|
|
14
|
-
version: string;
|
|
15
|
-
};
|
|
16
12
|
sdkName: string;
|
|
17
13
|
sdkVersion: string;
|
|
18
14
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stream-io/video-client",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.0",
|
|
4
4
|
"packageManager": "yarn@3.2.4",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"module": "dist/index.es.js",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@protobuf-ts/runtime": "^2.9.4",
|
|
32
32
|
"@protobuf-ts/runtime-rpc": "^2.9.4",
|
|
33
33
|
"@protobuf-ts/twirp-transport": "^2.9.4",
|
|
34
|
-
"axios": "^1.
|
|
34
|
+
"axios": "^1.8.1",
|
|
35
35
|
"rxjs": "~7.8.1",
|
|
36
36
|
"sdp-transform": "^2.15.0",
|
|
37
37
|
"ua-parser-js": "^1.0.40",
|
package/src/Call.ts
CHANGED
|
@@ -601,6 +601,7 @@ export class Call {
|
|
|
601
601
|
this.leaveCallHooks.forEach((hook) => hook());
|
|
602
602
|
this.initialized = false;
|
|
603
603
|
this.hasJoinedOnce = false;
|
|
604
|
+
this.ringingSubject.next(false);
|
|
604
605
|
this.clientStore.unregisterCall(this);
|
|
605
606
|
|
|
606
607
|
this.camera.dispose();
|
|
@@ -692,7 +693,7 @@ export class Call {
|
|
|
692
693
|
this.state.setMembers(response.members);
|
|
693
694
|
this.state.setOwnCapabilities(response.own_capabilities);
|
|
694
695
|
|
|
695
|
-
if (params?.ring
|
|
696
|
+
if (params?.ring) {
|
|
696
697
|
// the call response can indicate where the call is still ringing or not
|
|
697
698
|
this.ringingSubject.next(true);
|
|
698
699
|
}
|
|
@@ -723,7 +724,7 @@ export class Call {
|
|
|
723
724
|
this.state.setMembers(response.members);
|
|
724
725
|
this.state.setOwnCapabilities(response.own_capabilities);
|
|
725
726
|
|
|
726
|
-
if (data?.ring
|
|
727
|
+
if (data?.ring) {
|
|
727
728
|
// the call response can indicate where the call is still ringing or not
|
|
728
729
|
this.ringingSubject.next(true);
|
|
729
730
|
}
|
|
@@ -866,7 +867,7 @@ export class Call {
|
|
|
866
867
|
this.sfuClient = sfuClient;
|
|
867
868
|
this.dynascaleManager.setSfuClient(sfuClient);
|
|
868
869
|
|
|
869
|
-
const clientDetails = getClientDetails();
|
|
870
|
+
const clientDetails = await getClientDetails();
|
|
870
871
|
// we don't need to send JoinRequest if we are re-using an existing healthy SFU client
|
|
871
872
|
if (previousSfuClient !== sfuClient) {
|
|
872
873
|
// prepare a generic SDP and send it to the SFU.
|
|
@@ -1192,7 +1193,7 @@ export class Call {
|
|
|
1192
1193
|
this.state.setMembers(joinResponse.members);
|
|
1193
1194
|
this.state.setOwnCapabilities(joinResponse.own_capabilities);
|
|
1194
1195
|
|
|
1195
|
-
if (data?.ring
|
|
1196
|
+
if (data?.ring) {
|
|
1196
1197
|
this.ringingSubject.next(true);
|
|
1197
1198
|
}
|
|
1198
1199
|
|
|
@@ -2308,8 +2309,9 @@ export class Call {
|
|
|
2308
2309
|
custom,
|
|
2309
2310
|
}: Pick<CollectUserFeedbackRequest, 'reason' | 'custom'> = {},
|
|
2310
2311
|
): Promise<CollectUserFeedbackResponse> => {
|
|
2311
|
-
const { sdkName, sdkVersion, ...platform } =
|
|
2312
|
-
|
|
2312
|
+
const { sdkName, sdkVersion, ...platform } = getSdkSignature(
|
|
2313
|
+
await getClientDetails(),
|
|
2314
|
+
);
|
|
2313
2315
|
return this.streamClient.post<
|
|
2314
2316
|
CollectUserFeedbackResponse,
|
|
2315
2317
|
CollectUserFeedbackRequest
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
CreateGuestResponse,
|
|
39
39
|
} from '../../gen/coordinator';
|
|
40
40
|
import { makeSafePromise, type SafePromise } from '../../helpers/promise';
|
|
41
|
+
import { getLogLevel } from '../../logger';
|
|
41
42
|
|
|
42
43
|
export class StreamClient {
|
|
43
44
|
_user?: UserWithId;
|
|
@@ -61,7 +62,7 @@ export class StreamClient {
|
|
|
61
62
|
setUserPromise: ConnectAPIResponse | null;
|
|
62
63
|
tokenManager: TokenManager;
|
|
63
64
|
user?: UserWithId;
|
|
64
|
-
|
|
65
|
+
private cachedUserAgent?: string;
|
|
65
66
|
userID?: string;
|
|
66
67
|
wsBaseURL?: string;
|
|
67
68
|
wsConnection: StableWSConnection | null;
|
|
@@ -218,10 +219,7 @@ export class StreamClient {
|
|
|
218
219
|
);
|
|
219
220
|
}
|
|
220
221
|
|
|
221
|
-
if (
|
|
222
|
-
(this._isUsingServerAuth() || this.node) &&
|
|
223
|
-
!this.options.allowServerSideConnect
|
|
224
|
-
) {
|
|
222
|
+
if ((this.secret || this.node) && !this.options.allowServerSideConnect) {
|
|
225
223
|
this.logger(
|
|
226
224
|
'warn',
|
|
227
225
|
'Please do not use connectUser server side. Use our @stream-io/node-sdk instead: https://getstream.io/video/docs/api/',
|
|
@@ -326,7 +324,7 @@ export class StreamClient {
|
|
|
326
324
|
return;
|
|
327
325
|
}
|
|
328
326
|
|
|
329
|
-
|
|
327
|
+
this._setupConnectionIdPromise();
|
|
330
328
|
|
|
331
329
|
this.clientID = `${this.userID}--${randomId()}`;
|
|
332
330
|
const newWsPromise = this.connect();
|
|
@@ -382,7 +380,7 @@ export class StreamClient {
|
|
|
382
380
|
tokenOrProvider: TokenOrProvider,
|
|
383
381
|
) => {
|
|
384
382
|
addConnectionEventListeners(this.updateNetworkConnectionStatus);
|
|
385
|
-
|
|
383
|
+
this._setupConnectionIdPromise();
|
|
386
384
|
|
|
387
385
|
this.anonymous = true;
|
|
388
386
|
await this._setToken(user, tokenOrProvider, this.anonymous);
|
|
@@ -469,6 +467,7 @@ export class StreamClient {
|
|
|
469
467
|
config?: AxiosRequestConfig & { maxBodyLength?: number };
|
|
470
468
|
},
|
|
471
469
|
) => {
|
|
470
|
+
if (getLogLevel() !== 'trace') return;
|
|
472
471
|
this.logger('trace', `client: ${type} - Request - ${url}`, {
|
|
473
472
|
payload: data,
|
|
474
473
|
config,
|
|
@@ -480,6 +479,7 @@ export class StreamClient {
|
|
|
480
479
|
url: string,
|
|
481
480
|
response: AxiosResponse<T>,
|
|
482
481
|
) => {
|
|
482
|
+
if (getLogLevel() !== 'trace') return;
|
|
483
483
|
this.logger(
|
|
484
484
|
'trace',
|
|
485
485
|
`client:${type} - Response - url: ${url} > status ${response.status}`,
|
|
@@ -666,25 +666,25 @@ export class StreamClient {
|
|
|
666
666
|
return await this.wsConnection.connect(this.defaultWSTimeout);
|
|
667
667
|
};
|
|
668
668
|
|
|
669
|
-
getUserAgent = () => {
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
669
|
+
getUserAgent = (): string => {
|
|
670
|
+
if (!this.cachedUserAgent) {
|
|
671
|
+
const { clientAppIdentifier = {} } = this.options;
|
|
672
|
+
const {
|
|
673
|
+
sdkName = 'js',
|
|
674
|
+
sdkVersion = process.env.PKG_VERSION || '0.0.0',
|
|
675
|
+
...extras
|
|
676
|
+
} = clientAppIdentifier;
|
|
677
|
+
|
|
678
|
+
this.cachedUserAgent = [
|
|
679
|
+
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
680
|
+
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|
|
681
|
+
`client_bundle=${process.env.CLIENT_BUNDLE || (this.node ? 'node' : 'browser')}`,
|
|
682
|
+
].join('|');
|
|
683
|
+
}
|
|
678
684
|
|
|
679
|
-
|
|
680
|
-
this.userAgent = userAgent;
|
|
685
|
+
return this.cachedUserAgent;
|
|
681
686
|
};
|
|
682
687
|
|
|
683
|
-
/**
|
|
684
|
-
* _isUsingServerAuth - Returns true if we're using server side auth
|
|
685
|
-
*/
|
|
686
|
-
_isUsingServerAuth = () => !!this.secret;
|
|
687
|
-
|
|
688
688
|
_enrichAxiosOptions = (
|
|
689
689
|
options: AxiosRequestConfig & { config?: AxiosRequestConfig } & {
|
|
690
690
|
publicEndpoint?: boolean;
|
|
@@ -329,7 +329,7 @@ export class StableWSConnection {
|
|
|
329
329
|
return response;
|
|
330
330
|
}
|
|
331
331
|
} catch (err) {
|
|
332
|
-
|
|
332
|
+
this.client._setupConnectionIdPromise();
|
|
333
333
|
this.isConnecting = false;
|
|
334
334
|
// @ts-ignore
|
|
335
335
|
this._log(`_connect() - Error - `, err);
|
|
@@ -152,6 +152,20 @@ export type StreamClientOptions = Partial<AxiosRequestConfig> & {
|
|
|
152
152
|
* timer throttling issues in inactive browser tabs.
|
|
153
153
|
*/
|
|
154
154
|
enableTimerWorker?: boolean;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* The client app identifier.
|
|
158
|
+
*/
|
|
159
|
+
clientAppIdentifier?: ClientAppIdentifier;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export type ClientAppIdentifier = {
|
|
163
|
+
sdkName?: 'react' | 'react-native' | 'plain-javascript' | (string & {});
|
|
164
|
+
sdkVersion?: string;
|
|
165
|
+
app?: string;
|
|
166
|
+
app_version?: string;
|
|
167
|
+
os?: string;
|
|
168
|
+
device_model?: string;
|
|
155
169
|
};
|
|
156
170
|
|
|
157
171
|
export type TokenProvider = () => Promise<string>;
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
getInstanceKey,
|
|
6
6
|
} from '../clientUtils';
|
|
7
7
|
import { TokenProvider } from '../../coordinator/connection/types';
|
|
8
|
+
import { getSdkInfo, setSdkInfo } from '../client-details';
|
|
8
9
|
|
|
9
10
|
describe('clientUtils', () => {
|
|
10
11
|
describe('getInstanceKey', () => {
|
|
@@ -46,14 +47,39 @@ describe('clientUtils', () => {
|
|
|
46
47
|
|
|
47
48
|
describe('createCoordinatorClient', () => {
|
|
48
49
|
it('should create a coordinator client', () => {
|
|
49
|
-
const client = createCoordinatorClient('apiKey', {
|
|
50
|
+
const client = createCoordinatorClient('apiKey', {
|
|
51
|
+
timeout: 1000,
|
|
52
|
+
clientAppIdentifier: {
|
|
53
|
+
app: 'vitest',
|
|
54
|
+
app_version: '1.0.0',
|
|
55
|
+
device_model: 'iPhone',
|
|
56
|
+
os: 'iOS 18.4',
|
|
57
|
+
},
|
|
58
|
+
});
|
|
50
59
|
expect(client).toBeDefined();
|
|
51
|
-
expect(client.
|
|
52
|
-
'stream-video-
|
|
60
|
+
expect(client.getUserAgent()).toBe(
|
|
61
|
+
'stream-video-plain_javascript-v0.0.0' +
|
|
62
|
+
'|app=vitest' +
|
|
63
|
+
'|app_version=1.0.0' +
|
|
64
|
+
'|device_model=iPhone' +
|
|
65
|
+
'|os=iOS 18.4' +
|
|
66
|
+
'|client_bundle=node',
|
|
53
67
|
);
|
|
54
68
|
expect(client.logger).toBeDefined();
|
|
55
69
|
expect(client.options.persistUserOnConnectionFailure).toBe(true);
|
|
56
70
|
expect(client.options.timeout).toBe(1000);
|
|
57
71
|
});
|
|
72
|
+
|
|
73
|
+
it('should use default x-stream-client header', () => {
|
|
74
|
+
const sdk = getSdkInfo();
|
|
75
|
+
// @ts-expect-error
|
|
76
|
+
setSdkInfo(undefined);
|
|
77
|
+
const client = createCoordinatorClient('apiKey', {});
|
|
78
|
+
expect(client).toBeDefined();
|
|
79
|
+
expect(client.getUserAgent()).toBe(
|
|
80
|
+
`stream-video-js-v0.0.0|client_bundle=node`,
|
|
81
|
+
);
|
|
82
|
+
setSdkInfo(sdk!);
|
|
83
|
+
});
|
|
58
84
|
});
|
|
59
85
|
});
|
|
@@ -42,18 +42,10 @@ export const setOSInfo = (info: OS) => {
|
|
|
42
42
|
osInfo = info;
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
export const getOSInfo = () => {
|
|
46
|
-
return osInfo;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
45
|
export const setDeviceInfo = (info: Device) => {
|
|
50
46
|
deviceInfo = info;
|
|
51
47
|
};
|
|
52
48
|
|
|
53
|
-
export const getDeviceInfo = () => {
|
|
54
|
-
return deviceInfo;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
49
|
export const getWebRTCInfo = () => {
|
|
58
50
|
return webRtcInfo;
|
|
59
51
|
};
|
|
@@ -62,10 +54,6 @@ export const setWebRTCInfo = (info: WebRTCInfoType) => {
|
|
|
62
54
|
webRtcInfo = info;
|
|
63
55
|
};
|
|
64
56
|
|
|
65
|
-
export type LocalClientDetailsType = ClientDetails & {
|
|
66
|
-
webRTCInfo?: WebRTCInfoType;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
57
|
export const setThermalState = (state: string) => {
|
|
70
58
|
if (!osInfo) {
|
|
71
59
|
deviceState = { oneofKind: undefined };
|
|
@@ -142,27 +130,43 @@ export const getDeviceState = () => {
|
|
|
142
130
|
return deviceState;
|
|
143
131
|
};
|
|
144
132
|
|
|
145
|
-
export const getClientDetails = ():
|
|
133
|
+
export const getClientDetails = async (): Promise<ClientDetails> => {
|
|
146
134
|
if (isReactNative()) {
|
|
147
135
|
// Since RN doesn't support web, sharing browser info is not required
|
|
148
136
|
return {
|
|
149
|
-
sdk:
|
|
150
|
-
os:
|
|
151
|
-
device:
|
|
137
|
+
sdk: sdkInfo,
|
|
138
|
+
os: osInfo,
|
|
139
|
+
device: deviceInfo,
|
|
152
140
|
};
|
|
153
141
|
}
|
|
154
142
|
|
|
143
|
+
// @ts-expect-error - userAgentData is not yet in the TS types
|
|
144
|
+
const userAgentDataApi = navigator.userAgentData;
|
|
145
|
+
let userAgentData:
|
|
146
|
+
| { platform?: string; platformVersion?: string }
|
|
147
|
+
| undefined;
|
|
148
|
+
if (userAgentDataApi && userAgentDataApi.getHighEntropyValues) {
|
|
149
|
+
try {
|
|
150
|
+
userAgentData = await userAgentDataApi.getHighEntropyValues([
|
|
151
|
+
'platform',
|
|
152
|
+
'platformVersion',
|
|
153
|
+
]);
|
|
154
|
+
} catch (e) {
|
|
155
|
+
// Ignore the error
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
155
159
|
const userAgent = new UAParser(navigator.userAgent);
|
|
156
160
|
const { browser, os, device, cpu } = userAgent.getResult();
|
|
157
161
|
return {
|
|
158
|
-
sdk:
|
|
162
|
+
sdk: sdkInfo,
|
|
159
163
|
browser: {
|
|
160
164
|
name: browser.name || navigator.userAgent,
|
|
161
165
|
version: browser.version || '',
|
|
162
166
|
},
|
|
163
167
|
os: {
|
|
164
|
-
name: os.name || '',
|
|
165
|
-
version: os.version || '',
|
|
168
|
+
name: userAgentData?.platform || os.name || '',
|
|
169
|
+
version: userAgentData?.platformVersion || os.version || '',
|
|
166
170
|
architecture: cpu.architecture || '',
|
|
167
171
|
},
|
|
168
172
|
device: {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type {
|
|
2
|
+
ClientAppIdentifier,
|
|
2
3
|
StreamClientOptions,
|
|
3
4
|
TokenOrProvider,
|
|
4
5
|
User,
|
|
@@ -16,6 +17,23 @@ export const getInstanceKey = (apiKey: string, user: User) => {
|
|
|
16
17
|
return `${apiKey}/${user.id}`;
|
|
17
18
|
};
|
|
18
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Utility function to get the client app identifier.
|
|
22
|
+
*/
|
|
23
|
+
const getClientAppIdentifier = (
|
|
24
|
+
options?: StreamClientOptions,
|
|
25
|
+
): ClientAppIdentifier => {
|
|
26
|
+
const appId = options?.clientAppIdentifier || {};
|
|
27
|
+
const sdkInfo = getSdkInfo();
|
|
28
|
+
if (sdkInfo) {
|
|
29
|
+
// ensure the sdk name and version are set correctly,
|
|
30
|
+
// overriding any user-provided values
|
|
31
|
+
appId.sdkName = SdkType[sdkInfo.type].toLowerCase();
|
|
32
|
+
appId.sdkVersion = `${sdkInfo.major}.${sdkInfo.minor}.${sdkInfo.patch}`;
|
|
33
|
+
}
|
|
34
|
+
return appId;
|
|
35
|
+
};
|
|
36
|
+
|
|
19
37
|
/**
|
|
20
38
|
* Creates a coordinator client.
|
|
21
39
|
*/
|
|
@@ -23,22 +41,14 @@ export const createCoordinatorClient = (
|
|
|
23
41
|
apiKey: string,
|
|
24
42
|
options: StreamClientOptions | undefined,
|
|
25
43
|
) => {
|
|
44
|
+
const clientAppIdentifier = getClientAppIdentifier(options);
|
|
26
45
|
const coordinatorLogger = getLogger(['coordinator']);
|
|
27
|
-
|
|
46
|
+
return new StreamClient(apiKey, {
|
|
28
47
|
persistUserOnConnectionFailure: true,
|
|
29
48
|
...options,
|
|
49
|
+
clientAppIdentifier,
|
|
30
50
|
logger: coordinatorLogger,
|
|
31
51
|
});
|
|
32
|
-
const sdkInfo = getSdkInfo();
|
|
33
|
-
if (sdkInfo) {
|
|
34
|
-
const sdkName = SdkType[sdkInfo.type].toLowerCase();
|
|
35
|
-
const sdkVersion = `${sdkInfo.major}.${sdkInfo.minor}.${sdkInfo.patch}`;
|
|
36
|
-
const userAgent = streamClient.getUserAgent();
|
|
37
|
-
streamClient.setUserAgent(
|
|
38
|
-
`${userAgent}-video-${sdkName}-sdk-${sdkVersion}`,
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
return streamClient;
|
|
42
52
|
};
|
|
43
53
|
|
|
44
54
|
/**
|
|
@@ -206,6 +206,7 @@ export abstract class BasePeerConnection {
|
|
|
206
206
|
const state = this.pc.iceConnectionState;
|
|
207
207
|
this.logger('debug', `ICE connection state changed`, state);
|
|
208
208
|
|
|
209
|
+
if (this.state.callingState === CallingState.OFFLINE) return;
|
|
209
210
|
if (this.state.callingState === CallingState.RECONNECTING) return;
|
|
210
211
|
|
|
211
212
|
// do nothing when ICE is restarting
|
|
@@ -4,12 +4,9 @@ import { OwnCapability, StatsOptions } from '../gen/coordinator';
|
|
|
4
4
|
import { getLogger } from '../logger';
|
|
5
5
|
import { Publisher, Subscriber } from '../rtc';
|
|
6
6
|
import { flatten, getSdkName, getSdkVersion } from './utils';
|
|
7
|
+
import { getDeviceState, getWebRTCInfo } from '../helpers/client-details';
|
|
7
8
|
import {
|
|
8
|
-
|
|
9
|
-
getWebRTCInfo,
|
|
10
|
-
LocalClientDetailsType,
|
|
11
|
-
} from '../helpers/client-details';
|
|
12
|
-
import {
|
|
9
|
+
ClientDetails,
|
|
13
10
|
InputDevices,
|
|
14
11
|
WebsocketReconnectStrategy,
|
|
15
12
|
} from '../gen/video/sfu/models/models';
|
|
@@ -20,7 +17,7 @@ import { Telemetry } from '../gen/video/sfu/signal_rpc/signal';
|
|
|
20
17
|
|
|
21
18
|
export type SfuStatsReporterOptions = {
|
|
22
19
|
options: StatsOptions;
|
|
23
|
-
clientDetails:
|
|
20
|
+
clientDetails: ClientDetails;
|
|
24
21
|
subscriber: Subscriber;
|
|
25
22
|
publisher?: Publisher;
|
|
26
23
|
microphone: MicrophoneManager;
|
package/src/stats/utils.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Sdk, SdkType } from '../gen/video/sfu/models/models';
|
|
1
|
+
import { ClientDetails, Sdk, SdkType } from '../gen/video/sfu/models/models';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Flatten the stats report into an array of stats objects.
|
|
@@ -14,7 +13,7 @@ export const flatten = (report: RTCStatsReport) => {
|
|
|
14
13
|
return stats;
|
|
15
14
|
};
|
|
16
15
|
|
|
17
|
-
export const getSdkSignature = (clientDetails:
|
|
16
|
+
export const getSdkSignature = (clientDetails: ClientDetails) => {
|
|
18
17
|
const { sdk, ...platform } = clientDetails;
|
|
19
18
|
const sdkName = getSdkName(sdk);
|
|
20
19
|
const sdkVersion = getSdkVersion(sdk);
|