@wazo/sdk 0.37.2 → 0.37.6
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/dist/wazo-sdk.js +5 -5
- package/esm/__tests__/api-client.test.js +84 -22
- package/esm/__tests__/web-rtc-client.test.js +156 -55
- package/esm/api/call-logd.js +10 -22
- package/esm/api/calld.js +3 -0
- package/esm/api/confd.js +71 -0
- package/esm/api-client.js +84 -22
- package/esm/checker/Checker.js +160 -55
- package/esm/checker/checks/aor.js +84 -22
- package/esm/checker/checks/api.js +84 -22
- package/esm/checker/checks/index.js +160 -55
- package/esm/checker/checks/wazo-websocket.js +4 -0
- package/esm/checker/checks/webrtc-transport.js +156 -55
- package/esm/checker/checks/webrtc.js +156 -55
- package/esm/domain/AdHocAPIConference.js +84 -22
- package/esm/domain/CallLog.js +10 -22
- package/esm/domain/Features.js +84 -22
- package/esm/domain/Meeting.js +71 -0
- package/esm/domain/Phone/CTIPhone.js +84 -22
- package/esm/domain/Phone/WebRTCPhone.js +22 -8
- package/esm/domain/__tests__/CallLog.test.js +10 -22
- package/esm/index.js +726 -371
- package/esm/lib/WazoSessionDescriptionHandler.js +17 -9
- package/esm/service/CallApi.js +84 -22
- package/esm/service/getApiClient.js +84 -22
- package/esm/simple/Auth.js +717 -363
- package/esm/simple/Configuration.js +717 -363
- package/esm/simple/Directory.js +84 -22
- package/esm/simple/Phone.js +709 -355
- package/esm/simple/Websocket.js +6 -0
- package/esm/simple/index.js +719 -365
- package/esm/simple/room/LocalParticipant.js +689 -335
- package/esm/simple/room/Participant.js +689 -335
- package/esm/simple/room/RemoteParticipant.js +689 -335
- package/esm/simple/room/Room.js +689 -335
- package/esm/simple/room/SipRoom.js +13001 -0
- package/esm/simple/room/Stream.js +719 -365
- package/esm/simple/room/__tests__/RemoteParticipant.test.js +4130 -3774
- package/esm/simple/utils.js +719 -365
- package/esm/web-rtc-client.js +156 -55
- package/esm/websocket-client.js +5 -1
- package/lib/__tests__/api-client.test.js +84 -22
- package/lib/__tests__/web-rtc-client.test.js +156 -55
- package/lib/api/call-logd.js +10 -22
- package/lib/api/calld.js +3 -0
- package/lib/api/confd.js +71 -0
- package/lib/api-client.js +84 -22
- package/lib/checker/Checker.js +160 -55
- package/lib/checker/checks/aor.js +84 -22
- package/lib/checker/checks/api.js +84 -22
- package/lib/checker/checks/index.js +160 -55
- package/lib/checker/checks/wazo-websocket.js +4 -0
- package/lib/checker/checks/webrtc-transport.js +156 -55
- package/lib/checker/checks/webrtc.js +156 -55
- package/lib/domain/AdHocAPIConference.js +84 -22
- package/lib/domain/CallLog.js +10 -22
- package/lib/domain/Features.js +84 -22
- package/lib/domain/Meeting.js +73 -0
- package/lib/domain/Phone/CTIPhone.js +84 -22
- package/lib/domain/Phone/WebRTCPhone.js +22 -8
- package/lib/domain/__tests__/CallLog.test.js +10 -22
- package/lib/index.js +726 -371
- package/lib/lib/WazoSessionDescriptionHandler.js +17 -9
- package/lib/service/CallApi.js +84 -22
- package/lib/service/getApiClient.js +84 -22
- package/lib/simple/Auth.js +717 -363
- package/lib/simple/Configuration.js +717 -363
- package/lib/simple/Directory.js +84 -22
- package/lib/simple/Phone.js +709 -355
- package/lib/simple/Websocket.js +6 -0
- package/lib/simple/index.js +719 -365
- package/lib/simple/room/LocalParticipant.js +689 -335
- package/lib/simple/room/Participant.js +689 -335
- package/lib/simple/room/RemoteParticipant.js +689 -335
- package/lib/simple/room/Room.js +689 -335
- package/lib/simple/room/SipRoom.js +13011 -0
- package/lib/simple/room/Stream.js +719 -365
- package/lib/simple/room/__tests__/RemoteParticipant.test.js +4131 -3775
- package/lib/simple/utils.js +719 -365
- package/lib/web-rtc-client.js +156 -55
- package/lib/websocket-client.js +6 -0
- package/package.json +1 -1
- package/src/api/calld.js +3 -0
- package/src/api/confd.js +13 -0
- package/src/domain/CallLog.js +10 -22
- package/src/domain/Meeting.js +59 -0
- package/src/domain/Phone/WebRTCPhone.js +22 -8
- package/src/index.js +2 -0
- package/src/lib/WazoSessionDescriptionHandler.js +17 -9
- package/src/simple/Phone.js +13 -4
- package/src/simple/index.js +4 -0
- package/src/simple/room/Participant.js +7 -0
- package/src/simple/room/Room.js +158 -79
- package/src/simple/room/SipRoom.js +147 -0
- package/src/simple/room/Stream.js +1 -3
- package/src/simple/room/__tests__/RemoteParticipant.test.js +3 -2
- package/src/types/WebSocketMessage.js +21 -0
- package/src/web-rtc-client.js +55 -24
- package/src/websocket-client.js +4 -0
package/lib/web-rtc-client.js
CHANGED
|
@@ -780,7 +780,9 @@ class WazoSessionDescriptionHandler extends sessionDescriptionHandler.SessionDes
|
|
|
780
780
|
.then(() => {
|
|
781
781
|
if (isConference && (options.constraints && !options.constraints.video) && !('hold' in options) && !audioOnly) {
|
|
782
782
|
// Add a video an empty bundle to be able to replaceTrack when joining a conference without video
|
|
783
|
-
this.peerConnection.addTransceiver
|
|
783
|
+
if (this.peerConnection.addTransceiver) {
|
|
784
|
+
this.peerConnection.addTransceiver('video', { streams: [this._localMediaStream], direction: 'sendrecv' });
|
|
785
|
+
}
|
|
784
786
|
}
|
|
785
787
|
})
|
|
786
788
|
.then(() => this.createLocalOfferOrAnswer(options))
|
|
@@ -807,12 +809,6 @@ class WazoSessionDescriptionHandler extends sessionDescriptionHandler.SessionDes
|
|
|
807
809
|
};
|
|
808
810
|
})
|
|
809
811
|
.then((sessionDescription) => this.applyModifiers(sessionDescription, modifiers))
|
|
810
|
-
.then((sessionDescription) => {
|
|
811
|
-
// Set new constraints to avoid the constraints check issue in `sdh.getLocalMediaStream` later.
|
|
812
|
-
this.localMediaStreamConstraints = options.constraints;
|
|
813
|
-
|
|
814
|
-
return sessionDescription;
|
|
815
|
-
})
|
|
816
812
|
.then((sessionDescription) => ({ body: sessionDescription.sdp, contentType: 'application/sdp' }))
|
|
817
813
|
.catch((error) => {
|
|
818
814
|
wazoLogger.error('error when creating media', error);
|
|
@@ -1007,6 +1003,9 @@ class WazoSessionDescriptionHandler extends sessionDescriptionHandler.SessionDes
|
|
|
1007
1003
|
|
|
1008
1004
|
// set the transceiver direction to the answer direction
|
|
1009
1005
|
this._peerConnection.getTransceivers().forEach((transceiver) => {
|
|
1006
|
+
if (transceiver.stopped) {
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1010
1009
|
if (transceiver.direction /* guarding, but should always be true */) {
|
|
1011
1010
|
const { receiver } = transceiver;
|
|
1012
1011
|
if (isConference && audioOnly && receiver.track && receiver.track.kind === 'video') {
|
|
@@ -1051,10 +1050,15 @@ class WazoSessionDescriptionHandler extends sessionDescriptionHandler.SessionDes
|
|
|
1051
1050
|
if (kind !== 'audio' && kind !== 'video') {
|
|
1052
1051
|
throw new Error(`Unknown new track kind ${kind}.`);
|
|
1053
1052
|
}
|
|
1054
|
-
const sender = pc.getSenders().find((otherSender) =>
|
|
1053
|
+
const sender = pc.getSenders && pc.getSenders().find((otherSender) =>
|
|
1054
|
+
otherSender.track && otherSender.track.kind === kind);
|
|
1055
1055
|
|
|
1056
1056
|
// Do not reuse sender video tracks in SFU
|
|
1057
1057
|
if (sender && (!sfu || newTrack.kind === 'audio')) {
|
|
1058
|
+
if (sender.track) {
|
|
1059
|
+
// eslint-disable-next-line no-param-reassign
|
|
1060
|
+
newTrack.enabled = sender.track.enabled;
|
|
1061
|
+
}
|
|
1058
1062
|
trackUpdates.push(
|
|
1059
1063
|
new Promise((resolve) => {
|
|
1060
1064
|
this.logger.debug(`SessionDescriptionHandler.setLocalMediaStream - replacing sender ${kind} track`);
|
|
@@ -1088,7 +1092,11 @@ class WazoSessionDescriptionHandler extends sessionDescriptionHandler.SessionDes
|
|
|
1088
1092
|
// Review: could make streamless tracks a configurable option?
|
|
1089
1093
|
// https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addTrack#Usage_notes
|
|
1090
1094
|
try {
|
|
1091
|
-
pc.addTrack
|
|
1095
|
+
if (pc.addTrack) {
|
|
1096
|
+
pc.addTrack(newTrack, localStream);
|
|
1097
|
+
} else {
|
|
1098
|
+
pc.addStream(localStream);
|
|
1099
|
+
}
|
|
1092
1100
|
} catch (error) {
|
|
1093
1101
|
this.logger.error(`SessionDescriptionHandler.setLocalMediaStream - failed to add sender ${kind} track`);
|
|
1094
1102
|
throw error;
|
|
@@ -3045,6 +3053,65 @@ class ExternalApp {
|
|
|
3045
3053
|
}
|
|
3046
3054
|
}
|
|
3047
3055
|
|
|
3056
|
+
//
|
|
3057
|
+
|
|
3058
|
+
|
|
3059
|
+
|
|
3060
|
+
|
|
3061
|
+
|
|
3062
|
+
|
|
3063
|
+
|
|
3064
|
+
|
|
3065
|
+
|
|
3066
|
+
|
|
3067
|
+
|
|
3068
|
+
class Meeting {
|
|
3069
|
+
|
|
3070
|
+
|
|
3071
|
+
|
|
3072
|
+
|
|
3073
|
+
|
|
3074
|
+
|
|
3075
|
+
|
|
3076
|
+
|
|
3077
|
+
|
|
3078
|
+
|
|
3079
|
+
static parse(plain ) {
|
|
3080
|
+
return new Meeting({
|
|
3081
|
+
guestSipAuthorization: plain.guest_sip_authorization,
|
|
3082
|
+
hostname: plain.hostname,
|
|
3083
|
+
name: plain.name,
|
|
3084
|
+
ownerUuids: plain.owner_uuids,
|
|
3085
|
+
port: plain.port,
|
|
3086
|
+
uuid: plain.uuid,
|
|
3087
|
+
number: plain.number,
|
|
3088
|
+
});
|
|
3089
|
+
}
|
|
3090
|
+
|
|
3091
|
+
static newFrom(meeting ) {
|
|
3092
|
+
return newFrom(meeting, Meeting);
|
|
3093
|
+
}
|
|
3094
|
+
|
|
3095
|
+
constructor({ uuid, name, guestSipAuthorization, ownerUuids, port, hostname, number } = {}) {
|
|
3096
|
+
this.guestSipAuthorization = guestSipAuthorization;
|
|
3097
|
+
this.hostname = hostname;
|
|
3098
|
+
this.name = name;
|
|
3099
|
+
this.ownerUuids = ownerUuids;
|
|
3100
|
+
this.port = port;
|
|
3101
|
+
this.uuid = uuid;
|
|
3102
|
+
this.number = number;
|
|
3103
|
+
|
|
3104
|
+
// Useful to compare instead of instanceof with minified code
|
|
3105
|
+
this.type = 'Meeting';
|
|
3106
|
+
}
|
|
3107
|
+
|
|
3108
|
+
getGuestSipCredentials() {
|
|
3109
|
+
// eslint-disable-next-line no-undef
|
|
3110
|
+
const [username, secret] = atob(this.guestSipAuthorization).split(':');
|
|
3111
|
+
return { username, secret };
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
|
|
3048
3115
|
/* */
|
|
3049
3116
|
|
|
3050
3117
|
var confdMethods = (client , baseUrl ) => ({
|
|
@@ -3109,6 +3176,18 @@ var confdMethods = (client , baseUrl ) => ({
|
|
|
3109
3176
|
return null;
|
|
3110
3177
|
}
|
|
3111
3178
|
},
|
|
3179
|
+
|
|
3180
|
+
createMyMeeting: (name ) =>
|
|
3181
|
+
client.post(`${baseUrl}/users/me/meetings`, { name }).then(Meeting.parse),
|
|
3182
|
+
|
|
3183
|
+
deleteMyMeeting: (meetingUuid ) =>
|
|
3184
|
+
client.delete(`${baseUrl}/users/me/meetings/${meetingUuid}`, null),
|
|
3185
|
+
|
|
3186
|
+
getMeeting: (meetingUuid ) =>
|
|
3187
|
+
client.get(`${baseUrl}/meetings/${meetingUuid}`, null).then(Meeting.parse),
|
|
3188
|
+
|
|
3189
|
+
guestGetMeeting: (meetingUuid ) =>
|
|
3190
|
+
client.get(`${baseUrl}/guests/me/meetings/${meetingUuid}`, null).then(Meeting.parse),
|
|
3112
3191
|
});
|
|
3113
3192
|
|
|
3114
3193
|
//
|
|
@@ -3693,7 +3772,11 @@ class CallLog {
|
|
|
3693
3772
|
answered: plain.answered,
|
|
3694
3773
|
callDirection: plain.call_direction,
|
|
3695
3774
|
destination: {
|
|
3696
|
-
|
|
3775
|
+
// @TEMP: Temporarily assuming empty numbers are meetings
|
|
3776
|
+
// which is admittedly a very dangerous assumption. Did i mention it was temporary?
|
|
3777
|
+
extension: plain.requested_extension
|
|
3778
|
+
|| plain.destination_extension
|
|
3779
|
+
|| `meeting-${plain.requested_name || plain.destination_name || ''}`,
|
|
3697
3780
|
name: plain.requested_name || plain.destination_name || '',
|
|
3698
3781
|
},
|
|
3699
3782
|
source: {
|
|
@@ -3709,27 +3792,11 @@ class CallLog {
|
|
|
3709
3792
|
}
|
|
3710
3793
|
|
|
3711
3794
|
static parseNew(plain , session ) {
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
extension: plain.requested_extension || plain.destination_extension,
|
|
3718
|
-
name: plain.requested_name || plain.destination_name || '',
|
|
3719
|
-
},
|
|
3720
|
-
source: {
|
|
3721
|
-
extension: plain.source_extension,
|
|
3722
|
-
name: plain.source_name,
|
|
3723
|
-
},
|
|
3724
|
-
id: plain.id,
|
|
3725
|
-
duration: (plain.duration || 0) * 1000, // duration is in seconds
|
|
3726
|
-
start: moment__default['default'](plain.start).toDate(),
|
|
3727
|
-
end: plain.end ? moment__default['default'](plain.end).toDate() : null,
|
|
3728
|
-
recordings: Recording.parseMany(plain.recordings),
|
|
3729
|
-
// @TODO: FIXME add verification declined vs missed call
|
|
3730
|
-
newMissedCall: session
|
|
3731
|
-
&& session.hasExtension(plain.requested_extension || plain.destination_extension) && !plain.answered,
|
|
3732
|
-
});
|
|
3795
|
+
const callLog = CallLog.parse(plain);
|
|
3796
|
+
// @TODO: FIXME add verification declined vs missed call
|
|
3797
|
+
callLog.newMissedCall = session
|
|
3798
|
+
&& session.hasExtension(plain.requested_extension || plain.destination_extension) && !plain.answered;
|
|
3799
|
+
return callLog;
|
|
3733
3800
|
}
|
|
3734
3801
|
|
|
3735
3802
|
static newFrom(profile ) {
|
|
@@ -4486,6 +4553,9 @@ var calldMethods = (client , baseUrl ) => ({
|
|
|
4486
4553
|
getConferenceParticipantsAsUser: async (conferenceId ) =>
|
|
4487
4554
|
client.get(`${baseUrl}/users/me/conferences/${conferenceId}/participants`),
|
|
4488
4555
|
|
|
4556
|
+
getMeetingParticipantsAsUser: async (meetingUuid ) =>
|
|
4557
|
+
client.get(`${baseUrl}/users/me/meetings/${meetingUuid}/participants`),
|
|
4558
|
+
|
|
4489
4559
|
listTrunks: () => client.get(`${baseUrl}/trunks`),
|
|
4490
4560
|
|
|
4491
4561
|
mute: (callId ) =>
|
|
@@ -5121,6 +5191,7 @@ const MAX_REGISTER_TRIES = 10;
|
|
|
5121
5191
|
|
|
5122
5192
|
|
|
5123
5193
|
|
|
5194
|
+
|
|
5124
5195
|
|
|
5125
5196
|
|
|
5126
5197
|
|
|
@@ -5147,6 +5218,7 @@ class WebRTCClient extends Emitter {
|
|
|
5147
5218
|
|
|
5148
5219
|
|
|
5149
5220
|
|
|
5221
|
+
|
|
5150
5222
|
|
|
5151
5223
|
// sugar
|
|
5152
5224
|
|
|
@@ -5185,6 +5257,8 @@ class WebRTCClient extends Emitter {
|
|
|
5185
5257
|
super();
|
|
5186
5258
|
this.uaConfigOverrides = uaConfigOverrides;
|
|
5187
5259
|
this.config = config;
|
|
5260
|
+
this.skipRegister = config.skipRegister;
|
|
5261
|
+
|
|
5188
5262
|
this._buildConfig(config, session).then((newConfig ) => {
|
|
5189
5263
|
this.config = newConfig;
|
|
5190
5264
|
this.userAgent = this.createUserAgent(uaConfigOverrides);
|
|
@@ -5312,14 +5386,22 @@ class WebRTCClient extends Emitter {
|
|
|
5312
5386
|
}
|
|
5313
5387
|
|
|
5314
5388
|
register(tries = 0) {
|
|
5315
|
-
|
|
5389
|
+
const logInfo = {
|
|
5316
5390
|
userAgent: !!this.userAgent,
|
|
5317
5391
|
registered: this.isRegistered(),
|
|
5318
5392
|
connectionPromise: !!this.connectionPromise,
|
|
5319
5393
|
registerer: !!this.registerer,
|
|
5320
5394
|
waiting: this.registerer && this.registerer.waiting,
|
|
5321
5395
|
tries,
|
|
5322
|
-
|
|
5396
|
+
skipRegister: this.skipRegister,
|
|
5397
|
+
};
|
|
5398
|
+
|
|
5399
|
+
if (this.skipRegister) {
|
|
5400
|
+
logger.info('sdk webrtc skip register...', logInfo);
|
|
5401
|
+
return Promise.resolve();
|
|
5402
|
+
}
|
|
5403
|
+
|
|
5404
|
+
logger.info('sdk webrtc registering...', logInfo);
|
|
5323
5405
|
|
|
5324
5406
|
if (!this.userAgent) {
|
|
5325
5407
|
logger.info('sdk webrtc recreating User Agent');
|
|
@@ -5650,12 +5732,18 @@ class WebRTCClient extends Emitter {
|
|
|
5650
5732
|
}
|
|
5651
5733
|
|
|
5652
5734
|
if (pc.getSenders) {
|
|
5735
|
+
if (!pc.getSenders().length) {
|
|
5736
|
+
return false;
|
|
5737
|
+
}
|
|
5653
5738
|
pc.getSenders().forEach(sender => {
|
|
5654
5739
|
if (sender && sender.track && sender.track.kind === 'audio') {
|
|
5655
5740
|
muted = muted && !sender.track.enabled;
|
|
5656
5741
|
}
|
|
5657
5742
|
});
|
|
5658
5743
|
} else {
|
|
5744
|
+
if (!pc.getLocalStreams().length) {
|
|
5745
|
+
return false;
|
|
5746
|
+
}
|
|
5659
5747
|
pc.getLocalStreams().forEach(stream => {
|
|
5660
5748
|
stream.getAudioTracks().forEach(track => {
|
|
5661
5749
|
muted = muted && !track.enabled;
|
|
@@ -5714,6 +5802,8 @@ class WebRTCClient extends Emitter {
|
|
|
5714
5802
|
}
|
|
5715
5803
|
options.sessionDescriptionHandlerOptions = {
|
|
5716
5804
|
constraints: options.constraints,
|
|
5805
|
+
hold: true,
|
|
5806
|
+
conference: isConference,
|
|
5717
5807
|
};
|
|
5718
5808
|
|
|
5719
5809
|
// Send re-INVITE
|
|
@@ -5756,6 +5846,8 @@ class WebRTCClient extends Emitter {
|
|
|
5756
5846
|
}
|
|
5757
5847
|
options.sessionDescriptionHandlerOptions = {
|
|
5758
5848
|
constraints: options.constraints,
|
|
5849
|
+
hold: false,
|
|
5850
|
+
conference: isConference,
|
|
5759
5851
|
};
|
|
5760
5852
|
|
|
5761
5853
|
// Send re-INVITE
|
|
@@ -5939,7 +6031,8 @@ class WebRTCClient extends Emitter {
|
|
|
5939
6031
|
}
|
|
5940
6032
|
const sdh = session.sessionDescriptionHandler;
|
|
5941
6033
|
const pc = sdh.peerConnection;
|
|
5942
|
-
const
|
|
6034
|
+
const sessionId = this.getSipSessionId(session);
|
|
6035
|
+
const localStream = this.getLocalStream(sessionId);
|
|
5943
6036
|
|
|
5944
6037
|
// Release old video stream
|
|
5945
6038
|
if (localStream) {
|
|
@@ -5965,11 +6058,8 @@ class WebRTCClient extends Emitter {
|
|
|
5965
6058
|
|
|
5966
6059
|
// let's update the local stream
|
|
5967
6060
|
this.eventEmitter.emit('onVideoInputChange', stream);
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
} catch (_) {
|
|
5971
|
-
// Nothing to do
|
|
5972
|
-
}
|
|
6061
|
+
this.setLocalMediaStream(sessionId, stream);
|
|
6062
|
+
|
|
5973
6063
|
return stream;
|
|
5974
6064
|
});
|
|
5975
6065
|
}
|
|
@@ -6271,9 +6361,11 @@ class WebRTCClient extends Emitter {
|
|
|
6271
6361
|
remoteStream.removeTrack(track);
|
|
6272
6362
|
});
|
|
6273
6363
|
|
|
6274
|
-
pc.getReceivers
|
|
6275
|
-
|
|
6276
|
-
|
|
6364
|
+
if (pc.getReceivers) {
|
|
6365
|
+
pc.getReceivers().forEach(receiver => {
|
|
6366
|
+
remoteStream.addTrack(receiver.track);
|
|
6367
|
+
});
|
|
6368
|
+
}
|
|
6277
6369
|
}
|
|
6278
6370
|
|
|
6279
6371
|
getMediaConfiguration(enableVideo , conference = false, constraints = null) {
|
|
@@ -6308,6 +6400,25 @@ class WebRTCClient extends Emitter {
|
|
|
6308
6400
|
return sessionId in this.conferences;
|
|
6309
6401
|
}
|
|
6310
6402
|
|
|
6403
|
+
createAudioElementFor(sessionId ) {
|
|
6404
|
+
const audio = document.createElement('audio');
|
|
6405
|
+
audio.setAttribute('id', `audio-${sessionId}`);
|
|
6406
|
+
|
|
6407
|
+
if (audio.setSinkId && this.audioOutputDeviceId) {
|
|
6408
|
+
audio.setSinkId(this.audioOutputDeviceId);
|
|
6409
|
+
}
|
|
6410
|
+
|
|
6411
|
+
if (document.body) {
|
|
6412
|
+
document.body.appendChild(audio);
|
|
6413
|
+
}
|
|
6414
|
+
this.audioElements[sessionId] = audio;
|
|
6415
|
+
|
|
6416
|
+
audio.volume = this.audioOutputVolume;
|
|
6417
|
+
audio.autoplay = true;
|
|
6418
|
+
|
|
6419
|
+
return audio;
|
|
6420
|
+
}
|
|
6421
|
+
|
|
6311
6422
|
_onTransportError() {
|
|
6312
6423
|
this.eventEmitter.emit(TRANSPORT_ERROR);
|
|
6313
6424
|
this.attemptReconnection();
|
|
@@ -6537,7 +6648,7 @@ class WebRTCClient extends Emitter {
|
|
|
6537
6648
|
let hadRemoteVideo = false;
|
|
6538
6649
|
|
|
6539
6650
|
// Monkey patch `onInviteRequest` to be able to know if there was a remote video stream before `onInvite` is called
|
|
6540
|
-
//
|
|
6651
|
+
// Because when `onInvite` is called we already got the video track
|
|
6541
6652
|
session.onInviteRequest = (request) => {
|
|
6542
6653
|
hadRemoteVideo = this.hasARemoteVideoTrack(sipSessionId);
|
|
6543
6654
|
|
|
@@ -6612,20 +6723,10 @@ class WebRTCClient extends Emitter {
|
|
|
6612
6723
|
const isConference = this.isConference(sessionId);
|
|
6613
6724
|
|
|
6614
6725
|
if (this._hasAudio() && this._isWeb() && !(sessionId in this.audioElements)) {
|
|
6615
|
-
|
|
6616
|
-
audio.setAttribute('id', `audio-${sessionId}`);
|
|
6617
|
-
|
|
6618
|
-
if (audio.setSinkId && this.audioOutputDeviceId) {
|
|
6619
|
-
audio.setSinkId(this.audioOutputDeviceId);
|
|
6620
|
-
}
|
|
6621
|
-
|
|
6622
|
-
if (document.body) {
|
|
6623
|
-
document.body.appendChild(audio);
|
|
6624
|
-
}
|
|
6625
|
-
this.audioElements[sessionId] = audio;
|
|
6726
|
+
this.createAudioElementFor(sessionId);
|
|
6626
6727
|
}
|
|
6627
6728
|
|
|
6628
|
-
const audioElement = this.audioElements[
|
|
6729
|
+
const audioElement = this.audioElements[sessionId];
|
|
6629
6730
|
const stream = newStream || this.getRemoteStream(sessionId);
|
|
6630
6731
|
|
|
6631
6732
|
if (!this._isWeb() || !stream) {
|
package/lib/websocket-client.js
CHANGED
|
@@ -475,6 +475,8 @@ const CONFERENCE_USER_PARTICIPANT_JOINED = 'conference_user_participant_joined';
|
|
|
475
475
|
const CONFERENCE_USER_PARTICIPANT_LEFT = 'conference_user_participant_left';
|
|
476
476
|
const CONFERENCE_USER_PARTICIPANT_TALK_STARTED = 'conference_user_participant_talk_started';
|
|
477
477
|
const CONFERENCE_USER_PARTICIPANT_TALK_STOPPED = 'conference_user_participant_talk_stopped';
|
|
478
|
+
const MEETING_USER_PARTICIPANT_JOINED = 'meeting_user_participant_joined';
|
|
479
|
+
const MEETING_USER_PARTICIPANT_LEFT = 'meeting_user_participant_left';
|
|
478
480
|
const SWITCHBOARD_QUEUED_CALLS_UPDATED = 'switchboard_queued_calls_updated';
|
|
479
481
|
const SWITCHBOARD_QUEUED_CALL_ANSWERED = 'switchboard_queued_call_answered';
|
|
480
482
|
const SWITCHBOARD_HELD_CALLS_UPDATED = 'switchboard_held_calls_updated';
|
|
@@ -821,6 +823,8 @@ WebSocketClient.eventLists = [
|
|
|
821
823
|
CHATD_USER_ROOM_CREATED,
|
|
822
824
|
CONFERENCE_USER_PARTICIPANT_JOINED,
|
|
823
825
|
CONFERENCE_USER_PARTICIPANT_LEFT,
|
|
826
|
+
MEETING_USER_PARTICIPANT_JOINED,
|
|
827
|
+
MEETING_USER_PARTICIPANT_LEFT,
|
|
824
828
|
CONFERENCE_USER_PARTICIPANT_TALK_STARTED,
|
|
825
829
|
CONFERENCE_USER_PARTICIPANT_TALK_STOPPED,
|
|
826
830
|
SWITCHBOARD_QUEUED_CALLS_UPDATED,
|
|
@@ -908,6 +912,8 @@ exports.FAX_OUTBOUND_USER_FAILED = FAX_OUTBOUND_USER_FAILED;
|
|
|
908
912
|
exports.FAX_OUTBOUND_USER_SUCCEEDED = FAX_OUTBOUND_USER_SUCCEEDED;
|
|
909
913
|
exports.HEARTBEAT_ENGINE_VERSION = HEARTBEAT_ENGINE_VERSION;
|
|
910
914
|
exports.LINE_STATUS_UPDATED = LINE_STATUS_UPDATED;
|
|
915
|
+
exports.MEETING_USER_PARTICIPANT_JOINED = MEETING_USER_PARTICIPANT_JOINED;
|
|
916
|
+
exports.MEETING_USER_PARTICIPANT_LEFT = MEETING_USER_PARTICIPANT_LEFT;
|
|
911
917
|
exports.SOCKET_EVENTS = SOCKET_EVENTS;
|
|
912
918
|
exports.SWITCHBOARD_HELD_CALLS_UPDATED = SWITCHBOARD_HELD_CALLS_UPDATED;
|
|
913
919
|
exports.SWITCHBOARD_HELD_CALL_ANSWERED = SWITCHBOARD_HELD_CALL_ANSWERED;
|
package/package.json
CHANGED
package/src/api/calld.js
CHANGED
|
@@ -133,6 +133,9 @@ export default (client: ApiRequester, baseUrl: string) => ({
|
|
|
133
133
|
getConferenceParticipantsAsUser: async (conferenceId: string): Promise<Object> =>
|
|
134
134
|
client.get(`${baseUrl}/users/me/conferences/${conferenceId}/participants`),
|
|
135
135
|
|
|
136
|
+
getMeetingParticipantsAsUser: async (meetingUuid: string): Promise<Object> =>
|
|
137
|
+
client.get(`${baseUrl}/users/me/meetings/${meetingUuid}/participants`),
|
|
138
|
+
|
|
136
139
|
listTrunks: () => client.get(`${baseUrl}/trunks`),
|
|
137
140
|
|
|
138
141
|
mute: (callId: string) =>
|
package/src/api/confd.js
CHANGED
|
@@ -4,6 +4,7 @@ import type { UUID, ListConfdUsersResponse, ListApplicationsResponse } from '../
|
|
|
4
4
|
import Profile from '../domain/Profile';
|
|
5
5
|
import SipLine from '../domain/SipLine';
|
|
6
6
|
import ExternalApp from '../domain/ExternalApp';
|
|
7
|
+
import Meeting from '../domain/Meeting';
|
|
7
8
|
|
|
8
9
|
export default (client: ApiRequester, baseUrl: string) => ({
|
|
9
10
|
listUsers: (): Promise<ListConfdUsersResponse> => client.get(`${baseUrl}/users`, null),
|
|
@@ -67,4 +68,16 @@ export default (client: ApiRequester, baseUrl: string) => ({
|
|
|
67
68
|
return null;
|
|
68
69
|
}
|
|
69
70
|
},
|
|
71
|
+
|
|
72
|
+
createMyMeeting: (name: string): Promise<Meeting> =>
|
|
73
|
+
client.post(`${baseUrl}/users/me/meetings`, { name }).then(Meeting.parse),
|
|
74
|
+
|
|
75
|
+
deleteMyMeeting: (meetingUuid: string): Promise<Meeting> =>
|
|
76
|
+
client.delete(`${baseUrl}/users/me/meetings/${meetingUuid}`, null),
|
|
77
|
+
|
|
78
|
+
getMeeting: (meetingUuid: string): Promise<Meeting> =>
|
|
79
|
+
client.get(`${baseUrl}/meetings/${meetingUuid}`, null).then(Meeting.parse),
|
|
80
|
+
|
|
81
|
+
guestGetMeeting: (meetingUuid: string): Promise<Meeting> =>
|
|
82
|
+
client.get(`${baseUrl}/guests/me/meetings/${meetingUuid}`, null).then(Meeting.parse),
|
|
70
83
|
});
|
package/src/domain/CallLog.js
CHANGED
|
@@ -91,7 +91,11 @@ export default class CallLog {
|
|
|
91
91
|
answered: plain.answered,
|
|
92
92
|
callDirection: plain.call_direction,
|
|
93
93
|
destination: {
|
|
94
|
-
|
|
94
|
+
// @TEMP: Temporarily assuming empty numbers are meetings
|
|
95
|
+
// which is admittedly a very dangerous assumption. Did i mention it was temporary?
|
|
96
|
+
extension: plain.requested_extension
|
|
97
|
+
|| plain.destination_extension
|
|
98
|
+
|| `meeting-${plain.requested_name || plain.destination_name || ''}`,
|
|
95
99
|
name: plain.requested_name || plain.destination_name || '',
|
|
96
100
|
},
|
|
97
101
|
source: {
|
|
@@ -107,27 +111,11 @@ export default class CallLog {
|
|
|
107
111
|
}
|
|
108
112
|
|
|
109
113
|
static parseNew(plain: CallLogResponse, session: Session): CallLog {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
extension: plain.requested_extension || plain.destination_extension,
|
|
116
|
-
name: plain.requested_name || plain.destination_name || '',
|
|
117
|
-
},
|
|
118
|
-
source: {
|
|
119
|
-
extension: plain.source_extension,
|
|
120
|
-
name: plain.source_name,
|
|
121
|
-
},
|
|
122
|
-
id: plain.id,
|
|
123
|
-
duration: (plain.duration || 0) * 1000, // duration is in seconds
|
|
124
|
-
start: moment(plain.start).toDate(),
|
|
125
|
-
end: plain.end ? moment(plain.end).toDate() : null,
|
|
126
|
-
recordings: Recording.parseMany(plain.recordings),
|
|
127
|
-
// @TODO: FIXME add verification declined vs missed call
|
|
128
|
-
newMissedCall: session
|
|
129
|
-
&& session.hasExtension(plain.requested_extension || plain.destination_extension) && !plain.answered,
|
|
130
|
-
});
|
|
114
|
+
const callLog: CallLog = CallLog.parse(plain);
|
|
115
|
+
// @TODO: FIXME add verification declined vs missed call
|
|
116
|
+
callLog.newMissedCall = session
|
|
117
|
+
&& session.hasExtension(plain.requested_extension || plain.destination_extension) && !plain.answered;
|
|
118
|
+
return callLog;
|
|
131
119
|
}
|
|
132
120
|
|
|
133
121
|
static newFrom(profile: CallLog) {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import newFrom from '../utils/new-from';
|
|
3
|
+
|
|
4
|
+
export type MeetingCreationResponse = {
|
|
5
|
+
guest_sip_authorization: string,
|
|
6
|
+
hostname: string,
|
|
7
|
+
name: string,
|
|
8
|
+
owner_uuids: Array<string>,
|
|
9
|
+
port: string,
|
|
10
|
+
uuid: string,
|
|
11
|
+
number: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default class Meeting {
|
|
15
|
+
type: string;
|
|
16
|
+
|
|
17
|
+
guestSipAuthorization: string;
|
|
18
|
+
hostname: string;
|
|
19
|
+
uuid: string;
|
|
20
|
+
name: string;
|
|
21
|
+
port: string;
|
|
22
|
+
number: string;
|
|
23
|
+
ownerUuids: Array<string>;
|
|
24
|
+
|
|
25
|
+
static parse(plain: MeetingCreationResponse): Meeting {
|
|
26
|
+
return new Meeting({
|
|
27
|
+
guestSipAuthorization: plain.guest_sip_authorization,
|
|
28
|
+
hostname: plain.hostname,
|
|
29
|
+
name: plain.name,
|
|
30
|
+
ownerUuids: plain.owner_uuids,
|
|
31
|
+
port: plain.port,
|
|
32
|
+
uuid: plain.uuid,
|
|
33
|
+
number: plain.number,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static newFrom(meeting: Meeting) {
|
|
38
|
+
return newFrom(meeting, Meeting);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
constructor({ uuid, name, guestSipAuthorization, ownerUuids, port, hostname, number }: Object = {}) {
|
|
42
|
+
this.guestSipAuthorization = guestSipAuthorization;
|
|
43
|
+
this.hostname = hostname;
|
|
44
|
+
this.name = name;
|
|
45
|
+
this.ownerUuids = ownerUuids;
|
|
46
|
+
this.port = port;
|
|
47
|
+
this.uuid = uuid;
|
|
48
|
+
this.number = number;
|
|
49
|
+
|
|
50
|
+
// Useful to compare instead of instanceof with minified code
|
|
51
|
+
this.type = 'Meeting';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
getGuestSipCredentials() {
|
|
55
|
+
// eslint-disable-next-line no-undef
|
|
56
|
+
const [username, secret] = atob(this.guestSipAuthorization).split(':');
|
|
57
|
+
return { username, secret };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -200,7 +200,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
200
200
|
});
|
|
201
201
|
|
|
202
202
|
if (!sipSession) {
|
|
203
|
-
return
|
|
203
|
+
return false;
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
// $FlowFixMe
|
|
@@ -210,19 +210,17 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
210
210
|
// Downgrade
|
|
211
211
|
if (constraints && !isUpgrade) {
|
|
212
212
|
// No reinvite needed
|
|
213
|
-
|
|
213
|
+
this._downgradeToAudio(callSession);
|
|
214
|
+
return false;
|
|
214
215
|
}
|
|
215
216
|
|
|
216
217
|
if (isUpgrade) {
|
|
217
218
|
const shouldReinvite = await this._upgradeToVideo(callSession, constraints, conference);
|
|
218
219
|
if (!shouldReinvite) {
|
|
219
|
-
return;
|
|
220
|
+
return false;
|
|
220
221
|
}
|
|
221
222
|
}
|
|
222
223
|
|
|
223
|
-
// Force reinvite in SDH
|
|
224
|
-
sipSession.sessionDescriptionHandler.localMediaStreamConstraints = null;
|
|
225
|
-
|
|
226
224
|
return this.client.reinvite(sipSession, constraints, conference, audioOnly);
|
|
227
225
|
}
|
|
228
226
|
|
|
@@ -295,7 +293,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
295
293
|
_downgradeToAudio(callSession: ?CallSession) {
|
|
296
294
|
const sipSession = this.findSipSession(callSession);
|
|
297
295
|
if (!sipSession) {
|
|
298
|
-
return;
|
|
296
|
+
return false;
|
|
299
297
|
}
|
|
300
298
|
|
|
301
299
|
// Release local video stream when downgrading to audio
|
|
@@ -499,6 +497,10 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
499
497
|
localStream.removeTrack(videoTrack);
|
|
500
498
|
}
|
|
501
499
|
|
|
500
|
+
if (localStream) {
|
|
501
|
+
localStream.addTrack(screenTrack);
|
|
502
|
+
}
|
|
503
|
+
|
|
502
504
|
if (sender) {
|
|
503
505
|
sender.replaceTrack(screenTrack);
|
|
504
506
|
}
|
|
@@ -514,6 +516,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
514
516
|
}
|
|
515
517
|
|
|
516
518
|
logger.info('WebRTC phone - stop screen sharing');
|
|
519
|
+
let reinvited = false;
|
|
517
520
|
|
|
518
521
|
try {
|
|
519
522
|
if (this.currentScreenShare.stream) {
|
|
@@ -528,7 +531,8 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
528
531
|
// When upgrading directly to screenshare (eg: we don't have a videoLocalStream to replace)
|
|
529
532
|
// We have to downgrade to audio.
|
|
530
533
|
const screenshareStopped = this.currentScreenShare.sender && this.currentScreenShare.hadVideo;
|
|
531
|
-
|
|
534
|
+
const constraints = { audio: false, video: screenshareStopped };
|
|
535
|
+
reinvited = await this.sendReinvite(targetCallSession, constraints, conference);
|
|
532
536
|
}
|
|
533
537
|
} catch (e) {
|
|
534
538
|
console.warn(e);
|
|
@@ -542,6 +546,8 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
542
546
|
);
|
|
543
547
|
|
|
544
548
|
this.currentScreenShare = null;
|
|
549
|
+
|
|
550
|
+
return reinvited;
|
|
545
551
|
}
|
|
546
552
|
|
|
547
553
|
async _getStreamFromConstraints(constraints: Object, conference: boolean = false): Promise<?MediaStream> {
|
|
@@ -602,6 +608,10 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
602
608
|
this.client.changeAudioOutputDevice(id);
|
|
603
609
|
}
|
|
604
610
|
|
|
611
|
+
createAudioElementFor(sessionId: string) {
|
|
612
|
+
return this.client.createAudioElementFor(sessionId);
|
|
613
|
+
}
|
|
614
|
+
|
|
605
615
|
changeRingDevice(id: string) {
|
|
606
616
|
this.audioRingDeviceId = id;
|
|
607
617
|
}
|
|
@@ -1249,6 +1259,10 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
1249
1259
|
return callSession ? this.client.hasLocalVideo(callSession.sipCallId) : null;
|
|
1250
1260
|
}
|
|
1251
1261
|
|
|
1262
|
+
hasALocalVideoTrack(callSession: CallSession) {
|
|
1263
|
+
return callSession ? this.client.hasALocalVideoTrack(callSession.sipCallId) : null;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1252
1266
|
getRemoteStream(callSession: CallSession): ?MediaStream {
|
|
1253
1267
|
return callSession ? this.client.getRemoteStream(callSession.sipCallId) : null;
|
|
1254
1268
|
}
|
package/src/index.js
CHANGED
|
@@ -30,6 +30,7 @@ import IndirectTransfer from './domain/IndirectTransfer';
|
|
|
30
30
|
import SwitchboardCall from './domain/SwitchboardCall';
|
|
31
31
|
import WebRTCPhone from './domain/Phone/WebRTCPhone';
|
|
32
32
|
import CTIPhone from './domain/Phone/CTIPhone';
|
|
33
|
+
import Meeting from './domain/Meeting';
|
|
33
34
|
import type {
|
|
34
35
|
NewContact as NewContactType,
|
|
35
36
|
ContactResponse as ContactResponseType,
|
|
@@ -122,4 +123,5 @@ export default {
|
|
|
122
123
|
SOCKET_EVENTS,
|
|
123
124
|
Wazo,
|
|
124
125
|
WebRTCPhone,
|
|
126
|
+
Meeting,
|
|
125
127
|
};
|