Use Old ONVIF Scanner

master
Moe 2024-10-24 20:01:15 -07:00
parent 384752a422
commit 0c5485409d
4 changed files with 334 additions and 642 deletions

View File

@ -6637,7 +6637,7 @@ module.exports = function(s,config,lang){
{
"name": "ip",
"field": lang['IP Address'],
"description": lang.fieldTextIp,
"description": lang[lang["fieldTextIp"]],
"example": "10.1.100.1-10.1.100.254",
},
{
@ -6649,13 +6649,11 @@ module.exports = function(s,config,lang){
{
"name": "user",
"field": lang['Camera Username'],
"description": lang.fieldTextOnvifScanCameraUsername,
"placeholder": "Can be left blank.",
},
{
"name": "pass",
"field": lang['Camera Password'],
"description": lang.fieldTextOnvifScanCameraPassword,
"fieldType": "password",
},
{
@ -6664,13 +6662,8 @@ module.exports = function(s,config,lang){
{
"fieldType": "btn",
"forForm": true,
"class": `btn-success start-scan`,
"btnContent": `${lang['Search']}`,
},
{
"fieldType": "btn",
"class": `btn-danger stop-scan d-none`,
"btnContent": `${lang['Stop']}`,
"class": `btn-block btn-success`,
"btnContent": `${lang['Search']}<span class="_loading" style="display:none"> &nbsp; <i class="fa fa-pulse fa-spinner"></i></span>`,
},
{
"fieldType": "btn",
@ -6691,7 +6684,18 @@ module.exports = function(s,config,lang){
"class": "onvif_result row",
}
]
},
"Other Devices": {
"name": lang['Other Devices'],
"color": "danger",
"section-pre-class": "col-md-12",
"info": [
{
"fieldType": "div",
"class": "onvif_result_error row",
}
]
},
}
},
"Camera Probe": {

View File

@ -1,34 +1,16 @@
module.exports = function(s,config,lang,app,io){
const {
scanStatus,
runOnvifScanner,
stopOnvifScanner,
} = require('./scanners/utils.js')(s,config,lang)
const {
ffprobe,
} = require('./ffmpeg/utils.js')(s,config,lang)
const {
runOnvifScanner,
} = require('./scanners/utils.js')(s,config,lang)
const onWebSocketConnection = async (cn) => {
const tx = function(z){if(!z.ke){z.ke=cn.ke;};cn.emit('f',z);}
cn.on('f',(d) => {
switch(d.f){
case'onvif_scan_reconnect':
tx({f: 'onvif_scan_current', devices: Object.values(scanStatus.allSuccessful), isScanning: scanStatus.isActive})
break;
case'onvif_stop':
stopOnvifScanner()
tx({f: 'onvif_scan_stopped'})
break;
case'onvif':
const groupKey = `${cn.ke}`
runOnvifScanner(d, (data) => {
const response = { f: 'onvif', ...data }
s.tx(response, 'GRP_' + cn.ke)
}, (data) => {
const response = { f: 'onvif', ff: 'failed_capture', ...data }
s.tx(response, 'GRP_' + cn.ke)
}).then((responseList) => {
s.tx({ f: 'onvif_scan_complete' }, 'GRP_' + cn.ke)
})
runOnvifScanner(d,tx)
break;
}
})
@ -58,60 +40,4 @@ module.exports = function(s,config,lang,app,io){
})
},res,req);
})
/**
* API : ONVIF Scanner RUN
*/
app.get(config.webPaths.apiPrefix+':auth/onvifScanner/:ke/scan',function (req,res){
s.auth(req.params,function(user){
const {
isRestricted,
isRestrictedApiKey,
apiKeyPermissions,
} = s.checkPermission(user);
if(
isRestrictedApiKey && apiKeyPermissions.control_monitors_disallowed
){
s.closeJsonResponse(res,{
ok: false,
msg: lang['Not Authorized']
});
return
}
const groupKey = req.params.ke;
stopOnvifScanner()
s.closeJsonResponse(res, { ok: true });
},res,req);
})
/**
* API : ONVIF Scanner STOP
*/
app.get(config.webPaths.apiPrefix+':auth/onvifScanner/:ke/scan/stop',function (req,res){
s.auth(req.params,function(user){
const {
isRestricted,
isRestrictedApiKey,
apiKeyPermissions,
} = s.checkPermission(user);
if(
isRestrictedApiKey && apiKeyPermissions.control_monitors_disallowed
){
s.closeJsonResponse(res,{
ok: false,
msg: lang['Not Authorized']
});
return
}
const groupKey = req.params.ke;
runOnvifScanner(d, (data) => {
const response = { f: 'onvif', ...data }
s.tx(response, 'GRP_' + groupKey)
}, (data) => {
const response = { f: 'onvif', ff: 'failed_capture', ...data }
s.tx(response, 'GRP_' + groupKey)
}).then((responseList) => {
s.tx({ f: 'onvif_scan_complete' }, 'GRP_' + groupKey)
s.closeJsonResponse(res, responseList)
})
},res,req);
})
}

