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.
@@ -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";
@@ -156,6 +156,7 @@ class CamSwitcherAPI extends BasicAPI_1.BasicAPI {
156
156
  audio: {
157
157
  sampleRate: data.audioSampleRate,
158
158
  channelCount: data.audioChannelCount,
159
+ bitrate: data.audioBitrate,
159
160
  },
160
161
  keyboard: data.keyboard,
161
162
  };
@@ -207,6 +208,7 @@ class CamSwitcherAPI extends BasicAPI_1.BasicAPI {
207
208
  const settings = {
208
209
  audioSampleRate: saveData.audio?.sampleRate,
209
210
  audioChannelCount: saveData.audio?.channelCount,
211
+ audioBitrate: saveData.audio?.bitrate,
210
212
  keyboard: saveData.keyboard,
211
213
  fps: saveData.video.fps,
212
214
  resolution: saveData.video.resolution,
@@ -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;
@@ -99,11 +114,29 @@ export declare class PlaneTrackerAPI<Client extends IClient<TResponse, any>> ext
99
114
  priority: number;
100
115
  };
101
116
  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;
117
+ aircraft: {
118
+ firstRow: "blank" | "registration" | "call_sign" | "flight_number" | "icao";
119
+ secondRow: "blank" | "registration" | "call_sign" | "flight_number" | "icao";
120
+ thirdRow: "blank" | "registration" | "call_sign" | "flight_number" | "icao";
121
+ fourthRow: "blank" | "registration" | "call_sign" | "flight_number" | "icao";
122
+ opacity: number;
123
+ };
124
+ drone: {
125
+ firstRow: "blank" | "remote_id";
126
+ opacity: number;
127
+ };
128
+ } | {
129
+ aircraft: {
130
+ firstRow: "blank" | "registration" | "call_sign" | "flight_number" | "icao";
131
+ secondRow: "blank" | "registration" | "call_sign" | "flight_number" | "icao";
132
+ thirdRow: "blank" | "registration" | "call_sign" | "flight_number" | "icao";
133
+ fourthRow: "blank" | "registration" | "call_sign" | "flight_number" | "icao";
134
+ opacity: number;
135
+ };
136
+ drone: {
137
+ firstRow: "blank" | "remote_id";
138
+ opacity: number;
139
+ };
107
140
  };
108
141
  acs: {
109
142
  ip: string;
@@ -181,11 +214,11 @@ export declare class PlaneTrackerAPI<Client extends IClient<TResponse, any>> ext
181
214
  importAppSettings(dataType: TImportDataType, formData: Parameters<Client['post']>[0]['data'], options?: THttpRequestOptions): Promise<void>;
182
215
  getDomainList(options?: THttpRequestOptions): Promise<Partial<Record<"adsb" | "remoteId", {
183
216
  uiName: string;
184
- icon: "unknown" | "small" | "large" | "heavy" | "helicopter" | "drone" | "operator" | "vehicle";
217
+ icon: "unknown" | "small" | "drone" | "large" | "heavy" | "helicopter" | "operator" | "vehicle";
185
218
  categoryList: {
186
219
  categoryId: string;
187
220
  uiName: string;
188
- icon: "unknown" | "small" | "large" | "heavy" | "helicopter" | "drone" | "operator" | "vehicle";
221
+ icon: "unknown" | "small" | "drone" | "large" | "heavy" | "helicopter" | "operator" | "vehicle";
189
222
  }[];
190
223
  }>>>;
191
224
  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
+ });
@@ -785,6 +785,7 @@ export declare const cameraOptionsSchema: z.ZodObject<{
785
785
  bitrateVapixParams: z.ZodOptional<z.ZodNullable<z.ZodString>>;
786
786
  audioSampleRate: z.ZodOptional<z.ZodNumber>;
787
787
  audioChannelCount: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<1>, z.ZodLiteral<2>]>>;
788
+ audioBitrate: z.ZodOptional<z.ZodNumber>;
788
789
  keyboard: z.ZodOptional<z.ZodObject<{
789
790
  fromSource: z.ZodNullable<z.ZodString>;
790
791
  none: z.ZodNullable<z.ZodString>;
@@ -812,6 +813,7 @@ export declare const cameraOptionsSchema: z.ZodObject<{
812
813
  bitrateVapixParams?: string | null | undefined;
813
814
  audioSampleRate?: number | undefined;
814
815
  audioChannelCount?: 1 | 2 | undefined;
816
+ audioBitrate?: number | undefined;
815
817
  }, {
816
818
  resolution?: string | undefined;
817
819
  compression?: number | undefined;
@@ -829,6 +831,7 @@ export declare const cameraOptionsSchema: z.ZodObject<{
829
831
  bitrateVapixParams?: string | null | undefined;
830
832
  audioSampleRate?: number | undefined;
831
833
  audioChannelCount?: 1 | 2 | undefined;
834
+ audioBitrate?: number | undefined;
832
835
  }>;
833
836
  export type TCameraOptions = z.infer<typeof cameraOptionsSchema>;
834
837
  export declare const globalAudioSettingsTypeSchema: z.ZodUnion<[z.ZodLiteral<"fromSource">, z.ZodLiteral<"source">]>;
@@ -145,6 +145,7 @@ exports.cameraOptionsSchema = common_1.bitrateVapixParamsSchema
145
145
  bitrateVapixParams: zod_1.z.string().nullable(),
146
146
  audioSampleRate: zod_1.z.number(),
147
147
  audioChannelCount: common_1.audioChannelCountSchema,
148
+ audioBitrate: zod_1.z.number().int().nonnegative(),
148
149
  keyboard: zod_1.z.object({
149
150
  fromSource: common_1.keyboardShortcutSchema,
150
151
  none: common_1.keyboardShortcutSchema,