camstreamerlib 4.0.34 → 4.0.35-64a81fd

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.
@@ -14,6 +14,12 @@ export declare class CamOverlayAPI<Client extends IClient<TResponse, any>> exten
14
14
  }[]>;
15
15
  wsAuthorization(options?: THttpRequestOptions): Promise<string>;
16
16
  getMjpegStreamImage(mjpegUrl: string, options?: THttpRequestOptions): Promise<ReturnType<Awaited<ReturnType<Client["get"]>>["blob"]>>;
17
+ isViewerPassValid(pass: string, options?: THttpRequestOptions): Promise<boolean>;
18
+ getLiveViewAlias(rtspUrl: string, options?: THttpRequestOptions): Promise<{
19
+ alias: string;
20
+ ws: string;
21
+ ws_initial_message: string;
22
+ }>;
17
23
  listFiles<T extends TFileType>(fileType: T, options?: THttpRequestOptions): Promise<TFileList<T>>;
18
24
  uploadFile<T extends TFileType>(fileType: T, formData: Parameters<Client['post']>[0]['data'], storage: TFileStorageType<T>, options?: THttpRequestOptions): Promise<void>;
19
25
  removeFile<T extends TFileType>(fileType: T, fileParams: TFile<T>, options?: THttpRequestOptions): Promise<void>;
