diff --git a/README.md b/README.md index 3a7c328..0dc5422 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ The node-onvif provides you with the APIs as follows: * Device Management Service * Media Service * PTZ Service + * Search Service + * Replay Service Besides, the node-onvif provides you with simple APIs that allow you to control ONVIF network cameras easily even if you are not familiar with the ONVIF specifications. @@ -137,6 +139,14 @@ This package includes a sample application "[ONVIF Network Camera Manager](https * [`getPresets(params[, callback])` method](#OnvifServicePtz-getPresets-method) * [`gotoPreset(params[, callback])` method](#OnvifServicePtz-gotoPreset-method) * [`removePreset(params[, callback])` method](#OnvifServicePtz-removePreset-method) +* [`OnvifServiceSearch` object](#OnvifServiceSearch-object) + * [`getServiceCapabilities([callback])` method](#OnvifServiceSearch-getServiceCapabilities-method) + * [`getRecordingSummary([callback])` method](#OnvifServiceSearch-getRecordingSummary-method) + * [`findRecordings(params[, callback])` method](#OnvifServiceSearch-findRecordings-method) + * [`getRecordingSearchResults(params[, callback])` method](#OnvifServiceSearch-getRecordingSearchResults-method) +* [`OnvifServiceReplay` object](#OnvifServiceReplay-object) + * [`getServiceCapabilities([callback])` method](#OnvifServiceReplay-getServiceCapabilities-method) + * [`getReplayUri(params[, callback])` method](#OnvifServiceReplay-getReplayUri-method) * [References](#References) * [Release Note](#Release-Note) * [License](#License) @@ -601,6 +611,8 @@ Property | | Type | Description +- | `device` | Object | [`OnvifServiceDevice`](#OnvifServiceDevice-object) object +- | `media` | Object | [`OnvifServiceMedia`](#OnvifServiceMedia-object) object +- | `ptz` | Object | [`OnvifServicePtz`](#OnvifServicePtz-object) object ++- | `search` | Object | [`OnvifServiceSearch`](#OnvifServiceSearch-object) object ++- | `replay` | Object | [`OnvifServiceReplay`](#OnvifServiceReplay-object) object These objects will be set when the initialization process is completed calling the [`init()`](#OnvifDevice-init-method) method. See the section "[ONVIF commands](#ONVIF-commands)" for details. @@ -2225,6 +2237,127 @@ device.services.ptz.removePreset(params).then((result) => { }); ``` +--------------------------------------- +## `OnvifServiceSearch` object + +This object represents the [ONVIF Search Service](https://www.onvif.org/specs/srv/rsrch/ONVIF-RecordingSearch-Service-Spec.pdf). + +### getServiceCapabilities(*[callback]*) method + +This method sends a `GetServiceCapabilities` command. + +### getRecordingSummary(*[callback]*) method + +This method sends a `GetRecordingSummary` command. + +### findRecordings(*params[, callback]*) method + +This method sends a `FindRecordings` command. The 1st argument `params` must be a hash object consisting of the properties as follows: + +Property | | | Type | Required |Description +:---------------|:-----------------------------|:--------|:--------|:---------|:---------- +`Scope` | | |Object | required | scope defines the dataset to consider for this search ++- | `IncludedSources` | | Array | optional | a list of sources that are included in the scope ++- | +- | `Type` | String | optional | ++- | +- | `Token` | String | required | ++- | `IncludedRecordings` | | Array | optional | a list of recordings that are included in the scope ++- | `RecordingInformationFilter` | | String | optional | an xpath expression used to specify what recordings to search ++- | `Extension` | | String | optional | extension point +`MaxMatches` | | | Integer | optional | the search will be completed after this many matches +`KeepAliveTime` | | | Integer | required | the time the search session will be kept alive after responding to this and subsequent requests + +```JavaScript +let params = { + 'Scope': { + 'IncludedSources': [ + { + 'Type': 'sourceType', + 'Token': 'sourceToken' + } + ], + 'IncludedRecordings': [ + 'recording1' + ], + 'RecordingInformationFilter': 'filter', + 'Extension': 'extension', + }, + 'MaxMatches': 3, + 'KeepAliveTime': 100 +}; + +device.services.ptz.findRecordings(params).then((result) => { + console.log(JSON.stringify(result['data'], null, ' ')); +}).catch((error) => { + console.error(error); +}); +``` + +### getRecordingSearchResults(*params[, callback]*) method + +This method sends a `GetRecordingSearchResults` command. The 1st argument `params` must be a hash object consisting of the properties as follows: + +Property | Type | Required |Description +:----------------|:--------|:---------|:---------- +`SearchToken` | String | required | the search session to get results from +`MinResults` | Integer | optional | the minimum number of results to return in one response +`MaxResults` | Integer | optional | the maximum number of results to return in one response +`WaitTime` | Integer | optional | the maximum time before responding to the request + +```JavaScript +let params = { + 'SearchToken': 'token', + 'MinResults': 3, + 'MaxResults': 14, + 'WaitTime': 50 +}; + +device.services.ptz.findRecordings(params).then((result) => { + console.log(JSON.stringify(result['data'], null, ' ')); +}).catch((error) => { + console.error(error); +}); +``` + +--------------------------------------- +## `OnvifServiceReplay` object + +This object represents the [ONVIF Replay Service](https://www.onvif.org/specs/srv/replay/ONVIF-ReplayControl-Service-Spec.pdf). + +### getServiceCapabilities(*[callback]*) method + +This method sends a `GetServiceCapabilities` command. + +### getReplayUri(*params[, callback]*) method + +This method sends a `GetReplayUri` command. The 1st argument `params` must be a hash object consisting of the properties as follows: + +Property | | | Type | Required |Description +:----------------|:------------|:-----------|:-------|:---------|:---------- +`StreamSetup` | | | Object | required | the connection parameters to be used for the stream ++- | `Stream` | | String | required | either RTP-Unicast, RTP-Multicast ++- | `Transport` | | Object | required | ++- | +- | `Protocol` | String | required | either UDP, TCP, RTSP, HTTP ++- | +- | `Tunnel` | Object | optional | **`TODO`** not implemented +`RecordingToken` | | | String | required | identifier of the recording to be streamed + +```JavaScript +let params = { + 'StreamSetup': { + 'Stream': 'RTP-Unicast', + 'Transport': { + 'Protocol': 'TCP' + }, + }, + 'RecordingToken': 'token' +}; + +device.services.ptz.findRecordings(params).then((result) => { + console.log(JSON.stringify(result['data'], null, ' ')); +}).catch((error) => { + console.error(error); +}); +``` + --------------------------------------- ## References @@ -2233,10 +2366,14 @@ This module is based on the [ONVIF specifications](http://www.onvif.org/Document * [ONVIF Core Specification ver 16.06](http://www.onvif.org/specs/core/ONVIF-Core-Specification-v1606.pdf) * [ONVIF Media Service Specification ver 16.06](http://www.onvif.org/specs/srv/media/ONVIF-Media-Service-Spec-v1606.pdf) * [ONVIF PTZ Service Specification ver 2.6.1](http://www.onvif.org/specs/srv/ptz/ONVIF-PTZ-Service-Spec-v261.pdf) +* [ONVIF Search Service Specification ver 18.12](https://www.onvif.org/specs/srv/rsrch/ONVIF-RecordingSearch-Service-Spec-v1812.pdf) +* [ONVIF Replay Service Specification ver 17.06](https://www.onvif.org/specs/srv/replay/ONVIF-ReplayControl-Service-Spec-v1706.pdf) * [ONVIF Profile S Specification ver 1.1.1](http://www.onvif.org/Portals/0/documents/op/ONVIF_Profile_%20S_Specification_v1-1-1.pdf) * [ONVIF Device Management Service WSDL, ver 16.06](http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl) * [ONVIF Media Service WSDL, ver 2.6](http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl) * [ONVIF PTZ Service WSDL, ver 16.06](http://www.onvif.org/onvif/ver20/ptz/wsdl/ptz.wsdl) +* [ONVIF Search Service WSDL, ver 2.4.2](https://www.onvif.org/ver10/search.wsdl) +* [ONVIF Replay Service WSDL, ver 18.06](https://www.onvif.org/ver10/replay.wsdl) * [ONVIF Application Programmer's Guide ver 1.0](http://www.onvif.org/Portals/0/documents/WhitePapers/ONVIF_WG-APG-Application_Programmer%27s_Guide.pdf) --------------------------------------- diff --git a/lib/modules/device.js b/lib/modules/device.js index 29c0522..b32ec1d 100644 --- a/lib/modules/device.js +++ b/lib/modules/device.js @@ -15,6 +15,8 @@ const mOnvifServiceDevice = require('./service-device.js'); const mOnvifServiceMedia = require('./service-media.js'); const mOnvifServicePtz = require('./service-ptz.js'); const mOnvifServiceEvents = require('./service-events.js'); +const mOnvifServiceSearch = require('./service-search.js'); +const mOnvifServiceReplay = require('./service-replay.js'); const mOnvifHttpAuth = require('./http-auth.js'); /* ------------------------------------------------------------------ @@ -71,7 +73,9 @@ function OnvifDevice(params) { 'events': null, 'imaging': null, 'media': null, - 'ptz': null + 'ptz': null, + 'search': null, + 'replay': null }; this.profile_list = []; @@ -439,6 +443,24 @@ OnvifDevice.prototype._getCapabilities = function () { 'pass': this.pass }); } + let search = c['Extension'] && c['Extension']['Search']; + if (search && search['XAddr']) { + this.services.search = new mOnvifServiceSearch({ + 'xaddr': this._getXaddr(search['XAddr']), + 'time_diff': this.time_diff, + 'user': this.user, + 'pass': this.pass + }); + } + let replay = c['Extension'] && c['Extension']['Replay']; + if (replay && replay['XAddr']) { + this.services.replay = new mOnvifServiceReplay({ + 'xaddr': this._getXaddr(replay['XAddr']), + 'time_diff': this.time_diff, + 'user': this.user, + 'pass': this.pass + }); + } resolve(); }); }); diff --git a/lib/modules/service-replay.js b/lib/modules/service-replay.js new file mode 100644 index 0000000..8fd494e --- /dev/null +++ b/lib/modules/service-replay.js @@ -0,0 +1,214 @@ +/* ------------------------------------------------------------------ + * node-onvif - service-replay.js + * + * Copyright (c) 2018 - 2019, Gabriele Monaco, All rights reserved. + * Released under the MIT license + * Date: 2019-04-12 + * ---------------------------------------------------------------- */ +'use strict'; +const mUrl = require('url'); +const mOnvifSoap = require('./soap.js'); + +/* ------------------------------------------------------------------ + * Constructor: OnvifServiceReplay(params) + * - params: + * - xaddr : URL of the entry point for the replay service + * (Required) + * - user : User name (Optional) + * - pass : Password (Optional) + * - time_diff: ms + * ---------------------------------------------------------------- */ +function OnvifServiceReplay(params) { + this.xaddr = ''; + this.user = ''; + this.pass = ''; + + let err_msg = ''; + + if(err_msg = mOnvifSoap.isInvalidValue(params, 'object')) { + throw new Error('The value of "params" was invalid: ' + err_msg); + } + + if('xaddr' in params) { + if(err_msg = mOnvifSoap.isInvalidValue(params['xaddr'], 'string')) { + throw new Error('The "xaddr" property was invalid: ' + err_msg); + } else { + this.xaddr = params['xaddr']; + } + } else { + throw new Error('The "xaddr" property is required.'); + } + + if('user' in params) { + if(err_msg = mOnvifSoap.isInvalidValue(params['user'], 'string', true)) { + throw new Error('The "user" property was invalid: ' + err_msg); + } else { + this.user = params['user'] || ''; + } + } + + if('pass' in params) { + if(err_msg = mOnvifSoap.isInvalidValue(params['pass'], 'string', true)) { + throw new Error('The "pass" property was invalid: ' + err_msg); + } else { + this.pass = params['pass'] || ''; + } + } + + this.oxaddr = mUrl.parse(this.xaddr); + if(this.user) { + this.oxaddr.auth = this.user + ':' + this.pass; + } + + this.time_diff = params['time_diff']; + this.name_space_attr_list = [ + 'xmlns:trp="http://www.onvif.org/ver10/replay/wsdl"', + 'xmlns:tt="http://www.onvif.org/ver10/schema"' + ]; +}; + +OnvifServiceReplay.prototype._createRequestSoap = function(body) { + let soap = mOnvifSoap.createRequestSoap({ + 'body': body, + 'xmlns': this.name_space_attr_list, + 'diff': this.time_diff, + 'user': this.user, + 'pass': this.pass + }); + return soap; +}; + +/* ------------------------------------------------------------------ + * Method: setAuth(user, pass) + * ---------------------------------------------------------------- */ +OnvifServiceReplay.prototype.setAuth = function(user, pass) { + this.user = user || ''; + this.pass = pass || ''; + if(this.user) { + this.oxaddr.auth = this.user + ':' + this.pass; + } else { + this.oxaddr.auth = ''; + } +}; + +/* ------------------------------------------------------------------ + * Method: getServiceCapabilities([callback]) + * ---------------------------------------------------------------- */ +OnvifServiceReplay.prototype.getServiceCapabilities = function(callback) { + let promise = new Promise((resolve, reject) => { + let soap_body = ''; + let soap = this._createRequestSoap(soap_body); + mOnvifSoap.requestCommand(this.oxaddr, 'GetServiceCapabilities', soap).then((result) => { + try { + let d = result['data']['Capabilities']; + if(!Array.isArray(d)) { + result['data']['Capabilities'] = [d]; + } + } catch(e) {} + resolve(result); + }).catch((error) => { + reject(error); + }); + }); + if(callback) { + promise.then((result) => { + callback(null, result); + }).catch((error) => { + callback(error); + }); + } else { + return promise; + } +}; + +/* ------------------------------------------------------------------ + * Method: getReplayUri(params[, callback]) + * - params: + * - StreamSetup | Object | required | the connection parameters to be used for the stream + * - Stream | String | required | either RTP-Unicast, RTP-Multicast + * - Transport | Object | required | + * - Protocol | String | required | either UDP, TCP, RTSP, HTTP + * - Tunnel | Object | optional | TODO not implemented + * - RecordingToken | String | required | identifier of the recording to be streamed + * + * ---------------------------------------------------------------- */ +OnvifServiceReplay.prototype.getReplayUri = function(params, callback) { + let promise = new Promise((resolve, reject) => { + let err_msg = ''; + if(err_msg = mOnvifSoap.isInvalidValue(params, 'object')) { + reject(new Error('The value of "params" was invalid: ' + err_msg)); + return; + } + + if(err_msg = mOnvifSoap.isInvalidValue(params['StreamSetup'], 'object')) { + reject(new Error('The "Scope" property was invalid: ' + err_msg)); + return; + } + + let stream = params['StreamSetup']['Stream'] + if(err_msg = mOnvifSoap.isInvalidValue(stream, 'string')) { + reject(new Error('The "Stream" property was invalid: ' + err_msg)); + return; + } else if(!stream.match(/^(RTP-Unicast|RTP-Multicast)$/)) { + reject(new Error('The "Stream" property was invalid: The value must be either "RTP-Unicast" or "RTP-Multicast".')); + return; + } + + if(err_msg = mOnvifSoap.isInvalidValue(params['StreamSetup']['Transport'], 'object')) { + reject(new Error('The "Transport" property was invalid: ' + err_msg)); + return; + } + + let protocol = params['StreamSetup']['Transport']['Protocol'] + if(err_msg = mOnvifSoap.isInvalidValue(protocol, 'string')) { + reject(new Error('The "Stream" property was invalid: ' + err_msg)); + return; + } else if(!protocol.match(/^(UDP|TCP|RTSP|HTTP)$/)) { + reject(new Error('The "Stream" property was invalid: The value must be either "UDP", "TCP", "RTSP" or "HTTP".')); + return; + } + + if('Tunnel' in params['StreamSetup']['Transport']) { + //TODO implement this + } + + if(err_msg = mOnvifSoap.isInvalidValue(params['RecordingToken'], 'string')) { + reject(new Error('The "RecordingToken" property was invalid: ' + err_msg)); + return; + } + + let soap_body = ''; + soap_body += ''; + soap_body += ''; + soap_body += '' + params['StreamSetup']['Stream'] + ''; + soap_body += ''; + soap_body += '' + params['StreamSetup']['Transport']['Protocol'] + ''; + soap_body += ''; + soap_body += ''; + soap_body += '' + params['RecordingToken'] + ''; + soap_body += ''; + let soap = this._createRequestSoap(soap_body); + mOnvifSoap.requestCommand(this.oxaddr, 'GetReplayUri', soap).then((result) => { + try { + let d = result['data']['Uri']; + if(!Array.isArray(d)) { + result['data']['Uri'] = [d]; + } + } catch(e) {} + resolve(result); + }).catch((error) => { + reject(error); + }); + }); + if(callback) { + promise.then((result) => { + callback(null, result); + }).catch((error) => { + callback(error); + }); + } else { + return promise; + } +}; + +module.exports = OnvifServiceReplay; diff --git a/lib/modules/service-search.js b/lib/modules/service-search.js new file mode 100644 index 0000000..5d35b5e --- /dev/null +++ b/lib/modules/service-search.js @@ -0,0 +1,374 @@ +/* ------------------------------------------------------------------ + * node-onvif - service-search.js + * + * Copyright (c) 2018 - 2019, Gabriele Monaco, All rights reserved. + * Released under the MIT license + * Date: 2019-04-12 + * ---------------------------------------------------------------- */ +'use strict'; +const mUrl = require('url'); +const mOnvifSoap = require('./soap.js'); + +/* ------------------------------------------------------------------ + * Constructor: OnvifServiceSearch(params) + * - params: + * - xaddr : URL of the entry point for the search service + * (Required) + * - user : User name (Optional) + * - pass : Password (Optional) + * - time_diff: ms + * ---------------------------------------------------------------- */ +function OnvifServiceSearch(params) { + this.xaddr = ''; + this.user = ''; + this.pass = ''; + + let err_msg = ''; + + if(err_msg = mOnvifSoap.isInvalidValue(params, 'object')) { + throw new Error('The value of "params" was invalid: ' + err_msg); + } + + if('xaddr' in params) { + if(err_msg = mOnvifSoap.isInvalidValue(params['xaddr'], 'string')) { + throw new Error('The "xaddr" property was invalid: ' + err_msg); + } else { + this.xaddr = params['xaddr']; + } + } else { + throw new Error('The "xaddr" property is required.'); + } + + if('user' in params) { + if(err_msg = mOnvifSoap.isInvalidValue(params['user'], 'string', true)) { + throw new Error('The "user" property was invalid: ' + err_msg); + } else { + this.user = params['user'] || ''; + } + } + + if('pass' in params) { + if(err_msg = mOnvifSoap.isInvalidValue(params['pass'], 'string', true)) { + throw new Error('The "pass" property was invalid: ' + err_msg); + } else { + this.pass = params['pass'] || ''; + } + } + + this.oxaddr = mUrl.parse(this.xaddr); + if(this.user) { + this.oxaddr.auth = this.user + ':' + this.pass; + } + + this.time_diff = params['time_diff']; + this.name_space_attr_list = [ + 'xmlns:tse="http://www.onvif.org/ver10/search/wsdl"', + 'xmlns:tt="http://www.onvif.org/ver10/schema"' + ]; +}; + +OnvifServiceSearch.prototype._createRequestSoap = function(body) { + let soap = mOnvifSoap.createRequestSoap({ + 'body': body, + 'xmlns': this.name_space_attr_list, + 'diff': this.time_diff, + 'user': this.user, + 'pass': this.pass + }); + return soap; +}; + +/* ------------------------------------------------------------------ + * Method: setAuth(user, pass) + * ---------------------------------------------------------------- */ +OnvifServiceSearch.prototype.setAuth = function(user, pass) { + this.user = user || ''; + this.pass = pass || ''; + if(this.user) { + this.oxaddr.auth = this.user + ':' + this.pass; + } else { + this.oxaddr.auth = ''; + } +}; + +/* ------------------------------------------------------------------ + * Method: getServiceCapabilities([callback]) + * ---------------------------------------------------------------- */ +OnvifServiceSearch.prototype.getServiceCapabilities = function(callback) { + let promise = new Promise((resolve, reject) => { + let soap_body = ''; + let soap = this._createRequestSoap(soap_body); + mOnvifSoap.requestCommand(this.oxaddr, 'GetServiceCapabilities', soap).then((result) => { + try { + let d = result['data']['Capabilities']; + if(!Array.isArray(d)) { + result['data']['Capabilities'] = [d]; + } + } catch(e) {} + resolve(result); + }).catch((error) => { + reject(error); + }); + }); + if(callback) { + promise.then((result) => { + callback(null, result); + }).catch((error) => { + callback(error); + }); + } else { + return promise; + } +}; + +/* ------------------------------------------------------------------ + * Method: getRecordingSummary([callback]) + * ---------------------------------------------------------------- */ +OnvifServiceSearch.prototype.getRecordingSummary = function(callback) { + let promise = new Promise((resolve, reject) => { + let soap_body = ''; + let soap = this._createRequestSoap(soap_body); + mOnvifSoap.requestCommand(this.oxaddr, 'GetRecordingSummary', soap).then((result) => { + try { + let d = result['data']['Summary']; + if(!Array.isArray(d)) { + result['data']['Summary'] = [d]; + } + } catch(e) {} + resolve(result); + }).catch((error) => { + reject(error); + }); + }); + if(callback) { + promise.then((result) => { + callback(null, result); + }).catch((error) => { + callback(error); + }); + } else { + return promise; + } +}; + +/* ------------------------------------------------------------------ + * Method: findRecordings(params[, callback]) + * - params: + * - Scope | Object | required | scope defines the dataset to consider for this search + * - IncludedSources | Array | optional | a list of sources that are included in the scope + * - Type | String | optional | + * - Token | String | required | + * - IncludedRecordings | Array | optional | a list of recordings that are included in the scope + * - RecordingInformationFilter | String | optional | an xpath expression used to specify what recordings to search + * - Extension | String | optional | extension point + * - MaxMatches | Integer | optional | the search will be completed after this many matches + * - KeepAliveTime | Integer | required | the time the search session will be kept alive after responding to this and subsequent requests + * + * ---------------------------------------------------------------- */ +OnvifServiceSearch.prototype.findRecordings = function(params, callback) { + let promise = new Promise((resolve, reject) => { + let err_msg = ''; + if(err_msg = mOnvifSoap.isInvalidValue(params, 'object')) { + reject(new Error('The value of "params" was invalid: ' + err_msg)); + return; + } + + if(err_msg = mOnvifSoap.isInvalidValue(params['Scope'], 'object')) { + reject(new Error('The "Scope" property was invalid: ' + err_msg)); + return; + } + + let klist = ['IncludedSources', 'IncludedRecordings', 'RecordingInformationFilter', 'Extension']; + let tlist = ['array', 'array', 'string', 'string']; + + for(let i=0; i { + soap_body += ''; + soap_body += '' + o['Token'] + ''; + if(o['Type']){ + soap_body += '' + o['Type'] + ''; + } + soap_body += ''; + }) + } + if(params['Scope']['IncludedRecordings']){ + params['Scope']['IncludedRecordings'].forEach((s) => { + soap_body += '' + s + ''; + }) + } + if(params['Scope']['RecordingInformationFilter']){ + soap_body += '' + params['Scope']['RecordingInformationFilter'] + ''; + } + if(params['Scope']['Extension']){ + soap_body += '' + params['Scope']['Extension'] + ''; + } + soap_body += ''; + if(params['MaxMatches']){ + soap_body += '' + params['MaxMatches'] + ''; + } + soap_body += 'PT'+ params['KeepAliveTime'] + 'S'; + soap_body += ''; + let soap = this._createRequestSoap(soap_body); + mOnvifSoap.requestCommand(this.oxaddr, 'FindRecordings', soap).then((result) => { + try { + let d = result['data']['SearchToken']; + if(!Array.isArray(d)) { + result['data']['SearchToken'] = [d]; + } + } catch(e) {} + resolve(result); + }).catch((error) => { + reject(error); + }); + }); + if(callback) { + promise.then((result) => { + callback(null, result); + }).catch((error) => { + callback(error); + }); + } else { + return promise; + } +}; + +/* ------------------------------------------------------------------ + * Method: getRecordingSearchResults(params[, callback]) + * - params: + * - SearchToken | String | required | the search session to get results from + * - MinResults | Integer | optional | the minimum number of results to return in one response + * - MaxResults | Integer | optional | the maximum number of results to return in one response + * - WaitTime | Integer | optional | the maximum time before responding to the request + * + * ---------------------------------------------------------------- */ +OnvifServiceSearch.prototype.getRecordingSearchResults = function(params, callback) { + let promise = new Promise((resolve, reject) => { + let err_msg = ''; + if(err_msg = mOnvifSoap.isInvalidValue(params, 'object')) { + reject(new Error('The value of "params" was invalid: ' + err_msg)); + return; + } + + if(err_msg = mOnvifSoap.isInvalidValue(params['SearchToken'], 'string')) { + reject(new Error('The "SearchToken" property was invalid: ' + err_msg)); + return; + } + + if('MinResults' in params) { + if(err_msg = mOnvifSoap.isInvalidValue(params['MinResults'], 'integer')) { + reject(new Error('The "MinResults" property was invalid: ' + err_msg)); + return; + } + } + + if('MaxResults' in params) { + if(err_msg = mOnvifSoap.isInvalidValue(params['MaxResults'], 'integer')) { + reject(new Error('The "MaxResults" property was invalid: ' + err_msg)); + return; + } + } + + if('WaitTime' in params) { + if(err_msg = mOnvifSoap.isInvalidValue(params['WaitTime'], 'integer')) { + reject(new Error('The "WaitTime" property was invalid: ' + err_msg)); + return; + } + } + + let soap_body = ''; + soap_body += ''; + soap_body += '' + params['SearchToken'] + ''; + if(params['MinResults']) { + soap_body += '' + params['MinResults'] + ''; + } + if(params['MaxResults']) { + soap_body += '' + params['MaxResults'] + ''; + } + if(params['WaitTime']) { + soap_body += 'PT' + params['WaitTime'] + 'S'; + } + soap_body += ''; + let soap = this._createRequestSoap(soap_body); + mOnvifSoap.requestCommand(this.oxaddr, 'GetRecordingSearchResults', soap).then((result) => { + try { + let d = result['data']['ResultList']; + if(!Array.isArray(d)) { + result['data']['ResultList'] = [d]; + } + } catch(e) {} + resolve(result); + }).catch((error) => { + reject(error); + }); + }); + if(callback) { + promise.then((result) => { + callback(null, result); + }).catch((error) => { + callback(error); + }); + } else { + return promise; + } +}; + +module.exports = OnvifServiceSearch;