camstreamerlib 4.0.35 → 4.0.36-c07c9c1
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/cjs/CamSwitcherAPI.d.ts +1 -0
- package/cjs/CamSwitcherAPI.js +2 -0
- package/cjs/PlaneTrackerAPI.d.ts +42 -9
- package/cjs/VapixAPI.js +15 -11
- package/cjs/VapixAPI.test.d.ts +1 -0
- package/cjs/VapixAPI.test.js +146 -0
- package/cjs/types/CamSwitcherAPI.d.ts +3 -0
- package/cjs/types/CamSwitcherAPI.js +1 -0
- package/cjs/types/PlaneTrackerAPI.d.ts +205 -26
- package/cjs/types/PlaneTrackerAPI.js +60 -16
- package/cjs/types/PlaneTrackerAPI.test.d.ts +1 -0
- package/cjs/types/PlaneTrackerAPI.test.js +90 -0
- package/cjs/types/ws/CamSwitcherEvents.d.ts +8 -8
- package/esm/CamSwitcherAPI.js +2 -0
- package/esm/VapixAPI.js +16 -12
- package/esm/VapixAPI.test.js +144 -0
- package/esm/types/CamSwitcherAPI.js +1 -0
- package/esm/types/PlaneTrackerAPI.js +60 -16
- package/esm/types/PlaneTrackerAPI.test.js +88 -0
- package/package.json +107 -107
- package/types/CamSwitcherAPI.d.ts +1 -0
- package/types/PlaneTrackerAPI.d.ts +42 -9
- package/types/VapixAPI.test.d.ts +1 -0
- package/types/types/CamSwitcherAPI.d.ts +3 -0
- package/types/types/PlaneTrackerAPI.d.ts +205 -26
- package/types/types/PlaneTrackerAPI.test.d.ts +1 -0
- package/types/types/ws/CamSwitcherEvents.d.ts +8 -8
package/esm/VapixAPI.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { arrayToUrl, isNullish, paramToUrl } from './internal/utils';
|
|
2
|
-
import { sdCardWatchedStatuses, maxFpsResponseSchema, dateTimeinfoSchema, audioDeviceRequestSchema, audioSampleRatesResponseSchema, timeZoneSchema, getPortsResponseSchema, guardTourSchema, ptzOverviewSchema, cameraPTZItemDataSchema,
|
|
2
|
+
import { sdCardWatchedStatuses, maxFpsResponseSchema, dateTimeinfoSchema, audioDeviceRequestSchema, audioSampleRatesResponseSchema, applicationSchema, timeZoneSchema, getPortsResponseSchema, guardTourSchema, ptzOverviewSchema, cameraPTZItemDataSchema, sdCardInfoSchema, ALL_APP_IDS, allDateTimeInfoSchema, } from './types/VapixAPI';
|
|
3
3
|
import { ApplicationAPIError, MaxFPSError, NoDeviceInfoError, PtzNotSupportedError, ErrorWithResponse, SDCardActionError, SDCardJobError, SettingParameterError, TimezoneFetchError, } from './errors/errors';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { XMLParser } from 'fast-xml-parser';
|
|
@@ -562,17 +562,21 @@ export class VapixAPI extends BasicAPI {
|
|
|
562
562
|
allowBooleanAttributes: true,
|
|
563
563
|
});
|
|
564
564
|
const result = parser.parse(xml);
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
|
|
565
|
+
const rawApps = result.reply.application ?? [];
|
|
566
|
+
const apps = Array.isArray(rawApps) ? rawApps : [rawApps];
|
|
567
|
+
const appList = [];
|
|
568
|
+
for (const app of apps) {
|
|
569
|
+
const parsedApp = applicationSchema.safeParse(app);
|
|
570
|
+
if (!parsedApp.success) {
|
|
571
|
+
console.debug('Skipping invalid application entry:', app, parsedApp.error.issues);
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
appList.push({
|
|
575
|
+
...parsedApp.data,
|
|
576
|
+
appId: ALL_APP_IDS.find((id) => id.toLowerCase() === parsedApp.data.Name.toLowerCase()) ?? null,
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
return appList;
|
|
576
580
|
}
|
|
577
581
|
async startApplication(applicationId, options) {
|
|
578
582
|
const agent = this.getClient(options?.proxyParams);
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { VapixAPI } from './VapixAPI';
|
|
2
|
+
import { describe, test, expect, jest, afterEach } from '@jest/globals';
|
|
3
|
+
const createApi = (xml) => {
|
|
4
|
+
const client = {
|
|
5
|
+
get: () => Promise.resolve(new Response(xml)),
|
|
6
|
+
post: () => Promise.reject(new Error('unused')),
|
|
7
|
+
};
|
|
8
|
+
return new VapixAPI(client);
|
|
9
|
+
};
|
|
10
|
+
describe('VapixAPI', () => {
|
|
11
|
+
describe('getApplicationList', () => {
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
jest.restoreAllMocks();
|
|
14
|
+
});
|
|
15
|
+
test('skips the nameless entry of the live camera list', async () => {
|
|
16
|
+
const debugSpy = jest.spyOn(console, 'debug').mockImplementation(() => { });
|
|
17
|
+
const api = createApi(`<reply result="ok">
|
|
18
|
+
<application Name="camstreamer" NiceName="CamStreamer App" Vendor="CamStreamer" Version="5.0-15" ApplicationID="413135" License="None" Status="Running" ConfigurationPage="local/camstreamer/settings.html" />
|
|
19
|
+
<application Name="" License="None" Status="Stopped" />
|
|
20
|
+
<application Name="vmd" NiceName="AXIS Video Motion Detection" Vendor="Axis Communications" Version="4.4-5" ApplicationID="143440" License="None" Status="Stopped" ConfigurationPage="local/vmd/config.html" VendorHomePage="http://www.axis.com" LicenseName="Proprietary" />
|
|
21
|
+
</reply>`);
|
|
22
|
+
await expect(api.getApplicationList()).resolves.toEqual([
|
|
23
|
+
{
|
|
24
|
+
Name: 'camstreamer',
|
|
25
|
+
NiceName: 'CamStreamer App',
|
|
26
|
+
Vendor: 'CamStreamer',
|
|
27
|
+
Version: '5.0-15',
|
|
28
|
+
ApplicationID: '413135',
|
|
29
|
+
License: 'None',
|
|
30
|
+
Status: 'Running',
|
|
31
|
+
ConfigurationPage: 'local/camstreamer/settings.html',
|
|
32
|
+
appId: 'CamStreamer',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
Name: 'vmd',
|
|
36
|
+
NiceName: 'AXIS Video Motion Detection',
|
|
37
|
+
Vendor: 'Axis Communications',
|
|
38
|
+
Version: '4.4-5',
|
|
39
|
+
ApplicationID: '143440',
|
|
40
|
+
License: 'None',
|
|
41
|
+
Status: 'Stopped',
|
|
42
|
+
ConfigurationPage: 'local/vmd/config.html',
|
|
43
|
+
VendorHomePage: 'http://www.axis.com',
|
|
44
|
+
LicenseName: 'Proprietary',
|
|
45
|
+
appId: null,
|
|
46
|
+
},
|
|
47
|
+
]);
|
|
48
|
+
expect(debugSpy).toHaveBeenCalledTimes(1);
|
|
49
|
+
expect(debugSpy.mock.calls[0]).toContainEqual({ Name: '', License: 'None', Status: 'Stopped' });
|
|
50
|
+
});
|
|
51
|
+
test.each([
|
|
52
|
+
{
|
|
53
|
+
name: 'an entry without a name',
|
|
54
|
+
xml: '<reply result="ok"><application Name="vmd" NiceName="AXIS Video Motion Detection" Vendor="Axis Communications" Version="4.4-5" License="None" Status="Stopped" /><application License="None" Status="Stopped" /></reply>',
|
|
55
|
+
expected: [
|
|
56
|
+
{
|
|
57
|
+
Name: 'vmd',
|
|
58
|
+
NiceName: 'AXIS Video Motion Detection',
|
|
59
|
+
Vendor: 'Axis Communications',
|
|
60
|
+
Version: '4.4-5',
|
|
61
|
+
License: 'None',
|
|
62
|
+
Status: 'Stopped',
|
|
63
|
+
appId: null,
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'a named entry missing informational fields',
|
|
69
|
+
xml: '<reply result="ok"><application Name="vmd" NiceName="AXIS Video Motion Detection" Vendor="Axis Communications" Version="4.4-5" License="None" Status="Stopped" /><application Name="broken" License="None" Status="Stopped" /></reply>',
|
|
70
|
+
expected: [
|
|
71
|
+
{
|
|
72
|
+
Name: 'vmd',
|
|
73
|
+
NiceName: 'AXIS Video Motion Detection',
|
|
74
|
+
Vendor: 'Axis Communications',
|
|
75
|
+
Version: '4.4-5',
|
|
76
|
+
License: 'None',
|
|
77
|
+
Status: 'Stopped',
|
|
78
|
+
appId: null,
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
])('skips invalid entries individually: $name', async ({ xml, expected }) => {
|
|
83
|
+
const debugSpy = jest.spyOn(console, 'debug').mockImplementation(() => { });
|
|
84
|
+
await expect(createApi(xml).getApplicationList()).resolves.toEqual(expected);
|
|
85
|
+
expect(debugSpy).toHaveBeenCalledTimes(1);
|
|
86
|
+
});
|
|
87
|
+
test.each([
|
|
88
|
+
{
|
|
89
|
+
name: 'drops attributes outside the schema',
|
|
90
|
+
xml: '<reply result="ok"><application Name="camoverlay" NiceName="CamOverlay App" Vendor="CamStreamer" Version="3.0-1" License="None" Status="Running" Unexpected="value" /></reply>',
|
|
91
|
+
expected: [
|
|
92
|
+
{
|
|
93
|
+
Name: 'camoverlay',
|
|
94
|
+
NiceName: 'CamOverlay App',
|
|
95
|
+
Vendor: 'CamStreamer',
|
|
96
|
+
Version: '3.0-1',
|
|
97
|
+
License: 'None',
|
|
98
|
+
Status: 'Running',
|
|
99
|
+
appId: 'CamOverlay',
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: 'maps an application outside the core CamStreamer apps to its appId',
|
|
105
|
+
xml: '<reply result="ok"><application Name="sporttracker" NiceName="SportTracker App" Vendor="CamStreamer" Version="1.0-1" License="None" Status="Running" /></reply>',
|
|
106
|
+
expected: [
|
|
107
|
+
{
|
|
108
|
+
Name: 'sporttracker',
|
|
109
|
+
NiceName: 'SportTracker App',
|
|
110
|
+
Vendor: 'CamStreamer',
|
|
111
|
+
Version: '1.0-1',
|
|
112
|
+
License: 'None',
|
|
113
|
+
Status: 'Running',
|
|
114
|
+
appId: 'SportTracker',
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: 'maps a capitalised application name to its appId',
|
|
120
|
+
xml: '<reply result="ok"><application Name="CamOverlay" NiceName="CamOverlay App" Vendor="CamStreamer" Version="3.0-1" License="None" Status="Running" /></reply>',
|
|
121
|
+
expected: [
|
|
122
|
+
{
|
|
123
|
+
Name: 'CamOverlay',
|
|
124
|
+
NiceName: 'CamOverlay App',
|
|
125
|
+
Vendor: 'CamStreamer',
|
|
126
|
+
Version: '3.0-1',
|
|
127
|
+
License: 'None',
|
|
128
|
+
Status: 'Running',
|
|
129
|
+
appId: 'CamOverlay',
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: 'returns an empty list when the camera has no applications',
|
|
135
|
+
xml: '<reply result="ok"></reply>',
|
|
136
|
+
expected: [],
|
|
137
|
+
},
|
|
138
|
+
])('returns every valid entry with its appId and logs nothing: $name', async ({ xml, expected }) => {
|
|
139
|
+
const debugSpy = jest.spyOn(console, 'debug').mockImplementation(() => { });
|
|
140
|
+
await expect(createApi(xml).getApplicationList()).resolves.toEqual(expected);
|
|
141
|
+
expect(debugSpy).not.toHaveBeenCalled();
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
});
|
|
@@ -142,6 +142,7 @@ export const cameraOptionsSchema = bitrateVapixParamsSchema
|
|
|
142
142
|
bitrateVapixParams: z.string().nullable(),
|
|
143
143
|
audioSampleRate: z.number(),
|
|
144
144
|
audioChannelCount: audioChannelCountSchema,
|
|
145
|
+
audioBitrate: z.number().int().nonnegative(),
|
|
145
146
|
keyboard: z.object({
|
|
146
147
|
fromSource: keyboardShortcutSchema,
|
|
147
148
|
none: keyboardShortcutSchema,
|
|
@@ -32,13 +32,59 @@ const labelOptionsSchema = z.union([
|
|
|
32
32
|
z.literal('flight_number'),
|
|
33
33
|
z.literal('icao'),
|
|
34
34
|
]);
|
|
35
|
-
const
|
|
35
|
+
const aircraftIdentificationLabelSchema = z.object({
|
|
36
36
|
firstRow: labelOptionsSchema,
|
|
37
37
|
secondRow: labelOptionsSchema,
|
|
38
38
|
thirdRow: labelOptionsSchema,
|
|
39
39
|
fourthRow: labelOptionsSchema,
|
|
40
|
-
opacity: z.number().
|
|
40
|
+
opacity: z.number().nonnegative(),
|
|
41
41
|
});
|
|
42
|
+
const droneLabelOptionsSchema = z.union([z.literal('blank'), z.literal('remote_id')]);
|
|
43
|
+
const droneIdentificationLabelSchema = z.object({
|
|
44
|
+
firstRow: droneLabelOptionsSchema,
|
|
45
|
+
opacity: z.number().nonnegative(),
|
|
46
|
+
});
|
|
47
|
+
const DEFAULT_DRONE_IDENTIFICATION_LABEL = {
|
|
48
|
+
firstRow: 'remote_id',
|
|
49
|
+
opacity: 30,
|
|
50
|
+
};
|
|
51
|
+
const identificationLabelSchema = z.union([
|
|
52
|
+
z.object({
|
|
53
|
+
aircraft: aircraftIdentificationLabelSchema,
|
|
54
|
+
drone: droneIdentificationLabelSchema.default(DEFAULT_DRONE_IDENTIFICATION_LABEL),
|
|
55
|
+
}),
|
|
56
|
+
aircraftIdentificationLabelSchema.transform((aircraft) => ({
|
|
57
|
+
aircraft,
|
|
58
|
+
drone: { ...DEFAULT_DRONE_IDENTIFICATION_LABEL },
|
|
59
|
+
})),
|
|
60
|
+
]);
|
|
61
|
+
const captureConfigSchema = z.object({
|
|
62
|
+
defaultCaptureSizeMeters: z.number().positive(),
|
|
63
|
+
captureSizeExtensionMeters: z.number().positive(),
|
|
64
|
+
});
|
|
65
|
+
const DEFAULT_CAPTURE_CONFIG = {
|
|
66
|
+
defaultCaptureSizeMeters: 120,
|
|
67
|
+
captureSizeExtensionMeters: 80,
|
|
68
|
+
};
|
|
69
|
+
const cameraConfigSchema = z.union([
|
|
70
|
+
z.object({
|
|
71
|
+
aircraft: captureConfigSchema,
|
|
72
|
+
drone: captureConfigSchema.default(() => ({ ...DEFAULT_CAPTURE_CONFIG })),
|
|
73
|
+
}),
|
|
74
|
+
z
|
|
75
|
+
.object({
|
|
76
|
+
defaultCaptureSizeMeters: z.number().positive().default(DEFAULT_CAPTURE_CONFIG.defaultCaptureSizeMeters),
|
|
77
|
+
captureSizeExtensionMeters: z
|
|
78
|
+
.number()
|
|
79
|
+
.positive()
|
|
80
|
+
.default(DEFAULT_CAPTURE_CONFIG.captureSizeExtensionMeters),
|
|
81
|
+
})
|
|
82
|
+
.strict()
|
|
83
|
+
.transform((captureConfig) => ({
|
|
84
|
+
aircraft: captureConfig,
|
|
85
|
+
drone: { ...captureConfig },
|
|
86
|
+
})),
|
|
87
|
+
]);
|
|
42
88
|
export const cameraSettingsSchema = z.object({
|
|
43
89
|
units: z.union([z.literal('metric'), z.literal('imperial')]).default('imperial'),
|
|
44
90
|
adsbSource: z
|
|
@@ -76,15 +122,10 @@ export const cameraSettingsSchema = z.object({
|
|
|
76
122
|
nightSkyCalibrationFocusValue: 9000,
|
|
77
123
|
focusCalibrationPoints: '',
|
|
78
124
|
}),
|
|
79
|
-
cameraConfig:
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
})
|
|
84
|
-
.default({
|
|
85
|
-
defaultCaptureSizeMeters: 120,
|
|
86
|
-
captureSizeExtensionMeters: 80,
|
|
87
|
-
}),
|
|
125
|
+
cameraConfig: cameraConfigSchema.default(() => ({
|
|
126
|
+
aircraft: { ...DEFAULT_CAPTURE_CONFIG },
|
|
127
|
+
drone: { ...DEFAULT_CAPTURE_CONFIG },
|
|
128
|
+
})),
|
|
88
129
|
stream: z
|
|
89
130
|
.object({
|
|
90
131
|
width: z.number().positive(),
|
|
@@ -195,11 +236,14 @@ export const cameraSettingsSchema = z.object({
|
|
|
195
236
|
port: 80,
|
|
196
237
|
}),
|
|
197
238
|
identificationLabel: identificationLabelSchema.default({
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
239
|
+
aircraft: {
|
|
240
|
+
firstRow: 'icao',
|
|
241
|
+
secondRow: 'call_sign',
|
|
242
|
+
thirdRow: 'blank',
|
|
243
|
+
fourthRow: 'blank',
|
|
244
|
+
opacity: 30,
|
|
245
|
+
},
|
|
246
|
+
drone: DEFAULT_DRONE_IDENTIFICATION_LABEL,
|
|
203
247
|
}),
|
|
204
248
|
acs: connectionSchema
|
|
205
249
|
.extend({
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { cameraSettingsSchema } from './PlaneTrackerAPI';
|
|
2
|
+
import { describe, test, expect } from '@jest/globals';
|
|
3
|
+
const AIRCRAFT_LABEL = {
|
|
4
|
+
firstRow: 'icao',
|
|
5
|
+
secondRow: 'call_sign',
|
|
6
|
+
thirdRow: 'blank',
|
|
7
|
+
fourthRow: 'registration',
|
|
8
|
+
opacity: 50,
|
|
9
|
+
};
|
|
10
|
+
const DEFAULT_DRONE_LABEL = { firstRow: 'remote_id', opacity: 30 };
|
|
11
|
+
describe('cameraSettingsSchema identificationLabel', () => {
|
|
12
|
+
test('defaults both aircraft and drone labels', () => {
|
|
13
|
+
expect(cameraSettingsSchema.parse({}).identificationLabel).toEqual({
|
|
14
|
+
aircraft: {
|
|
15
|
+
firstRow: 'icao',
|
|
16
|
+
secondRow: 'call_sign',
|
|
17
|
+
thirdRow: 'blank',
|
|
18
|
+
fourthRow: 'blank',
|
|
19
|
+
opacity: 30,
|
|
20
|
+
},
|
|
21
|
+
drone: DEFAULT_DRONE_LABEL,
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
test('parses the aircraft and drone label', () => {
|
|
25
|
+
const identificationLabel = { aircraft: AIRCRAFT_LABEL, drone: { firstRow: 'blank', opacity: 70 } };
|
|
26
|
+
expect(cameraSettingsSchema.parse({ identificationLabel }).identificationLabel).toEqual(identificationLabel);
|
|
27
|
+
});
|
|
28
|
+
test('defaults a missing drone label', () => {
|
|
29
|
+
expect(cameraSettingsSchema.parse({ identificationLabel: { aircraft: AIRCRAFT_LABEL } }).identificationLabel).toEqual({ aircraft: AIRCRAFT_LABEL, drone: DEFAULT_DRONE_LABEL });
|
|
30
|
+
});
|
|
31
|
+
test('converts the legacy flat label to the aircraft label', () => {
|
|
32
|
+
expect(cameraSettingsSchema.parse({ identificationLabel: AIRCRAFT_LABEL }).identificationLabel).toEqual({
|
|
33
|
+
aircraft: AIRCRAFT_LABEL,
|
|
34
|
+
drone: DEFAULT_DRONE_LABEL,
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
test('returns an independent drone label for each legacy parse', () => {
|
|
38
|
+
const first = cameraSettingsSchema.parse({ identificationLabel: AIRCRAFT_LABEL });
|
|
39
|
+
first.identificationLabel.drone.opacity = 90;
|
|
40
|
+
const second = cameraSettingsSchema.parse({ identificationLabel: AIRCRAFT_LABEL });
|
|
41
|
+
expect(second.identificationLabel.drone).toEqual(DEFAULT_DRONE_LABEL);
|
|
42
|
+
});
|
|
43
|
+
test('rejects an unknown drone label row', () => {
|
|
44
|
+
const identificationLabel = { aircraft: AIRCRAFT_LABEL, drone: { firstRow: 'icao', opacity: 30 } };
|
|
45
|
+
expect(cameraSettingsSchema.safeParse({ identificationLabel }).success).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
const CAPTURE_CONFIG = { defaultCaptureSizeMeters: 50, captureSizeExtensionMeters: 20 };
|
|
49
|
+
const DEFAULT_CAPTURE_CONFIG = { defaultCaptureSizeMeters: 120, captureSizeExtensionMeters: 80 };
|
|
50
|
+
describe('cameraSettingsSchema cameraConfig', () => {
|
|
51
|
+
test('defaults both aircraft and drone capture config', () => {
|
|
52
|
+
expect(cameraSettingsSchema.parse({}).cameraConfig).toEqual({
|
|
53
|
+
aircraft: DEFAULT_CAPTURE_CONFIG,
|
|
54
|
+
drone: DEFAULT_CAPTURE_CONFIG,
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
test('parses independent aircraft and drone capture config', () => {
|
|
58
|
+
const cameraConfig = {
|
|
59
|
+
aircraft: CAPTURE_CONFIG,
|
|
60
|
+
drone: { defaultCaptureSizeMeters: 5, captureSizeExtensionMeters: 3 },
|
|
61
|
+
};
|
|
62
|
+
expect(cameraSettingsSchema.parse({ cameraConfig }).cameraConfig).toEqual(cameraConfig);
|
|
63
|
+
});
|
|
64
|
+
test('defaults a missing drone capture config', () => {
|
|
65
|
+
expect(cameraSettingsSchema.parse({ cameraConfig: { aircraft: CAPTURE_CONFIG } }).cameraConfig).toEqual({
|
|
66
|
+
aircraft: CAPTURE_CONFIG,
|
|
67
|
+
drone: DEFAULT_CAPTURE_CONFIG,
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
test('copies the legacy flat capture config to both aircraft and drone', () => {
|
|
71
|
+
expect(cameraSettingsSchema.parse({ cameraConfig: CAPTURE_CONFIG }).cameraConfig).toEqual({
|
|
72
|
+
aircraft: CAPTURE_CONFIG,
|
|
73
|
+
drone: CAPTURE_CONFIG,
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
test('returns independent aircraft and drone objects', () => {
|
|
77
|
+
const legacy = cameraSettingsSchema.parse({ cameraConfig: CAPTURE_CONFIG }).cameraConfig;
|
|
78
|
+
legacy.drone.defaultCaptureSizeMeters = 1;
|
|
79
|
+
expect(legacy.aircraft).toEqual(CAPTURE_CONFIG);
|
|
80
|
+
const first = cameraSettingsSchema.parse({}).cameraConfig;
|
|
81
|
+
first.drone.defaultCaptureSizeMeters = 1;
|
|
82
|
+
expect(cameraSettingsSchema.parse({}).cameraConfig.drone).toEqual(DEFAULT_CAPTURE_CONFIG);
|
|
83
|
+
});
|
|
84
|
+
test('rejects a non-positive drone capture size', () => {
|
|
85
|
+
const cameraConfig = { aircraft: CAPTURE_CONFIG, drone: { ...CAPTURE_CONFIG, defaultCaptureSizeMeters: 0 } };
|
|
86
|
+
expect(cameraSettingsSchema.safeParse({ cameraConfig }).success).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
});
|
package/package.json
CHANGED
|
@@ -1,114 +1,114 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
"name": "camstreamerlib",
|
|
3
|
+
"version": "4.0.36-c07c9c1",
|
|
4
|
+
"description": "Helper library for CamStreamer ACAP applications.",
|
|
5
|
+
"prettier": "@camstreamer/prettier-config",
|
|
6
|
+
"engine": {
|
|
7
|
+
"node": ">=18.0.0"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"adm-zip": "^0.6.0",
|
|
11
|
+
"eventemitter2": "^5.0.1",
|
|
12
|
+
"fast-xml-parser": "^5.3.6",
|
|
13
|
+
"type-fest": "^4.41.0",
|
|
14
|
+
"undici": "^6.28.1",
|
|
15
|
+
"ws": "^8.18.0",
|
|
16
|
+
"zod": "^3.25.76"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@camstreamer/eslint-config": "^1.0.10",
|
|
20
|
+
"@camstreamer/prettier-config": "^2.0.4",
|
|
21
|
+
"@eslint/js": "^10.0.1",
|
|
22
|
+
"@types/adm-zip": "^0.5.5",
|
|
23
|
+
"@types/jest": "^30.0.0",
|
|
24
|
+
"@types/node": "^18.19.127",
|
|
25
|
+
"@types/ws": "^8.5.10",
|
|
26
|
+
"esbuild": "^0.28.2",
|
|
27
|
+
"eslint": "^10.12.0",
|
|
28
|
+
"eslint-plugin-unused-imports": "^4.4.1",
|
|
29
|
+
"globals": "^17.13.0",
|
|
30
|
+
"jest": "^30.5.2",
|
|
31
|
+
"npm-run-all": "^4.1.5",
|
|
32
|
+
"prettier": "^2.7.1",
|
|
33
|
+
"ts-jest": "^29.4.14",
|
|
34
|
+
"ts-node": "^10.7.0",
|
|
35
|
+
"typescript": "5.3.3",
|
|
36
|
+
"typescript-eslint": "^8.71.2"
|
|
37
|
+
},
|
|
38
|
+
"overrides": {
|
|
39
|
+
"js-yaml": "^4.3.2",
|
|
40
|
+
"browserslist": "^4.28.7"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"clean": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
44
|
+
"build": "npm-run-all clean build:cjs build:esm copyPackage",
|
|
45
|
+
"build:cjs": "tsc --project tsconfig.cjs.json",
|
|
46
|
+
"build:esm": "tsc --project tsconfig.esm.json",
|
|
47
|
+
"build:browser": "esbuild src/browser.index.ts --bundle --format=esm --platform=browser --target=es2022 --outfile=examples/camstreamerlib.js",
|
|
48
|
+
"copyPackage": "cp -f LICENSE dist/ && cp -f README.md dist/ && cp -f package.json dist/ && cp -r -f examples dist/",
|
|
49
|
+
"publishPackage": "./publish.sh",
|
|
50
|
+
"lint": "eslint \"src/**/*.ts\"",
|
|
51
|
+
"lint:fix": "eslint \"src/**/*.ts\" --fix",
|
|
52
|
+
"pretty": "prettier --write \"./{{src,doc}/**/*.{ts,tsx,md},README.md}\"",
|
|
53
|
+
"pretty:check": "prettier --check \"./{{src,doc}/**/*.{ts,tsx,md},README.md}\"",
|
|
54
|
+
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
55
|
+
"audit": "npm audit --audit-level=high"
|
|
56
|
+
},
|
|
57
|
+
"files": [
|
|
58
|
+
"/**/*.js",
|
|
59
|
+
"/**/*.ts",
|
|
60
|
+
"/examples/**/*"
|
|
61
|
+
],
|
|
62
|
+
"main": "./cjs/index.js",
|
|
63
|
+
"module": "./esm/index.js",
|
|
64
|
+
"types": "types/index.d.ts",
|
|
65
|
+
"exports": {
|
|
66
|
+
".": {
|
|
67
|
+
"import": "./esm/index.js",
|
|
68
|
+
"require": "./cjs/index.js",
|
|
69
|
+
"types": "./types/index.d.ts"
|
|
8
70
|
},
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"type-fest": "^4.41.0",
|
|
14
|
-
"undici": "^6.28.1",
|
|
15
|
-
"ws": "^8.18.0",
|
|
16
|
-
"zod": "^3.25.76"
|
|
71
|
+
"./web": {
|
|
72
|
+
"import": "./esm/web/index.js",
|
|
73
|
+
"require": "./cjs/web/index.js",
|
|
74
|
+
"types": "./types/web/index.d.ts"
|
|
17
75
|
},
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"@types/adm-zip": "^0.5.5",
|
|
23
|
-
"@types/jest": "^30.0.0",
|
|
24
|
-
"@types/node": "^18.19.127",
|
|
25
|
-
"@types/ws": "^8.5.10",
|
|
26
|
-
"esbuild": "^0.28.2",
|
|
27
|
-
"eslint": "^10.12.0",
|
|
28
|
-
"eslint-plugin-unused-imports": "^4.4.1",
|
|
29
|
-
"globals": "^17.13.0",
|
|
30
|
-
"jest": "^30.5.2",
|
|
31
|
-
"npm-run-all": "^4.1.5",
|
|
32
|
-
"prettier": "^2.7.1",
|
|
33
|
-
"ts-jest": "^29.4.14",
|
|
34
|
-
"ts-node": "^10.7.0",
|
|
35
|
-
"typescript": "5.3.3",
|
|
36
|
-
"typescript-eslint": "^8.71.2"
|
|
76
|
+
"./node": {
|
|
77
|
+
"import": "./esm/node/index.js",
|
|
78
|
+
"require": "./cjs/node/index.js",
|
|
79
|
+
"types": "./types/node/index.d.ts"
|
|
37
80
|
},
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
|
|
81
|
+
"./cjs/index": {
|
|
82
|
+
"import": "./esm/index.js",
|
|
83
|
+
"require": "./cjs/index.js",
|
|
84
|
+
"types": "./types/index.d.ts"
|
|
41
85
|
},
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"build:esm": "tsc --project tsconfig.esm.json",
|
|
47
|
-
"build:browser": "esbuild src/browser.index.ts --bundle --format=esm --platform=browser --target=es2022 --outfile=examples/camstreamerlib.js",
|
|
48
|
-
"copyPackage": "cp -f LICENSE dist/ && cp -f README.md dist/ && cp -f package.json dist/ && cp -r -f examples dist/",
|
|
49
|
-
"publishPackage": "./publish.sh",
|
|
50
|
-
"lint": "eslint \"src/**/*.ts\"",
|
|
51
|
-
"lint:fix": "eslint \"src/**/*.ts\" --fix",
|
|
52
|
-
"pretty": "prettier --write \"./{{src,doc}/**/*.{ts,tsx,md},README.md}\"",
|
|
53
|
-
"pretty:check": "prettier --check \"./{{src,doc}/**/*.{ts,tsx,md},README.md}\"",
|
|
54
|
-
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
55
|
-
"audit": "npm audit --audit-level=high"
|
|
86
|
+
"./cjs/node": {
|
|
87
|
+
"import": "./esm/node/index.js",
|
|
88
|
+
"require": "./cjs/node/index.js",
|
|
89
|
+
"types": "./types/node/index.js"
|
|
56
90
|
},
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
},
|
|
81
|
-
"./cjs/index": {
|
|
82
|
-
"import": "./esm/index.js",
|
|
83
|
-
"require": "./cjs/index.js",
|
|
84
|
-
"types": "./types/index.d.ts"
|
|
85
|
-
},
|
|
86
|
-
"./cjs/node": {
|
|
87
|
-
"import": "./esm/node/index.js",
|
|
88
|
-
"require": "./cjs/node/index.js",
|
|
89
|
-
"types": "./types/node/index.js"
|
|
90
|
-
},
|
|
91
|
-
"./cjs/node/index": {
|
|
92
|
-
"import": "./esm/node/index.js",
|
|
93
|
-
"require": "./cjs/node/index.js",
|
|
94
|
-
"types": "./types/node/index.js"
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
"repository": {
|
|
98
|
-
"type": "git",
|
|
99
|
-
"url": "git+https://github.com/CamStreamer/CamStreamerLib.git"
|
|
100
|
-
},
|
|
101
|
-
"keywords": [
|
|
102
|
-
"CamStreamer",
|
|
103
|
-
"CamOverlay",
|
|
104
|
-
"CamScripter",
|
|
105
|
-
"Camera",
|
|
106
|
-
"Axis"
|
|
107
|
-
],
|
|
108
|
-
"author": "CamStreamer s.r.o",
|
|
109
|
-
"license": "ISC",
|
|
110
|
-
"bugs": {
|
|
111
|
-
"url": "https://github.com/CamStreamer/CamStreamerLib/issues"
|
|
112
|
-
},
|
|
113
|
-
"homepage": "https://github.com/CamStreamer/CamStreamerLib#readme"
|
|
91
|
+
"./cjs/node/index": {
|
|
92
|
+
"import": "./esm/node/index.js",
|
|
93
|
+
"require": "./cjs/node/index.js",
|
|
94
|
+
"types": "./types/node/index.js"
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"repository": {
|
|
98
|
+
"type": "git",
|
|
99
|
+
"url": "git+https://github.com/CamStreamer/CamStreamerLib.git"
|
|
100
|
+
},
|
|
101
|
+
"keywords": [
|
|
102
|
+
"CamStreamer",
|
|
103
|
+
"CamOverlay",
|
|
104
|
+
"CamScripter",
|
|
105
|
+
"Camera",
|
|
106
|
+
"Axis"
|
|
107
|
+
],
|
|
108
|
+
"author": "CamStreamer s.r.o",
|
|
109
|
+
"license": "ISC",
|
|
110
|
+
"bugs": {
|
|
111
|
+
"url": "https://github.com/CamStreamer/CamStreamerLib/issues"
|
|
112
|
+
},
|
|
113
|
+
"homepage": "https://github.com/CamStreamer/CamStreamerLib#readme"
|
|
114
114
|
}
|
|
@@ -161,6 +161,7 @@ export declare class CamSwitcherAPI<Client extends IClient<TResponse, any>> exte
|
|
|
161
161
|
bitrateVapixParams?: string | null | undefined;
|
|
162
162
|
audioSampleRate?: number | undefined;
|
|
163
163
|
audioChannelCount?: 1 | 2 | undefined;
|
|
164
|
+
audioBitrate?: number | undefined;
|
|
164
165
|
} | undefined>;
|
|
165
166
|
getGlobalAudioSettings(options?: THttpRequestOptions): Promise<{
|
|
166
167
|
type: "source" | "fromSource";
|