@@ -29,6 +29,22 @@ class CamOverlayAPI extends BasicAPI_1.BasicAPI {
29
29
  async getMjpegStreamImage(mjpegUrl, options) {
30
30
  return await this._getBlob(`${BASE_PATH}/fetch_mjpeg_image.cgi`, { mjpeg_url: decodeURIComponent(mjpegUrl) }, options);
31
31
  }
32
+ async isViewerPassValid(pass, options) {
33
+ const res = await this._getJson(`${BASE_PATH}/check_pass.cgi`, { pass }, options);
34
+ if (res.status !== 200) {
35
+ throw new Error(res.message);
36
+ }
37
+ return res.data === '1';
38
+ }
39
+ async getLiveViewAlias(rtspUrl, options) {
40
+ const agent = this.getClient(options?.proxyParams);
41
+ const res = await agent.get({
42
+ path: `${BASE_PATH}/getLiveViewAlias.cgi`,
43
+ parameters: { rtsp_url: rtspUrl },
44
+ timeout: options?.timeout,
45
+ });
46
+ return common_1.wsAliasResponseSchema.parse(await res.json());
47
+ }
32
48
  async listFiles(fileType, options) {
33
49
  const res = await this._getJson(`${BASE_PATH}/upload_${fileType}.cgi`, { action: 'list' }, options);
34
50
  return (0, CamOverlayAPI_1.getFileListSchema)(fileType).parse(res.list);
@@ -28,9 +28,9 @@ export declare class CamSwitcherAPI<Client extends IClient<TResponse, any>> exte
28
28
  }[]>;
29
29
  wsAuthorization(options?: THttpRequestOptions): Promise<string>;
30
30
  getOutputInfo(options?: THttpRequestOptions): Promise<{
31
- rtspUrl: string;
32
31
  ws: string;
33
32
  wsInitialMessage: string;
33
+ rtspUrl: string;
34
34
  }>;
35
35
  getAudioPushInfo(options?: THttpRequestOptions): Promise<{
36
36
  ws: string;
@@ -12,9 +12,9 @@ export declare class PlaneTrackerAPI<Client extends IClient<TResponse, any>> ext
12
12
  checkCameraTime(options?: THttpRequestOptions): Promise<boolean>;
13
13
  serverRunCheck(options?: THttpRequestOptions): Promise<boolean>;
14
14
  getLiveViewAlias(rtspUrl: string, options?: THttpRequestOptions): Promise<{
15
+ alias: string;
15
16
  ws: string;
16
17
  ws_initial_message: string;
17
- alias: string;
18
18
  }>;
19
19
  resetPtzCalibration(options?: THttpRequestOptions): Promise<void>;
20
20
  resetFocusCalibration(options?: THttpRequestOptions): Promise<void>;
@@ -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<{
@@ -4,6 +4,7 @@ exports.PlaneTrackerAPI = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const PlaneTrackerAPI_1 = require("./types/PlaneTrackerAPI");
6
6
  const errors_1 = require("./errors/errors");
7
+ const common_1 = require("./types/common");
7
8
  const BasicAPI_1 = require("./internal/BasicAPI");
8
9
  const PlaneTrackerEvents_1 = require("./types/ws/PlaneTrackerEvents");
9
10
  const BASE_PATH = '/local/planetracker';
@@ -37,7 +38,7 @@ class PlaneTrackerAPI extends BasicAPI_1.BasicAPI {
37
38
  parameters: { rtsp_url: rtspUrl },
38
39
  timeout: options?.timeout,
39
40
  });
40
- return PlaneTrackerAPI_1.wsAliasResponseSchema.parse(await res.json());
41
+ return common_1.wsAliasResponseSchema.parse(await res.json());
41
42
  }
42
43
  async resetPtzCalibration(options) {
43
44
  const agent = this.getClient(options?.proxyParams);
package/cjs/VapixAPI.js CHANGED
@@ -93,7 +93,9 @@ class VapixAPI extends BasicAPI_1.BasicAPI {
93
93
  };
94
94
  await this._postJsonEncoded('/axis-cgi/opticscontrol.cgi', data, undefined, options);
95
95
  }
96
- catch {
96
+ catch (error) {
97
+ console.warn('Failed to perform autofocus: ', error instanceof Error ? error.message : String(error));
98
+ console.warn('Falling back to deprecated optics setup API');
97
99
  await this._postUrlEncoded('/axis-cgi/opticssetup.cgi', {
98
100
  autofocus: 'perform',
99
101
  source: '1',
@@ -144,7 +146,7 @@ class VapixAPI extends BasicAPI_1.BasicAPI {
144
146
  return data.data.activeTimeZone;
145
147
  }
146
148
  catch (error) {
147
- console.warn('Failed to fetch time zone data from time API v2:', error instanceof Error ? error.message : JSON.stringify(error));
149
+ console.warn('Failed to fetch time zone data from time API v2:', error instanceof Error ? error.message : String(error));
148
150
  console.warn('Falling back to deprecated time API v1');
149
151
  }
150
152
  try {
@@ -563,17 +565,21 @@ class VapixAPI extends BasicAPI_1.BasicAPI {
563
565
  allowBooleanAttributes: true,
564
566
  });
565
567
  const result = parser.parse(xml);
566
- let apps = result.reply.application ?? [];
567
- if (!Array.isArray(apps)) {
568
- apps = [apps];
569
- }
570
- const appList = apps.map((app) => {
571
- return {
572
- ...app,
573
- appId: VapixAPI_1.ALL_APP_IDS.find((id) => id.toLowerCase() === app.Name.toLowerCase()) ?? null,
574
- };
575
- });
576
- 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;
577
583
  }
578
584
  async startApplication(applicationId, options) {
579
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
+ });
@@ -26,7 +26,7 @@ class ServiceNotFoundError extends Error {
26
26
  exports.ServiceNotFoundError = ServiceNotFoundError;
27
27
  class ParsingBlobError extends Error {
28
28
  constructor(err) {
29
- super('Error parsing response as Blob: ' + err);
29
+ super('Error parsing response as Blob: ' + String(err));
30
30
  this.name = 'ParsingBlobError';
31
31
  }
32
32
  }
@@ -99,7 +99,7 @@ class NoDeviceInfoError extends Error {
99
99
  exports.NoDeviceInfoError = NoDeviceInfoError;
100
100
  class FetchDeviceInfoError extends Error {
101
101
  constructor(err) {
102
- super('Error fetching remote camera data: ' + err);
102
+ super('Error fetching remote camera data: ' + String(err));
103
103
  this.name = 'NoDeviceInfoFromCameraError';
104
104
  }
105
105
  }
@@ -127,7 +127,7 @@ class PtzNotSupportedError extends Error {
127
127
  exports.PtzNotSupportedError = PtzNotSupportedError;
128
128
  class StorageDataFetchError extends Error {
129
129
  constructor(err) {
130
- super('Error fetching storage data: ' + err);
130
+ super('Error fetching storage data: ' + String(err));
131
131
  this.name = 'StorageDataFetchError';
132
132
  }
133
133
  }
@@ -155,7 +155,7 @@ class TimezoneNotSetupError extends Error {
155
155
  exports.TimezoneNotSetupError = TimezoneNotSetupError;
156
156
  class TimezoneFetchError extends Error {
157
157
  constructor(err) {
158
- super('Error fetching time zone information: ' + err);
158
+ super('Error fetching time zone information: ' + String(err));
159
159
  this.name = 'TimezoneFetchError';
160
160
  }
161
161
  }
@@ -25,21 +25,21 @@ export declare const outputInfoSchema: z.ZodEffects<z.ZodObject<{
25
25
  ws: z.ZodString;
26
26
  ws_initial_message: z.ZodString;
27
27
  }, "strip", z.ZodTypeAny, {
28
- rtsp_url: string;
29
28
  ws: string;
30
29
  ws_initial_message: string;
31
- }, {
32
30
  rtsp_url: string;
31
+ }, {
33
32
  ws: string;
34
33
  ws_initial_message: string;
34
+ rtsp_url: string;
35
35
  }>, {
36
- rtspUrl: string;
37
36
  ws: string;
38
37
  wsInitialMessage: string;
38
+ rtspUrl: string;
39
39
  }, {
40
- rtsp_url: string;
41
40
  ws: string;
42
41
  ws_initial_message: string;
42
+ rtsp_url: string;
43
43
  }>;
44
44
  export type TOutputInfo = z.infer<typeof outputInfoSchema>;
45
45
  export declare const audioPushInfoSchema: z.ZodEffects<z.ZodObject<{