camstreamerlib 3.3.2 → 3.4.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/CamOverlayDrawingAPI.d.ts +9 -0
- package/CamOverlayDrawingAPI.js +77 -29
- package/CamOverlayPainter/Frame.d.ts +6 -0
- package/CamOverlayPainter/Frame.js +14 -10
- package/CamOverlayPainter/Painter.js +6 -0
- package/CamOverlayPainter/ResourceManager.d.ts +4 -4
- package/CamOverlayPainter/ResourceManager.js +12 -12
- package/CamScripterAPICameraEventsGenerator.d.ts +9 -0
- package/CamScripterAPICameraEventsGenerator.js +53 -18
- package/internal/WsClient.d.ts +1 -0
- package/internal/WsClient.js +86 -74
- package/package.json +1 -1
|
@@ -20,6 +20,10 @@ export type TUploadImageResponse = {
|
|
|
20
20
|
height: number;
|
|
21
21
|
call_id: number;
|
|
22
22
|
};
|
|
23
|
+
export type TErrorResponse = {
|
|
24
|
+
error: string;
|
|
25
|
+
call_id?: number;
|
|
26
|
+
};
|
|
23
27
|
export type TService = {
|
|
24
28
|
id: number;
|
|
25
29
|
enabled: number;
|
|
@@ -55,6 +59,7 @@ export declare class CamOverlayDrawingAPI extends EventEmitter {
|
|
|
55
59
|
private zIndex;
|
|
56
60
|
private callId;
|
|
57
61
|
private sendMessages;
|
|
62
|
+
private timeoutCheckTimer;
|
|
58
63
|
private wsConnected;
|
|
59
64
|
private ws;
|
|
60
65
|
constructor(options?: CamOverlayDrawingOptions);
|
|
@@ -69,8 +74,12 @@ export declare class CamOverlayDrawingAPI extends EventEmitter {
|
|
|
69
74
|
showCairoImageAbsolute(cairoImage: string, posX: number, posY: number, width: number, height: number): Promise<TCairoResponse>;
|
|
70
75
|
removeImage(): Promise<TCairoResponse>;
|
|
71
76
|
private createWsClient;
|
|
77
|
+
private incomingWsMessageHandler;
|
|
72
78
|
private sendMessage;
|
|
73
79
|
private sendBinaryMessage;
|
|
80
|
+
private startMsgsTimeoutCheck;
|
|
81
|
+
private stopMsgsTimeoutCheck;
|
|
82
|
+
private reconnectWithError;
|
|
74
83
|
private reportMessage;
|
|
75
84
|
private reportError;
|
|
76
85
|
private reportClose;
|
package/CamOverlayDrawingAPI.js
CHANGED
|
@@ -29,9 +29,11 @@ class CamOverlayDrawingAPI extends EventEmitter {
|
|
|
29
29
|
}
|
|
30
30
|
connect() {
|
|
31
31
|
this.ws.open();
|
|
32
|
+
this.startMsgsTimeoutCheck();
|
|
32
33
|
}
|
|
33
34
|
disconnect() {
|
|
34
35
|
this.ws.close();
|
|
36
|
+
this.stopMsgsTimeoutCheck();
|
|
35
37
|
}
|
|
36
38
|
isConnected() {
|
|
37
39
|
return this.wsConnected;
|
|
@@ -88,73 +90,119 @@ class CamOverlayDrawingAPI extends EventEmitter {
|
|
|
88
90
|
};
|
|
89
91
|
this.ws = new WsClient_1.WsClient(options);
|
|
90
92
|
this.ws.on('open', () => {
|
|
93
|
+
console.log('CamOverlay connection opened');
|
|
91
94
|
this.wsConnected = true;
|
|
92
95
|
this.emit('open');
|
|
93
96
|
});
|
|
94
|
-
this.ws.on('message', (
|
|
95
|
-
const dataJSON = JSON.parse(data.toString());
|
|
96
|
-
if (Object.hasOwn(dataJSON, 'call_id') && dataJSON['call_id'] in this.sendMessages) {
|
|
97
|
-
if (Object.hasOwn(dataJSON, 'error')) {
|
|
98
|
-
this.sendMessages[dataJSON.call_id].reject(new Error(dataJSON.error));
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
this.sendMessages[dataJSON.call_id].resolve(dataJSON);
|
|
102
|
-
}
|
|
103
|
-
delete this.sendMessages[dataJSON['call_id']];
|
|
104
|
-
}
|
|
105
|
-
if (Object.hasOwn(dataJSON, 'error')) {
|
|
106
|
-
this.reportError(new Error(dataJSON.error));
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
this.reportMessage(data.toString());
|
|
110
|
-
}
|
|
111
|
-
});
|
|
97
|
+
this.ws.on('message', (msgData) => this.incomingWsMessageHandler(msgData));
|
|
112
98
|
this.ws.on('error', (error) => {
|
|
113
99
|
this.reportError(error);
|
|
114
100
|
});
|
|
115
101
|
this.ws.on('close', () => {
|
|
102
|
+
console.log('CamOverlay connection closed');
|
|
116
103
|
this.wsConnected = false;
|
|
117
104
|
this.reportClose();
|
|
118
105
|
});
|
|
119
106
|
}
|
|
107
|
+
incomingWsMessageHandler(msgData) {
|
|
108
|
+
const dataJSON = JSON.parse(msgData.toString());
|
|
109
|
+
let errorResponse;
|
|
110
|
+
if ('error' in dataJSON) {
|
|
111
|
+
errorResponse = dataJSON;
|
|
112
|
+
}
|
|
113
|
+
if (dataJSON.call_id !== undefined && this.sendMessages[dataJSON.call_id] !== undefined) {
|
|
114
|
+
if (errorResponse !== undefined) {
|
|
115
|
+
this.sendMessages[dataJSON.call_id].reject(new Error(errorResponse.error));
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
this.sendMessages[dataJSON.call_id].resolve(dataJSON);
|
|
119
|
+
}
|
|
120
|
+
delete this.sendMessages[dataJSON.call_id];
|
|
121
|
+
}
|
|
122
|
+
if (errorResponse !== undefined) {
|
|
123
|
+
this.reconnectWithError(new Error(errorResponse.error));
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
this.reportMessage(msgData.toString());
|
|
127
|
+
}
|
|
128
|
+
}
|
|
120
129
|
sendMessage(msgJson) {
|
|
121
130
|
return new Promise((resolve, reject) => {
|
|
122
131
|
try {
|
|
123
|
-
this.sendMessages[this.callId] = { resolve, reject };
|
|
124
|
-
msgJson['call_id'] = this.callId++;
|
|
125
132
|
if (!this.wsConnected) {
|
|
126
133
|
throw new Error('No CamOverlay connection');
|
|
127
134
|
}
|
|
135
|
+
msgJson.call_id = ++this.callId;
|
|
128
136
|
this.ws.send(JSON.stringify(msgJson));
|
|
137
|
+
this.sendMessages[this.callId] = { resolve, reject, sentTimestamp: Date.now() };
|
|
129
138
|
}
|
|
130
139
|
catch (err) {
|
|
131
|
-
const errorMessage = err instanceof Error ? err.message :
|
|
132
|
-
|
|
140
|
+
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
|
141
|
+
const error = new Error(`Send message error: ${errorMessage}`);
|
|
142
|
+
reject(error);
|
|
143
|
+
if (!this.wsConnected) {
|
|
144
|
+
this.reportError(error);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
this.reconnectWithError(error);
|
|
148
|
+
}
|
|
133
149
|
}
|
|
134
150
|
});
|
|
135
151
|
}
|
|
136
152
|
sendBinaryMessage(msgJson, data) {
|
|
137
153
|
return new Promise((resolve, reject) => {
|
|
138
154
|
try {
|
|
139
|
-
this.
|
|
140
|
-
|
|
155
|
+
if (!this.wsConnected) {
|
|
156
|
+
throw new Error('No CamOverlay connection');
|
|
157
|
+
}
|
|
158
|
+
msgJson.call_id = ++this.callId;
|
|
141
159
|
const jsonBuffer = Buffer.from(JSON.stringify(msgJson));
|
|
142
160
|
const header = new ArrayBuffer(5);
|
|
143
161
|
const headerView = new DataView(header);
|
|
144
162
|
headerView.setInt8(0, 1);
|
|
145
163
|
headerView.setInt32(1, jsonBuffer.byteLength);
|
|
146
164
|
const msgBuffer = Buffer.concat([Buffer.from(header), jsonBuffer, data]);
|
|
147
|
-
if (!this.wsConnected) {
|
|
148
|
-
throw new Error('No CamOverlay connection');
|
|
149
|
-
}
|
|
150
165
|
this.ws.send(msgBuffer);
|
|
166
|
+
this.sendMessages[this.callId] = { resolve, reject, sentTimestamp: Date.now() };
|
|
151
167
|
}
|
|
152
168
|
catch (err) {
|
|
153
|
-
const errorMessage = err instanceof Error ? err.message :
|
|
154
|
-
|
|
169
|
+
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
|
170
|
+
const error = new Error(`Send binary message error: ${errorMessage}`);
|
|
171
|
+
reject(error);
|
|
172
|
+
if (!this.wsConnected) {
|
|
173
|
+
this.reportError(error);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
this.reconnectWithError(error);
|
|
177
|
+
}
|
|
155
178
|
}
|
|
156
179
|
});
|
|
157
180
|
}
|
|
181
|
+
startMsgsTimeoutCheck() {
|
|
182
|
+
clearInterval(this.timeoutCheckTimer);
|
|
183
|
+
this.timeoutCheckTimer = setInterval(() => {
|
|
184
|
+
let reconnect = false;
|
|
185
|
+
const now = Date.now();
|
|
186
|
+
for (const callId in this.sendMessages) {
|
|
187
|
+
const msg = this.sendMessages[callId];
|
|
188
|
+
if (now - msg.sentTimestamp > 10000) {
|
|
189
|
+
reconnect = true;
|
|
190
|
+
msg.reject(new Error('Message timeout'));
|
|
191
|
+
delete this.sendMessages[callId];
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (reconnect) {
|
|
195
|
+
this.reconnectWithError(new Error('Message timeout'));
|
|
196
|
+
}
|
|
197
|
+
}, 5000);
|
|
198
|
+
}
|
|
199
|
+
stopMsgsTimeoutCheck() {
|
|
200
|
+
clearInterval(this.timeoutCheckTimer);
|
|
201
|
+
}
|
|
202
|
+
reconnectWithError(err) {
|
|
203
|
+
this.reportError(err);
|
|
204
|
+
this.ws.reconnect();
|
|
205
|
+
}
|
|
158
206
|
reportMessage(msg) {
|
|
159
207
|
this.emit('message', msg);
|
|
160
208
|
}
|
|
@@ -30,7 +30,11 @@ export type TFrameInfo = {
|
|
|
30
30
|
};
|
|
31
31
|
export type TDrawingCallback = (cod: CamOverlayDrawingAPI, cairo: string, info: TFrameInfo) => Promise<void>;
|
|
32
32
|
export interface Frame {
|
|
33
|
+
on(event: 'open', listener: () => void): this;
|
|
34
|
+
on(event: 'close', listener: () => void): this;
|
|
33
35
|
on(event: 'layoutChanged', listener: () => void): this;
|
|
36
|
+
emit(event: 'open'): boolean;
|
|
37
|
+
emit(event: 'close'): boolean;
|
|
34
38
|
emit(event: 'layoutChanged'): boolean;
|
|
35
39
|
}
|
|
36
40
|
export declare class Frame extends EventEmitter {
|
|
@@ -41,10 +45,12 @@ export declare class Frame extends EventEmitter {
|
|
|
41
45
|
protected height: number;
|
|
42
46
|
private text;
|
|
43
47
|
private fontColor;
|
|
48
|
+
private fontName?;
|
|
44
49
|
private font?;
|
|
45
50
|
private align;
|
|
46
51
|
private textType;
|
|
47
52
|
private bgColor?;
|
|
53
|
+
private bgImageName?;
|
|
48
54
|
private bgImage?;
|
|
49
55
|
private bgType?;
|
|
50
56
|
private borderRadius;
|
|
@@ -26,9 +26,9 @@ class Frame extends events_1.EventEmitter {
|
|
|
26
26
|
this.height = opt.height;
|
|
27
27
|
this.setText((_b = opt.text) !== null && _b !== void 0 ? _b : '', 'A_LEFT');
|
|
28
28
|
this.fontColor = (_c = opt.fontColor) !== null && _c !== void 0 ? _c : [1.0, 1.0, 1.0];
|
|
29
|
-
this.
|
|
29
|
+
this.fontName = opt.font;
|
|
30
30
|
this.bgColor = opt.bgColor;
|
|
31
|
-
this.
|
|
31
|
+
this.bgImageName = opt.bgImage;
|
|
32
32
|
this.bgType = opt.bgType;
|
|
33
33
|
this.borderRadius = (_d = opt.borderRadius) !== null && _d !== void 0 ? _d : 0;
|
|
34
34
|
this.borderWidth = (_e = opt.borderWidth) !== null && _e !== void 0 ? _e : 0;
|
|
@@ -62,19 +62,23 @@ class Frame extends events_1.EventEmitter {
|
|
|
62
62
|
this.fontColor = fontColor;
|
|
63
63
|
}
|
|
64
64
|
setFont(fontName) {
|
|
65
|
-
this.
|
|
65
|
+
this.fontName = fontName;
|
|
66
|
+
this.font = undefined;
|
|
66
67
|
}
|
|
67
68
|
setFontData(fontData) {
|
|
69
|
+
this.fontName = undefined;
|
|
68
70
|
this.font = fontData;
|
|
69
71
|
}
|
|
70
72
|
setBgColor(color) {
|
|
71
73
|
this.bgColor = color;
|
|
72
74
|
}
|
|
73
75
|
setBgImage(imageName, type = 'fit') {
|
|
74
|
-
this.
|
|
76
|
+
this.bgImageName = imageName;
|
|
77
|
+
this.bgImage = undefined;
|
|
75
78
|
this.bgType = type;
|
|
76
79
|
}
|
|
77
80
|
setBgImageData(imageData, type = 'fit') {
|
|
81
|
+
this.bgImageName = undefined;
|
|
78
82
|
this.bgImage = imageData;
|
|
79
83
|
this.bgType = type;
|
|
80
84
|
}
|
|
@@ -157,11 +161,11 @@ class Frame extends events_1.EventEmitter {
|
|
|
157
161
|
}
|
|
158
162
|
prepareResources(resourceManager) {
|
|
159
163
|
return __awaiter(this, void 0, void 0, function* () {
|
|
160
|
-
if (
|
|
161
|
-
this.bgImage = yield resourceManager.image(this.
|
|
164
|
+
if (this.bgImageName !== undefined) {
|
|
165
|
+
this.bgImage = yield resourceManager.image(this.bgImageName);
|
|
162
166
|
}
|
|
163
|
-
if (
|
|
164
|
-
this.font = yield resourceManager.font(this.
|
|
167
|
+
if (this.fontName !== undefined) {
|
|
168
|
+
this.font = yield resourceManager.font(this.fontName);
|
|
165
169
|
}
|
|
166
170
|
});
|
|
167
171
|
}
|
|
@@ -171,7 +175,7 @@ class Frame extends events_1.EventEmitter {
|
|
|
171
175
|
return;
|
|
172
176
|
}
|
|
173
177
|
const promises = new Array();
|
|
174
|
-
if (this.font !== undefined
|
|
178
|
+
if (this.font !== undefined) {
|
|
175
179
|
promises.push(cod.cairo('cairo_set_font_face', cairo, this.font.var));
|
|
176
180
|
}
|
|
177
181
|
else {
|
|
@@ -230,7 +234,7 @@ class Frame extends events_1.EventEmitter {
|
|
|
230
234
|
}
|
|
231
235
|
drawImage(cod, cairo, scale, ppX, ppY) {
|
|
232
236
|
return __awaiter(this, void 0, void 0, function* () {
|
|
233
|
-
if (this.bgImage === undefined
|
|
237
|
+
if (this.bgImage === undefined) {
|
|
234
238
|
return;
|
|
235
239
|
}
|
|
236
240
|
const bgImage = this.bgImage.var;
|
|
@@ -46,6 +46,12 @@ class Painter extends Frame_1.Frame {
|
|
|
46
46
|
connect() {
|
|
47
47
|
this.cod.on('open', () => {
|
|
48
48
|
this.rm.clear();
|
|
49
|
+
this.layers = [];
|
|
50
|
+
this.refreshLayers = true;
|
|
51
|
+
this.emit('open');
|
|
52
|
+
});
|
|
53
|
+
this.cod.on('close', () => {
|
|
54
|
+
this.emit('close');
|
|
49
55
|
});
|
|
50
56
|
this.cod.on('error', (err) => {
|
|
51
57
|
console.error('Painter:', err);
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { CamOverlayDrawingAPI, TUploadImageResponse, TCairoCreateResponse } from '../CamOverlayDrawingAPI';
|
|
2
2
|
export default class ResourceManager {
|
|
3
3
|
private co;
|
|
4
|
-
private
|
|
5
|
-
private
|
|
4
|
+
private imgFileNames;
|
|
5
|
+
private fontFileNames;
|
|
6
6
|
private images;
|
|
7
7
|
private fonts;
|
|
8
8
|
constructor(co: CamOverlayDrawingAPI);
|
|
9
|
-
image(moniker: string): Promise<TUploadImageResponse>;
|
|
10
|
-
font(moniker: string): Promise<TCairoCreateResponse>;
|
|
11
9
|
registerImage(moniker: string, fileName: string): void;
|
|
12
10
|
registerFont(moniker: string, fileName: string): void;
|
|
11
|
+
image(moniker: string): Promise<TUploadImageResponse>;
|
|
12
|
+
font(moniker: string): Promise<TCairoCreateResponse>;
|
|
13
13
|
clear(): void;
|
|
14
14
|
}
|
|
@@ -13,18 +13,24 @@ const fs = require("fs/promises");
|
|
|
13
13
|
class ResourceManager {
|
|
14
14
|
constructor(co) {
|
|
15
15
|
this.co = co;
|
|
16
|
-
this.
|
|
17
|
-
this.
|
|
16
|
+
this.imgFileNames = {};
|
|
17
|
+
this.fontFileNames = {};
|
|
18
18
|
this.images = {};
|
|
19
19
|
this.fonts = {};
|
|
20
20
|
}
|
|
21
|
+
registerImage(moniker, fileName) {
|
|
22
|
+
this.imgFileNames[moniker] = process.env.INSTALL_PATH + '/images/' + fileName;
|
|
23
|
+
}
|
|
24
|
+
registerFont(moniker, fileName) {
|
|
25
|
+
this.fontFileNames[moniker] = process.env.INSTALL_PATH + '/fonts/' + fileName;
|
|
26
|
+
}
|
|
21
27
|
image(moniker) {
|
|
22
28
|
return __awaiter(this, void 0, void 0, function* () {
|
|
23
29
|
if (moniker in this.images) {
|
|
24
30
|
return this.images[moniker];
|
|
25
31
|
}
|
|
26
|
-
else if (moniker in this.
|
|
27
|
-
const imgData = yield fs.readFile(this.
|
|
32
|
+
else if (moniker in this.imgFileNames) {
|
|
33
|
+
const imgData = yield fs.readFile(this.imgFileNames[moniker]);
|
|
28
34
|
this.images[moniker] = yield this.co.uploadImageData(imgData);
|
|
29
35
|
return this.images[moniker];
|
|
30
36
|
}
|
|
@@ -38,8 +44,8 @@ class ResourceManager {
|
|
|
38
44
|
if (moniker in this.fonts) {
|
|
39
45
|
return this.fonts[moniker];
|
|
40
46
|
}
|
|
41
|
-
else if (moniker in this.
|
|
42
|
-
const fontData = yield fs.readFile(this.
|
|
47
|
+
else if (moniker in this.fontFileNames) {
|
|
48
|
+
const fontData = yield fs.readFile(this.fontFileNames[moniker]);
|
|
43
49
|
this.fonts[moniker] = yield this.co.uploadFontData(fontData);
|
|
44
50
|
return this.fonts[moniker];
|
|
45
51
|
}
|
|
@@ -48,12 +54,6 @@ class ResourceManager {
|
|
|
48
54
|
}
|
|
49
55
|
});
|
|
50
56
|
}
|
|
51
|
-
registerImage(moniker, fileName) {
|
|
52
|
-
this.imgFiles[moniker] = process.env.INSTALL_PATH + '/images/' + fileName;
|
|
53
|
-
}
|
|
54
|
-
registerFont(moniker, fileName) {
|
|
55
|
-
this.fontFiles[moniker] = process.env.INSTALL_PATH + '/fonts/' + fileName;
|
|
56
|
-
}
|
|
57
57
|
clear() {
|
|
58
58
|
this.images = {};
|
|
59
59
|
this.fonts = {};
|
|
@@ -35,6 +35,10 @@ export type TResponse = {
|
|
|
35
35
|
call_id: number;
|
|
36
36
|
message: string;
|
|
37
37
|
};
|
|
38
|
+
export type TErrorResponse = {
|
|
39
|
+
error: string;
|
|
40
|
+
call_id?: number;
|
|
41
|
+
};
|
|
38
42
|
export interface CamScripterAPICameraEventsGenerator {
|
|
39
43
|
on(event: 'open', listener: () => void): this;
|
|
40
44
|
on(event: 'close', listener: () => void): this;
|
|
@@ -53,6 +57,7 @@ export declare class CamScripterAPICameraEventsGenerator extends EventEmitter {
|
|
|
53
57
|
private camScripterAcapName;
|
|
54
58
|
private callId;
|
|
55
59
|
private sendMessages;
|
|
60
|
+
private timeoutCheckTimer;
|
|
56
61
|
private wsConnected;
|
|
57
62
|
private ws;
|
|
58
63
|
constructor(options?: CamScripterOptions);
|
|
@@ -62,7 +67,11 @@ export declare class CamScripterAPICameraEventsGenerator extends EventEmitter {
|
|
|
62
67
|
undeclareEvent(eventUndeclaration: TEventUndeclaration): Promise<TResponse>;
|
|
63
68
|
sendEvent(event: TEvent): Promise<TResponse>;
|
|
64
69
|
private createWsClient;
|
|
70
|
+
private incomingWsMessageHandler;
|
|
65
71
|
private sendMessage;
|
|
72
|
+
private startMsgsTimeoutCheck;
|
|
73
|
+
private stopMsgsTimeoutCheck;
|
|
74
|
+
private reconnectWithError;
|
|
66
75
|
private reportErr;
|
|
67
76
|
private reportClose;
|
|
68
77
|
}
|
|
@@ -22,9 +22,11 @@ class CamScripterAPICameraEventsGenerator extends EventEmitter {
|
|
|
22
22
|
}
|
|
23
23
|
connect() {
|
|
24
24
|
this.ws.open();
|
|
25
|
+
this.startMsgsTimeoutCheck();
|
|
25
26
|
}
|
|
26
27
|
disconnect() {
|
|
27
28
|
this.ws.close();
|
|
29
|
+
this.stopMsgsTimeoutCheck();
|
|
28
30
|
}
|
|
29
31
|
declareEvent(eventDeclaration) {
|
|
30
32
|
return this.sendMessage({
|
|
@@ -63,21 +65,7 @@ class CamScripterAPICameraEventsGenerator extends EventEmitter {
|
|
|
63
65
|
this.wsConnected = true;
|
|
64
66
|
this.emit('open');
|
|
65
67
|
});
|
|
66
|
-
this.ws.on('message', (
|
|
67
|
-
const dataJSON = JSON.parse(data.toString());
|
|
68
|
-
if (Object.hasOwn(dataJSON, 'call_id') && dataJSON['call_id'] in this.sendMessages) {
|
|
69
|
-
if (Object.hasOwn(dataJSON, 'error')) {
|
|
70
|
-
this.sendMessages[dataJSON['call_id']].reject(new Error(dataJSON.error));
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
this.sendMessages[dataJSON['call_id']].resolve(dataJSON);
|
|
74
|
-
}
|
|
75
|
-
delete this.sendMessages[dataJSON['call_id']];
|
|
76
|
-
}
|
|
77
|
-
if (Object.hasOwn(dataJSON, 'error')) {
|
|
78
|
-
this.reportErr(new Error(dataJSON.error));
|
|
79
|
-
}
|
|
80
|
-
});
|
|
68
|
+
this.ws.on('message', (msgData) => this.incomingWsMessageHandler(msgData));
|
|
81
69
|
this.ws.on('error', (error) => {
|
|
82
70
|
this.reportErr(error);
|
|
83
71
|
});
|
|
@@ -86,21 +74,68 @@ class CamScripterAPICameraEventsGenerator extends EventEmitter {
|
|
|
86
74
|
this.reportClose();
|
|
87
75
|
});
|
|
88
76
|
}
|
|
77
|
+
incomingWsMessageHandler(msgData) {
|
|
78
|
+
const dataJSON = JSON.parse(msgData.toString());
|
|
79
|
+
let errorResponse;
|
|
80
|
+
if ('error' in dataJSON) {
|
|
81
|
+
errorResponse = dataJSON;
|
|
82
|
+
}
|
|
83
|
+
if (dataJSON.call_id !== undefined && this.sendMessages[dataJSON.call_id] !== undefined) {
|
|
84
|
+
if (errorResponse !== undefined) {
|
|
85
|
+
this.sendMessages[dataJSON.call_id].reject(new Error(errorResponse.error));
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
this.sendMessages[dataJSON.call_id].resolve(dataJSON);
|
|
89
|
+
}
|
|
90
|
+
delete this.sendMessages[dataJSON.call_id];
|
|
91
|
+
}
|
|
92
|
+
if (errorResponse !== undefined) {
|
|
93
|
+
this.reconnectWithError(new Error(errorResponse.error));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
89
96
|
sendMessage(msgJson) {
|
|
90
97
|
return new Promise((resolve, reject) => {
|
|
91
98
|
if (!this.wsConnected) {
|
|
92
99
|
throw new Error("Websocket hasn't been opened yet.");
|
|
93
100
|
}
|
|
94
101
|
try {
|
|
95
|
-
|
|
96
|
-
msgJson.call_id = this.callId++;
|
|
102
|
+
msgJson.call_id = ++this.callId;
|
|
97
103
|
this.ws.send(JSON.stringify(msgJson));
|
|
104
|
+
this.sendMessages[this.callId] = { resolve, reject, sentTimestamp: Date.now() };
|
|
98
105
|
}
|
|
99
106
|
catch (err) {
|
|
100
|
-
|
|
107
|
+
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
|
108
|
+
const error = new Error(`Send message error: ${errorMessage}`);
|
|
109
|
+
reject(error);
|
|
110
|
+
this.reconnectWithError(error);
|
|
101
111
|
}
|
|
102
112
|
});
|
|
103
113
|
}
|
|
114
|
+
startMsgsTimeoutCheck() {
|
|
115
|
+
clearInterval(this.timeoutCheckTimer);
|
|
116
|
+
this.timeoutCheckTimer = setInterval(() => {
|
|
117
|
+
let reconnect = false;
|
|
118
|
+
const now = Date.now();
|
|
119
|
+
for (const callId in this.sendMessages) {
|
|
120
|
+
const msg = this.sendMessages[callId];
|
|
121
|
+
if (now - msg.sentTimestamp > 10000) {
|
|
122
|
+
reconnect = true;
|
|
123
|
+
msg.reject(new Error('Message timeout'));
|
|
124
|
+
delete this.sendMessages[callId];
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (reconnect) {
|
|
128
|
+
this.reconnectWithError(new Error('Message timeout'));
|
|
129
|
+
}
|
|
130
|
+
}, 5000);
|
|
131
|
+
}
|
|
132
|
+
stopMsgsTimeoutCheck() {
|
|
133
|
+
clearInterval(this.timeoutCheckTimer);
|
|
134
|
+
}
|
|
135
|
+
reconnectWithError(err) {
|
|
136
|
+
this.reportErr(err);
|
|
137
|
+
this.ws.reconnect();
|
|
138
|
+
}
|
|
104
139
|
reportErr(err) {
|
|
105
140
|
this.emit('error', err);
|
|
106
141
|
}
|
package/internal/WsClient.d.ts
CHANGED
package/internal/WsClient.js
CHANGED
|
@@ -12,7 +12,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.WsClient = void 0;
|
|
13
13
|
const EventEmitter = require("events");
|
|
14
14
|
const WebSocket = require("ws");
|
|
15
|
-
const timersPromises = require("timers/promises");
|
|
16
15
|
const Digest_1 = require("./Digest");
|
|
17
16
|
class WsClient extends EventEmitter {
|
|
18
17
|
constructor(options) {
|
|
@@ -38,54 +37,63 @@ class WsClient extends EventEmitter {
|
|
|
38
37
|
};
|
|
39
38
|
}
|
|
40
39
|
open(wwwAuthenticateHeader) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
else {
|
|
49
|
-
this.ws = new WebSocket(this.address, this.protocol, this.wsOptions);
|
|
50
|
-
}
|
|
51
|
-
this.ws.binaryType = 'arraybuffer';
|
|
52
|
-
this.isAlive = true;
|
|
53
|
-
this.pingTimer = setInterval(() => __awaiter(this, void 0, void 0, function* () {
|
|
54
|
-
var _a;
|
|
55
|
-
if ((this.ws && this.ws.readyState !== WebSocket.OPEN) || this.isAlive === false) {
|
|
56
|
-
this.emit('error', new Error('Connection timeout'));
|
|
57
|
-
yield this.closeWsConnection();
|
|
40
|
+
try {
|
|
41
|
+
if (this.ws !== undefined) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
this.isClosed = false;
|
|
45
|
+
if (this.protocol === undefined) {
|
|
46
|
+
this.ws = new WebSocket(this.address, this.wsOptions);
|
|
58
47
|
}
|
|
59
48
|
else {
|
|
60
|
-
this.
|
|
61
|
-
(_a = this.ws) === null || _a === void 0 ? void 0 : _a.ping();
|
|
49
|
+
this.ws = new WebSocket(this.address, this.protocol, this.wsOptions);
|
|
62
50
|
}
|
|
63
|
-
|
|
64
|
-
this.ws.on('pong', () => {
|
|
51
|
+
this.ws.binaryType = 'arraybuffer';
|
|
65
52
|
this.isAlive = true;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
var _b;
|
|
72
|
-
if (res.statusCode === 401 && res.headers['www-authenticate'] !== undefined) {
|
|
73
|
-
if (this.pingTimer) {
|
|
74
|
-
clearInterval(this.pingTimer);
|
|
53
|
+
this.pingTimer = setInterval(() => __awaiter(this, void 0, void 0, function* () {
|
|
54
|
+
var _a;
|
|
55
|
+
if ((this.ws && this.ws.readyState !== WebSocket.OPEN) || this.isAlive === false) {
|
|
56
|
+
this.emit('error', new Error('Connection timeout'));
|
|
57
|
+
yield this.closeWsConnection();
|
|
75
58
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
59
|
+
else {
|
|
60
|
+
this.isAlive = false;
|
|
61
|
+
(_a = this.ws) === null || _a === void 0 ? void 0 : _a.ping();
|
|
62
|
+
}
|
|
63
|
+
}), this.pingInterval);
|
|
64
|
+
this.ws.on('pong', () => {
|
|
65
|
+
this.isAlive = true;
|
|
66
|
+
});
|
|
67
|
+
if (wwwAuthenticateHeader !== undefined) {
|
|
68
|
+
this.wsOptions.headers['Authorization'] = new Digest_1.Digest().getAuthHeader(this.user, this.pass, 'GET', this.digestAddress, wwwAuthenticateHeader);
|
|
83
69
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
70
|
+
this.ws.on('unexpected-response', (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
71
|
+
var _b;
|
|
72
|
+
if (res.statusCode === 401 && res.headers['www-authenticate'] !== undefined) {
|
|
73
|
+
if (this.pingTimer) {
|
|
74
|
+
clearInterval(this.pingTimer);
|
|
75
|
+
}
|
|
76
|
+
(_b = this.ws) === null || _b === void 0 ? void 0 : _b.removeAllListeners();
|
|
77
|
+
this.ws = undefined;
|
|
78
|
+
this.open(res.headers['www-authenticate']);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
this.emit('error', new Error('Status code: ' + res.statusCode));
|
|
82
|
+
yield this.closeWsConnection();
|
|
83
|
+
}
|
|
84
|
+
}));
|
|
85
|
+
this.ws.on('open', () => this.emit('open'));
|
|
86
|
+
this.ws.on('message', (data) => this.emit('message', data));
|
|
87
|
+
this.ws.on('error', (error) => {
|
|
88
|
+
this.emit('error', error);
|
|
89
|
+
this.closeWsConnection();
|
|
90
|
+
});
|
|
91
|
+
this.ws.on('close', () => this.closeWsConnection());
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
this.emit('error', error instanceof Error ? error : new Error('Unknown error'));
|
|
95
|
+
this.closeWsConnection();
|
|
96
|
+
}
|
|
89
97
|
}
|
|
90
98
|
send(data) {
|
|
91
99
|
if (this.ws === undefined) {
|
|
@@ -100,41 +108,45 @@ class WsClient extends EventEmitter {
|
|
|
100
108
|
return;
|
|
101
109
|
}
|
|
102
110
|
this.isClosed = true;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
setTimeout(() => {
|
|
108
|
-
if (currentWs && currentWs.readyState !== WebSocket.CLOSED) {
|
|
109
|
-
currentWs.terminate();
|
|
110
|
-
}
|
|
111
|
-
}, 5000);
|
|
111
|
+
this.closeWsConnection();
|
|
112
|
+
}
|
|
113
|
+
reconnect() {
|
|
114
|
+
this.closeWsConnection();
|
|
112
115
|
}
|
|
113
116
|
closeWsConnection() {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
if (this.ws.readyState !== WebSocket.CONNECTING &&
|
|
124
|
-
this.ws.readyState !== WebSocket.CLOSING &&
|
|
125
|
-
this.ws.readyState !== WebSocket.CLOSED) {
|
|
126
|
-
this.ws.close();
|
|
127
|
-
}
|
|
128
|
-
this.ws = undefined;
|
|
129
|
-
this.emit('close');
|
|
130
|
-
if (!this.isClosed) {
|
|
131
|
-
yield timersPromises.setTimeout(10000);
|
|
132
|
-
yield this.open();
|
|
133
|
-
}
|
|
117
|
+
if (this.ws === undefined) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const wsCopy = this.ws;
|
|
121
|
+
this.ws = undefined;
|
|
122
|
+
try {
|
|
123
|
+
if (this.pingTimer) {
|
|
124
|
+
clearInterval(this.pingTimer);
|
|
134
125
|
}
|
|
135
|
-
|
|
126
|
+
wsCopy.removeAllListeners();
|
|
127
|
+
wsCopy.on('error', () => { });
|
|
128
|
+
if (wsCopy.readyState !== WebSocket.CLOSING && wsCopy.readyState !== WebSocket.CLOSED) {
|
|
129
|
+
wsCopy.close();
|
|
136
130
|
}
|
|
137
|
-
|
|
131
|
+
setTimeout(() => {
|
|
132
|
+
if (wsCopy.readyState !== WebSocket.CLOSED) {
|
|
133
|
+
wsCopy.terminate();
|
|
134
|
+
}
|
|
135
|
+
}, 5000);
|
|
136
|
+
this.emit('close');
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
console.error(err);
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
const shouldRestart = !this.isClosed;
|
|
143
|
+
setTimeout(() => {
|
|
144
|
+
wsCopy.removeAllListeners();
|
|
145
|
+
if (shouldRestart && !this.isClosed) {
|
|
146
|
+
this.open();
|
|
147
|
+
}
|
|
148
|
+
}, 10000);
|
|
149
|
+
}
|
|
138
150
|
}
|
|
139
151
|
}
|
|
140
152
|
exports.WsClient = WsClient;
|