View File

@ -1,381 +1,202 @@
var os = require('os');
const async = require('async');
const onvif = require("shinobi-onvif");
const {
addCredentialsToUrl,
stringContains,
getBuffer,
} = require('../common.js')
const scanStatus = {
current: [],
allSuccessful: {},
cancelPromises: null,
abortController: null
};
module.exports = (s,config,lang) => {
const ipRange = (start_ip, end_ip) => {
const startLong = toLong(start_ip);
const endLong = toLong(end_ip);
var startLong = toLong(start_ip);
var endLong = toLong(end_ip);
if (startLong > endLong) {
const tmp = startLong;
startLong = endLong;
var tmp = startLong;
startLong = endLong
endLong = tmp;
}
const rangeArray = [];
for (let i = startLong; i <= endLong; i++) {
var rangeArray = [];
var i;
for (i = startLong; i <= endLong; i++) {
rangeArray.push(fromLong(i));
}
return rangeArray;
};
}
const portRange = (lowEnd,highEnd) => {
const list = [];
for (let i = lowEnd; i <= highEnd; i++) {
var list = [];
for (var i = lowEnd; i <= highEnd; i++) {
list.push(i);
}
return list;
};
}
//toLong taken from NPM package 'ip'
const toLong = (ip) => {
let ipl = 0;
var ipl = 0;
ip.split('.').forEach(function(octet) {
ipl <<= 8;
ipl += parseInt(octet);
});
return(ipl >>> 0);
};
}
//fromLong taken from NPM package 'ip'
const fromLong = (ipl) => {
return ((ipl >>> 24) + '.' +
(ipl >> 16 & 255) + '.' +
(ipl >> 8 & 255) + '.' +
(ipl & 255) );
};
const getNetworkAddresses = () => {
const interfaces = os.networkInterfaces();
const addresses = [];
for (const k in interfaces) {
for (const k2 in interfaces[k]) {
const address = interfaces[k][k2];
}
const runOnvifScanner = (options,foundCameraCallback) => {
var ip = options.ip.replace(/ /g,'')
var ports = options.port.replace(/ /g,'')
if(options.ip === ''){
var interfaces = os.networkInterfaces()
var addresses = []
for (var k in interfaces) {
for (var k2 in interfaces[k]) {
var address = interfaces[k][k2]
if (address.family === 'IPv4' && !address.internal) {
addresses.push(address.address);
addresses.push(address.address)
}
}
}
return addresses;
};
const getAddressRange = (addresses) => {
const addressRange = [];
addresses.forEach((address) => {
if (address.indexOf('0.0.0') > -1) return;
const addressPrefix = address.split('.').slice(0, 3).join('.');
addressRange.push(`${addressPrefix}.1-${addressPrefix}.254`);
});
return addressRange.join(',');
};
const getPorts = (ports) => {
const addressRange = []
addresses.forEach(function(address){
if(address.indexOf('0.0.0')>-1){return false}
var addressPrefix = address.split('.')
delete(addressPrefix[3]);
addressPrefix = addressPrefix.join('.')
addressRange.push(`${addressPrefix}1-${addressPrefix}254`)
})
ip = addressRange.join(',')
}
if(ports === ''){
return '80,8080,8000,7575,8081,9080,8090,8999,8899'.split(',');
ports = '80,8080,8000,7575,8081,9080,8090,8999,8899'
}
if(ports.indexOf('-') > -1){
const [start, end] = ports.split('-');
return portRange(start, end);
ports = ports.split('-')
var portRangeStart = ports[0]
var portRangeEnd = ports[1]
ports = portRange(portRangeStart,portRangeEnd);
}else{
ports = ports.split(',')
}
return ports.split(',');
};
const getIpList = (ip) => {
const ipList = [];
ip.split(',').forEach((range) => {
const [start, end] = range.indexOf('-') > -1 ? range.split('-') : [range, range];
ipList.push(...ipRange(start, end));
});
return ipList;
};
const createHitList = (ipList, ports, onvifUsername = '', onvifPassword = '') => {
const hitList = [];
const usernameVariants = onvifUsername.split(',');
const passwordVariants = onvifPassword.split(',');
for (const username of usernameVariants) {
for (const password of passwordVariants) {
hitList.push(...ipList.flatMap((ipEntry) =>
ports.map((portEntry) => ({
xaddr: `http://${ipEntry}:${portEntry}/onvif/device_service`,
user: username,
pass: password,
var ipList = options.ipList
var onvifUsername = options.user || ''
var onvifPassword = options.pass || ''
ip.split(',').forEach(function(addressRange){
var ipRangeStart = addressRange[0]
var ipRangeEnd = addressRange[1]
if(addressRange.indexOf('-')>-1){
addressRange = addressRange.split('-');
ipRangeStart = addressRange[0]
ipRangeEnd = addressRange[1]
}else{
ipRangeStart = addressRange
ipRangeEnd = addressRange
}
if(!ipList){
ipList = ipRange(ipRangeStart,ipRangeEnd);
}else{
ipList = ipList.concat(ipRange(ipRangeStart,ipRangeEnd))
}
})
var hitList = []
ipList.forEach((ipEntry,n) => {
ports.forEach((portEntry,nn) => {
hitList.push({
xaddr : 'http://' + ipEntry + ':' + portEntry + '/onvif/device_service',
user : onvifUsername,
pass : onvifPassword,
ip: ipEntry,
port: portEntry,
}))
));
}
}
return hitList;
};
const takeSnapshot = async (cameraResponse, device) => {
})
})
})
var responseList = []
hitList.forEach(async (camera) => {
try{
const snapUri = addCredentialsToUrl({
username: cameraResponse.user,
password: cameraResponse.pass,
url: (await device.services.media.getSnapshotUri({ ProfileToken: device.current_profile.token })).data.GetSnapshotUriResponse.MediaUri.Uri,
});
const imgBuffer = await getBuffer(snapUri);
cameraResponse.snapShot = imgBuffer.toString('base64');
} catch (err) {
console.error('Failed to get snapshot via ONVIF:', err);
}
return cameraResponse;
}
const fetchCameraDetails = async (camera, onvifUsername, onvifPassword, foundCameraCallback, failedCameraCallback) => {
// const previousSuccess = scanStatus.allSuccessful[camera.ip];
// if (previousSuccess) {
// // console.log('FOUND PREVIOUS', camera.ip);
// foundCameraCallback(previousSuccess);
// return;
// }
try {
const device = new onvif.OnvifDevice(camera);
const info = await device.init();
const stream = await device.services.media.getStreamUri({
var device = new onvif.OnvifDevice(camera)
var info = await device.init()
var date = await device.services.device.getSystemDateAndTime()
var stream = await device.services.media.getStreamUri({
ProfileToken : device.current_profile.token,
Protocol : 'RTSP'
});
const cameraResponse = {
ip: camera.ip,
port: camera.port,
user: camera.user,
pass: camera.pass,
info: info,
uri: stream.data.GetStreamUriResponse.MediaUri.Uri
};
try {
const camPtzConfigs = (await device.services.ptz.getConfigurations()).data.GetConfigurationsResponse;
if (camPtzConfigs.PTZConfiguration && (camPtzConfigs.PTZConfiguration.PanTiltLimits || camPtzConfigs.PTZConfiguration.ZoomLimits)) {
cameraResponse.isPTZ = true;
}
} catch (err) {
console.error(er)
// s.debugLog(err);
}
await takeSnapshot(cameraResponse, device)
scanStatus.allSuccessful[camera.ip] = cameraResponse;
foundCameraCallback(cameraResponse);
return cameraResponse;
} catch (err) {
return handleCameraError(camera, err, failedCameraCallback);
}
};
const handleCameraError = (camera, err, failedCameraCallback) => {
// const previousSuccess = scanStatus.allSuccessful[camera.ip];
// if (previousSuccess) {
// // console.log('FOUND PREVIOUS AFTER ERROR', camera.ip);
// return previousSuccess;
// }
const searchError = (find) => stringContains(find, err.message, true);
const commonIgnoredErrors = ['ECONNREFUSED', 'socket hang up'];
let foundDevice = false;
let errorMessage = '';
switch (true) {
case searchError('ECONNREFUSED'):
errorMessage = `ECONNREFUSED`;
return {refused: true}
break;
case searchError('TIMEDOUT'):
foundDevice = true;
errorMessage = lang.ONVIFErr401;
break;
case searchError('401'):
foundDevice = true;
errorMessage = lang.ONVIFErr401;
break;
case searchError('400'):
foundDevice = true;
errorMessage = lang.ONVIFErr400;
break;
case searchError('405'):
foundDevice = true;
errorMessage = lang.ONVIFErr405;
break;
case searchError('404'):
foundDevice = true;
errorMessage = lang.ONVIFErr404;
break;
default:
break;
}
if (foundDevice) {
const cameraResponse = {
ip: camera.ip,
port: camera.port,
error: errorMessage,
failedConnection: true,
};
failedCameraCallback(cameraResponse);
return cameraResponse;
}
return null;
};
function isValidOnvifResult(result) {
return result.info || result.uri;
}
function detectAndReplaceReolinkRTSP(camera, url){
const possibilities = [`/h264Preview_01_main`, `/h265Preview_01_main`]
for(possible of possibilities){
// console.log(url, possible, url.indexOf(possible) > -1)
if(url.indexOf(possible) > -1){
return `rtmp://${camera.user}:${camera.pass}@${camera.ip}:1935/bcs/channel0_main.bcs?token=sdasdasd&channel=0&stream=0&user=${camera.user}&password=${camera.pass}`
}
}
return url
}
const runOnvifScanner = async (options, foundCameraCallback, failedCameraCallback) => {
if (scanStatus.isActive) return scanStatus.current;
scanStatus.isActive = true;
scanStatus.abortController = new AbortController();
const { signal } = scanStatus.abortController;
const cancelPromises = [];
scanStatus.cancelPromises = cancelPromises;
let ip = options.ip.replace(/ /g, '');
let ports = options.port.replace(/ /g, '');
const onvifUsername = options.user || 'admin';
const onvifPassword = options.pass || '';
if (ip === '') {
const addresses = getNetworkAddresses();
ip = getAddressRange(addresses);
}
ports = getPorts(ports);
const ipList = getIpList(ip);
const hitList = createHitList(ipList, ports, onvifUsername, onvifPassword);
const ipQueues = {};
const responseList = [];
const allPingSuccess = {};
const fetchWithTimeout = async (camera, onvifUsername, onvifPassword, foundCameraCallback, failedCameraCallback, signal) => {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error('Timeout')), 2500); // Adjust the timeout as needed
fetchCameraDetails(camera, onvifUsername, onvifPassword, foundCameraCallback, failedCameraCallback, signal)
.then(result => {
clearTimeout(timeout);
resolve(result);
})
.catch(error => {
clearTimeout(timeout);
reject(error);
});
});
};
var cameraResponse = {
ip: camera.ip,
port: camera.port,
info: info,
date: date,
uri: stream.data.GetStreamUriResponse.MediaUri.Uri
}
try{
for (const camera of hitList) {
if (!ipQueues[camera.ip]) {
ipQueues[camera.ip] = async.queue(async (task) => {
if (signal.aborted) {
throw new Error('Aborted');
const camPtzConfigs = (await device.services.ptz.getConfigurations()).data.GetConfigurationsResponse
if(
camPtzConfigs.PTZConfiguration &&
(
camPtzConfigs.PTZConfiguration.PanTiltLimits ||
camPtzConfigs.PTZConfiguration.ZoomLimits
)
){
cameraResponse.isPTZ = true
}
// if(!scanStatus.allSuccessful[camera.ip]){
const cameraIp = task.camera.ip;
const hasPingSuccess = allPingSuccess[cameraIp];
if (hasPingSuccess !== false) {
const fetchPromise = fetchWithTimeout(task.camera, task.onvifUsername, task.onvifPassword, task.foundCameraCallback, task.failedCameraCallback, signal);
cancelPromises.push(fetchPromise);
const result = await fetchPromise;
if (result.refused) allPingSuccess[cameraIp] = !result.refused;
if (result.uri){
}catch(err){
s.debugLog(err)
}
responseList.push(cameraResponse)
var imageSnap
try{
result.uri = detectAndReplaceReolinkRTSP(task.camera, addCredentialsToUrl({ url: result.uri, username: task.camera.user, password: task.camera.pass }));
}catch(err){
console.error(err)
}
}
responseList.push({...result});
}
// }
}, 1);
}
ipQueues[camera.ip].push({
camera,
onvifUsername: camera.user,
onvifPassword: camera.pass,
foundCameraCallback,
failedCameraCallback
const snapUri = addCredentialsToUrl({
username: onvifUsername,
password: onvifPassword,
url: (await device.services.media.getSnapshotUri({
ProfileToken : device.current_profile.token,
})).data.GetSnapshotUriResponse.MediaUri.Uri,
});
}
await Promise.all(Object.values(ipQueues).map(queue => new Promise((resolve) => queue.drain(resolve))));
imageSnap = (await getBuffer(snapUri)).toString('base64');
}catch(err){
if (err.message === 'Aborted') {
console.log('Scan aborted');
} else {
console.error('big error', err);
s.debugLog(err)
}
if(foundCameraCallback)foundCameraCallback(Object.assign(cameraResponse,{f: 'onvif', snapShot: imageSnap}))
}catch(err){
const searchError = (find) => {
return stringContains(find,err.message,true)
}
scanStatus.isActive = false;
scanStatus.abortController = null;
scanStatus.cancelPromises = null;
s.debugLog('Done Scan');
return responseList;
};
const stopOnvifScanner = () => {
if (scanStatus.isActive && scanStatus.abortController) {
scanStatus.abortController.abort();
scanStatus.cancelPromises.forEach(promise => promise.catch(() => {}));
scanStatus.isActive = false;
s.debugLog('Scan stopped');
var foundDevice = false
var errorMessage = ''
switch(true){
//ONVIF camera found but denied access
case searchError('400'): //Bad Request - Sender not Authorized
foundDevice = true
errorMessage = lang.ONVIFErr400
break;
case searchError('405'): //Method Not Allowed
foundDevice = true
errorMessage = lang.ONVIFErr405
break;
//Webserver exists but undetermined if IP Camera
case searchError('404'): //Not Found
foundDevice = true
errorMessage = lang.ONVIFErr404
break;
}
};
function expandIPRange(rangeStr) {
const ipRangeToArray = (start, end) => {
const startParts = start.split('.').map(Number);
const endParts = end.split('.').map(Number);
const ips = [];
for (let a = startParts[0]; a <= endParts[0]; a++) {
for (let b = startParts[1]; b <= endParts[1]; b++) {
for (let c = startParts[2]; c <= endParts[2]; c++) {
for (let d = startParts[3]; d <= endParts[3]; d++) {
ips.push([a, b, c, d].join('.'));
if(foundDevice && foundCameraCallback){
foundCameraCallback({
f: 'onvif',
ff: 'failed_capture',
ip: camera.ip,
port: camera.port,
error: errorMessage
})
}
if(config.debugLogVerbose)s.debugLog(err);
}
})
return responseList
}
}
return ips;
};
return rangeStr.split(',')
.flatMap(range => {
const [start, end] = range.split('-');
return ipRangeToArray(start.trim(), end.trim());
});
}
return {
expandIPRange,
ipRange,
portRange,
scanStatus,
runOnvifScanner,
stopOnvifScanner,
};
ipRange: ipRange,
portRange: portRange,
runOnvifScanner: runOnvifScanner,
}
}

View File

@ -4,8 +4,6 @@ $(document).ready(function(e){
var loadedResultsByIp = {}
var monitorEditorWindow = $('#tab-monitorSettings')
var onvifScannerWindow = $('#tab-onvifScanner')
var onvifScannerStartButton = onvifScannerWindow.find('.start-scan')
var onvifScannerStopButton = onvifScannerWindow.find('.stop-scan')
var onvifScannerResultPane = onvifScannerWindow.find('.onvif_result')
var onvifScannerErrorResultPane = onvifScannerWindow.find('.onvif_result_error')
var scanForm = onvifScannerWindow.find('form');
@ -32,60 +30,21 @@ $(document).ready(function(e){
var html = buildSubMenuItems(allFound)
sideMenuList.html(html)
}
var showStopButton = function(appearance){
var setAsLoading = function(appearance){
if(appearance){
onvifScannerStartButton.addClass('d-none')
onvifScannerStopButton.removeClass('d-none')
onvifScannerWindow.find('._loading').show()
onvifScannerWindow.find('[type="submit"]').prop('disabled',true)
}else{
onvifScannerStartButton.removeClass('d-none')
onvifScannerStopButton.addClass('d-none')
onvifScannerWindow.find('._loading').hide()
onvifScannerWindow.find('[type="submit"]').prop('disabled',false)
}
}
function drawDeviceTableRow(device, gotAccess){
var ip = device.ip;
var el = onvifScannerResultPane.find(`[scan-item="${ip}"]`)
var hasError = !!device.error;
var uriText = !hasError ? device.uri ? device.uri.split('?')[0] : '' : device.error;
var statusColor = hasError ? 'red' : 'green';
var snapShot = device.snapShot;
// console.log(ip, device.error, hasError)
if(gotAccess)loadMonitorConfigFromResult(device)
if(el.length === 0){
var html = `<div scan-item="${ip}" class="col-md-6">
<div class="card btn-default d-flex flex-row align-items-center p-2 mb-3 mx-2" style="border:none;border-left: 3px solid;border-color: ${statusColor}">
<div class="pr-2"><i class="fa fa-square" style="color:${statusColor}"></i></div>
<div class="pr-2"><div class="scan-item-img copy ${snapShot ? `cursor-pointer` : ''}" style="${snapShot ? `background-image:url(data:image/jpeg;base64,${snapShot})` : 'background-color:${statusColor};'}"></div></div>
<div class="pr-2 flex-grow-1">${ip}<br><small class="uri">${uriText}</small></div>
<div class="text-center copy-button pr-2">${!hasError ? makeButton({text: lang.Copy, class:'copy', color: 'primary'}) : ''}</div>
</div>
</div>`
onvifScannerResultPane.append(html)
}else{
var copyButton = el.find('.copy-button');
var imgEl = el.find('.scan-item-img');
if(hasError){
copyButton.empty()
imgEl.removeClass('copy cursor-pointer')
}else{
copyButton.html(makeButton({text: lang.Copy, class:'copy', color: 'primary'}))
imgEl.addClass('copy cursor-pointer')
}
if(snapShot){
imgEl.css('background-image', `url("data:image/jpeg;base64,${snapShot}")`)
}else{
imgEl.css('background-image', '')
}
imgEl.css('background-color', statusColor)
el.find('.uri').text(uriText)
el.find('.card').css('border-color', statusColor)
el.find('.fa-circle').css('color', statusColor)
}
}
function loadMonitorConfigFromResult(options){
var monitorId = removeSpecialCharacters(options.ip)
var currentUsername = options.user
var currentPassword = options.pass
function drawProbeResult(options){
if(!options.error){
var currentUsername = onvifScannerWindow.find('[name="user"]').val()
var currentPassword = onvifScannerWindow.find('[name="pass"]').val()
var tempID = generateId()
var info = options.info ? jsonToHtmlBlock(options.info) : ''
var streamUrl = ''
var launchWebPage = `target="_blank" href="http${options.port == 443 ? 's' : ''}://${options.ip}:${options.port}"`
if(options.uri){
@ -95,7 +54,7 @@ $(document).ready(function(e){
var pathLocation = theLocation.location
var monitorConfigPartial = {
name: pathLocation.hostname,
mid: monitorId,
mid: tempID + `${options.port}`,
host: pathLocation.hostname,
port: pathLocation.port,
path: pathLocation.pathname + (pathLocation.search && pathLocation.search !== '?' ? pathLocation.search : ''),
@ -119,9 +78,37 @@ $(document).ready(function(e){
if(monitorAlreadyAdded){
monitorConfigPartial.mid = monitorAlreadyAdded.mid;
}
var monitorId = monitorConfigPartial.mid
loadedResults[monitorId] = monitorConfigPartial;
loadedResultsByIp[monitorConfigPartial.host] = monitorConfigPartial;
return monitorConfigPartial
onvifScannerResultPane.append(`
<div class="col-md-4 mb-3" onvif_row="${monitorId}" id="onvif-result-${monitorId}">
<div style="display:block" class="card shadow btn-default copy">
<div class="preview-image card-header" style="background-image:url(${options.snapShot ? 'data:image/png;base64,' + options.snapShot : placeholder.getData(placeholder.plcimg({text: ' ', fsize: 25, bgcolor:'#1f80f9'}))})"></div>
<div class="card-body" style="min-height:190px">
<div>${info}</div>
<div class="url">${streamUrl}</div>
</div>
<div class="card-footer">${options.ip}:${options.port}</div>
</div>
</div>
`)
onvifScannerWindow.find('._notfound').remove()
setAsLoading(false)
drawFoundCamerasSubMenu()
}else{
if(!loadedResultsByIp[options.ip]){
onvifScannerErrorResultPane.append(`
<div onvif_error_row="${options.ip}" class="d-flex flex-row">
<div class="py-2 px-1" style="min-width:170px"><b>${options.ip}:${options.port}</b></div>
<div class="py-2 px-1 flex-grow-1">${options.error}</div>
<div class="py-2 px-1 text-right">
<a target="_blank" class="btn btn-sm btn-secondary" href="http://${options.ip}:${options.port}"><i class="fa fa-external-link"></i></a>
</div>
</div>
`)
}
}
}
function isOnvifRowAlreadyALoadedMonitor(onvifRow){
var matches = null;
@ -187,7 +174,7 @@ $(document).ready(function(e){
var form = el.serializeObject();
onvifScannerResultPane.empty();
onvifScannerErrorResultPane.empty();
showStopButton(true)
setAsLoading(true)
mainSocket.f({
f: 'onvif',
ip: form.ip,
@ -197,80 +184,34 @@ $(document).ready(function(e){
});
clearTimeout(checkTimeout)
checkTimeout = setTimeout(function(){
if(onvifScannerResultPane.find('[scan-item]').length === 0){
showStopButton(false)
if(onvifScannerResultPane.find('.card').length === 0){
setAsLoading(false)
onvifScannerResultPane.append(`<div class="p-2 text-center ${definitions.Theme.isDark ? 'text-white' : ''} _notfound text-white epic-text">${lang.sorryNothingWasFound}</div>`)
}
},5000)
return false;
});
onvifScannerWindow.on('click','.copy',function(e){
e.preventDefault()
onvifScannerWindow.on('click','.copy',function(){
openMonitorEditorPage()
var el = $(this).parents('[scan-item]');
var id = el.attr('scan-item');
var onvifRecord = loadedResultsByIp[id];
var el = $(this).parents('[onvif_row]');
var id = el.attr('onvif_row');
var onvifRecord = loadedResults[id];
var streamURL = onvifRecord.details.auto_host
writeToMonitorSettingsWindow(onvifRecord)
})
onvifScannerWindow.on('click','.add-all',function(){
filterOutMonitorsThatAreAlreadyAdded(loadedResults,function(importableCameras){
const numberOfCameras = importableCameras.length
if(numberOfCameras === 0){
new PNotify({
title: lang["ONVIF Scanner"],
text: lang.sorryNothingWasFound,
type: 'danger',
})
}else{
$.confirm.create({
title: lang['Add Cameras'],
body: `<p>${lang.addAllCamerasText.replace('9001', numberOfCameras)}</p><ul>${importableCameras.map(item => `<li>${item.host}</li>`).join('')}</ul>`,
clickOptions: {
class: 'btn-success',
title: lang.Add,
},
clickCallback: function(){
$.each(importableCameras,function(n,camera){
// console.log(camera)
postMonitor(camera)
})
}
})
}
})
})
onvifScannerWindow.on('click','.stop-scan',function(){
mainSocket.f({ f: 'onvif_stop' });
})
loadLocalOptions()
onInitWebsocket(function (){
mainSocket.f({ f: 'onvif_scan_reconnect' });
})
onWebSocketEvent(function (d){
switch(d.f){
case'onvif':
try{
drawDeviceTableRow(d, d.ff !== 'failed_capture' && !d.failedConnection);
}catch(err){
console.error(err)
}
break;
case'onvif_scan_current':
console.log(d)
if(d.isScanning){
showStopButton(true)
}else{
showStopButton(false)
}
d.devices.forEach(device => {
console.log('onvif_scan_current', device)
drawDeviceTableRow(device, !device.error && !d.failedConnection)
});
break;
case'onvif_scan_complete':
showStopButton(false)
drawProbeResult(d)
break;
}
})