diff --git a/camera.js b/camera.js
index 809a995..f5939a0 100644
--- a/camera.js
+++ b/camera.js
@@ -34,13 +34,13 @@ loadLib('ffmpeg')(s,config,function(){
//express web server with ejs
var app = loadLib('webServer')(s,config,lang,io)
//web server routes : page handling..
- loadLib('webServerPaths')(s,config,lang,app)
+ loadLib('webServerPaths')(s,config,lang,app,io)
//web server routes for streams : streams..
- loadLib('webServerStreamPaths')(s,config,lang,app)
+ loadLib('webServerStreamPaths')(s,config,lang,app,io)
//web server admin routes : create sub accounts, share monitors, share videos
- loadLib('webServerAdminPaths')(s,config,lang,app)
+ loadLib('webServerAdminPaths')(s,config,lang,app,io)
//web server superuser routes : create admin accounts and manage system functions
- loadLib('webServerSuperPaths')(s,config,lang,app)
+ loadLib('webServerSuperPaths')(s,config,lang,app,io)
//websocket connection handlers : login and streams..
loadLib('socketio')(s,config,lang,io)
//user and group functions
diff --git a/cron.js b/cron.js
index 234ee9a..6856322 100644
--- a/cron.js
+++ b/cron.js
@@ -46,22 +46,35 @@ if(databaseOptions.client === 'sqlite3' && databaseOptions.connection.filename =
databaseOptions.connection.filename = __dirname+"/shinobi.sqlite"
}
s.databaseEngine = knex(databaseOptions)
+s.dateSubtract = function(date, interval, units){
+ var ret = date
+ var checkRollover = function() { if(ret.getDate() != date.getDate()) ret.setDate(0);};
+ switch(interval.toLowerCase()) {
+ case 'year' : ret.setFullYear(ret.getFullYear() - units); checkRollover(); break;
+ case 'quarter': ret.setMonth(ret.getMonth() - 3*units); checkRollover(); break;
+ case 'month' : ret.setMonth(ret.getMonth() - units); checkRollover(); break;
+ case 'week' : ret.setDate(ret.getDate() - 7*units); break;
+ case 'day' : ret.setDate(ret.getDate() - units); break;
+ case 'hour' : ret.setTime(ret.getTime() - units*3600000); break;
+ case 'minute' : ret.setTime(ret.getTime() - units*60000); break;
+ case 'second' :default: ret.setTime(ret.getTime() - units*1000); break;
+ }
+ return (new Date(ret))
+}
s.sqlDate = function(value){
- var dateQueryFunction = ''
- if(databaseOptions.client === 'sqlite3'){
- value = value.toLowerCase()
- if (value.slice(-1) !== 's') {
- value = value+'s'
- }
- dateQueryFunction = "datetime('now', '-"+value+"')"
- }else{
- value = value.toUpperCase()
- if (value.slice(-1) === 'S') {
- value = value.slice(0, -1);
- }
- dateQueryFunction = "DATE_SUB(NOW(), INTERVAL "+value+")"
+ var value = value.toLowerCase()
+ var splitValue = value.split(' ')
+ var amount = parseFloat(splitValue[0])
+ var today = new Date()
+ var query
+ if(value.indexOf('min') > -1){
+ query = s.dateSubtract(today,'minute',amount)
+ }else if(value.indexOf('day') > -1){
+ query = s.dateSubtract(today,'day',amount)
+ }else if(value.indexOf('hour') > -1){
+ query = s.dateSubtract(today,'hour',amount)
}
- return dateQueryFunction
+ return query
}
s.mergeQueryValues = function(query,values){
if(!values){values=[]}
@@ -187,12 +200,11 @@ s.checkFilterRules = function(v,callback){
v.d.filters={};
}
//delete old videos with filter
- if(config.cron.deleteOld===true){
+ if(config.cron.deleteOld === true){
var where = [{
"p1":"end",
- "p2":"<",
- "p3":s.sqlDate(v.d.days+" DAYS"),
- "p3_type":"function",
+ "p2":"<=",
+ "p3":s.sqlDate(v.d.days+" DAY")
}]
//exclude monitors with their own max days
v.monitorsWithMaxKeepDays.forEach(function(mid){
@@ -298,7 +310,7 @@ s.deleteRowsWithNoVideo = function(v,callback){
){
s.alreadyDeletedRowsWithNoVideosOnStart[v.ke]=true;
es={};
- s.sqlQuery('SELECT * FROM Videos WHERE ke=? AND status!=0 AND details NOT LIKE \'%"archived":"1"%\' AND time < '+s.sqlDate('10 MINUTE'),[v.ke],function(err,evs){
+ s.sqlQuery('SELECT * FROM Videos WHERE ke=? AND status!=0 AND details NOT LIKE \'%"archived":"1"%\' AND time < ?',[v.ke,s.sqlDate('10 MINUTE')],function(err,evs){
if(evs&&evs[0]){
es.del=[];es.ar=[v.ke];
evs.forEach(function(ev){
@@ -341,7 +353,7 @@ s.deleteRowsWithNoVideo = function(v,callback){
s.deleteOldLogs = function(v,callback){
if(!v.d.log_days||v.d.log_days==''){v.d.log_days=10}else{v.d.log_days=parseFloat(v.d.log_days)};
if(config.cron.deleteLogs===true&&v.d.log_days!==0){
- s.sqlQuery("DELETE FROM Logs WHERE ke=? AND `time` < "+s.sqlDate('? DAYS'),[v.ke,v.d.log_days],function(err,rrr){
+ s.sqlQuery("DELETE FROM Logs WHERE ke=? AND `time` < ?",[v.ke,s.sqlDate(v.d.log_days+' DAY')],function(err,rrr){
callback()
if(err)return console.error(err);
if(rrr.affectedRows && rrr.affectedRows.length>0 || config.debugLog === true){
@@ -356,7 +368,7 @@ s.deleteOldLogs = function(v,callback){
s.deleteOldEvents = function(v,callback){
if(!v.d.event_days||v.d.event_days==''){v.d.event_days=10}else{v.d.event_days=parseFloat(v.d.event_days)};
if(config.cron.deleteEvents===true&&v.d.event_days!==0){
- s.sqlQuery("DELETE FROM Events WHERE ke=? AND `time` < "+s.sqlDate('? DAYS'),[v.ke,v.d.event_days],function(err,rrr){
+ s.sqlQuery("DELETE FROM Events WHERE ke=? AND `time` < ?",[v.ke,s.sqlDate(v.d.event_days+' DAY')],function(err,rrr){
callback()
if(err)return console.error(err);
if(rrr.affectedRows && rrr.affectedRows.length>0 || config.debugLog === true){
@@ -371,8 +383,8 @@ s.deleteOldEvents = function(v,callback){
s.deleteOldFileBins = function(v,callback){
if(!v.d.fileBin_days||v.d.fileBin_days==''){v.d.fileBin_days=10}else{v.d.fileBin_days=parseFloat(v.d.fileBin_days)};
if(config.cron.deleteFileBins===true&&v.d.fileBin_days!==0){
- var fileBinQuery = " FROM Files WHERE ke=? AND `time` < "+s.sqlDate('? DAYS');
- s.sqlQuery("SELECT *"+fileBinQuery,[v.ke,v.d.fileBin_days],function(err,files){
+ var fileBinQuery = " FROM Files WHERE ke=? AND `time` < ?";
+ s.sqlQuery("SELECT *"+fileBinQuery,[v.ke,s.sqlDate(v.d.fileBin_days+' DAY')],function(err,files){
if(files&&files[0]){
//delete the files
files.forEach(function(file){
@@ -451,8 +463,7 @@ s.processUser = function(number,rows){
},{
"p1":"end",
"p2":"<",
- "p3":s.sqlDate(b.details.max_keep_days+" DAYS"),
- "p3_type":"function",
+ "p3":s.sqlDate(b.details.max_keep_days+" DAY")
}]
};
}
diff --git a/languages/en_CA.json b/languages/en_CA.json
index 24a4f60..e72d952 100644
--- a/languages/en_CA.json
+++ b/languages/en_CA.json
@@ -206,6 +206,9 @@
"Save Links to Database": "Save Links to Database",
"Bucket": "Bucket",
"Region": "Region",
+ "Use Global Amazon S3 Video Storage": "Use Global Amazon S3 Video Storage",
+ "Use Global Backblaze B2 Video Storage": "Use Global Backblaze B2 Video Storage",
+ "Use Global WebDAV Video Storage": "Use Global WebDAV Video Storage",
"Amazon S3 Upload Error": "Amazon S3 Upload Error",
"accountId": "Account ID",
"applicationKey": "Application Key",
@@ -574,6 +577,8 @@
"Monitor is now Disabled": "Monitor is now Disabled",
"Monitor is now Watching": "Monitor is now Watching",
"Monitor is now Recording": "Monitor is now Recording",
+ "Space Used": "Space Used",
+ "Processor": "Processor",
"coProcessor": "coProcessor",
"coProcessor Stopped": "coProcessor Stopped",
"coProcessor Started": "coProcessor Started",
@@ -606,6 +611,7 @@
"Control Error": "Control Error",
"Database row does not exist": "Database row does not exist",
"File Delete Error": "File Delete Error",
+ "List of Videos Delete Error": "List of Videos Delete Error",
"postDataBroken": "Check the format of the JSON. Ensure it is stringified and defined under 'data'",
"ControlErrorText1": "Control is not enabled",
"ControlErrorText2": "Check your connection details. You may need to point the Base URL at port 8000 or 80. Check your authentication info.",
@@ -643,7 +649,14 @@
"Thumbnail": "Thumbnail",
"Host Type": "Host Type",
"Edit": "Edit",
+ "No Monitor ID Present in Form": "No Monitor ID Present in Form",
+ "State Configuration has no monitors associated": "State Configuration has no monitors associated",
+ "State Configuration Not Found": "State Configuration Not Found",
+ "Inserted State Configuration": "Inserted State Configuration",
+ "Edited State Configuration": "Edited State Configuration",
+ "Deleted State Configuration": "Deleted State Configuration",
"Dashboard Language": "Dashboard Language",
+ "Form Data Not Found": "Form Data Not Found",
"File Not Found": "File Not Found",
"File Not Found in Filesystem": "File Not Found in Filesystem",
"File Not Found in Database": "File Not Found in Database",
diff --git a/libs/cloudUploaders.js b/libs/cloudUploaders.js
index b0188cd..cbc4128 100644
--- a/libs/cloudUploaders.js
+++ b/libs/cloudUploaders.js
@@ -6,6 +6,7 @@ module.exports = function(s,config,lang){
// WebDAV
var beforeAccountSaveForWebDav = function(d){
//d = save event
+ d.form.details.webdav_use_global=d.d.webdav_use_global
d.form.details.use_webdav=d.d.use_webdav
}
var cloudDiskUseStartupForWebDav = function(group,userDetails){
@@ -20,6 +21,15 @@ module.exports = function(s,config,lang){
var loadWebDavForUser = function(e){
// e = user
var ar = JSON.parse(e.details);
+ if(ar.webdav_use_global === '1' && config.cloudUploaders && config.cloudUploaders.WebDAV){
+ // {
+ // webdav_user: "",
+ // webdav_pass: "",
+ // webdav_url: "",
+ // webdav_dir: "",
+ // }
+ ar = Object.assign(ar,config.cloudUploaders.WebDAV)
+ }
//owncloud/webdav
if(!s.group[e.ke].webdav &&
ar.webdav_user&&
@@ -150,6 +160,7 @@ module.exports = function(s,config,lang){
//Amazon S3
var beforeAccountSaveForAmazonS3 = function(d){
//d = save event
+ d.form.details.aws_use_global=d.d.aws_use_global
d.form.details.use_aws_s3=d.d.use_aws_s3
}
var cloudDiskUseStartupForAmazonS3 = function(group,userDetails){
@@ -163,7 +174,17 @@ module.exports = function(s,config,lang){
}
var loadAmazonS3ForUser = function(e){
// e = user
- var ar = JSON.parse(e.details);
+ var ar = JSON.parse(e.details)
+ if(ar.aws_use_global === '1' && config.cloudUploaders && config.cloudUploaders.AmazonS3){
+ // {
+ // aws_accessKeyId: "",
+ // aws_secretAccessKey: "",
+ // aws_region: "",
+ // aws_s3_bucket: "",
+ // aws_s3_dir: "",
+ // }
+ ar = Object.assign(ar,config.cloudUploaders.AmazonS3)
+ }
//Amazon S3
if(!s.group[e.ke].aws &&
!s.group[e.ke].aws_s3 &&
@@ -262,7 +283,8 @@ module.exports = function(s,config,lang){
//Backblaze B2
var beforeAccountSaveForBackblazeB2 = function(d){
//d = save event
- d.form.details.use_aws_s3=d.d.use_bb_b2
+ d.form.details.b2_use_global=d.d.b2_use_global
+ d.form.details.use_bb_b2=d.d.use_bb_b2
}
var cloudDiskUseStartupForBackblazeB2 = function(group,userDetails){
group.cloudDiskUse['b2'].name = 'Backblaze B2'
@@ -276,6 +298,15 @@ module.exports = function(s,config,lang){
var loadBackblazeB2ForUser = function(e){
var ar = JSON.parse(e.details);
try{
+ if(ar.b2_use_global === '1' && config.cloudUploaders && config.cloudUploaders.BackblazeB2){
+ // {
+ // bb_b2_accountId: "",
+ // bb_b2_applicationKey: "",
+ // bb_b2_bucket: "",
+ // bb_b2_dir: "",
+ // }
+ ar = Object.assign(ar,config.cloudUploaders.BackblazeB2)
+ }
if(!s.group[e.ke].bb_b2 &&
ar.bb_b2_accountId &&
ar.bb_b2_accountId !=='' &&
diff --git a/libs/detector.js b/libs/detector.js
index 7cfbc05..2ea4034 100644
--- a/libs/detector.js
+++ b/libs/detector.js
@@ -1,5 +1,6 @@
var P2P = require('pipe2pam');
-var PamDiff = require('pam-diff');
+// pamDiff is based on https://www.npmjs.com/package/pam-diff
+var PamDiff = require('./detectorPamDiff.js');
module.exports = function(s,config){
s.createPamDiffEngine = function(e){
var width,
@@ -64,12 +65,13 @@ module.exports = function(s,config){
plug:'built-in',
name:trigger.name,
reason:'motion',
- confidence:trigger.percent,
+ confidence:trigger.percent
},
plates:[],
- imgHeight:height,
- imgWidth:width
+ imgHeight:e.details.detector_scale_y,
+ imgWidth:e.details.detector_scale_x
}
+ if(trigger.matrix)detectorObject.details.matrices = [trigger.matrix]
var region = Object.values(regionJson).find(x => x.name == detectorObject.name)
s.checkMaximumSensitivity(e, region, detectorObject, function() {
s.checkTriggerThreshold(e, region, detectorObject, function() {
diff --git a/libs/detectorPamDiff.js b/libs/detectorPamDiff.js
new file mode 100644
index 0000000..a24ef3b
--- /dev/null
+++ b/libs/detectorPamDiff.js
@@ -0,0 +1,622 @@
+//
+// Shinobi - fork of pam-diff
+// Copyright (C) 2018 Kevin Godell
+// Author : Kevin Godell, https://github.com/kevinGodell
+// npmjs : https://www.npmjs.com/package/pam-diff
+// Github : https://github.com/kevinGodell/pam-diff
+//
+'use strict';
+
+const { Transform } = require('stream');
+
+const PP = require('polygon-points');
+/**
+ *
+ * @param chunk
+ * @private
+ */
+ var _getMatrixFromPoints = function(thisRegion) {
+ var coordinates = [
+ thisRegion.topLeft,
+ {"x" : thisRegion.bottomRight.x, "y" : thisRegion.topLeft.y},
+ thisRegion.bottomRight
+ ]
+ var width = Math.sqrt( Math.pow(coordinates[1].x - coordinates[0].x, 2) + Math.pow(coordinates[1].y - coordinates[0].y, 2));
+ var height = Math.sqrt( Math.pow(coordinates[2].x - coordinates[1].x, 2) + Math.pow(coordinates[2].y - coordinates[1].y, 2))
+ return {
+ x: coordinates[0].x,
+ y: coordinates[0].y,
+ width: width,
+ height: height,
+ tag: thisRegion.name
+ }
+ }
+class PamDiff extends Transform {
+
+ /**
+ *
+ * @param [options] {Object}
+ * @param [callback] {Function}
+ */
+ constructor(options, callback) {
+ super(options);
+ Transform.call(this, {objectMode: true});
+ this.difference = PamDiff._parseOptions('difference', options);//global option, can be overridden per region
+ this.percent = PamDiff._parseOptions('percent', options);//global option, can be overridden per region
+ this.regions = PamDiff._parseOptions('regions', options);//can be no regions or a single region or multiple regions. if no regions, all pixels will be compared.
+ this.callback = callback;//callback function to be called when pixel difference is detected
+ this._parseChunk = this._parseFirstChunk;//first parsing will be reading settings and configuring internal pixel reading
+ }
+
+ /**
+ *
+ * @param option {String}
+ * @param options {Object}
+ * @return {*}
+ * @private
+ */
+ static _parseOptions(option, options) {
+ if (options && options.hasOwnProperty(option)) {
+ return options[option];
+ }
+ return null;
+ }
+
+ /**
+ *
+ * @param number {Number}
+ * @param def {Number}
+ * @param low {Number}
+ * @param high {Number}
+ * @return {Number}
+ * @private
+ */
+ static _validateNumber(number, def, low, high) {
+ if (isNaN(number)) {
+ return def;
+ } else if (number < low) {
+ return low;
+ } else if (number > high) {
+ return high;
+ } else {
+ return number;
+ }
+ }
+
+ /**
+ *
+ * @deprecated
+ * @param string {String}
+ */
+ setGrayscale(string) {
+ console.warn('grayscale option has been removed, "average" has proven to most accurate and is the default');
+ }
+
+ /**
+ *
+ * @param number {Number}
+ */
+ set difference(number) {
+ this._difference = PamDiff._validateNumber(parseInt(number), 5, 1, 255);
+ }
+
+ /**
+ *
+ * @return {Number}
+ */
+ get difference() {
+ return this._difference;
+ }
+
+ /**
+ *
+ * @param number {Number}
+ * @return {PamDiff}
+ */
+ setDifference(number) {
+ this.difference = number;
+ return this;
+ }
+
+ /**
+ *
+ * @param number {Number}
+ */
+ set percent(number) {
+ this._percent = PamDiff._validateNumber(parseInt(number), 5, 1, 100);
+ }
+
+ /**
+ *
+ * @return {Number}
+ */
+ get percent() {
+ return this._percent;
+ }
+
+ /**
+ *
+ * @param number {Number}
+ * @return {PamDiff}
+ */
+ setPercent(number) {
+ this.percent = number;
+ return this;
+ }
+
+ /**
+ *
+ * @param array {Array}
+ */
+ set regions(array) {
+ if (!array) {
+ if (this._regions) {
+ delete this._regions;
+ delete this._regionsLength;
+ delete this._minDiff;
+ }
+ this._diffs = 0;
+ } else if (!Array.isArray(array) || array.length < 1) {
+ throw new Error(`Regions must be an array of at least 1 region object {name: 'region1', difference: 10, percent: 10, polygon: [[0, 0], [0, 50], [50, 50], [50, 0]]}`);
+ } else {
+ this._regions = [];
+ this._minDiff = 255;
+ for (const region of array) {
+ if (!region.hasOwnProperty('name') || !region.hasOwnProperty('polygon')) {
+ throw new Error('Region must include a name and a polygon property');
+ }
+ const polygonPoints = new PP(region.polygon);
+ const difference = PamDiff._validateNumber(parseInt(region.difference), this._difference, 1, 255);
+ const percent = PamDiff._validateNumber(parseInt(region.percent), this._percent, 1, 100);
+ this._minDiff = Math.min(this._minDiff, difference);
+ this._regions.push(
+ {
+ name: region.name,
+ polygon: polygonPoints,
+ difference: difference,
+ percent: percent,
+ diffs: 0
+ }
+ );
+ }
+ this._regionsLength = this._regions.length;
+ this._createPointsInPolygons(this._regions, this._width, this._height);
+ }
+ }
+
+ /**
+ *
+ * @return {Array}
+ */
+ get regions() {
+ return this._regions;
+ }
+
+ /**
+ *
+ * @param array {Array}
+ * @return {PamDiff}
+ */
+ setRegions(array) {
+ this.regions = array;
+ return this;
+ }
+
+ /**
+ *
+ * @param func {Function}
+ */
+ set callback(func) {
+ if (!func) {
+ delete this._callback;
+ } else if (typeof func === 'function' && func.length === 1) {
+ this._callback = func;
+ } else {
+ throw new Error('Callback must be a function that accepts 1 argument.');
+ }
+ }
+
+ /**
+ *
+ * @return {Function}
+ */
+ get callback() {
+ return this._callback;
+ }
+
+ /**
+ *
+ * @param func {Function}
+ * @return {PamDiff}
+ */
+ setCallback(func) {
+ this.callback = func;
+ return this;
+ }
+
+ /**
+ *
+ * @return {PamDiff}
+ */
+ resetCache() {
+ //delete this._oldPix;
+ //delete this._newPix;
+ //delete this._width;
+ //delete this._length;
+ this._parseChunk = this._parseFirstChunk;
+ return this;
+ }
+
+ /**
+ *
+ * @param regions {Array}
+ * @param width {Number}
+ * @param height {Number}
+ * @private
+ */
+ _createPointsInPolygons(regions, width, height) {
+ if (regions && width && height) {
+ this._pointsInPolygons = [];
+ for (const region of regions) {
+ const bitset = region.polygon.getBitset(this._width, this._height);
+ region.pointsLength = bitset.count;
+ this._pointsInPolygons.push(bitset.buffer);
+ }
+ }
+ }
+
+ /**
+ *
+ * @param chunk
+ * @private
+ */
+ _blackAndWhitePixelDiff(chunk) {
+ this._newPix = chunk.pixels;
+ for (let y = 0, i = 0; y < this._height; y++) {
+ for (let x = 0; x < this._width; x++, i++) {
+ const diff = this._oldPix[i] !== this._newPix[i];
+ if (this._regions && diff === true) {
+ for (let j = 0; j < this._regionsLength; j++) {
+ if (this._pointsInPolygons[j][i]) {
+ this._regions[j].diffs++;
+ }
+ }
+ } else if (diff === true) {
+ this._diffs++;
+ }
+ }
+ }
+ if (this._regions) {
+ const regionDiffArray = [];
+ for (let i = 0; i < this._regionsLength; i++) {
+ const percent = Math.floor(100 * this._regions[i].diffs / this._regions[i].pointsLength);
+ if (percent >= this._regions[i].percent) {
+ regionDiffArray.push({name: this._regions[i].name, percent: percent});
+ }
+ this._regions[i].diffs = 0;
+ }
+ if (regionDiffArray.length > 0) {
+ const data = {trigger: regionDiffArray, pam: chunk.pam};
+ if (this._callback) {
+ this._callback(data);
+ }
+ if (this._readableState.pipesCount > 0) {
+ this.push(data);
+ }
+ if (this.listenerCount('diff') > 0) {
+ this.emit('diff', data);
+ }
+ }
+ } else {
+ const percent = Math.floor(100 * this._diffs / this._length);
+ if (percent >= this._percent) {
+ const data = {trigger: [{name: 'percent', percent: percent}], pam: chunk.pam};
+ if (this._callback) {
+ this._callback(data);
+ }
+ if (this._readableState.pipesCount > 0) {
+ this.push(data);
+ }
+ if (this.listenerCount('diff') > 0) {
+ this.emit('diff', data);
+ }
+ }
+ this._diffs = 0;
+ }
+ this._oldPix = this._newPix;
+ }
+
+ /**
+ *
+ * @param chunk
+ * @private
+ */
+ _grayScalePixelDiff(chunk) {
+ this._newPix = chunk.pixels;
+ for (let j = 0; j < this._regionsLength; j++) {
+ this._regions[j].topLeft = {
+ x: this._width,
+ y: this._height
+ }
+ this._regions[j].bottomRight = {
+ x: 0,
+ y: 0
+ }
+ }
+ this.topLeft = {
+ x: this._width,
+ y: this._height
+ }
+ this.bottomRight = {
+ x: 0,
+ y: 0
+ }
+ for (let y = 0, i = 0; y < this._height; y++) {
+ for (let x = 0; x < this._width; x++, i++) {
+ if (this._oldPix[i] !== this._newPix[i]) {
+ const diff = Math.abs(this._oldPix[i] - this._newPix[i]);
+ if (this._regions && diff >= this._minDiff) {
+ for (let j = 0; j < this._regionsLength; j++) {
+ if (this._pointsInPolygons[j][i] && diff >= this._regions[j].difference) {
+ var theRegion = this._regions[j]
+ theRegion.diffs++;
+ if(theRegion.topLeft.x > x && theRegion.topLeft.y > y){
+ theRegion.topLeft.x = x
+ theRegion.topLeft.y = y
+ }
+ if(theRegion.bottomRight.x < x && theRegion.bottomRight.y < y){
+ theRegion.bottomRight.x = x
+ theRegion.bottomRight.y = y
+ }
+ }
+ }
+ } else if (diff >= this._difference) {
+ this._diffs++;
+ if(this.topLeft.x > x && this.topLeft.y > y){
+ this.topLeft.x = x
+ this.topLeft.y = y
+ }
+ if(this.bottomRight.x < x && this.bottomRight.y < y){
+ this.bottomRight.x = x
+ this.bottomRight.y = y
+ }
+ }
+ }
+ }
+ }
+ if (this._regions) {
+ const regionDiffArray = [];
+ for (let i = 0; i < this._regionsLength; i++) {
+ var thisRegion = this._regions[i]
+ const percent = Math.floor(100 * thisRegion.diffs / thisRegion.pointsLength);
+ if (percent >= thisRegion.percent) {
+ // create matrix from points >>
+ thisRegion._matrix = _getMatrixFromPoints(thisRegion)
+ // create matrix from points />>
+ regionDiffArray.push({name: thisRegion.name, percent: percent, matrix: thisRegion._matrix});
+ }
+ thisRegion.diffs = 0;
+ }
+ if (regionDiffArray.length > 0) {
+ this._matrix = _getMatrixFromPoints(this)
+ const data = {trigger: regionDiffArray, pam: chunk.pam, matrix: this._matrix};
+ if (this._callback) {
+ this._callback(data);
+ }
+ if (this._readableState.pipesCount > 0) {
+ this.push(data);
+ }
+ if (this.listenerCount('diff') > 0) {
+ this.emit('diff', data);
+ }
+ }
+ } else {
+ const percent = Math.floor(100 * this._diffs / this._length);
+ if (percent >= this._percent) {
+ this._matrix = _getMatrixFromPoints(this)
+ const data = {trigger: [{name: 'percent', percent: percent, matrix: this._matrix}], pam: chunk.pam};
+ if (this._callback) {
+ this._callback(data);
+ }
+ if (this._readableState.pipesCount > 0) {
+ this.push(data);
+ }
+ if (this.listenerCount('diff') > 0) {
+ this.emit('diff', data);
+ }
+ }
+ this._diffs = 0;
+ }
+ this._oldPix = this._newPix;
+ }
+
+ /**
+ *
+ * @param chunk
+ * @private
+ */
+ _rgbPixelDiff(chunk) {
+ this._newPix = chunk.pixels;
+ for (let y = 0, i = 0, p = 0; y < this._height; y++) {
+ for (let x = 0; x < this._width; x++, i += 3, p++) {
+ if (this._oldPix[i] !== this._newPix[i] || this._oldPix[i + 1] !== this._newPix[i + 1] || this._oldPix[i + 2] !== this._newPix[i + 2]) {
+ const diff = Math.abs(this._oldPix[i] + this._oldPix[i + 1] + this._oldPix[i + 2] - this._newPix[i] - this._newPix[i + 1] - this._newPix[i + 2])/3;
+ if (this._regions && diff >= this._minDiff) {
+ for (let j = 0; j < this._regionsLength; j++) {
+ if (this._pointsInPolygons[j][p] && diff >= this._regions[j].difference) {
+ this._regions[j].diffs++;
+ }
+ }
+ } else {
+ if (diff >= this._difference) {
+ this._diffs++;
+ }
+ }
+ }
+ }
+ }
+ if (this._regions) {
+ const regionDiffArray = [];
+ for (let i = 0; i < this._regionsLength; i++) {
+ const percent = Math.floor(100 * this._regions[i].diffs / this._regions[i].pointsLength);
+ if (percent >= this._regions[i].percent) {
+ regionDiffArray.push({name: this._regions[i].name, percent: percent});
+ }
+ this._regions[i].diffs = 0;
+ }
+ if (regionDiffArray.length > 0) {
+ const data = {trigger: regionDiffArray, pam: chunk.pam};
+ if (this._callback) {
+ this._callback(data);
+ }
+ if (this._readableState.pipesCount > 0) {
+ this.push(data);
+ }
+ if (this.listenerCount('diff') > 0) {
+ this.emit('diff', data);
+ }
+ }
+ } else {
+ const percent = Math.floor(100 * this._diffs / this._length);
+ if (percent >= this._percent) {
+ const data = {trigger: [{name: 'percent', percent: percent}], pam: chunk.pam};
+ if (this._callback) {
+ this._callback(data);
+ }
+ if (this._readableState.pipesCount > 0) {
+ this.push(data);
+ }
+ if (this.listenerCount('diff') > 0) {
+ this.emit('diff', data);
+ }
+ }
+ this._diffs = 0;
+ }
+ this._oldPix = this._newPix;
+ }
+
+ /**
+ *
+ * @param chunk
+ * @private
+ */
+ _rgbAlphaPixelDiff(chunk) {
+ this._newPix = chunk.pixels;
+ for (let y = 0, i = 0, p = 0; y < this._height; y++) {
+ for (let x = 0; x < this._width; x++, i += 4, p++) {
+ if (this._oldPix[i] !== this._newPix[i] || this._oldPix[i + 1] !== this._newPix[i + 1] || this._oldPix[i + 2] !== this._newPix[i + 2]) {
+ const diff = Math.abs(this._oldPix[i] + this._oldPix[i + 1] + this._oldPix[i + 2] - this._newPix[i] - this._newPix[i + 1] - this._newPix[i + 2])/3;
+ if (this._regions && diff >= this._minDiff) {
+ for (let j = 0; j < this._regionsLength; j++) {
+ if (this._pointsInPolygons[j][p] && diff >= this._regions[j].difference) {
+ this._regions[j].diffs++;
+ }
+ }
+ } else {
+ if (diff >= this._difference) {
+ this._diffs++;
+ }
+ }
+ }
+ }
+ }
+ if (this._regions) {
+ const regionDiffArray = [];
+ for (let i = 0; i < this._regionsLength; i++) {
+ const percent = Math.floor(100 * this._regions[i].diffs / this._regions[i].pointsLength);
+ if (percent >= this._regions[i].percent) {
+ regionDiffArray.push({name: this._regions[i].name, percent: percent});
+ }
+ this._regions[i].diffs = 0;
+ }
+ if (regionDiffArray.length > 0) {
+ const data = {trigger: regionDiffArray, pam: chunk.pam};
+ if (this._callback) {
+ this._callback(data);
+ }
+ if (this._readableState.pipesCount > 0) {
+ this.push(data);
+ }
+ if (this.listenerCount('diff') > 0) {
+ this.emit('diff', data);
+ }
+ }
+ } else {
+ const percent = Math.floor(100 * this._diffs / this._length);
+ if (percent >= this._percent) {
+ const data = {trigger: [{name: 'percent', percent: percent}], pam: chunk.pam};
+ if (this._callback) {
+ this._callback(data);
+ }
+ if (this._readableState.pipesCount > 0) {
+ this.push(data);
+ }
+ if (this.listenerCount('diff') > 0) {
+ this.emit('diff', data);
+ }
+ }
+ this._diffs = 0;
+ }
+ this._oldPix = this._newPix;
+ }
+
+ /**
+ *
+ * @param chunk
+ * @private
+ */
+ _parseFirstChunk(chunk) {
+ this._width = parseInt(chunk.width);
+ this._height = parseInt(chunk.height);
+ this._oldPix = chunk.pixels;
+ this._length = this._width * this._height;
+ this._createPointsInPolygons(this._regions, this._width, this._height);
+ switch (chunk.tupltype) {
+ case 'blackandwhite' :
+ this._parseChunk = this._blackAndWhitePixelDiff;
+ break;
+ case 'grayscale' :
+ this._parseChunk = this._grayScalePixelDiff;
+ break;
+ case 'rgb' :
+ this._parseChunk = this._rgbPixelDiff;
+ //this._increment = 3;//future use
+ break;
+ case 'rgb_alpha' :
+ this._parseChunk = this._rgbAlphaPixelDiff;
+ //this._increment = 4;//future use
+ break;
+ default :
+ throw Error(`Unsupported tupltype: ${chunk.tupltype}. Supported tupltypes include grayscale(gray), blackandwhite(monob), rgb(rgb24), and rgb_alpha(rgba).`);
+ }
+ }
+
+ /**
+ *
+ * @param chunk
+ * @param encoding
+ * @param callback
+ * @private
+ */
+ _transform(chunk, encoding, callback) {
+ this._parseChunk(chunk);
+ callback();
+ }
+
+ /**
+ *
+ * @param callback
+ * @private
+ */
+ _flush(callback) {
+ this.resetCache();
+ callback();
+ }
+}
+
+/**
+ *
+ * @type {PamDiff}
+ */
+module.exports = PamDiff;
+//todo get bounding box of all regions combined to exclude some pixels before checking if they exist inside specific regions
diff --git a/libs/events.js b/libs/events.js
index b2dd494..9682fa7 100644
--- a/libs/events.js
+++ b/libs/events.js
@@ -12,9 +12,7 @@ module.exports = function(s,config,lang){
})
break;
case'delete':
- d.videos.forEach(function(v,n){
- s.deleteVideo(v)
- })
+ s.deleteListOfVideos(d.videos)
break;
case'execute':
exec(d.execute,{detached: true})
@@ -373,6 +371,7 @@ module.exports = function(s,config,lang){
delete(s.api[d.auth])
s.userLog(d,{type:"Traditional Recording",msg:'Clear Recorder Process'})
delete(s.group[d.ke].mon[d.id].eventBasedRecording.process)
+ clearTimeout(s.group[d.ke].mon[d.id].eventBasedRecording.timeout)
delete(s.group[d.ke].mon[d.id].eventBasedRecording.timeout)
clearTimeout(s.group[d.ke].mon[d.id].recordingChecker)
})
diff --git a/libs/ffmpeg.js b/libs/ffmpeg.js
index 0c338fe..409156e 100644
--- a/libs/ffmpeg.js
+++ b/libs/ffmpeg.js
@@ -526,12 +526,9 @@ module.exports = function(s,config,onFinish){
//add input feed map
x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.stream)
}
- console.log('x.cust_stream',x.cust_stream)
-
if(x.stream_fps && (e.details.stream_vcodec !== 'copy' || e.details.stream_type === 'mjpeg' || e.details.stream_type === 'b64')){
x.cust_stream += x.stream_fps
}
- console.log('x.cust_stream',x.cust_stream)
switch(e.details.stream_type){
case'mp4':
x.cust_stream+=' -movflags +frag_keyframe+empty_moov+default_base_moof -metadata title="Poseidon Stream" -reset_timestamps 1'
diff --git a/libs/folders.js b/libs/folders.js
index d73b10b..b2341d5 100644
--- a/libs/folders.js
+++ b/libs/folders.js
@@ -1,31 +1,31 @@
var fs = require('fs');
module.exports = function(s,config){
//directories
- s.group={};
+ s.group = {}
if(!config.windowsTempDir&&s.isWin===true){config.windowsTempDir='C:/Windows/Temp'}
if(!config.defaultMjpeg){config.defaultMjpeg=s.mainDirectory+'/web/libs/img/bg.jpg'}
//default stream folder check
if(!config.streamDir){
- if(s.isWin===false){
- config.streamDir='/dev/shm'
+ if(s.isWin === false){
+ config.streamDir = '/dev/shm'
}else{
- config.streamDir=config.windowsTempDir
+ config.streamDir = config.windowsTempDir
}
if(!fs.existsSync(config.streamDir)){
- config.streamDir=s.mainDirectory+'/streams/'
+ config.streamDir = s.mainDirectory+'/streams/'
}else{
- config.streamDir+='/streams/'
+ config.streamDir += '/streams/'
}
}
if(!config.videosDir){config.videosDir=s.mainDirectory+'/videos/'}
if(!config.binDir){config.binDir=s.mainDirectory+'/fileBin/'}
if(!config.addStorage){config.addStorage=[]}
s.dir={
- videos:s.checkCorrectPathEnding(config.videosDir),
- streams:s.checkCorrectPathEnding(config.streamDir),
- fileBin:s.checkCorrectPathEnding(config.binDir),
- addStorage:config.addStorage,
- languages:s.location.languages+'/'
+ videos: s.checkCorrectPathEnding(config.videosDir),
+ streams: s.checkCorrectPathEnding(config.streamDir),
+ fileBin: s.checkCorrectPathEnding(config.binDir),
+ addStorage: config.addStorage,
+ languages: s.location.languages+'/'
};
//streams dir
if(!fs.existsSync(s.dir.streams)){
@@ -41,7 +41,7 @@ module.exports = function(s,config){
}
//additional storage areas
s.dir.addStorage.forEach(function(v,n){
- v.path=s.checkCorrectPathEnding(v.path)
+ v.path = s.checkCorrectPathEnding(v.path)
if(!fs.existsSync(v.path)){
fs.mkdirSync(v.path);
}
diff --git a/libs/monitor.js b/libs/monitor.js
index 7cceed9..1c0a425 100644
--- a/libs/monitor.js
+++ b/libs/monitor.js
@@ -73,6 +73,16 @@ module.exports = function(s,config,lang){
if(noPath !== true)url += e.path
return url
}
+ s.cleanMonitorObjectForDatabase = function(dirtyMonitor){
+ var cleanMonitor = {}
+ var acceptedFields = ['mid','ke','name','shto','shfr','details','type','ext','protocol','host','path','port','fps','mode','width','height']
+ Object.keys(dirtyMonitor).forEach(function(key){
+ if(acceptedFields.indexOf(key) > -1){
+ cleanMonitor[key] = dirtyMonitor[key]
+ }
+ })
+ return cleanMonitor
+ }
s.cleanMonitorObject = function(e){
x={keys:Object.keys(e),ar:{}};
x.keys.forEach(function(v){
@@ -89,18 +99,28 @@ module.exports = function(s,config,lang){
}
var url
var runExtraction = function(){
- var snapBuffer = []
- var snapProcess = spawn(config.ffmpegDir,('-loglevel quiet -re -i '+url+options+' -frames:v 1 -f mjpeg pipe:1').split(' '),{detached: true})
- snapProcess.stdout.on('data',function(data){
- snapBuffer.push(data)
- });
- snapProcess.stderr.on('data',function(data){
- console.log(data.toString())
- });
- snapProcess.on('close',function(data){
- snapBuffer = Buffer.concat(snapBuffer)
- callback(snapBuffer,false)
- })
+ try{
+ var snapBuffer = []
+ var snapProcess = spawn(config.ffmpegDir,('-loglevel quiet -re -i '+url+options+' -frames:v 1 -f image2pipe pipe:1').split(' '),{detached: true})
+ snapProcess.stdout.on('data',function(data){
+ snapBuffer.push(data)
+ })
+ snapProcess.stderr.on('data',function(data){
+ console.log(data.toString())
+ })
+ snapProcess.on('exit',function(data){
+ clearTimeout(snapProcessTimeout)
+ snapBuffer = Buffer.concat(snapBuffer)
+ callback(snapBuffer,false)
+ })
+ var snapProcessTimeout = setTimeout(function(){
+ snapProcess.stdin.setEncoding('utf8')
+ snapProcess.stdin.write('q')
+ delete(snapProcessTimeout)
+ },5000)
+ }catch(err){
+ callback(fs.readFileSync(config.defaultMjpeg,'binary'),false)
+ }
}
var checkExists = function(localStream,callback){
fs.stat(localStream,function(err){
@@ -230,6 +250,7 @@ module.exports = function(s,config,lang){
clearTimeout(s.group[e.ke].mon[e.id].watchdog_stop);
delete(s.group[e.ke].mon[e.id].watchdog_stop);
delete(s.group[e.ke].mon[e.id].lastJpegDetectorFrame);
+ delete(s.group[e.ke].mon[e.id].detectorFrameSaveBuffer);
clearTimeout(s.group[e.ke].mon[e.id].recordingSnapper);
clearInterval(s.group[e.ke].mon[e.id].getMonitorCpuUsage);
if(s.group[e.ke].mon[e.id].onChildNodeExit){
@@ -377,7 +398,7 @@ module.exports = function(s,config,lang){
}
}
//create onvif connection
- if(!s.group[e.ke].mon[e.id].onvifConnection){
+ if(!s.group[e.ke].mon[e.id].onvifConnection || !s.group[e.ke].mon[e.id].onvifConnection.current_profile || !s.group[e.ke].mon[e.id].onvifConnection.current_profile.token){
s.group[e.ke].mon[e.id].onvifConnection = new onvif.OnvifDevice({
xaddr : 'http://' + controlURLOptions.host + ':' + controlURLOptions.port + '/onvif/device_service',
user : controlURLOptions.username,
@@ -494,8 +515,8 @@ module.exports = function(s,config,lang){
}
s.cameraSendSnapshot = function(e){
s.checkDetails(e)
- if(config.doSnapshot===true){
- if(e.mon.mode!=='stop'){
+ if(config.doSnapshot === true){
+ if(e.mon.mode !== 'stop'){
var pathDir = s.dir.streams+e.ke+'/'+e.mid+'/'
fs.stat(pathDir+'icon.jpg',function(err){
if(!err){
@@ -505,36 +526,22 @@ module.exports = function(s,config,lang){
})
}else{
e.url = s.buildMonitorUrl(e.mon)
- switch(e.mon.type){
- case'mjpeg':case'h264':case'local':
- if(e.mon.type==='local'){e.url=e.mon.path;}
- s.getRawSnapshotFromMonitor(e.mon,'-s 200x200',function(data,isStaticFile){
- if((data[data.length-2] === 0xFF && data[data.length-1] === 0xD9)){
- if(!isStaticFile){
- fs.writeFile(s.dir.streams+e.ke+'/'+e.mid+'/icon.jpg',data,function(){})
- }
- s.tx({
- f:'monitor_snapshot',
- snapshot:data.toString('base64'),
- snapshot_format:'b64',
- mid:e.mid,
- ke:e.ke
- },'GRP_'+e.ke)
- }else{
- s.tx({f:'monitor_snapshot',snapshot:e.mon.name,snapshot_format:'plc',mid:e.mid,ke:e.ke},'GRP_'+e.ke)
- }
- })
- break;
- case'jpeg':
- request({url:e.url,method:'GET',encoding:null},function(err,data){
- if(err){s.tx({f:'monitor_snapshot',snapshot:e.mon.name,snapshot_format:'plc',mid:e.mid,ke:e.ke},'GRP_'+e.ke);return};
- s.tx({f:'monitor_snapshot',snapshot:data.body,snapshot_format:'ab',mid:e.mid,ke:e.ke},'GRP_'+e.ke)
- })
- break;
- default:
- s.tx({f:'monitor_snapshot',snapshot:'...',snapshot_format:'plc',mid:e.mid,ke:e.ke},'GRP_'+e.ke)
- break;
- }
+ s.getRawSnapshotFromMonitor(e.mon,'-s 200x200',function(data,isStaticFile){
+ if((data[data.length-2] === 0xFF && data[data.length-1] === 0xD9)){
+ if(!isStaticFile){
+ fs.writeFile(s.dir.streams+e.ke+'/'+e.mid+'/icon.jpg',data,function(){})
+ }
+ s.tx({
+ f: 'monitor_snapshot',
+ snapshot: data.toString('base64'),
+ snapshot_format: 'b64',
+ mid: e.mid,
+ ke: e.ke
+ },'GRP_'+e.ke)
+ }else{
+ s.tx({f:'monitor_snapshot',snapshot:e.mon.name,snapshot_format:'plc',mid:e.mid,ke:e.ke},'GRP_'+e.ke)
+ }
+ })
}
})
}else{
@@ -777,10 +784,34 @@ module.exports = function(s,config,lang){
s.group[e.ke].mon[e.id].lastJpegDetectorFrame = d
})
}
- }else{
- s.group[e.ke].mon[e.id].spawn.stdio[3].on('data',function(d){
- s.ocvTx({f:'frame',mon:s.group[e.ke].mon_conf[e.id].details,ke:e.ke,id:e.id,time:s.formattedTime(),frame:d});
- })
+ }else if(s.ocv){
+ if(s.ocv.connectionType !== 'ram'){
+ s.group[e.ke].mon[e.id].spawn.stdio[3].on('data',function(d){
+ s.ocvTx({f:'frame',mon:s.group[e.ke].mon_conf[e.id].details,ke:e.ke,id:e.id,time:s.formattedTime(),frame:d});
+ })
+ }else{
+ s.group[e.ke].mon[e.id].spawn.stdio[3].on('data',function(d){
+ if(!s.group[e.ke].mon[e.id].detectorFrameSaveBuffer){
+ s.group[e.ke].mon[e.id].detectorFrameSaveBuffer=[d]
+ }else{
+ s.group[e.ke].mon[e.id].detectorFrameSaveBuffer.push(d)
+ }
+ if(d[d.length-2] === 0xFF && d[d.length-1] === 0xD9){
+ var buffer = Buffer.concat(s.group[e.ke].mon[e.id].detectorFrameSaveBuffer);
+ var frameLocation = s.dir.streams + e.ke + '/' + e.id + '/' + s.gid(5) + '.jpg'
+ if(s.ocv){
+ fs.writeFile(frameLocation,buffer,function(err){
+ if(err){
+ s.debugLog(err)
+ }else{
+ s.ocvTx({f:'frameFromRam',mon:s.group[e.ke].mon_conf[e.id].details,ke:e.ke,id:e.id,time:s.formattedTime(),frameLocation:frameLocation})
+ }
+ })
+ }
+ s.group[e.ke].mon[e.id].detectorFrameSaveBuffer = null;
+ }
+ })
+ }
}
}
//frames to stream
@@ -1192,6 +1223,92 @@ module.exports = function(s,config,lang){
}catch(err){}
return false
}
+ s.addOrEditMonitor = function(form,callback,user){
+ var endData = {
+ ok: false
+ }
+ if(!form.mid){
+ endData.msg = lang['No Monitor ID Present in Form']
+ callback(endData)
+ return
+ }
+ form.mid = form.mid.replace(/[^\w\s]/gi,'').replace(/ /g,'')
+ form = s.cleanMonitorObjectForDatabase(form)
+ s.sqlQuery('SELECT * FROM Monitors WHERE ke=? AND mid=?',[form.ke,form.mid],function(er,r){
+ var affectMonitor = false
+ var monitorQuery = []
+ var monitorQueryValues = []
+ var txData = {
+ f: 'monitor_edit',
+ mid: form.mid,
+ ke: form.ke,
+ mon: form
+ }
+ if(r&&r[0]){
+ txData.new = false
+ Object.keys(form).forEach(function(v){
+ if(form[v]&&form[v]!==''){
+ monitorQuery.push(v+'=?')
+ if(form[v] instanceof Object){
+ form[v] = s.s(form[v])
+ }
+ monitorQueryValues.push(form[v])
+ }
+ })
+ monitorQuery = monitorQuery.join(',')
+ monitorQueryValues.push(form.ke)
+ monitorQueryValues.push(form.mid)
+ s.userLog(form,{type:'Monitor Updated',msg:'by user : '+user.uid})
+ endData.msg = user.lang['Monitor Updated by user']+' : '+user.uid
+ s.sqlQuery('UPDATE Monitors SET '+monitorQuery+' WHERE ke=? AND mid=?',monitorQueryValues)
+ affectMonitor = true
+ }else if(
+ !s.group[form.ke].init.max_camera ||
+ s.group[form.ke].init.max_camera === '' ||
+ Object.keys(s.group[form.ke].mon).length <= parseInt(s.group[form.ke].init.max_camera)
+ ){
+ txData.new = true
+ monitorQueryInsertValues = []
+ Object.keys(form).forEach(function(v){
+ if(form[v] && form[v] !== ''){
+ monitorQuery.push(v)
+ monitorQueryInsertValues.push('?')
+ if(form[v] instanceof Object){
+ form[v] = s.s(form[v])
+ }
+ monitorQueryValues.push(form[v])
+ }
+ })
+ monitorQuery = monitorQuery.join(',')
+ monitorQueryInsertValues = monitorQueryInsertValues.join(',')
+ s.userLog(form,{type:'Monitor Added',msg:'by user : '+user.uid})
+ endData.msg = user.lang['Monitor Added by user']+' : '+user.uid
+ s.sqlQuery('INSERT INTO Monitors ('+monitorQuery+') VALUES ('+monitorQueryInsertValues+')',monitorQueryValues)
+ affectMonitor = true
+ }else{
+ txData.f = 'monitor_edit_failed'
+ txData.ff = 'max_reached'
+ endData.msg = user.lang.monitorEditFailedMaxReached
+ }
+ if(affectMonitor === true){
+ form.details = JSON.parse(form.details)
+ endData.ok = true
+ s.initiateMonitorObject({mid:form.mid,ke:form.ke})
+ s.group[form.ke].mon_conf[form.mid] = s.cleanMonitorObject(form)
+ if(form.mode === 'stop'){
+ s.camera('stop',form)
+ }else{
+ s.camera('stop',form)
+ setTimeout(function(){
+ s.camera(form.mode,form)
+ },5000)
+ }
+ s.tx(txData,'STR_'+form.ke)
+ }
+ s.tx(txData,'GRP_'+form.ke)
+ callback(!endData.ok,endData)
+ })
+ }
s.camera = function(x,e,cn){
// x = function or mode
// e = monitor object
diff --git a/libs/plugins.js b/libs/plugins.js
index 44f989e..24eaf51 100644
--- a/libs/plugins.js
+++ b/libs/plugins.js
@@ -30,8 +30,15 @@ module.exports = function(s,config,lang){
s.systemLog('Connected to plugin : Detector - '+d.plug+' - '+d.type)
switch(d.type){
default:case'detector':
- s.ocv={started:s.timeObject(),id:cn.id,plug:d.plug,notice:d.notice,isClientPlugin:true};
- cn.ocv=1;
+ s.ocv = {
+ started: s.timeObject(),
+ id: cn.id,
+ plug: d.plug,
+ notice: d.notice,
+ isClientPlugin: true,
+ connectionType: d.connectionType
+ };
+ cn.ocv = 1;
s.tx({f:'detector_plugged',plug:d.plug,notice:d.notice},'CPU')
break;
}
@@ -39,7 +46,14 @@ module.exports = function(s,config,lang){
//is in host mode (camera.js is client)
switch(d.type){
default:case'detector':
- s.ocv={started:s.timeObject(),id:"host",plug:d.plug,notice:d.notice,isHostPlugin:true};
+ s.ocv = {
+ started:s.timeObject(),
+ id:"host",
+ plug:d.plug,
+ notice:d.notice,
+ isHostPlugin:true,
+ connectionType: d.connectionType
+ };
break;
}
}
diff --git a/libs/socketio.js b/libs/socketio.js
index 0f7058d..50e8031 100644
--- a/libs/socketio.js
+++ b/libs/socketio.js
@@ -6,6 +6,7 @@ var spawn = require('child_process').spawn;
var jsonfile = require("jsonfile");
var onvif = require("node-onvif");
module.exports = function(s,config,lang,io){
+ s.clientSocketConnection = {}
//send data to detector plugin
s.ocvTx=function(data){
if(!s.ocv){return}
@@ -426,7 +427,15 @@ module.exports = function(s,config,lang,io){
// if(!s.group[d.ke].vid)s.group[d.ke].vid={};
if(!s.group[d.ke].users)s.group[d.ke].users={};
// s.group[d.ke].vid[cn.id]={uid:d.uid};
- s.group[d.ke].users[d.auth]={cnid:cn.id,uid:r.uid,mail:r.mail,details:JSON.parse(r.details),logged_in_at:s.timeObject(new Date).format(),login_type:'Dashboard'}
+ s.group[d.ke].users[d.auth] = {
+ cnid: cn.id,
+ uid: r.uid,
+ mail: r.mail,
+ details: JSON.parse(r.details),
+ logged_in_at: s.timeObject(new Date).format(),
+ login_type: 'Dashboard'
+ }
+ s.clientSocketConnection[cn.id] = cn
try{s.group[d.ke].users[d.auth].details=JSON.parse(r.details)}catch(er){}
if(s.group[d.ke].users[d.auth].details.get_server_log!=='0'){
cn.join('GRPLOG_'+d.ke)
@@ -588,75 +597,8 @@ module.exports = function(s,config,lang,io){
}
break;
case'edit':
- s.sqlQuery('SELECT details FROM Users WHERE ke=? AND uid=?',[d.ke,d.uid],function(err,r){
- if(r&&r[0]){
- r=r[0];
- d.d=JSON.parse(r.details);
- if(!d.d.sub || d.d.user_change === "1"){
- if(d.d.get_server_log==='1'){
- cn.join('GRPLOG_'+d.ke)
- }else{
- cn.leave('GRPLOG_'+d.ke)
- }
- ///unchangeable from client side, so reset them in case they did.
- d.form.details=JSON.parse(d.form.details)
- s.beforeAccountSaveExtensions.forEach(function(extender){
- extender(d)
- })
- //admin permissions
- d.form.details.permissions=d.d.permissions
- d.form.details.edit_size=d.d.edit_size
- d.form.details.edit_days=d.d.edit_days
- d.form.details.use_admin=d.d.use_admin
- d.form.details.use_ldap=d.d.use_ldap
- //check
- if(d.d.edit_days=="0"){
- d.form.details.days=d.d.days;
- }
- if(d.d.edit_size=="0"){
- d.form.details.size=d.d.size;
- }
- if(d.d.sub){
- d.form.details.sub=d.d.sub;
- if(d.d.monitors){d.form.details.monitors=d.d.monitors;}
- if(d.d.allmonitors){d.form.details.allmonitors=d.d.allmonitors;}
- if(d.d.monitor_create){d.form.details.monitor_create=d.d.monitor_create;}
- if(d.d.video_delete){d.form.details.video_delete=d.d.video_delete;}
- if(d.d.video_view){d.form.details.video_view=d.d.video_view;}
- if(d.d.monitor_edit){d.form.details.monitor_edit=d.d.monitor_edit;}
- if(d.d.size){d.form.details.size=d.d.size;}
- if(d.d.days){d.form.details.days=d.d.days;}
- delete(d.form.details.mon_groups)
- }
- var newSize = d.form.details.size || 10000
- d.form.details=JSON.stringify(d.form.details)
- ///
- d.set=[],d.ar=[];
- if(d.form.pass&&d.form.pass!==''){d.form.pass=s.createHash(d.form.pass);}else{delete(d.form.pass)};
- delete(d.form.password_again);
- d.for=Object.keys(d.form);
- d.for.forEach(function(v){
- d.set.push(v+'=?'),d.ar.push(d.form[v]);
- });
- d.ar.push(d.ke),d.ar.push(d.uid);
- s.sqlQuery('UPDATE Users SET '+d.set.join(',')+' WHERE ke=? AND uid=?',d.ar,function(err,r){
- if(!d.d.sub){
- var user = Object.assign(d.form,{ke : d.ke})
- var userDetails = JSON.parse(d.form.details)
- s.group[d.ke].sizeLimit = parseFloat(newSize)
- s.onAccountSaveExtensions.forEach(function(extender){
- extender(s.group[d.ke],userDetails)
- })
- s.unloadGroupAppExtensions.forEach(function(extender){
- extender(user)
- })
- s.loadGroupApps(d)
- }
- tx({f:'user_settings_change',uid:d.uid,ke:d.ke,form:d.form});
- });
- }
- }
- })
+ d.cnid = cn.id
+ s.accountSettingsEdit(d)
break;
}
break;
@@ -1428,6 +1370,7 @@ module.exports = function(s,config,lang,io){
if(cn.superSessionKey){
delete(s.superUsersApi[cn.superSessionKey])
}
+ delete(s.clientSocketConnection[cn.id])
})
});
}
diff --git a/libs/sql.js b/libs/sql.js
index 398fbeb..d9d3d7b 100644
--- a/libs/sql.js
+++ b/libs/sql.js
@@ -50,6 +50,9 @@ module.exports = function(s,config){
if(!onMoveOn){onMoveOn=function(){}}
var mergedQuery = s.mergeQueryValues(query,values)
s.debugLog('s.sqlQuery QUERY',mergedQuery)
+ if(!s.databaseEngine || !s.databaseEngine.raw){
+ s.connectDatabase()
+ }
return s.databaseEngine
.raw(query,values)
.asCallback(function(err,r){
@@ -70,11 +73,18 @@ module.exports = function(s,config){
}
})
}
+ s.connectDatabase = function(){
+ s.databaseEngine = require('knex')(s.databaseOptions)
+ }
s.preQueries = function(){
//add Cloud Videos table, will remove in future
s.sqlQuery('CREATE TABLE IF NOT EXISTS `Cloud Videos` (`mid` varchar(50) NOT NULL,`ke` varchar(50) DEFAULT NULL,`href` text NOT NULL,`size` float DEFAULT NULL,`time` timestamp NULL DEFAULT NULL,`end` timestamp NULL DEFAULT NULL,`status` int(1) DEFAULT \'0\' COMMENT \'0:Complete,1:Read,2:Archive\',`details` text) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;',[],function(err){
// if(err)console.log(err)
},true)
+ //add monitorStates to Preset ENUM
+ s.sqlQuery('ALTER TABLE `Presets` CHANGE COLUMN `type` `type` VARCHAR(50) NULL DEFAULT NULL AFTER `details`;',[],function(err){
+ // if(err)console.log(err)
+ },true)
//create Files table
s.sqlQuery('CREATE TABLE IF NOT EXISTS `Files` (`ke` varchar(50) NOT NULL,`mid` varchar(50) NOT NULL,`name` tinytext NOT NULL,`size` float NOT NULL DEFAULT \'0\',`details` text NOT NULL,`status` int(1) NOT NULL DEFAULT \'0\') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;',[],function(err){
// if(err)console.log(err)
diff --git a/libs/startup.js b/libs/startup.js
index f6d796b..3a7b937 100644
--- a/libs/startup.js
+++ b/libs/startup.js
@@ -29,6 +29,7 @@ module.exports = function(s,config,lang,io){
orphanedVideosForMonitors[monitor.ke][monitor.mid] += orphanedFilesCount
}
s.group[monitor.ke].mon_conf[monitor.mid] = monitor
+ s.sendMonitorStatus({id:monitor.mid,ke:monitor.ke,status:'Stopped'});
var monObj = Object.assign(monitor,{id : monitor.mid})
s.camera(monitor.mode,monObj)
++loadCompleted
diff --git a/libs/user.js b/libs/user.js
index 1ed2e34..6c595ac 100644
--- a/libs/user.js
+++ b/libs/user.js
@@ -230,6 +230,79 @@ module.exports = function(s,config){
s.group[e.ke].init[v]=ar[v]
})
}
- });
+ })
+ }
+ s.accountSettingsEdit = function(d){
+ s.sqlQuery('SELECT details FROM Users WHERE ke=? AND uid=?',[d.ke,d.uid],function(err,r){
+ if(r&&r[0]){
+ r=r[0];
+ d.d=JSON.parse(r.details);
+ if(!d.d.sub || d.d.user_change !== "0"){
+ if(d.cnid){
+ if(d.d.get_server_log==='1'){
+ s.clientSocketConnection[d.cnid].join('GRPLOG_'+d.ke)
+ }else{
+ s.clientSocketConnection[d.cnid].leave('GRPLOG_'+d.ke)
+ }
+ }
+ ///unchangeable from client side, so reset them in case they did.
+ d.form.details=JSON.parse(d.form.details)
+ s.beforeAccountSaveExtensions.forEach(function(extender){
+ extender(d)
+ })
+ //admin permissions
+ d.form.details.permissions=d.d.permissions
+ d.form.details.edit_size=d.d.edit_size
+ d.form.details.edit_days=d.d.edit_days
+ d.form.details.use_admin=d.d.use_admin
+ d.form.details.use_ldap=d.d.use_ldap
+ //check
+ if(d.d.edit_days=="0"){
+ d.form.details.days=d.d.days;
+ }
+ if(d.d.edit_size=="0"){
+ d.form.details.size=d.d.size;
+ }
+ if(d.d.sub){
+ d.form.details.sub=d.d.sub;
+ if(d.d.monitors){d.form.details.monitors=d.d.monitors;}
+ if(d.d.allmonitors){d.form.details.allmonitors=d.d.allmonitors;}
+ if(d.d.monitor_create){d.form.details.monitor_create=d.d.monitor_create;}
+ if(d.d.video_delete){d.form.details.video_delete=d.d.video_delete;}
+ if(d.d.video_view){d.form.details.video_view=d.d.video_view;}
+ if(d.d.monitor_edit){d.form.details.monitor_edit=d.d.monitor_edit;}
+ if(d.d.size){d.form.details.size=d.d.size;}
+ if(d.d.days){d.form.details.days=d.d.days;}
+ delete(d.form.details.mon_groups)
+ }
+ var newSize = d.form.details.size || 10000
+ d.form.details=JSON.stringify(d.form.details)
+ ///
+ d.set=[],d.ar=[];
+ if(d.form.pass&&d.form.pass!==''){d.form.pass=s.createHash(d.form.pass);}else{delete(d.form.pass)};
+ delete(d.form.password_again);
+ d.for=Object.keys(d.form);
+ d.for.forEach(function(v){
+ d.set.push(v+'=?'),d.ar.push(d.form[v]);
+ });
+ d.ar.push(d.ke),d.ar.push(d.uid);
+ s.sqlQuery('UPDATE Users SET '+d.set.join(',')+' WHERE ke=? AND uid=?',d.ar,function(err,r){
+ if(!d.d.sub){
+ var user = Object.assign(d.form,{ke : d.ke})
+ var userDetails = JSON.parse(d.form.details)
+ s.group[d.ke].sizeLimit = parseFloat(newSize)
+ s.onAccountSaveExtensions.forEach(function(extender){
+ extender(s.group[d.ke],userDetails)
+ })
+ s.unloadGroupAppExtensions.forEach(function(extender){
+ extender(user)
+ })
+ s.loadGroupApps(d)
+ }
+ if(d.cnid)s.tx({f:'user_settings_change',uid:d.uid,ke:d.ke,form:d.form},d.cnid)
+ })
+ }
+ }
+ })
}
}
diff --git a/libs/videos.js b/libs/videos.js
index 614994f..4b54c34 100644
--- a/libs/videos.js
+++ b/libs/videos.js
@@ -230,17 +230,69 @@ module.exports = function(s,config,lang){
})
})
})
- var videoSnap = s.dir.videoSnaps + e.ke + '/' + e.mid + '/' + filename.split('.')[0] + '.jpg'
fs.chmod(videoSnap,0o777,function(err){
if(!err){
fs.unlink(videoSnap,function(err){})
}
})
}else{
+ console.log(new Error())
console.log(lang['Database row does not exist'],queryValues)
}
})
}
+ s.deleteListOfVideos = function(videos){
+ var query = 'DELETE FROM Videos WHERE '
+ var videoQuery = []
+ var queryValues = []
+ videos.forEach(function(video){
+ s.checkDetails(video)
+ //e = video object
+ video.dir = s.getVideoDirectory(video)
+ if(!video.filename && video.time){
+ video.filename = s.formattedTime(video.time)
+ }
+ var filename,
+ time
+ if(video.filename.indexOf('.')>-1){
+ filename = video.filename
+ }else{
+ filename = video.filename+'.'+video.ext
+ }
+ if(video.filename && !video.time){
+ time = s.nameToTime(filename)
+ }else{
+ time = video.time
+ }
+ time = new Date(time)
+ fs.chmod(video.dir+filename,0o777,function(err){
+ s.tx({
+ f: 'video_delete',
+ filename: filename,
+ mid: video.id,
+ ke: video.ke,
+ time: s.nameToTime(filename),
+ end: s.formattedTime(new Date,'YYYY-MM-DD HH:mm:ss')
+ },'GRP_'+video.ke);
+ s.setDiskUsedForGroup(video,-(video.size / 1000000))
+ fs.unlink(video.dir+filename,function(err){
+ fs.stat(video.dir+filename,function(err){
+ if(!err){
+ s.file('delete',video.dir+filename)
+ }
+ })
+ })
+ })
+ videoQuery.push('(`mid`=? AND `ke`=? AND `time`=?)')
+ queryValues = queryValues.concat([video.id,video.ke,time])
+ })
+ query += videoQuery.join(' OR ')
+ s.sqlQuery(query,queryValues,function(err){
+ if(err){
+ s.systemLog(lang['List of Videos Delete Error'],err)
+ }
+ })
+ }
s.deleteVideoFromCloudExtensions = {}
s.deleteVideoFromCloudExtensionsRunner = function(e,storageType,video){
// e = user
diff --git a/libs/webServer.js b/libs/webServer.js
index bc86631..9cc5363 100644
--- a/libs/webServer.js
+++ b/libs/webServer.js
@@ -4,7 +4,52 @@ var https = require('https');
var express = require('express');
var app = express()
module.exports = function(s,config,lang,io){
- var server = http.createServer(app);
+ //get page URL
+ if(!config.baseURL){
+ config.baseURL = ""
+ }else if(config.baseURL !== ''){
+ config.baseURL = s.checkCorrectPathEnding(config.baseURL)
+ }
+ //Render Configurations - Web Paths
+ if(config.webPaths === undefined){config.webPaths={}}
+ //main access URI
+ if(config.webPaths.home === undefined){config.webPaths.home='/'}
+ //Super User URI
+ if(config.webPaths.super === undefined){config.webPaths.super='/super'}
+ //Admin URI
+ if(config.webPaths.admin === undefined){config.webPaths.admin='/admin'}
+ //Libraries URI
+ if(config.webPaths.libs === undefined){config.webPaths.libs='/libs'}
+ //API Prefix
+ if(config.webPaths.apiPrefix === undefined){config.webPaths.apiPrefix = s.checkCorrectPathEnding(config.webPaths.home)}else{config.webPaths.apiPrefix = s.checkCorrectPathEnding(config.webPaths.apiPrefix)}
+ //Admin API Prefix
+ if(config.webPaths.adminApiPrefix === undefined){config.webPaths.adminApiPrefix=s.checkCorrectPathEnding(config.webPaths.admin)}else{config.webPaths.adminApiPrefix = s.checkCorrectPathEnding(config.webPaths.adminApiPrefix)}
+ //Super API Prefix
+ if(config.webPaths.superApiPrefix === undefined){config.webPaths.superApiPrefix=s.checkCorrectPathEnding(config.webPaths.super)}else{config.webPaths.superApiPrefix = s.checkCorrectPathEnding(config.webPaths.superApiPrefix)}
+ //Render Configurations - Page Render Paths
+ if(config.renderPaths === undefined){config.renderPaths={}}
+ //login page
+ if(config.renderPaths.index === undefined){config.renderPaths.index='pages/index'}
+ //dashboard page
+ if(config.renderPaths.home === undefined){config.renderPaths.home='pages/home'}
+ //sub-account administration page
+ if(config.renderPaths.admin === undefined){config.renderPaths.admin='pages/admin'}
+ //superuser page
+ if(config.renderPaths.super === undefined){config.renderPaths.super='pages/super'}
+ //2-Factor Auth page
+ if(config.renderPaths.factorAuth === undefined){config.renderPaths.factorAuth='pages/factor'}
+ //Streamer v1 (Dashcam Prototype) page
+ if(config.renderPaths.streamer === undefined){config.renderPaths.streamer='pages/streamer'}
+ //Streamer v2 (Dashcam) page
+ if(config.renderPaths.dashcam === undefined){config.renderPaths.dashcam='pages/dashcam'}
+ //embeddable widget page
+ if(config.renderPaths.embed === undefined){config.renderPaths.embed='pages/embed'}
+ //mjpeg full screen page
+ if(config.renderPaths.mjpeg === undefined){config.renderPaths.mjpeg='pages/mjpeg'}
+ //gridstack only page
+ if(config.renderPaths.grid === undefined){config.renderPaths.grid='pages/grid'}
+ //slick.js (cycle) page
+ if(config.renderPaths.cycle === undefined){config.renderPaths.cycle='pages/cycle'}
//SSL options
if(config.ssl&&config.ssl.key&&config.ssl.cert){
config.ssl.key=fs.readFileSync(s.checkRelativePath(config.ssl.key),'utf8')
@@ -24,12 +69,47 @@ module.exports = function(s,config,lang,io){
serverHTTPS.listen(config.ssl.port,config.bindip,function(){
console.log('SSL '+lang.Shinobi+' : SSL Web Server Listening on '+config.ssl.port);
});
- io.attach(serverHTTPS);
+ if(config.webPaths.home !== '/'){
+ io.attach(serverHTTPS,{
+ path:'/socket.io',
+ transports: ['websocket']
+ })
+ }
+ io.attach(serverHTTPS,{
+ path:s.checkCorrectPathEnding(config.webPaths.home)+'socket.io',
+ transports: ['websocket']
+ })
+ io.attach(serverHTTPS,{
+ path:s.checkCorrectPathEnding(config.webPaths.admin)+'socket.io',
+ transports: ['websocket']
+ })
+ io.attach(serverHTTPS,{
+ path:s.checkCorrectPathEnding(config.webPaths.super)+'socket.io',
+ transports: ['websocket']
+ })
}
//start HTTP
+ var server = http.createServer(app);
server.listen(config.port,config.bindip,function(){
console.log(lang.Shinobi+' : Web Server Listening on '+config.port);
});
- io.attach(server);
+ if(config.webPaths.home !== '/'){
+ io.attach(server,{
+ path:'/socket.io',
+ transports: ['websocket']
+ })
+ }
+ io.attach(server,{
+ path:s.checkCorrectPathEnding(config.webPaths.home)+'socket.io',
+ transports: ['websocket']
+ })
+ io.attach(server,{
+ path:s.checkCorrectPathEnding(config.webPaths.admin)+'socket.io',
+ transports: ['websocket']
+ })
+ io.attach(server,{
+ path:s.checkCorrectPathEnding(config.webPaths.super)+'socket.io',
+ transports: ['websocket']
+ })
return app
}
diff --git a/libs/webServerAdminPaths.js b/libs/webServerAdminPaths.js
index 3cc62e2..7cebd97 100644
--- a/libs/webServerAdminPaths.js
+++ b/libs/webServerAdminPaths.js
@@ -7,10 +7,6 @@ var exec = require('child_process').exec;
var spawn = require('child_process').spawn;
var execSync = require('child_process').execSync;
module.exports = function(s,config,lang,app){
- var closeResponse = function(res,endData){
- res.setHeader('Content-Type', 'application/json')
- res.end(s.prettyPrint(endData))
- }
/**
* API : Administrator : Edit Sub-Account (Account to share cameras with)
*/
@@ -21,7 +17,7 @@ module.exports = function(s,config,lang,app){
}
if(user.details.sub){
endData.msg = user.lang['Not Permitted']
- closeResponse(res,endData)
+ s.closeJsonResponse(res,endData)
return
}
var form = s.getPostData(req)
@@ -56,7 +52,7 @@ module.exports = function(s,config,lang,app){
}else{
endData.msg = lang.postDataBroken
}
- closeResponse(res,endData)
+ s.closeJsonResponse(res,endData)
},res,req)
})
/**
@@ -69,7 +65,7 @@ module.exports = function(s,config,lang,app){
}
if(user.details.sub){
endData.msg = user.lang['Not Permitted']
- closeResponse(res,endData)
+ s.closeJsonResponse(res,endData)
return
}
var uid = s.getPostData(req,'uid',false)
@@ -90,7 +86,7 @@ module.exports = function(s,config,lang,app){
mail: mail
},'ADM_'+req.params.ke)
endData.ok = true
- closeResponse(res,endData)
+ s.closeJsonResponse(res,endData)
},res,req)
})
/**
@@ -109,7 +105,7 @@ module.exports = function(s,config,lang,app){
s.auth(req.params,function(user){
if(user.details.sub){
endData.msg = user.lang['Not an Administrator Account']
- closeResponse(res,endData)
+ s.closeJsonResponse(res,endData)
return
}
var form = s.getPostData(req)
@@ -159,7 +155,9 @@ module.exports = function(s,config,lang,app){
config.webPaths.adminApiPrefix+':auth/configureMonitor/:ke/:id',
config.webPaths.adminApiPrefix+':auth/configureMonitor/:ke/:id/:f'
], function (req,res){
- req.ret={ok:false};
+ var endData = {
+ ok: false
+ }
res.setHeader('Content-Type', 'application/json');
res.header("Access-Control-Allow-Origin",req.headers.origin);
s.auth(req.params,function(user){
@@ -167,88 +165,28 @@ module.exports = function(s,config,lang,app){
if(req.params.f !== 'delete'){
var form = s.getPostData(req)
if(!form){
- req.ret.msg = user.lang.monitorEditText1;
- res.end(s.prettyPrint(req.ret))
- return
+ endData.msg = user.lang.monitorEditText1;
+ res.end(s.prettyPrint(endData))
+ return
}
+ form.mid = req.params.id.replace(/[^\w\s]/gi,'').replace(/ /g,'')
if(!user.details.sub ||
user.details.allmonitors === '1' ||
hasRestrictions && user.details.monitor_edit.indexOf(form.mid) >- 1 ||
hasRestrictions && user.details.monitor_create === '1'){
- if(form&&form.mid&&form.name){
- req.set=[],req.ar=[];
- form.mid=req.params.id.replace(/[^\w\s]/gi,'').replace(/ /g,'');
- try{
- JSON.parse(form.details)
- }catch(er){
- if(!form.details||!form.details.stream_type){
- req.ret.msg=user.lang.monitorEditText2;
- res.end(s.prettyPrint(req.ret))
- return
- }else{
- form.details=JSON.stringify(form.details)
- }
- }
- form.ke=req.params.ke
- req.logObject={details:JSON.parse(form.details),ke:req.params.ke,mid:req.params.id}
- s.sqlQuery('SELECT * FROM Monitors WHERE ke=? AND mid=?',[form.ke,form.mid],function(er,r){
- req.tx={f:'monitor_edit',mid:form.mid,ke:form.ke,mon:form};
- if(r&&r[0]){
- req.tx.new=false;
- Object.keys(form).forEach(function(v){
- if(form[v]&&form[v]!==''){
- req.set.push(v+'=?'),req.ar.push(form[v]);
- }
- })
- req.set=req.set.join(',');
- req.ar.push(form.ke),req.ar.push(form.mid);
- s.userLog(form,{type:'Monitor Updated',msg:'by user : '+user.uid});
- req.ret.msg=user.lang['Monitor Updated by user']+' : '+user.uid;
- s.sqlQuery('UPDATE Monitors SET '+req.set+' WHERE ke=? AND mid=?',req.ar)
- req.finish=1;
- }else{
- if(!s.group[form.ke].init.max_camera||s.group[form.ke].init.max_camera==''||Object.keys(s.group[form.ke].mon).length <= parseInt(s.group[form.ke].init.max_camera)){
- req.tx.new=true;
- req.st=[];
- Object.keys(form).forEach(function(v){
- if(form[v]&&form[v]!==''){
- req.set.push(v),req.st.push('?'),req.ar.push(form[v]);
- }
- })
- // req.set.push('ke'),req.st.push('?'),req.ar.push(form.ke);
- req.set=req.set.join(','),req.st=req.st.join(',');
- s.userLog(form,{type:'Monitor Added',msg:'by user : '+user.uid});
- req.ret.msg=user.lang['Monitor Added by user']+' : '+user.uid;
- s.sqlQuery('INSERT INTO Monitors ('+req.set+') VALUES ('+req.st+')',req.ar)
- req.finish=1;
- }else{
- req.tx.f='monitor_edit_failed';
- req.tx.ff='max_reached';
- req.ret.msg=user.lang.monitorEditFailedMaxReached;
- }
- }
- if(req.finish===1){
- form.details=JSON.parse(form.details)
- req.ret.ok=true;
- s.initiateMonitorObject({mid:form.mid,ke:form.ke});
- s.group[form.ke].mon_conf[form.mid]=s.cleanMonitorObject(form);
- if(form.mode==='stop'){
- s.camera('stop',form);
- }else{
- s.camera('stop',form);setTimeout(function(){s.camera(form.mode,form);},5000)
- };
- s.tx(req.tx,'STR_'+form.ke);
- };
- s.tx(req.tx,'GRP_'+form.ke);
- res.end(s.prettyPrint(req.ret))
- })
+ if(form && form.name){
+ s.checkDetails(form)
+ form.ke = req.params.ke
+ s.addOrEditMonitor(form,function(err,endData){
+ res.end(s.prettyPrint(endData))
+ },user)
}else{
- req.ret.msg=user.lang.monitorEditText1;
- res.end(s.prettyPrint(req.ret))
+ endData.msg = user.lang.monitorEditText1;
+ res.end(s.prettyPrint(endData))
}
}else{
- req.ret.msg=user.lang['Not Permitted'];
- res.end(s.prettyPrint(req.ret))
+ endData.msg = user.lang['Not Permitted']
+ res.end(s.prettyPrint(endData))
}
}else{
if(!user.details.sub || user.details.allmonitors === '1' || user.details.monitor_edit.indexOf(req.params.id) > -1 || hasRestrictions && user.details.monitor_create === '1'){
@@ -281,12 +219,12 @@ module.exports = function(s,config,lang,app){
}
})
}
- req.ret.ok=true;
- req.ret.msg='Monitor Deleted by user : '+user.uid
- res.end(s.prettyPrint(req.ret))
+ endData.ok=true;
+ endData.msg='Monitor Deleted by user : '+user.uid
+ res.end(s.prettyPrint(endData))
}else{
- req.ret.msg=user.lang['Not Permitted'];
- res.end(s.prettyPrint(req.ret))
+ endData.msg=user.lang['Not Permitted'];
+ res.end(s.prettyPrint(endData))
}
}
},res,req)
@@ -328,11 +266,11 @@ module.exports = function(s,config,lang,app){
},'GRP_' + req.params.ke)
endData.ok = true
}
- closeResponse(res,endData)
+ s.closeJsonResponse(res,endData)
})
}else{
endData.msg = lang.postDataBroken
- closeResponse(res,endData)
+ s.closeJsonResponse(res,endData)
}
},res,req)
})
@@ -359,7 +297,7 @@ module.exports = function(s,config,lang,app){
form:'APIs'
},'GRP_' + req.params.ke)
endData.msg = lang.postDataBroken
- closeResponse(res,endData)
+ s.closeJsonResponse(res,endData)
return
}
var row = {
@@ -381,11 +319,11 @@ module.exports = function(s,config,lang,app){
endData.ok = true
delete(s.api[row.code])
}
- closeResponse(res,endData)
+ s.closeJsonResponse(res,endData)
})
}else{
endData.msg = lang.postDataBroken
- closeResponse(res,endData)
+ s.closeJsonResponse(res,endData)
}
},res,req)
})
@@ -421,8 +359,143 @@ module.exports = function(s,config,lang,app){
endData.ke = user.ke
endData.keys = rows
}
- closeResponse(res,endData)
+ s.closeJsonResponse(res,endData)
})
},res,req)
})
+ /**
+ * API : Administrator : Change Group Preset. Currently affects Monitors only.
+ */
+ app.all([
+ config.webPaths.apiPrefix+':auth/monitorStates/:ke/:stateName',
+ config.webPaths.apiPrefix+':auth/monitorStates/:ke/:stateName/:action',
+ config.webPaths.adminApiPrefix+':auth/monitorStates/:ke/:stateName',
+ config.webPaths.adminApiPrefix+':auth/monitorStates/:ke/:stateName/:action',
+ ],function (req,res){
+ s.auth(req.params,function(user){
+ var endData = {
+ ok : false
+ }
+ if(user.details.sub){
+ endData.msg = user.lang['Not Permitted']
+ s.closeJsonResponse(res,endData)
+ return
+ }
+ var findPreset = function(callback){
+ s.sqlQuery("SELECT * FROM Presets WHERE ke=? AND type=? AND name=? LIMIT 1",[req.params.ke,'monitorStates',req.params.stateName],function(err,presets){
+ var preset
+ var notFound = false
+ if(presets && presets[0]){
+ preset = presets[0]
+ s.checkDetails(preset)
+ }else{
+ notFound = true
+ }
+ callback(notFound,preset)
+ })
+ }
+ switch(req.params.action){
+ case'insert':case'edit':
+ var form = s.getPostData(req)
+ s.checkDetails(form)
+ if(!form || !form.monitors){
+ endData.msg = user.lang['Form Data Not Found']
+ s.closeJsonResponse(res,endData)
+ return
+ }
+ findPreset(function(notFound,preset){
+ if(notFound === true){
+ endData.msg = lang["Inserted State Configuration"]
+ var details = {
+ monitors : form.monitors
+ }
+ var insertData = {
+ ke: req.params.ke,
+ name: req.params.stateName,
+ details: s.s(details),
+ type: 'monitorStates'
+ }
+ s.sqlQuery('INSERT INTO Presets ('+Object.keys(insertData).join(',')+') VALUES (?,?,?,?)',Object.values(insertData))
+ s.tx({
+ f: 'add_group_state',
+ details: details,
+ ke: req.params.ke,
+ name: req.params.stateName
+ },'GRP_'+req.params.ke)
+ }else{
+ endData.msg = lang["Edited State Configuration"]
+ var details = Object.assign(preset.details,{
+ monitors : form.monitors
+ })
+ s.sqlQuery('UPDATE Presets SET details=? WHERE ke=? AND name=?',[s.s(details),req.params.ke,req.params.stateName])
+ s.tx({
+ f: 'edit_group_state',
+ details: details,
+ ke: req.params.ke,
+ name: req.params.stateName
+ },'GRP_'+req.params.ke)
+ }
+ endData.ok = true
+ s.closeJsonResponse(res,endData)
+ })
+ break;
+ case'delete':
+ findPreset(function(notFound,preset){
+ if(notFound === true){
+ endData.msg = user.lang['State Configuration Not Found']
+ s.closeJsonResponse(res,endData)
+ }else{
+ s.sqlQuery('DELETE FROM Presets WHERE ke=? AND name=?',[req.params.ke,req.params.stateName],function(err){
+ if(!err){
+ endData.msg = lang["Deleted State Configuration"]
+ endData.ok = true
+ }
+ s.closeJsonResponse(res,endData)
+ })
+ }
+ })
+ break;
+ default://change monitors according to state
+ findPreset(function(notFound,preset){
+ if(notFound === false){
+ var sqlQuery = 'SELECT * FROM Monitors WHERE ke=? AND '
+ var monitorQuery = []
+ var sqlQueryValues = [req.params.ke]
+ var monitorPresets = {}
+ preset.details.monitors.forEach(function(monitor){
+ monitorQuery.push('mid=?')
+ sqlQueryValues.push(monitor.mid)
+ monitorPresets[monitor.mid] = monitor
+ })
+ sqlQuery += '('+monitorQuery.join(' OR ')+')'
+ s.sqlQuery(sqlQuery,sqlQueryValues,function(err,monitors){
+ if(monitors && monitors[0]){
+ monitors.forEach(function(monitor){
+ s.checkDetails(monitor)
+ s.checkDetails(monitorPresets[monitor.mid])
+ var monitorPreset = monitorPresets[monitor.mid]
+ monitorPreset.details = Object.assign(monitor.details,monitorPreset.details)
+ monitor = s.cleanMonitorObjectForDatabase(Object.assign(monitor,monitorPreset))
+ monitor.details = JSON.stringify(monitor.details)
+ s.addOrEditMonitor(Object.assign(monitor,{}),function(err,endData){
+
+ },user)
+ })
+ endData.ok = true
+ s.tx({f:'change_group_state',ke:req.params.ke,name:req.params.stateName},'GRP_'+req.params.ke)
+ s.closeJsonResponse(res,endData)
+ }else{
+ endData.msg = user.lang['State Configuration has no monitors associated']
+ s.closeJsonResponse(res,endData)
+ }
+ })
+ }else{
+ endData.msg = user.lang['State Configuration Not Found']
+ s.closeJsonResponse(res,endData)
+ }
+ })
+ break;
+ }
+ },res,req)
+ })
}
diff --git a/libs/webServerPaths.js b/libs/webServerPaths.js
index 47b94be..c01f26f 100644
--- a/libs/webServerPaths.js
+++ b/libs/webServerPaths.js
@@ -11,54 +11,15 @@ var httpProxy = require('http-proxy');
var proxy = httpProxy.createProxyServer({})
var ejs = require('ejs');
var CircularJSON = require('circular-json');
-module.exports = function(s,config,lang,app){
+module.exports = function(s,config,lang,app,io){
if(config.productType==='Pro'){
var LdapAuth = require('ldapauth-fork');
}
- //get page URL
- if(!config.baseURL){
- config.baseURL = ""
- }else if(config.baseURL !== ''){
- config.baseURL = s.checkCorrectPathEnding(config.baseURL)
+ s.renderPage = function(req,res,paths,passables,callback){
+ passables.window = {}
+ passables.originalURL = s.getOriginalUrl(req)
+ res.render(paths,passables,callback)
}
- //Render Configurations - Web Paths
- if(config.webPaths === undefined){config.webPaths={}}
- //main access URI
- if(config.webPaths.home === undefined){config.webPaths.home='/'}
- //Super User URI
- if(config.webPaths.super === undefined){config.webPaths.super='/super'}
- //Admin URI
- if(config.webPaths.admin === undefined){config.webPaths.admin='/admin'}
- //API Prefix
- if(config.webPaths.apiPrefix === undefined){config.webPaths.apiPrefix='/'}else{config.webPaths.apiPrefix = s.checkCorrectPathEnding(config.webPaths.apiPrefix)}
- //Admin API Prefix
- if(config.webPaths.adminApiPrefix === undefined){config.webPaths.adminApiPrefix='/admin/'}else{config.webPaths.adminApiPrefix = s.checkCorrectPathEnding(config.webPaths.adminApiPrefix)}
- //Super API Prefix
- if(config.webPaths.superApiPrefix === undefined){config.webPaths.superApiPrefix='/super/'}else{config.webPaths.superApiPrefix = s.checkCorrectPathEnding(config.webPaths.superApiPrefix)}
- //Render Configurations - Page Render Paths
- if(config.renderPaths === undefined){config.renderPaths={}}
- //login page
- if(config.renderPaths.index === undefined){config.renderPaths.index='pages/index'}
- //dashboard page
- if(config.renderPaths.home === undefined){config.renderPaths.home='pages/home'}
- //sub-account administration page
- if(config.renderPaths.admin === undefined){config.renderPaths.admin='pages/admin'}
- //superuser page
- if(config.renderPaths.super === undefined){config.renderPaths.super='pages/super'}
- //2-Factor Auth page
- if(config.renderPaths.factorAuth === undefined){config.renderPaths.factorAuth='pages/factor'}
- //Streamer v1 (Dashcam Prototype) page
- if(config.renderPaths.streamer === undefined){config.renderPaths.streamer='pages/streamer'}
- //Streamer v2 (Dashcam) page
- if(config.renderPaths.dashcam === undefined){config.renderPaths.dashcam='pages/dashcam'}
- //embeddable widget page
- if(config.renderPaths.embed === undefined){config.renderPaths.embed='pages/embed'}
- //mjpeg full screen page
- if(config.renderPaths.mjpeg === undefined){config.renderPaths.mjpeg='pages/mjpeg'}
- //gridstack only page
- if(config.renderPaths.grid === undefined){config.renderPaths.grid='pages/grid'}
- //slick.js (cycle) page
- if(config.renderPaths.cycle === undefined){config.renderPaths.cycle='pages/cycle'}
//child node proxy check
//params = parameters
//cb = callback
@@ -72,6 +33,10 @@ module.exports = function(s,config,lang,app){
cb()
}
}
+ s.closeJsonResponse = function(res,endData){
+ res.setHeader('Content-Type', 'application/json')
+ res.end(s.prettyPrint(endData))
+ }
//get post data
s.getPostData = function(req,target,parseJSON){
if(!target)target = 'data'
@@ -93,13 +58,18 @@ module.exports = function(s,config,lang,app){
}
////Pages
app.enable('trust proxy');
- app.use('/libs',express.static(s.mainDirectory + '/web/libs'));
+ if(config.webPaths.home !== '/'){
+ app.use('/libs',express.static(s.mainDirectory + '/web/libs'))
+ }
+ app.use(s.checkCorrectPathEnding(config.webPaths.home)+'libs',express.static(s.mainDirectory + '/web/libs'))
+ app.use(s.checkCorrectPathEnding(config.webPaths.admin)+'libs',express.static(s.mainDirectory + '/web/libs'))
+ app.use(s.checkCorrectPathEnding(config.webPaths.super)+'libs',express.static(s.mainDirectory + '/web/libs'))
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.set('views', s.mainDirectory + '/web');
app.set('view engine','ejs');
//add template handler
- if(config.renderPaths.handler!==undefined){require(s.mainDirectory+'/web/'+config.renderPaths.handler+'.js').addHandlers(s,app,io)}
+ if(config.renderPaths.handler!==undefined){require(s.mainDirectory+'/web/'+config.renderPaths.handler+'.js').addHandlers(s,app,io,config)}
/**
* API : Logout
@@ -118,7 +88,7 @@ module.exports = function(s,config,lang,app){
* Page : Login Screen
*/
app.get(config.webPaths.home, function (req,res){
- res.render(config.renderPaths.index,{lang:lang,config:config,screen:'dashboard',originalURL:s.getOriginalUrl(req)},function(err,html){
+ s.renderPage(req,res,config.renderPaths.index,{lang:lang,config:config,screen:'dashboard'},function(err,html){
if(err){
s.systemLog(err)
}
@@ -129,7 +99,7 @@ module.exports = function(s,config,lang,app){
* Page : Administrator Login Screen
*/
app.get(config.webPaths.admin, function (req,res){
- res.render(config.renderPaths.index,{lang:lang,config:config,screen:'admin',originalURL:s.getOriginalUrl(req)},function(err,html){
+ s.renderPage(req,res,config.renderPaths.index,{lang:lang,config:config,screen:'admin'},function(err,html){
if(err){
s.systemLog(err)
}
@@ -141,7 +111,7 @@ module.exports = function(s,config,lang,app){
*/
app.get(config.webPaths.super, function (req,res){
- res.render(config.renderPaths.index,{lang:lang,config:config,screen:'super',originalURL:s.getOriginalUrl(req)},function(err,html){
+ s.renderPage(req,res,config.renderPaths.index,{lang:lang,config:config,screen:'super'},function(err,html){
if(err){
s.systemLog(err)
}
@@ -171,23 +141,48 @@ module.exports = function(s,config,lang,app){
/**
* API : Login handler. Dashboard, Streamer, Dashcam Administrator, Superuser
*/
- app.post([config.webPaths.home,s.checkCorrectPathEnding(config.webPaths.home)+':screen'],function (req,res){
+ app.post([
+ config.webPaths.home,
+ config.webPaths.admin,
+ config.webPaths.super,
+ s.checkCorrectPathEnding(config.webPaths.home)+':screen',
+ s.checkCorrectPathEnding(config.webPaths.admin)+':screen',
+ s.checkCorrectPathEnding(config.webPaths.super)+':screen',
+ ],function (req,res){
req.ip = s.getClientIp(req)
if(req.query.json === 'true'){
res.header("Access-Control-Allow-Origin",req.headers.origin);
}
+ var screenChooser = function(screen){
+ var search = function(screen){
+ if(req.url.indexOf(screen) > -1){
+ return true
+ }
+ return false
+ }
+ switch(true){
+ case search(config.webPaths.admin):
+ return 'admin'
+ break;
+ case search(config.webPaths.super):
+ return 'super'
+ break;
+ default:
+ return 'dashboard'
+ break;
+ }
+ }
// brute check
if(s.failedLoginAttempts[req.body.mail] && s.failedLoginAttempts[req.body.mail].failCount >= 5){
if(req.query.json=='true'){
res.end(s.prettyPrint({ok:false}))
}else{
- res.render(config.renderPaths.index,{
- failedLogin:true,
- message:lang.failedLoginText1,
- lang:lang,
- config:config,
- screen:req.params.screen,
- originalURL:s.getOriginalUrl(req)
+ s.renderPage(req,res,config.renderPaths.index,{
+ failedLogin: true,
+ message: lang.failedLoginText1,
+ lang: lang,
+ config: config,
+ screen: screenChooser(req.params.screen)
},function(err,html){
if(err){
s.systemLog(err)
@@ -209,9 +204,8 @@ module.exports = function(s,config,lang,app){
res.setHeader('Content-Type', 'application/json');
res.end(s.prettyPrint(data))
}else{
- data.originalURL = s.getOriginalUrl(req)
data.screen=req.params.screen
- res.render(focus,data,function(err,html){
+ s.renderPage(req,res,focus,data,function(err,html){
if(err){
s.systemLog(err)
}
@@ -241,13 +235,12 @@ module.exports = function(s,config,lang,app){
res.setHeader('Content-Type', 'application/json')
res.end(s.prettyPrint({ok:false}))
}else{
- res.render(config.renderPaths.index,{
- failedLogin:true,
- message:lang.failedLoginText2,
- lang:lang,
- config:config,
- screen:req.params.screen,
- originalURL:s.getOriginalUrl(req)
+ s.renderPage(req,res,config.renderPaths.index,{
+ failedLogin: true,
+ message: lang.failedLoginText2,
+ lang: lang,
+ config: config,
+ screen: screenChooser(req.params.screen)
},function(err,html){
if(err){
s.systemLog(err)
@@ -344,7 +337,7 @@ module.exports = function(s,config,lang,app){
r.details=JSON.parse(r.details);
r.lang=s.getLanguageFile(r.details.lang)
req.factorAuth=function(cb){
- if(r.details.factorAuth==="1"){
+ if(r.details.factorAuth === "1"){
if(!r.details.acceptedMachines||!(r.details.acceptedMachines instanceof Object)){
r.details.acceptedMachines={}
}
@@ -671,14 +664,13 @@ module.exports = function(s,config,lang,app){
if(req.path.indexOf('/cycle/') > -1){
page = config.renderPaths.cycle
}
- res.render(page,{
+ s.renderPage(req,res,page,{
data:Object.assign(req.params,req.query),
baseUrl:req.protocol+'://'+req.hostname,
config:config,
lang:user.lang,
$user:user,
monitors:r,
- originalURL:s.getOriginalUrl(req),
query:req.query
});
})
@@ -1490,17 +1482,22 @@ module.exports = function(s,config,lang,app){
req.ext=req.params.file.split('.')[1];
var total = fs.statSync(req.dir).size;
if (req.headers['range']) {
- var range = req.headers.range;
- var parts = range.replace(/bytes=/, "").split("-");
- var partialstart = parts[0];
- var partialend = parts[1];
-
- var start = parseInt(partialstart, 10);
- var end = partialend ? parseInt(partialend, 10) : total-1;
- var chunksize = (end-start)+1;
- var file = fs.createReadStream(req.dir, {start: start, end: end});
- req.headerWrite={ 'Content-Range': 'bytes ' + start + '-' + end + '/' + total, 'Accept-Ranges': 'bytes', 'Content-Length': chunksize, 'Content-Type': 'video/'+req.ext }
- req.writeCode=206
+ try{
+ var range = req.headers.range;
+ var parts = range.replace(/bytes=/, "").split("-");
+ var partialstart = parts[0];
+ var partialend = parts[1];
+ var start = parseInt(partialstart, 10);
+ var end = partialend ? parseInt(partialend, 10) : total-1;
+ var chunksize = (end-start)+1;
+ var file = fs.createReadStream(req.dir, {start: start, end: end});
+ req.headerWrite={ 'Content-Range': 'bytes ' + start + '-' + end + '/' + total, 'Accept-Ranges': 'bytes', 'Content-Length': chunksize, 'Content-Type': 'video/'+req.ext }
+ req.writeCode=206
+ }catch(err){
+ req.headerWrite={ 'Content-Length': total, 'Content-Type': 'video/'+req.ext};
+ var file = fs.createReadStream(req.dir)
+ req.writeCode=200
+ }
} else {
req.headerWrite={ 'Content-Length': total, 'Content-Type': 'video/'+req.ext};
var file=fs.createReadStream(req.dir)
@@ -1844,4 +1841,27 @@ module.exports = function(s,config,lang,app){
}
},res,req);
})
+ /**
+ * API : Account Edit from Dashboard
+ */
+ app.all(config.webPaths.apiPrefix+':auth/accounts/:ke/edit',function (req,res){
+ s.auth(req.params,function(user){
+ var endData = {
+ ok : false
+ }
+ var form = s.getPostData(req)
+ if(form){
+ endData.ok = true
+ s.accountSettingsEdit({
+ ke: req.params.ke,
+ uid: user.uid,
+ form: form,
+ cnid: user.cnid
+ })
+ }else{
+ endData.msg = lang.postDataBroken
+ }
+ s.closeJsonResponse(res,endData)
+ },res,req)
+ })
}
diff --git a/libs/webServerStreamPaths.js b/libs/webServerStreamPaths.js
index 7ac6509..ad5e490 100644
--- a/libs/webServerStreamPaths.js
+++ b/libs/webServerStreamPaths.js
@@ -26,7 +26,7 @@ module.exports = function(s,config,lang,app){
if(s.group[req.params.ke]&&s.group[req.params.ke].mon[req.params.id]){
if(s.group[req.params.ke].mon[req.params.id].isStarted === true){
req.params.uid=user.uid;
- res.render(config.renderPaths.embed,{data:req.params,baseUrl:req.protocol+'://'+req.hostname,config:config,lang:user.lang,mon:CircularJSON.parse(CircularJSON.stringify(s.group[req.params.ke].mon_conf[req.params.id])),originalURL:s.getOriginalUrl(req)});
+ s.renderPage(req,res,config.renderPaths.embed,{data:req.params,baseUrl:req.protocol+'://'+req.hostname,config:config,lang:user.lang,mon:CircularJSON.parse(CircularJSON.stringify(s.group[req.params.ke].mon_conf[req.params.id])),originalURL:s.getOriginalUrl(req)});
res.end()
}else{
res.end(user.lang['Cannot watch a monitor that isn\'t running.'])
@@ -102,7 +102,7 @@ module.exports = function(s,config,lang,app){
app.get([config.webPaths.apiPrefix+':auth/mjpeg/:ke/:id',config.webPaths.apiPrefix+':auth/mjpeg/:ke/:id/:channel'], function(req,res) {
res.header("Access-Control-Allow-Origin",req.headers.origin);
if(req.query.full=='true'){
- res.render(config.renderPaths.mjpeg,{url:'/'+req.params.auth+'/mjpeg/'+req.params.ke+'/'+req.params.id,originalURL:s.getOriginalUrl(req)});
+ s.renderPage(req,res,config.renderPaths.mjpeg,{url:config.webPaths.apiPrefix + req.params.auth+'/mjpeg/'+req.params.ke+'/'+req.params.id,originalURL:s.getOriginalUrl(req)});
res.end()
}else{
s.auth(req.params,function(user){
diff --git a/web/libs/js/basic.js b/web/libs/js/basic.js
index 5c98c92..1610677 100644
--- a/web/libs/js/basic.js
+++ b/web/libs/js/basic.js
@@ -34,3 +34,10 @@ tool.getVideoImage = function (path, secs, callback) {
};
video.src = path;
}
+tool.checkCorrectPathEnding = function(x){
+ var length=x.length
+ if(x.charAt(length-1)!=='/'){
+ x=x+'/'
+ }
+ return x
+}
diff --git a/web/libs/js/flv.shinobi.js b/web/libs/js/flv.shinobi.js
index 3310137..1ec69c9 100644
--- a/web/libs/js/flv.shinobi.js
+++ b/web/libs/js/flv.shinobi.js
@@ -3902,7 +3902,8 @@
return (new Date(now.getTime() + now.getTimezoneOffset() * 60000));
}
var _this = this;
- var t = _this._ws = io(e.url,{
+ var t = _this._ws = io(e.config.url,{
+ path: e.config.path,
transports:['websocket']
});
l._ws = t;
@@ -6277,4 +6278,4 @@
}, {}]
}, {}, [21])(21)
});
-//# sourceMappingURL=flv.min.js.map
\ No newline at end of file
+//# sourceMappingURL=flv.min.js.map
diff --git a/web/libs/js/main.dash2.js b/web/libs/js/main.dash2.js
index a5f7f9a..160ad3f 100644
--- a/web/libs/js/main.dash2.js
+++ b/web/libs/js/main.dash2.js
@@ -7,7 +7,11 @@ window.chartColors = {
purple: 'rgb(153, 102, 255)',
grey: 'rgb(201, 203, 207)'
};
-$user.details=JSON.parse($user.details)
+try{
+ $user.details = JSON.parse($user.details)
+}catch(err){
+
+}
$.ccio={
fr:$('#files_recent'),
mon:{}
@@ -209,7 +213,7 @@ switch($user.details.lang){
url=url+'/'
}
}else{
- url = '<%-originalURL%>'
+ url = '<%-window.libURL%>'
}
return url
break;
@@ -575,7 +579,7 @@ switch($user.details.lang){
k.e=$('#monitor_live_'+d.mid+user.auth_token+' .stream-element');
$.ccio.init('jpegModeStop',d,user);
k.run=function(){
- k.e.attr('src',user.auth_token+'/jpeg/'+d.ke+'/'+d.mid+'/s.jpg?time='+(new Date()).getTime())
+ k.e.attr('src',$.ccio.init('location',user)+user.auth_token+'/jpeg/'+d.ke+'/'+d.mid+'/s.jpg?time='+(new Date()).getTime())
}
k.e.load(function(){
$.ccio.mon[d.ke+d.mid+user.auth_token].jpegInterval=setTimeout(k.run,1000/k.jpegInterval);
@@ -1876,7 +1880,7 @@ switch($user.details.lang){
return ii.o
}
//websocket functions
-$.users={}
+$.users = {}
$.ccio.globalWebsocket=function(d,user){
if(d.f!=='monitor_frame'&&d.f!=='os'&&d.f!=='video_delete'&&d.f!=='detector_trigger'&&d.f!=='detector_record_timeout_start'&&d.f!=='log'){$.ccio.log(d);}
if(!user){
@@ -2127,16 +2131,7 @@ $.ccio.globalWebsocket=function(d,user){
if($.ccio.op().jpeg_on===true){
$.ccio.init('jpegMode',$.ccio.mon[d.ke+d.id+user.auth_token]);
}else{
- var url = $.ccio.init('location',user);
- var prefix = 'ws'
- if(location.protocol==='https:'){
- prefix = 'wss'
- }
- if(url==''){
- url = prefix+'://'+location.host+location.pathname
- }else{
- url = prefix+'://'+url.split('://')[1]
- }
+ var path = tool.checkCorrectPathEnding(location.pathname)+'socket.io'
switch(d.d.stream_type){
case'jpeg':
$.ccio.init('jpegMode',$.ccio.mon[d.ke+d.id+user.auth_token]);
@@ -2145,7 +2140,7 @@ $.ccio.globalWebsocket=function(d,user){
if($.ccio.mon[d.ke+d.id+user.auth_token].Base64 && $.ccio.mon[d.ke+d.id+user.auth_token].Base64.connected){
$.ccio.mon[d.ke+d.id+user.auth_token].Base64.disconnect()
}
- $.ccio.mon[d.ke+d.id+user.auth_token].Base64 = io(url,{transports: ['websocket'], forceNew: false})
+ $.ccio.mon[d.ke+d.id+user.auth_token].Base64 = io(location.origin,{ path: path, transports: ['websocket'], forceNew: false})
var ws = $.ccio.mon[d.ke+d.id+user.auth_token].Base64
var buffer
ws.on('diconnect',function(){
@@ -2153,7 +2148,6 @@ $.ccio.globalWebsocket=function(d,user){
})
ws.on('connect',function(){
ws.emit('Base64',{
- url: url,
auth: user.auth_token,
uid: user.uid,
ke: d.ke,
@@ -2224,7 +2218,8 @@ $.ccio.globalWebsocket=function(d,user){
ke:d.ke,
uid:user.uid,
id:d.id,
- url: url,
+ url: location.origin,
+ path: path,
onError : onPoseidonError
})
$.ccio.mon[d.ke+d.id+user.auth_token].Poseidon.start();
@@ -2261,7 +2256,8 @@ $.ccio.globalWebsocket=function(d,user){
id:d.id,
maxLatency:d.d.stream_flv_maxLatency,
hasAudio:false,
- url: url
+ url: location.origin,
+ path: path
}
}else{
options = {
@@ -2338,14 +2334,13 @@ $.ccio.globalWebsocket=function(d,user){
$.ccio.mon[d.ke+d.id+user.auth_token].h265HttpStream.abort()
}
if(d.d.stream_flv_type==='ws'){
- $.ccio.mon[d.ke+d.id+user.auth_token].h265Socket = io(url,{transports: ['websocket'], forceNew: false})
+ $.ccio.mon[d.ke+d.id+user.auth_token].h265Socket = io(location.origin,{ path: path, transports: ['websocket'], forceNew: false})
var ws = $.ccio.mon[d.ke+d.id+user.auth_token].h265Socket
ws.on('diconnect',function(){
console.log('h265Socket Stream Disconnected')
})
ws.on('connect',function(){
ws.emit('h265',{
- url: url,
auth: user.auth_token,
uid: user.uid,
ke: d.ke,
@@ -2469,7 +2464,9 @@ $.ccio.globalWebsocket=function(d,user){
}
$.ccio.init('monitorInfo',d)
$.gR.drawList();
- $.ccio.init('note',{title:'Monitor Saved',text:''+d.mon.name+' '+d.mon.mid+' has been saved.',type:'success'});
+ if(!d.silenceNote){
+ $.ccio.init('note',{title:'Monitor Saved',text:''+d.mon.name+' '+d.mon.mid+' has been saved.',type:'success'});
+ }
break;
case'monitor_starting':
// switch(d.mode){case'start':d.mode='Watch';break;case'record':d.mode='Record';break;}
@@ -2632,7 +2629,7 @@ $.ccio.globalWebsocket=function(d,user){
}
}
$user.ws=io(location.origin,{
- path : location.pathname+'socket.io'
+ path : tool.checkCorrectPathEnding(location.pathname)+'socket.io'
});
$user.ws.on('connect',function (d){
$(document).ready(function(e){
@@ -2751,7 +2748,6 @@ $user.ws.on('f',function (d){
d.currentlyEditing=$.aM.e.attr('mid')
if(d.currentlyEditing&&d.currentlyEditing!==''){
d.currentlyEditing=JSON.parse(JSON.parse($.ccio.mon[d.currentlyEditing].details).detector_cascades)
- console.log(d.currentlyEditing)
$.each(d.currentlyEditing,function(m,b){
d.e=$('.detector_cascade_selection[value="'+m+'"]').prop('checked',true)
d.p=d.e.parents('.mdl-js-switch')
@@ -3001,6 +2997,7 @@ $.zO.initCanvas=function(){
$.zO.f.find('[name="sensitivity"]').val('')
$.zO.f.find('[name="max_sensitivity"]').val('')
$.zO.f.find('[name="threshold"]').val('')
+ $.zO.f.find('[name="color_threshold"]').val('')
$.zO.rp.empty()
}else{
e.cord=$.zO.regionViewerDetails.cords[e.val];
@@ -3016,6 +3013,7 @@ $.zO.initCanvas=function(){
$.zO.f.find('[name="sensitivity"]').val(e.cord.sensitivity)
$.zO.f.find('[name="max_sensitivity"]').val(e.cord.max_sensitivity)
$.zO.f.find('[name="threshold"]').val(e.cord.threshold)
+ $.zO.f.find('[name="color_threshold"]').val(e.cord.color_threshold)
$.zO.e.find('.canvas_holder canvas').remove();
$.zO.initLiveStream()
@@ -3120,7 +3118,7 @@ $.zO.e.on('click','.add',function(e){
}
})
$.zO.regionViewerDetails.cords=e.save;
- $.zO.regionViewerDetails.cords[e.gid]={name:e.gid,sensitivity:0.0005,max_sensitivity:'',threshold:1,points:[[0,0],[0,100],[100,0]]};
+ $.zO.regionViewerDetails.cords[e.gid]={name:e.gid,sensitivity:0.0005,max_sensitivity:'',threshold:1,color_threshold:9,points:[[0,0],[0,100],[100,0]]};
$.zO.rl.append('');
$.zO.rl.val(e.gid)
$.zO.rl.change();
@@ -3576,6 +3574,7 @@ $.aM.generateDefaultMonitorSettings=function(){
"detector_sensitivity": "",
"detector_max_sensitivity": "",
"detector_threshold": "1",
+ "detector_color_threshold": "",
"cords": "[]",
"detector_buffer_vcodec": "auto",
"detector_buffer_fps": "",
@@ -4204,6 +4203,10 @@ $.apM.f.submit(function(e){
if(!e.s.ip||e.s.ip.length<7){e.er.push('Enter atleast one IP')}
if(e.er.length>0){$.apM.e.find('.msg').html(e.er.join('
'));return;}
$.each(e.s,function(n,v){e.s[n]=v.trim()})
+ // e.s = {
+ // "ip": "",
+ // "details": "{\"get_monitors\":\"1\",\"control_monitors\":\"1\",\"get_logs\":\"1\",\"watch_stream\":\"1\",\"watch_snapshot\":\"1\",\"watch_videos\":\"1\",\"delete_videos\":\"1\"}"
+ // }
$.post($.ccio.init('location',$user)+$user.auth_token+'/api/'+$user.ke+'/add',{data:JSON.stringify(e.s)},function(d){
$.ccio.log(d)
})
@@ -5695,7 +5698,7 @@ $('body')
}
if(!e.d.cords||e.d.cords===''){
e.d.cords={
- red:{ name:"red",sensitivity:0.0005, max_sensitivity:"",points:[[0,0],[0,100],[100,0]] },
+ red:{ name:"red",sensitivity:0.0005, max_sensitivity:"",color_threshold:"",points:[[0,0],[0,100],[100,0]] },
}
}
$.zO.regionViewerDetails=e.d;
@@ -6104,14 +6107,14 @@ $('body')
})
}
//set dropdown toggle preferences
- e.o=$.ccio.op().dropdown_toggle;
+ e.o = $.ccio.op().dropdown_toggle
if(e.o){
$.each(e.o,function(n,v){
$('[dropdown_toggle="'+n+'"]').val(v).change()
})
}
//set localStorage input values
- e.o=$.ccio.op();
+ e.o = $.ccio.op()
if(e.o){
$.each(e.o,function(n,v){
if(typeof v==='string'){
diff --git a/web/libs/js/poseidon.js b/web/libs/js/poseidon.js
index 3ef6b37..7a64a22 100644
--- a/web/libs/js/poseidon.js
+++ b/web/libs/js/poseidon.js
@@ -243,7 +243,7 @@ var Poseidon = function () {
}
this._playing = true;
var _this = this;
- this._socket = io(_this._monitor.url, { transports: ['websocket'], forceNew: false });
+ this._socket = io(_this._monitor.url, { path: _this._monitor.path, transports: ['websocket'], forceNew: false });
this._addSocketEvents();
if (this._startstop) {
this._startstop.disabled = false;
diff --git a/web/pages/admin.ejs b/web/pages/admin.ejs
index a06649e..0a29f86 100644
--- a/web/pages/admin.ejs
+++ b/web/pages/admin.ejs
@@ -1,14 +1,17 @@
+<%
+ window.libURL = originalURL + global.s.checkCorrectPathEnding(config.webPaths.admin)
+%>
<% include blocks/header %>
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
For information about how to use Shinobi you can check out the Docs.
If you would like to get professional support please consider ordering a support package. You can find Moe Alam, The Author, on the Discord Community Chat. For general questions and community support you can also try Reddit.
Support from @moeiscool
diff --git a/web/pages/blocks/mainpermissions.ejs b/web/pages/blocks/mainpermissions.ejs index 82d17c8..2f31428 100644 --- a/web/pages/blocks/mainpermissions.ejs +++ b/web/pages/blocks/mainpermissions.ejs @@ -143,6 +143,30 @@