camstreamerlib 4.0.35 → 4.0.36-05711a0

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.
@@ -45,8 +45,23 @@ export declare class PlaneTrackerAPI<Client extends IClient<TResponse, any>> ext
45
45
  focusCalibrationPoints: string;
46
46
  };
47
47
  cameraConfig: {
48
- defaultCaptureSizeMeters: number;
49
- captureSizeExtensionMeters: number;
48
+ aircraft: {
49
+ defaultCaptureSizeMeters: number;
50
+ captureSizeExtensionMeters: number;
51
+ };
52
+ drone: {
53
+ defaultCaptureSizeMeters: number;
54
+ captureSizeExtensionMeters: number;
55
+ };
56
+ } | {
57
+ aircraft: {
58
+ defaultCaptureSizeMeters: number;
59
+ captureSizeExtensionMeters: number;
60
+ };
61
+ drone: {
62
+ defaultCaptureSizeMeters: number;
63
+ captureSizeExtensionMeters: number;
64
+ };
50
65
  };
51
66
  stream: {
52
67
  width: number;
@@ -72,6 +87,7 @@ export declare class PlaneTrackerAPI<Client extends IClient<TResponse, any>> ext
72
87
  widget: {
73
88
  enabled: boolean;
74
89
  scale: number;
90
+ units: "rotate" | "metric" | "imperial";
75
91
  coord: "top_left" | "top_right" | "bottom_left" | "bottom_right";
76
92
  posX: number;
77
93
  posY: number;
@@ -85,6 +101,8 @@ export declare class PlaneTrackerAPI<Client extends IClient<TResponse, any>> ext
85
101
  posY: number;
86
102
  showWeather: boolean;
87
103
  weatherLocationKey: string;
104
+ windSpeedUnits: "rotate" | "metric" | "imperial";
105
+ temperatureUnits: "rotate" | "metric" | "imperial";
88
106
  };
89
107
  fr24FlightInfoSource: {
90
108
  enabled: boolean;
@@ -99,11 +117,29 @@ export declare class PlaneTrackerAPI<Client extends IClient<TResponse, any>> ext
99
117
  priority: number;
100
118
  };
101
119
  identificationLabel: {
102
- firstRow: "blank" | "registration" | "call_sign" | "flight_number" | "icao";
103
- secondRow: "blank" | "registration" | "call_sign" | "flight_number" | "icao";
104
- thirdRow: "blank" | "registration" | "call_sign" | "flight_number" | "icao";
105
- fourthRow: "blank" | "registration" | "call_sign" | "flight_number" | "icao";
106
- opacity: number;
120
+ aircraft: {
121
+ firstRow: "blank" | "registration" | "call_sign" | "flight_number" | "icao";
122
+ secondRow: "blank" | "registration" | "call_sign" | "flight_number" | "icao";
123
+ thirdRow: "blank" | "registration" | "call_sign" | "flight_number" | "icao";
124
+ fourthRow: "blank" | "registration" | "call_sign" | "flight_number" | "icao";
125
+ opacity: number;
126
+ };
127
+ drone: {
128
+ firstRow: "blank" | "remote_id";
129
+ opacity: number;
130
+ };
131
+ } | {
132
+ aircraft: {
133
+ firstRow: "blank" | "registration" | "call_sign" | "flight_number" | "icao";
134
+ secondRow: "blank" | "registration" | "call_sign" | "flight_number" | "icao";
135
+ thirdRow: "blank" | "registration" | "call_sign" | "flight_number" | "icao";
136
+ fourthRow: "blank" | "registration" | "call_sign" | "flight_number" | "icao";
137
+ opacity: number;
138
+ };
139
+ drone: {
140
+ firstRow: "blank" | "remote_id";
141
+ opacity: number;
142
+ };
107
143
  };
108
144
  acs: {
109
145
  ip: string;
@@ -181,11 +217,11 @@ export declare class PlaneTrackerAPI<Client extends IClient<TResponse, any>> ext
181
217
  importAppSettings(dataType: TImportDataType, formData: Parameters<Client['post']>[0]['data'], options?: THttpRequestOptions): Promise<void>;
182
218
  getDomainList(options?: THttpRequestOptions): Promise<Partial<Record<"adsb" | "remoteId", {
183
219
  uiName: string;
184
- icon: "unknown" | "small" | "large" | "heavy" | "helicopter" | "drone" | "operator" | "vehicle";
220
+ icon: "unknown" | "small" | "drone" | "large" | "heavy" | "helicopter" | "operator" | "vehicle";
185
221
  categoryList: {
186
222
  categoryId: string;
187
223
  uiName: string;
188
- icon: "unknown" | "small" | "large" | "heavy" | "helicopter" | "drone" | "operator" | "vehicle";
224
+ icon: "unknown" | "small" | "drone" | "large" | "heavy" | "helicopter" | "operator" | "vehicle";
189
225
  }[];
190
226
  }>>>;
191
227
  fetchFlightInfo(targetId: string, options?: THttpRequestOptions): Promise<{
package/cjs/VapixAPI.js CHANGED
@@ -565,17 +565,21 @@ class VapixAPI extends BasicAPI_1.BasicAPI {
565
565
  allowBooleanAttributes: true,
566
566
  });
567
567
  const result = parser.parse(xml);
568
- let apps = result.reply.application ?? [];
569
- if (!Array.isArray(apps)) {
570
- apps = [apps];
571
- }
572
- const appList = apps.map((app) => {
573
- return {
574
- ...app,
575
- appId: VapixAPI_1.ALL_APP_IDS.find((id) => id.toLowerCase() === app.Name.toLowerCase()) ?? null,
576
- };
577
- });
578
- return VapixAPI_1.applicationListSchema.parse(appList);
568
+ const rawApps = result.reply.application ?? [];
569
+ const apps = Array.isArray(rawApps) ? rawApps : [rawApps];
570
+ const appList = [];
571
+ for (const app of apps) {
572
+ const parsedApp = VapixAPI_1.applicationSchema.safeParse(app);
573
+ if (!parsedApp.success) {
574
+ console.debug('Skipping invalid application entry:', app, parsedApp.error.issues);
575
+ continue;
576
+ }
577
+ appList.push({
578
+ ...parsedApp.data,
579
+ appId: VapixAPI_1.ALL_APP_IDS.find((id) => id.toLowerCase() === parsedApp.data.Name.toLowerCase()) ?? null,
580
+ });
581
+ }
582
+ return appList;
579
583
  }
580
584
  async startApplication(applicationId, options) {
581
585
  const agent = this.getClient(options?.proxyParams);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const VapixAPI_1 = require("./VapixAPI");
4
+ const globals_1 = require("@jest/globals");
5
+ const createApi = (xml) => {
6
+ const client = {
7
+ get: () => Promise.resolve(new Response(xml)),
8
+ post: () => Promise.reject(new Error('unused')),
9
+ };
10
+ return new VapixAPI_1.VapixAPI(client);
11
+ };
12
+ (0, globals_1.describe)('VapixAPI', () => {
13
+ (0, globals_1.describe)('getApplicationList', () => {
14
+ (0, globals_1.afterEach)(() => {
15
+ globals_1.jest.restoreAllMocks();
16
+ });
17
+ (0, globals_1.test)('skips the nameless entry of the live camera list', async () => {
18
+ const debugSpy = globals_1.jest.spyOn(console, 'debug').mockImplementation(() => { });
19
+ const api = createApi(`<reply result="ok">
20
+ <application Name="camstreamer" NiceName="CamStreamer App" Vendor="CamStreamer" Version="5.0-15" ApplicationID="413135" License="None" Status="Running" ConfigurationPage="local/camstreamer/settings.html" />
21
+ <application Name="" License="None" Status="Stopped" />
22
+ <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" />
23
+ </reply>`);
24
+ await (0, globals_1.expect)(api.getApplicationList()).resolves.toEqual([
25
+ {
26
+ Name: 'camstreamer',
27
+ NiceName: 'CamStreamer App',
28
+ Vendor: 'CamStreamer',
29
+ Version: '5.0-15',
30
+ ApplicationID: '413135',
31
+ License: 'None',
32
+ Status: 'Running',
33
+ ConfigurationPage: 'local/camstreamer/settings.html',
34
+ appId: 'CamStreamer',
35
+ },
36
+ {
37
+ Name: 'vmd',
38
+ NiceName: 'AXIS Video Motion Detection',
39
+ Vendor: 'Axis Communications',
40
+ Version: '4.4-5',
41
+ ApplicationID: '143440',
42
+ License: 'None',
43
+ Status: 'Stopped',
44
+ ConfigurationPage: 'local/vmd/config.html',
45
+ VendorHomePage: 'http://www.axis.com',
46
+ LicenseName: 'Proprietary',
47
+ appId: null,
48
+ },
49
+ ]);
50
+ (0, globals_1.expect)(debugSpy).toHaveBeenCalledTimes(1);
51
+ (0, globals_1.expect)(debugSpy.mock.calls[0]).toContainEqual({ Name: '', License: 'None', Status: 'Stopped' });
52
+ });
53
+ globals_1.test.each([
54
+ {
55
+ name: 'an entry without a name',
56
+ 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>',
57
+ expected: [
58
+ {
59
+ Name: 'vmd',
60
+ NiceName: 'AXIS Video Motion Detection',
61
+ Vendor: 'Axis Communications',
62
+ Version: '4.4-5',
63
+ License: 'None',
64
+ Status: 'Stopped',
65
+ appId: null,
66
+ },
67
+ ],
68
+ },
69
+ {
70
+ name: 'a named entry missing informational fields',
71
+ 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>',
72
+ expected: [
73
+ {
74
+ Name: 'vmd',
75
+ NiceName: 'AXIS Video Motion Detection',
76
+ Vendor: 'Axis Communications',
77
+ Version: '4.4-5',
78
+ License: 'None',
79
+ Status: 'Stopped',
80
+ appId: null,
81
+ },
82
+ ],
83
+ },
84
+ ])('skips invalid entries individually: $name', async ({ xml, expected }) => {
85
+ const debugSpy = globals_1.jest.spyOn(console, 'debug').mockImplementation(() => { });
86
+ await (0, globals_1.expect)(createApi(xml).getApplicationList()).resolves.toEqual(expected);
87
+ (0, globals_1.expect)(debugSpy).toHaveBeenCalledTimes(1);
88
+ });
89
+ globals_1.test.each([
90
+ {
91
+ name: 'drops attributes outside the schema',
92
+ xml: '<reply result="ok"><application Name="camoverlay" NiceName="CamOverlay App" Vendor="CamStreamer" Version="3.0-1" License="None" Status="Running" Unexpected="value" /></reply>',
93
+ expected: [
94
+ {
95
+ Name: 'camoverlay',
96
+ NiceName: 'CamOverlay App',
97
+ Vendor: 'CamStreamer',
98
+ Version: '3.0-1',
99
+ License: 'None',
100
+ Status: 'Running',
101
+ appId: 'CamOverlay',
102
+ },
103
+ ],
104
+ },
105
+ {
106
+ name: 'maps an application outside the core CamStreamer apps to its appId',
107
+ xml: '<reply result="ok"><application Name="sporttracker" NiceName="SportTracker App" Vendor="CamStreamer" Version="1.0-1" License="None" Status="Running" /></reply>',
108
+ expected: [
109
+ {
110
+ Name: 'sporttracker',
111
+ NiceName: 'SportTracker App',
112
+ Vendor: 'CamStreamer',
113
+ Version: '1.0-1',
114
+ License: 'None',
115
+ Status: 'Running',
116
+ appId: 'SportTracker',
117
+ },
118
+ ],
119
+ },
120
+ {
121
+ name: 'maps a capitalised application name to its appId',
122
+ xml: '<reply result="ok"><application Name="CamOverlay" NiceName="CamOverlay App" Vendor="CamStreamer" Version="3.0-1" License="None" Status="Running" /></reply>',
123
+ expected: [
124
+ {
125
+ Name: 'CamOverlay',
126
+ NiceName: 'CamOverlay App',
127
+ Vendor: 'CamStreamer',
128
+ Version: '3.0-1',
129
+ License: 'None',
130
+ Status: 'Running',
131
+ appId: 'CamOverlay',
132
+ },
133
+ ],
134
+ },
135
+ {
136
+ name: 'returns an empty list when the camera has no applications',
137
+ xml: '<reply result="ok"></reply>',
138
+ expected: [],
139
+ },
140
+ ])('returns every valid entry with its appId and logs nothing: $name', async ({ xml, expected }) => {
141
+ const debugSpy = globals_1.jest.spyOn(console, 'debug').mockImplementation(() => { });
142
+ await (0, globals_1.expect)(createApi(xml).getApplicationList()).resolves.toEqual(expected);
143
+ (0, globals_1.expect)(debugSpy).not.toHaveBeenCalled();
144
+ });
145
+ });
146
+ });