videomail-client 16.5.0 → 16.7.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/README.md +1 -1
- package/dist/cjs/index.cjs +225 -87
- package/dist/esm/index.js +156 -70
- package/dist/esm/types/events/params.d.ts +1 -0
- package/dist/esm/util/getAutomationSignals.d.ts +5 -0
- package/dist/esm/wrappers/form.d.ts +3 -0
- package/dist/esm/wrappers/visuals/notifier.d.ts +1 -1
- package/dist/esm/wrappers/visuals/recorder/capture.d.ts +1 -0
- package/dist/esm/wrappers/visuals/recorder/connection.d.ts +2 -0
- package/dist/esm/wrappers/visuals.d.ts +5 -2
- package/dist/umd/index.js +180 -80
- package/package.json +7 -7
package/dist/esm/index.js
CHANGED
|
@@ -3620,7 +3620,7 @@ const constants = {
|
|
|
3620
3620
|
}
|
|
3621
3621
|
};
|
|
3622
3622
|
var package_namespaceObject = {
|
|
3623
|
-
rE: "16.
|
|
3623
|
+
rE: "16.7.0"
|
|
3624
3624
|
};
|
|
3625
3625
|
function isAudioEnabled(options) {
|
|
3626
3626
|
return Boolean(options.audio.enabled);
|
|
@@ -4286,6 +4286,17 @@ class Form extends util_Despot {
|
|
|
4286
4286
|
enable(buttonsToo) {
|
|
4287
4287
|
this.setDisabled(false, buttonsToo);
|
|
4288
4288
|
}
|
|
4289
|
+
disableForSubmission() {
|
|
4290
|
+
for (const formControl of this.formElement.elements){
|
|
4291
|
+
this.submissionDisabledStates.set(formControl, formControl.hasAttribute("disabled"));
|
|
4292
|
+
formControl.setAttribute("disabled", "disabled");
|
|
4293
|
+
}
|
|
4294
|
+
}
|
|
4295
|
+
restoreAfterFailedSubmission() {
|
|
4296
|
+
for (const [formControl, wasDisabled] of this.submissionDisabledStates)if (wasDisabled) formControl.setAttribute("disabled", "disabled");
|
|
4297
|
+
else formControl.removeAttribute("disabled");
|
|
4298
|
+
this.submissionDisabledStates.clear();
|
|
4299
|
+
}
|
|
4289
4300
|
build() {
|
|
4290
4301
|
this.options.logger.debug("Form: build()");
|
|
4291
4302
|
this.keyInput = this.formElement.querySelector(`input[name="${this.options.selectors.keyInputName}"]`);
|
|
@@ -4446,7 +4457,7 @@ class Form extends util_Despot {
|
|
|
4446
4457
|
html_showElement(this.formElement);
|
|
4447
4458
|
}
|
|
4448
4459
|
constructor(container, formElement, options){
|
|
4449
|
-
super("Form", options), form_define_property(this, "container", void 0), form_define_property(this, "formElement", void 0), form_define_property(this, "keyInput", void 0), form_define_property(this, "submitButton", void 0), form_define_property(this, "submitButtonFormAttribute", null), form_define_property(this, "generatedFormId", void 0), form_define_property(this, "isSubmitting", false), form_define_property(this, "FORM_FIELDS", {}), form_define_property(this, "validate", (event)=>{
|
|
4460
|
+
super("Form", options), form_define_property(this, "container", void 0), form_define_property(this, "formElement", void 0), form_define_property(this, "keyInput", void 0), form_define_property(this, "submitButton", void 0), form_define_property(this, "submitButtonFormAttribute", null), form_define_property(this, "generatedFormId", void 0), form_define_property(this, "isSubmitting", false), form_define_property(this, "submissionDisabledStates", new Map()), form_define_property(this, "FORM_FIELDS", {}), form_define_property(this, "validate", (event)=>{
|
|
4450
4461
|
this.container.validate(event);
|
|
4451
4462
|
}), form_define_property(this, "handleSubmit", (event)=>{
|
|
4452
4463
|
if (!event.defaultPrevented) this.doTheSubmit(event);
|
|
@@ -5085,8 +5096,13 @@ class Buttons extends util_Despot {
|
|
|
5085
5096
|
this.hide();
|
|
5086
5097
|
}
|
|
5087
5098
|
onUserMediaReady(params) {
|
|
5088
|
-
this.onFormReady();
|
|
5099
|
+
this.onFormReady(params);
|
|
5089
5100
|
html_showElement(this.buttonsElement);
|
|
5101
|
+
if (params.recovering && params.paused) {
|
|
5102
|
+
html_hideElement(this.recordButton);
|
|
5103
|
+
html_disableElement(this.recordButton);
|
|
5104
|
+
return;
|
|
5105
|
+
}
|
|
5090
5106
|
html_showElement(this.audioOnRadioPair);
|
|
5091
5107
|
html_showElement(this.audioOffRadioPair);
|
|
5092
5108
|
if (html_isShown(this.recordButton) && !params.recordWhenReady) html_enableElement(this.recordButton);
|
|
@@ -5789,8 +5805,8 @@ class RecorderInsides extends util_Despot {
|
|
|
5789
5805
|
this.on("PAUSED", ()=>{
|
|
5790
5806
|
this.pauseRecording();
|
|
5791
5807
|
});
|
|
5792
|
-
this.on("ERROR", ()=>{
|
|
5793
|
-
this.onResetting();
|
|
5808
|
+
this.on("ERROR", ({ recoverable })=>{
|
|
5809
|
+
if (!recoverable) this.onResetting();
|
|
5794
5810
|
});
|
|
5795
5811
|
this.on("RESETTING", ()=>{
|
|
5796
5812
|
this.onResetting();
|
|
@@ -5973,12 +5989,12 @@ class Notifier extends util_Despot {
|
|
|
5973
5989
|
this.entertainTimeoutId = void 0;
|
|
5974
5990
|
this.entertaining = false;
|
|
5975
5991
|
}
|
|
5976
|
-
error(err) {
|
|
5992
|
+
error(err, blocking = true) {
|
|
5977
5993
|
const message = err.message;
|
|
5978
5994
|
const explanation = err.explanation;
|
|
5979
5995
|
if (!message) this.options.logger.debug(`Weird empty error message generated for error ${string_pretty(err)}`);
|
|
5980
5996
|
this.notify(message, explanation, {
|
|
5981
|
-
blocking
|
|
5997
|
+
blocking,
|
|
5982
5998
|
problem: true,
|
|
5983
5999
|
classList: err.getClassList(),
|
|
5984
6000
|
removeDimensions: util_getBrowser(this.options).isMobile()
|
|
@@ -6103,6 +6119,18 @@ function summarize(title, contents) {
|
|
|
6103
6119
|
return line;
|
|
6104
6120
|
}
|
|
6105
6121
|
const string_summarize = summarize;
|
|
6122
|
+
function isAutomatedUserAgent(userAgent = navigator.userAgent) {
|
|
6123
|
+
return /bot|crawler|spider|headlesschrome|phantomjs|puppeteer|playwright/iu.test(userAgent);
|
|
6124
|
+
}
|
|
6125
|
+
const util_isAutomatedUserAgent = isAutomatedUserAgent;
|
|
6126
|
+
function getAutomationSignals() {
|
|
6127
|
+
const webdriver = navigator.webdriver;
|
|
6128
|
+
return {
|
|
6129
|
+
webdriver: true === webdriver,
|
|
6130
|
+
userAgentMatched: util_isAutomatedUserAgent()
|
|
6131
|
+
};
|
|
6132
|
+
}
|
|
6133
|
+
const util_getAutomationSignals = getAutomationSignals;
|
|
6106
6134
|
function getEdgeCodes(value, amount, fromEnd) {
|
|
6107
6135
|
const chars = fromEnd ? value.slice(-amount) : value.slice(0, amount);
|
|
6108
6136
|
const codes = Array.from(chars, (char)=>String(char.codePointAt(0))).join(".");
|
|
@@ -6130,15 +6158,16 @@ function getLanguagesValue() {
|
|
|
6130
6158
|
if (languageList.length > 0) return languageList.join(",");
|
|
6131
6159
|
return navigator.language || "none";
|
|
6132
6160
|
}
|
|
6133
|
-
function
|
|
6161
|
+
function getDiagnosticSignals(probeBase, probeSimpleQuery) {
|
|
6134
6162
|
const reasons = [];
|
|
6135
|
-
|
|
6136
|
-
if (
|
|
6163
|
+
const automation = util_getAutomationSignals();
|
|
6164
|
+
if (automation.webdriver) reasons.push("webdriver=true");
|
|
6165
|
+
if (automation.userAgentMatched) reasons.push("headless_ua_marker");
|
|
6137
6166
|
if (0 === navigator.languages.length) reasons.push("no_languages");
|
|
6138
6167
|
if ("ok" !== probeBase) reasons.push("probe_base_failed");
|
|
6139
6168
|
if ("ok" !== probeSimpleQuery) reasons.push("probe_simple_query_failed");
|
|
6140
6169
|
return {
|
|
6141
|
-
looksAutomated:
|
|
6170
|
+
looksAutomated: automation.webdriver || automation.userAgentMatched,
|
|
6142
6171
|
reasons: reasons.length > 0 ? reasons.join(",") : "none"
|
|
6143
6172
|
};
|
|
6144
6173
|
}
|
|
@@ -6155,7 +6184,7 @@ function getWebSocketDiagnostic(url2Connect) {
|
|
|
6155
6184
|
const probeBase = probeWebSocketConstructor("wss://videomail.io");
|
|
6156
6185
|
const probeWithSimpleQuery = probeWebSocketConstructor("wss://videomail.io/?a=b");
|
|
6157
6186
|
const languages = getLanguagesValue();
|
|
6158
|
-
const automationSignals =
|
|
6187
|
+
const automationSignals = getDiagnosticSignals(probeBase, probeWithSimpleQuery);
|
|
6159
6188
|
const diagnosticPairs = [
|
|
6160
6189
|
`webdriver=${String(navigator.webdriver)}`,
|
|
6161
6190
|
`wsCtorNative=${String(wsCtorNative)}`,
|
|
@@ -6182,10 +6211,6 @@ function getWebSocketDiagnostic(url2Connect) {
|
|
|
6182
6211
|
};
|
|
6183
6212
|
}
|
|
6184
6213
|
const error_getWebSocketDiagnostic = getWebSocketDiagnostic;
|
|
6185
|
-
function isAutomatedUserAgent(userAgent = navigator.userAgent) {
|
|
6186
|
-
return /bot|crawler|spider|headlesschrome|phantomjs|puppeteer|playwright/iu.test(userAgent);
|
|
6187
|
-
}
|
|
6188
|
-
const util_isAutomatedUserAgent = isAutomatedUserAgent;
|
|
6189
6214
|
function boundedWriteQueue_define_property(obj, key, value) {
|
|
6190
6215
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
6191
6216
|
value: value,
|
|
@@ -6381,6 +6406,9 @@ class RecorderStreamWriter extends util_Despot {
|
|
|
6381
6406
|
explanation: "The server connection closed before this recording completed. It cannot be safely resumed, so it has been stopped. Please start a new recording.",
|
|
6382
6407
|
options: this.options,
|
|
6383
6408
|
cause: {
|
|
6409
|
+
transportPhase: "recording",
|
|
6410
|
+
mediaTransmissionStarted: true,
|
|
6411
|
+
automation: util_getAutomationSignals(),
|
|
6384
6412
|
unconfirmedFrames,
|
|
6385
6413
|
unconfirmedSamples
|
|
6386
6414
|
}
|
|
@@ -6448,15 +6476,32 @@ class RecorderConnection extends streamWriter {
|
|
|
6448
6476
|
this.resumeAfterDrain = false;
|
|
6449
6477
|
this.reconnecting = true;
|
|
6450
6478
|
this.initSocket();
|
|
6451
|
-
} else if (this.shouldReportStreamClose())
|
|
6452
|
-
this.
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6479
|
+
} else if (this.shouldReportStreamClose()) {
|
|
6480
|
+
const stream = this.stream;
|
|
6481
|
+
window.setTimeout(()=>{
|
|
6482
|
+
if (this.stream === stream) this.handleConnectionFailure({
|
|
6483
|
+
url2Connect,
|
|
6484
|
+
cause: "closed"
|
|
6485
|
+
});
|
|
6486
|
+
}, 0);
|
|
6487
|
+
}
|
|
6457
6488
|
}
|
|
6458
6489
|
shouldIgnoreStreamClose() {
|
|
6459
|
-
return this.recordingTransportFailed || Boolean(this.unloaded) || this.validate();
|
|
6490
|
+
return this.recordingTransportFailed || Boolean(this.unloaded) || this.validate() || this.blocking && !this.mediaTransmissionStarted;
|
|
6491
|
+
}
|
|
6492
|
+
closeIdleConnection(reason) {
|
|
6493
|
+
if (this.mediaTransmissionStarted) return;
|
|
6494
|
+
this.options.logger.debug(string_summarize("Recorder: closing idle connection", {
|
|
6495
|
+
reason,
|
|
6496
|
+
connected: this.connected,
|
|
6497
|
+
connecting: this.connecting,
|
|
6498
|
+
visibilityState: document.visibilityState
|
|
6499
|
+
}));
|
|
6500
|
+
this.connected = this.connecting = this.reconnecting = false;
|
|
6501
|
+
this.clearConnectionTimeout();
|
|
6502
|
+
this.clearReconnectTimeout();
|
|
6503
|
+
this.stopPings();
|
|
6504
|
+
this.discardSocket();
|
|
6460
6505
|
}
|
|
6461
6506
|
handleStreamError(url2Connect, err) {
|
|
6462
6507
|
if ("u" > typeof Event && err instanceof Event) this.lastSocketError = error_getEventDetails(err);
|
|
@@ -6465,28 +6510,20 @@ class RecorderConnection extends streamWriter {
|
|
|
6465
6510
|
error: serializeError(err)
|
|
6466
6511
|
});
|
|
6467
6512
|
this.options.logger.debug(debugLine);
|
|
6468
|
-
if (this.
|
|
6513
|
+
if (this.shouldIgnoreStreamClose()) {
|
|
6469
6514
|
this.connected = false;
|
|
6470
6515
|
this.stopPings();
|
|
6471
6516
|
this.discardSocket();
|
|
6472
6517
|
return;
|
|
6473
6518
|
}
|
|
6474
|
-
if (
|
|
6519
|
+
if (this.mediaTransmissionStarted) return void this.failInterruptedRecording();
|
|
6520
|
+
this.reconnecting ||= this.canReconnectAfterStreamClose();
|
|
6521
|
+
this.connected = false;
|
|
6522
|
+
this.stopPings();
|
|
6523
|
+
this.handleConnectionFailure({
|
|
6475
6524
|
url2Connect,
|
|
6476
6525
|
cause: "error"
|
|
6477
6526
|
});
|
|
6478
|
-
const streamError = err instanceof Error ? err : new Error("WebSocket stream emitted an error event", {
|
|
6479
|
-
cause: this.lastSocketError ?? err
|
|
6480
|
-
});
|
|
6481
|
-
if (this.mediaTransmissionStarted) return void this.failInterruptedRecording();
|
|
6482
|
-
this.emit("ERROR", {
|
|
6483
|
-
err: error_createError({
|
|
6484
|
-
message: "WebSocket stream error",
|
|
6485
|
-
explanation: `The WebSocket stream emitted an error event. Details: ${string_pretty(err)}`,
|
|
6486
|
-
options: this.options,
|
|
6487
|
-
exc: streamError
|
|
6488
|
-
})
|
|
6489
|
-
});
|
|
6490
6527
|
}
|
|
6491
6528
|
sendPings() {
|
|
6492
6529
|
this.stopPings();
|
|
@@ -6546,6 +6583,7 @@ class RecorderConnection extends streamWriter {
|
|
|
6546
6583
|
this.connectionFailed = true;
|
|
6547
6584
|
this.connecting = false;
|
|
6548
6585
|
this.reconnecting = false;
|
|
6586
|
+
const reconnectAttempts = this.reconnectAttempts;
|
|
6549
6587
|
this.reconnectAttempts = 0;
|
|
6550
6588
|
this.clearReconnectTimeout();
|
|
6551
6589
|
this.clearConnectionTimeout();
|
|
@@ -6556,6 +6594,14 @@ class RecorderConnection extends streamWriter {
|
|
|
6556
6594
|
const socketError = this.lastSocketError;
|
|
6557
6595
|
const contents = {
|
|
6558
6596
|
cause,
|
|
6597
|
+
transportPhase: void 0 === this.connectionOpenedAt ? "initial" : "idle",
|
|
6598
|
+
mediaTransmissionStarted: this.mediaTransmissionStarted,
|
|
6599
|
+
recordingActive: this.recordingActive,
|
|
6600
|
+
framesCount: this.framesCount,
|
|
6601
|
+
samplesCount: this.samplesCount,
|
|
6602
|
+
reconnectAttempts,
|
|
6603
|
+
connectionOpenedAt: this.connectionOpenedAt,
|
|
6604
|
+
automation: util_getAutomationSignals(),
|
|
6559
6605
|
closeCode: closeEvent?.code,
|
|
6560
6606
|
closeReason: closeEvent?.reason,
|
|
6561
6607
|
elapsedMs,
|
|
@@ -6597,7 +6643,7 @@ class RecorderConnection extends streamWriter {
|
|
|
6597
6643
|
});
|
|
6598
6644
|
}
|
|
6599
6645
|
shouldIgnoreConnectionFailure() {
|
|
6600
|
-
return this.connectionFailed || this.connected || Boolean(this.unloaded) || this.validate();
|
|
6646
|
+
return this.connectionFailed || this.connected || Boolean(this.unloaded) || this.validate() || this.blocking && !this.mediaTransmissionStarted;
|
|
6601
6647
|
}
|
|
6602
6648
|
initSocket(cb) {
|
|
6603
6649
|
if (this.connected) {
|
|
@@ -6607,7 +6653,8 @@ class RecorderConnection extends streamWriter {
|
|
|
6607
6653
|
}
|
|
6608
6654
|
if (this.connecting) return void this.options.logger.debug("Recorder: already **connecting**. Not going to initialize a new WebSocket connection.");
|
|
6609
6655
|
this.clearReconnectTimeout();
|
|
6610
|
-
|
|
6656
|
+
const automation = util_getAutomationSignals();
|
|
6657
|
+
if (automation.userAgentMatched) {
|
|
6611
6658
|
this.connecting = false;
|
|
6612
6659
|
this.options.logger.debug("Recorder: skipping web socket connection for an automated crawler");
|
|
6613
6660
|
return;
|
|
@@ -6641,6 +6688,7 @@ class RecorderConnection extends streamWriter {
|
|
|
6641
6688
|
try {
|
|
6642
6689
|
nativeSocket = new WebSocket(url2Connect);
|
|
6643
6690
|
nativeSocket.addEventListener("close", (event)=>{
|
|
6691
|
+
if (this.stream?.socket !== nativeSocket) return;
|
|
6644
6692
|
this.lastCloseEvent = {
|
|
6645
6693
|
code: event.code,
|
|
6646
6694
|
reason: event.reason,
|
|
@@ -6648,6 +6696,7 @@ class RecorderConnection extends streamWriter {
|
|
|
6648
6696
|
};
|
|
6649
6697
|
});
|
|
6650
6698
|
nativeSocket.addEventListener("error", (event)=>{
|
|
6699
|
+
if (this.stream?.socket !== nativeSocket) return;
|
|
6651
6700
|
this.lastSocketError = error_getEventDetails(event);
|
|
6652
6701
|
});
|
|
6653
6702
|
} catch (exc) {
|
|
@@ -6659,7 +6708,10 @@ class RecorderConnection extends streamWriter {
|
|
|
6659
6708
|
message,
|
|
6660
6709
|
explanation,
|
|
6661
6710
|
options: this.options,
|
|
6662
|
-
exc
|
|
6711
|
+
exc,
|
|
6712
|
+
cause: {
|
|
6713
|
+
automation
|
|
6714
|
+
}
|
|
6663
6715
|
});
|
|
6664
6716
|
this.emit("ERROR", {
|
|
6665
6717
|
err
|
|
@@ -6697,14 +6749,16 @@ class RecorderConnection extends streamWriter {
|
|
|
6697
6749
|
});
|
|
6698
6750
|
}, connectionTimeoutMs);
|
|
6699
6751
|
stream.on("close", ()=>{
|
|
6700
|
-
this.handleStreamClose(url2Connect);
|
|
6752
|
+
if (this.stream === stream) this.handleStreamClose(url2Connect);
|
|
6701
6753
|
});
|
|
6702
6754
|
stream.on("connect", (args)=>{
|
|
6755
|
+
if (this.stream !== stream) return;
|
|
6703
6756
|
this.options.logger.debug(`${PIPE_SYMBOL}Stream *connect* event emitted with args: ${string_pretty(args)}`);
|
|
6704
6757
|
this.clearConnectionTimeout();
|
|
6705
|
-
const isClosing =
|
|
6758
|
+
const isClosing = stream.socket.readyState === WebSocket.CLOSING;
|
|
6706
6759
|
if (!this.connected && !isClosing && !this.unloaded) {
|
|
6707
6760
|
this.connected = true;
|
|
6761
|
+
this.connectionOpenedAt = Date.now();
|
|
6708
6762
|
this.connecting = this.unloaded = false;
|
|
6709
6763
|
this.reconnecting = false;
|
|
6710
6764
|
this.reconnectAttempts = 0;
|
|
@@ -6714,6 +6768,7 @@ class RecorderConnection extends streamWriter {
|
|
|
6714
6768
|
}
|
|
6715
6769
|
});
|
|
6716
6770
|
stream.on("data", (data)=>{
|
|
6771
|
+
if (this.stream !== stream) return;
|
|
6717
6772
|
this.options.logger.debug(`${PIPE_SYMBOL}Stream *data* event emitted`);
|
|
6718
6773
|
try {
|
|
6719
6774
|
const command = JSON.parse(data.toString());
|
|
@@ -6732,10 +6787,10 @@ class RecorderConnection extends streamWriter {
|
|
|
6732
6787
|
}
|
|
6733
6788
|
});
|
|
6734
6789
|
stream.on("error", (err)=>{
|
|
6735
|
-
this.handleStreamError(url2Connect, err);
|
|
6790
|
+
if (this.stream === stream) this.handleStreamError(url2Connect, err);
|
|
6736
6791
|
});
|
|
6737
6792
|
stream.on("drain", ()=>{
|
|
6738
|
-
this.handleStreamDrain(stream);
|
|
6793
|
+
if (this.stream === stream) this.handleStreamDrain(stream);
|
|
6739
6794
|
});
|
|
6740
6795
|
stream.on("preend", (args)=>{
|
|
6741
6796
|
this.options.logger.debug(`${PIPE_SYMBOL}Stream *preend* event emitted with args: ${string_pretty(args)}`);
|
|
@@ -6789,7 +6844,7 @@ class RecorderConnection extends streamWriter {
|
|
|
6789
6844
|
}
|
|
6790
6845
|
}
|
|
6791
6846
|
constructor(options){
|
|
6792
|
-
super(options), connection_define_property(this, "reconnectTimeout", void 0), connection_define_property(this, "connectionTimeout", void 0), connection_define_property(this, "userMediaLoaded", void 0), connection_define_property(this, "userMediaLoading", false), connection_define_property(this, "unloaded", void 0), connection_define_property(this, "connectionFailed", false), connection_define_property(this, "reconnectAttempts", 0), connection_define_property(this, "blocking", false), connection_define_property(this, "connectingStartedAt", void 0), connection_define_property(this, "lastCloseEvent", void 0), connection_define_property(this, "lastSocketError", void 0), connection_define_property(this, "pingInterval", void 0);
|
|
6847
|
+
super(options), connection_define_property(this, "reconnectTimeout", void 0), connection_define_property(this, "connectionTimeout", void 0), connection_define_property(this, "userMediaLoaded", void 0), connection_define_property(this, "userMediaLoading", false), connection_define_property(this, "unloaded", void 0), connection_define_property(this, "connectionFailed", false), connection_define_property(this, "reconnectAttempts", 0), connection_define_property(this, "blocking", false), connection_define_property(this, "connectingStartedAt", void 0), connection_define_property(this, "lastCloseEvent", void 0), connection_define_property(this, "lastSocketError", void 0), connection_define_property(this, "pingInterval", void 0), connection_define_property(this, "connectionOpenedAt", void 0);
|
|
6793
6848
|
}
|
|
6794
6849
|
}
|
|
6795
6850
|
const connection = RecorderConnection;
|
|
@@ -6851,6 +6906,7 @@ class RecorderMedia extends connection {
|
|
|
6851
6906
|
const characteristics = this.userMedia?.getCharacteristics();
|
|
6852
6907
|
this.options.logger.debug(`Recorder: userMediaErrorCallback(), name: ${err.name}, message: ${err.message} and Webcam characteristics: ${characteristics ? string_pretty(characteristics) : "none"}`);
|
|
6853
6908
|
const errorListeners = this.getListeners("ERROR");
|
|
6909
|
+
if (err.name !== error_VideomailError.MEDIA_DEVICE_NOT_SUPPORTED) this.closeIdleConnection("camera-access-failed");
|
|
6854
6910
|
if (errorListeners?.length) if (err.name !== error_VideomailError.MEDIA_DEVICE_NOT_SUPPORTED) {
|
|
6855
6911
|
const videomailError = error_createError({
|
|
6856
6912
|
err,
|
|
@@ -7332,6 +7388,9 @@ class RecorderCapture extends media {
|
|
|
7332
7388
|
if (this.streamBackpressured) this.resumeAfterDrain = true;
|
|
7333
7389
|
else loop.start();
|
|
7334
7390
|
}
|
|
7391
|
+
resumeMediaForCountdown() {
|
|
7392
|
+
this.userMedia?.resume();
|
|
7393
|
+
}
|
|
7335
7394
|
onFlushed(opts) {
|
|
7336
7395
|
const frameNumber = opts.frameNumber;
|
|
7337
7396
|
if (1 === frameNumber) this.emit("FIRST_FRAME_SENT");
|
|
@@ -8471,6 +8530,7 @@ class Recorder extends capture {
|
|
|
8471
8530
|
this.cancelCameraRecovery();
|
|
8472
8531
|
this.blocking = true;
|
|
8473
8532
|
this.clearUserMediaTimeout();
|
|
8533
|
+
this.closeIdleConnection("blocking-error");
|
|
8474
8534
|
});
|
|
8475
8535
|
this.on("PREVIEW", ()=>{
|
|
8476
8536
|
this.hide();
|
|
@@ -9009,21 +9069,36 @@ class Visuals extends util_Despot {
|
|
|
9009
9069
|
this.clearCameraRecoveryNotice();
|
|
9010
9070
|
return this.recorder.recoverCamera();
|
|
9011
9071
|
}
|
|
9012
|
-
|
|
9072
|
+
canResumeCountdown(restored) {
|
|
9073
|
+
return restored && Boolean(this.recorderInsides.isCountingDown());
|
|
9074
|
+
}
|
|
9075
|
+
async resumeCountdown() {
|
|
9076
|
+
const restored = await this.recoverCamera();
|
|
9077
|
+
if (!this.canResumeCountdown(restored)) return;
|
|
9078
|
+
this.recorder.resumeMediaForCountdown();
|
|
9079
|
+
this.recorderInsides.resumeCountdown();
|
|
9080
|
+
this.recorderInsides.hidePause();
|
|
9081
|
+
}
|
|
9082
|
+
async resume() {
|
|
9013
9083
|
this.clearCameraRecoveryNotice();
|
|
9014
|
-
if (this.recorderInsides.isCountingDown())
|
|
9015
|
-
|
|
9016
|
-
this.recorderInsides.hidePause();
|
|
9017
|
-
} else {
|
|
9084
|
+
if (this.recorderInsides.isCountingDown()) await this.resumeCountdown();
|
|
9085
|
+
else {
|
|
9018
9086
|
this.recorder.resume();
|
|
9019
9087
|
if (!this.recorder.isPaused()) this.recorderInsides.hidePause();
|
|
9020
9088
|
}
|
|
9021
9089
|
}
|
|
9090
|
+
resumeFromControl() {
|
|
9091
|
+
this.resume().catch((exc)=>{
|
|
9092
|
+
this.emit("ERROR", {
|
|
9093
|
+
exc
|
|
9094
|
+
});
|
|
9095
|
+
});
|
|
9096
|
+
}
|
|
9022
9097
|
pauseOrResume() {
|
|
9023
|
-
if (this.cameraRecoveryFailed && this.recorder.isPaused()) this.
|
|
9098
|
+
if (this.cameraRecoveryFailed && this.recorder.isPaused()) this.resumeFromControl();
|
|
9024
9099
|
else if (this.isRecordable()) {
|
|
9025
9100
|
if (this.isRecording()) this.pause();
|
|
9026
|
-
else if (this.recorder.isPaused()) this.
|
|
9101
|
+
else if (this.recorder.isPaused()) this.resumeFromControl();
|
|
9027
9102
|
else if (this.recorder.isReady()) this.recorder.record();
|
|
9028
9103
|
}
|
|
9029
9104
|
}
|
|
@@ -9052,8 +9127,8 @@ class Visuals extends util_Despot {
|
|
|
9052
9127
|
isPaused() {
|
|
9053
9128
|
return this.recorder.isPaused();
|
|
9054
9129
|
}
|
|
9055
|
-
error(err) {
|
|
9056
|
-
this.notifier.error(err);
|
|
9130
|
+
error(err, blocking = true) {
|
|
9131
|
+
this.notifier.error(err, blocking);
|
|
9057
9132
|
}
|
|
9058
9133
|
hide() {
|
|
9059
9134
|
if (this.visualsElement) {
|
|
@@ -9162,6 +9237,7 @@ function container_define_property(obj, key, value) {
|
|
|
9162
9237
|
else obj[key] = value;
|
|
9163
9238
|
return obj;
|
|
9164
9239
|
}
|
|
9240
|
+
const INTERACTIVE_SPACE_TARGETS = "button, input, select, textarea, a[href], summary, label, audio[controls], video[controls], [contenteditable]:not([contenteditable='false']), [role='button'], [role='link'], [role='checkbox'], [role='radio'], [role='switch'], [role='menuitem'], [role='tab'], [tabindex]:not([tabindex='-1'])";
|
|
9165
9241
|
class Container extends util_Despot {
|
|
9166
9242
|
buildChildren(playerOnly = false, parentElement) {
|
|
9167
9243
|
const parentElementInfo = parentElement ? `, parentElement="${string_pretty(parentElement)}"` : "";
|
|
@@ -9233,9 +9309,9 @@ class Container extends util_Despot {
|
|
|
9233
9309
|
if (params.exc.stack) this.options.logger.error(params.exc.stack);
|
|
9234
9310
|
else if (params.exc.message) this.options.logger.error(params.exc.message);
|
|
9235
9311
|
} else this.options.logger.error(params.exc);
|
|
9236
|
-
if (this.options.displayErrors && params.err) this.visuals.error(params.err);
|
|
9237
|
-
else if (this.options.displayErrors && params.exc && params.exc instanceof error_VideomailError) this.visuals.error(params.exc);
|
|
9238
|
-
else this.visuals.reset();
|
|
9312
|
+
if (this.options.displayErrors && params.err) this.visuals.error(params.err, !params.recoverable);
|
|
9313
|
+
else if (this.options.displayErrors && params.exc && params.exc instanceof error_VideomailError) this.visuals.error(params.exc, !params.recoverable);
|
|
9314
|
+
else if (!params.recoverable) this.visuals.reset();
|
|
9239
9315
|
}
|
|
9240
9316
|
initEvents(playerOnly = false) {
|
|
9241
9317
|
this.options.logger.debug(`Container: initEvents (playerOnly = ${playerOnly})`);
|
|
@@ -9253,7 +9329,7 @@ class Container extends util_Despot {
|
|
|
9253
9329
|
}
|
|
9254
9330
|
});
|
|
9255
9331
|
if (this.options.enableSpace) {
|
|
9256
|
-
if (!playerOnly)
|
|
9332
|
+
if (!playerOnly) this.containerElement?.addEventListener("keydown", this.handleKeyDown);
|
|
9257
9333
|
}
|
|
9258
9334
|
this.on("ERROR", (params)=>{
|
|
9259
9335
|
this.processError(params);
|
|
@@ -9293,7 +9369,7 @@ class Container extends util_Despot {
|
|
|
9293
9369
|
removeBrowserEventListeners() {
|
|
9294
9370
|
window.removeEventListener("resize", this.handleWindowResize);
|
|
9295
9371
|
window.removeEventListener("beforeunload", this.handleBeforeUnload);
|
|
9296
|
-
|
|
9372
|
+
this.containerElement?.removeEventListener("keydown", this.handleKeyDown);
|
|
9297
9373
|
this.stopListeningToVisibility?.();
|
|
9298
9374
|
this.stopListeningToVisibility = void 0;
|
|
9299
9375
|
}
|
|
@@ -9532,6 +9608,7 @@ class Container extends util_Despot {
|
|
|
9532
9608
|
async submitAll(formData, method, url, hasHostAction = false) {
|
|
9533
9609
|
let response;
|
|
9534
9610
|
let hostResponse;
|
|
9611
|
+
let submissionFailed = false;
|
|
9535
9612
|
const videomailKey = formData[this.options.selectors.keyInputName];
|
|
9536
9613
|
try {
|
|
9537
9614
|
const hasVideomailKey = Boolean(videomailKey);
|
|
@@ -9542,7 +9619,7 @@ class Container extends util_Despot {
|
|
|
9542
9619
|
].filter(Boolean).join(": ");
|
|
9543
9620
|
this.options.logger.debug(`Container: submitAll(${output})`);
|
|
9544
9621
|
this.beginWaiting();
|
|
9545
|
-
if (this.options.disableFormWhenSubmitting) this.
|
|
9622
|
+
if (this.options.disableFormWhenSubmitting) this.form?.disableForSubmission();
|
|
9546
9623
|
this.emit("SUBMITTING");
|
|
9547
9624
|
if (hasVideomailKey) {
|
|
9548
9625
|
response = this.getPendingHostSubmissionResponse(videomailKey) ?? await this.submitVideomail(formData, method);
|
|
@@ -9572,15 +9649,18 @@ class Container extends util_Despot {
|
|
|
9572
9649
|
});
|
|
9573
9650
|
}
|
|
9574
9651
|
} catch (exc) {
|
|
9652
|
+
submissionFailed = true;
|
|
9575
9653
|
const err = error_createError({
|
|
9576
9654
|
exc,
|
|
9577
9655
|
options: this.options
|
|
9578
9656
|
});
|
|
9579
9657
|
this.emit("ERROR", {
|
|
9580
|
-
err
|
|
9658
|
+
err,
|
|
9659
|
+
recoverable: true
|
|
9581
9660
|
});
|
|
9582
9661
|
} finally{
|
|
9583
9662
|
const finalResponse = hostResponse ?? response;
|
|
9663
|
+
if (submissionFailed && this.options.disableFormWhenSubmitting) this.form?.restoreAfterFailedSubmission();
|
|
9584
9664
|
if (finalResponse?.text && "text/html" === finalResponse.type) document.body.innerHTML = dompurify.sanitize(finalResponse.text);
|
|
9585
9665
|
this.endWaiting();
|
|
9586
9666
|
}
|
|
@@ -9634,7 +9714,11 @@ class Container extends util_Despot {
|
|
|
9634
9714
|
this.visuals.record();
|
|
9635
9715
|
}
|
|
9636
9716
|
resume() {
|
|
9637
|
-
this.visuals.resume()
|
|
9717
|
+
this.visuals.resume().catch((exc)=>{
|
|
9718
|
+
this.emit("ERROR", {
|
|
9719
|
+
exc
|
|
9720
|
+
});
|
|
9721
|
+
});
|
|
9638
9722
|
}
|
|
9639
9723
|
stop() {
|
|
9640
9724
|
this.visuals.stop();
|
|
@@ -9654,14 +9738,16 @@ class Container extends util_Despot {
|
|
|
9654
9738
|
e
|
|
9655
9739
|
});
|
|
9656
9740
|
}), container_define_property(this, "handleKeyDown", (e)=>{
|
|
9657
|
-
const
|
|
9658
|
-
|
|
9659
|
-
const
|
|
9660
|
-
|
|
9661
|
-
|
|
9662
|
-
|
|
9663
|
-
|
|
9664
|
-
|
|
9741
|
+
const containerElement = this.containerElement;
|
|
9742
|
+
if (!containerElement) return;
|
|
9743
|
+
const eventPath = e.composedPath();
|
|
9744
|
+
const targetContainerIndex = eventPath.findIndex((eventTarget)=>eventTarget instanceof Element && eventTarget.classList.contains(this.options.selectors.containerClass));
|
|
9745
|
+
const targetContainer = eventPath[targetContainerIndex];
|
|
9746
|
+
const isInteractiveTarget = eventPath.slice(0, targetContainerIndex + 1).some((eventTarget)=>eventTarget instanceof Element && eventTarget.matches(INTERACTIVE_SPACE_TARGETS));
|
|
9747
|
+
if (!eventPath.includes(containerElement) || targetContainer !== containerElement || isInteractiveTarget || e.defaultPrevented || e.repeat || e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || "Space" !== e.code) return;
|
|
9748
|
+
e.preventDefault();
|
|
9749
|
+
if (this.options.enablePause) this.visuals.pauseOrResume();
|
|
9750
|
+
else this.visuals.recordOrStop();
|
|
9665
9751
|
});
|
|
9666
9752
|
this.visuals = new wrappers_visuals(this, options);
|
|
9667
9753
|
this.buttons = new buttons(this, options);
|
|
@@ -18,6 +18,7 @@ declare class Form extends Despot {
|
|
|
18
18
|
private submitButtonFormAttribute;
|
|
19
19
|
private generatedFormId;
|
|
20
20
|
private isSubmitting;
|
|
21
|
+
private readonly submissionDisabledStates;
|
|
21
22
|
private readonly FORM_FIELDS;
|
|
22
23
|
constructor(container: Container, formElement: HTMLFormElement, options: VideomailClientOptions);
|
|
23
24
|
private readonly validate;
|
|
@@ -406,6 +407,8 @@ declare class Form extends Despot {
|
|
|
406
407
|
private getRegisteredFormElements;
|
|
407
408
|
disable(buttonsToo: boolean): void;
|
|
408
409
|
enable(buttonsToo: boolean): void;
|
|
410
|
+
disableForSubmission(): void;
|
|
411
|
+
restoreAfterFailedSubmission(): void;
|
|
409
412
|
build(): void;
|
|
410
413
|
private removeAllInputListeners;
|
|
411
414
|
private hideSubmitButton;
|
|
@@ -31,7 +31,7 @@ declare class Notifier extends Despot {
|
|
|
31
31
|
private show;
|
|
32
32
|
private runEntertainment;
|
|
33
33
|
private cancelEntertainment;
|
|
34
|
-
error(err: VideomailError): void;
|
|
34
|
+
error(err: VideomailError, blocking?: boolean): void;
|
|
35
35
|
private getMessageElement;
|
|
36
36
|
private setMessage;
|
|
37
37
|
private setExplanation;
|
|
@@ -20,6 +20,7 @@ declare abstract class RecorderConnection extends RecorderStreamWriter {
|
|
|
20
20
|
} | undefined;
|
|
21
21
|
protected lastSocketError?: Record<string, unknown> | undefined;
|
|
22
22
|
protected pingInterval?: number | undefined;
|
|
23
|
+
private connectionOpenedAt?;
|
|
23
24
|
protected abstract executeCommand(command: VideomailCommand): void;
|
|
24
25
|
abstract isUserMediaLoaded(): boolean | undefined;
|
|
25
26
|
abstract validate(): boolean;
|
|
@@ -28,6 +29,7 @@ declare abstract class RecorderConnection extends RecorderStreamWriter {
|
|
|
28
29
|
private shouldReportStreamClose;
|
|
29
30
|
private handleStreamClose;
|
|
30
31
|
private shouldIgnoreStreamClose;
|
|
32
|
+
protected closeIdleConnection(reason: string): void;
|
|
31
33
|
private handleStreamError;
|
|
32
34
|
protected sendPings(): void;
|
|
33
35
|
protected stopPings(): void;
|
|
@@ -40,7 +40,10 @@ declare class Visuals extends Despot {
|
|
|
40
40
|
}): void;
|
|
41
41
|
private clearCameraRecoveryNotice;
|
|
42
42
|
recoverCamera(): Promise<boolean>;
|
|
43
|
-
|
|
43
|
+
private canResumeCountdown;
|
|
44
|
+
private resumeCountdown;
|
|
45
|
+
resume(): Promise<void>;
|
|
46
|
+
private resumeFromControl;
|
|
44
47
|
pauseOrResume(): void;
|
|
45
48
|
recordOrStop(): void;
|
|
46
49
|
getRecorder(): Recorder;
|
|
@@ -52,7 +55,7 @@ declare class Visuals extends Despot {
|
|
|
52
55
|
} | undefined;
|
|
53
56
|
getAudioSampleRate(): number | undefined;
|
|
54
57
|
isPaused(): boolean | undefined;
|
|
55
|
-
error(err: VideomailError): void;
|
|
58
|
+
error(err: VideomailError, blocking?: boolean): void;
|
|
56
59
|
hide(): void;
|
|
57
60
|
isHidden(): boolean;
|
|
58
61
|
showVisuals(): void;
|