1
0
Fork 0
mirror of https://gitlab.com/Shinobi-Systems/ShinobiCE.git synced 2025-03-09 15:40:15 +00:00

Kicking Kangaroo

This commit is contained in:
Moe 2018-11-10 20:06:56 -08:00
parent bc01b19714
commit e0f7c135af
37 changed files with 1642 additions and 518 deletions

View file

@ -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

59
cron.js
View file

@ -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'
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)
}
dateQueryFunction = "datetime('now', '-"+value+"')"
}else{
value = value.toUpperCase()
if (value.slice(-1) === 'S') {
value = value.slice(0, -1);
}
dateQueryFunction = "DATE_SUB(NOW(), INTERVAL "+value+")"
}
return dateQueryFunction
return query
}
s.mergeQueryValues = function(query,values){
if(!values){values=[]}
@ -190,9 +203,8 @@ s.checkFilterRules = function(v,callback){
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")
}]
};
}

View file

@ -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",

View file

@ -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 !=='' &&

View file

@ -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() {

622
libs/detectorPamDiff.js Normal file
View file

@ -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

View file

@ -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)
})

View file

@ -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'

View file

@ -1,7 +1,7 @@
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

View file

@ -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(){
try{
var snapBuffer = []
var snapProcess = spawn(config.ffmpegDir,('-loglevel quiet -re -i '+url+options+' -frames:v 1 -f mjpeg pipe:1').split(' '),{detached: true})
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('close',function(data){
})
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,
@ -505,9 +526,6 @@ 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){
@ -524,17 +542,6 @@ module.exports = function(s,config,lang){
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;
}
}
})
}else{
@ -777,10 +784,34 @@ module.exports = function(s,config,lang){
s.group[e.ke].mon[e.id].lastJpegDetectorFrame = d
})
}
}else{
}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

View file

@ -30,7 +30,14 @@ 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};
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;
}
}

View file

@ -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])
})
});
}

View file

@ -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)

View file

@ -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

View file

@ -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)
})
}
}
})
}
}

View file

@ -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

View file

@ -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
}

View file

@ -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))
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)
}
}
if(form && form.name){
s.checkDetails(form)
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;
s.addOrEditMonitor(form,function(err,endData){
res.end(s.prettyPrint(endData))
},user)
}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))
})
}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)
})
}

View file

@ -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,{
s.renderPage(req,res,config.renderPaths.index,{
failedLogin: true,
message: lang.failedLoginText1,
lang: lang,
config: config,
screen:req.params.screen,
originalURL:s.getOriginalUrl(req)
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,{
s.renderPage(req,res,config.renderPaths.index,{
failedLogin: true,
message: lang.failedLoginText2,
lang: lang,
config: config,
screen:req.params.screen,
originalURL:s.getOriginalUrl(req)
screen: screenChooser(req.params.screen)
},function(err,html){
if(err){
s.systemLog(err)
@ -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']) {
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)
})
}

View file

@ -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){

View file

@ -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
}

View file

@ -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;

View file

@ -7,7 +7,11 @@ window.chartColors = {
purple: 'rgb(153, 102, 255)',
grey: 'rgb(201, 203, 207)'
};
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);
@ -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();
if(!d.silenceNote){
$.ccio.init('note',{title:'Monitor Saved',text:'<b>'+d.mon.name+'</b> <small>'+d.mon.mid+'</small> 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('<option value="'+e.gid+'">'+e.gid+'</option>');
$.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('<br>'));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'){

View file

@ -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;

View file

@ -1,14 +1,17 @@
<%
window.libURL = originalURL + global.s.checkCorrectPathEnding(config.webPaths.admin)
%>
<% include blocks/header %>
<script>$user=<%-JSON.stringify($user)%></script>
<link rel="stylesheet" href="<%-originalURL%>libs/css/pnotify.custom.min.css">
<link rel="stylesheet" href="<%-originalURL%>libs/css/vbox.css">
<link rel="stylesheet" href="<%-originalURL%>libs/css/circles.css">
<link rel="stylesheet" href="<%-originalURL%>libs/css/admin-page.css">
<link rel="stylesheet" href="<%-originalURL%>libs/css/bootstrap.min.css">
<link rel="stylesheet" href="<%-originalURL%>libs/css/font-awesome.min.css">
<link rel="stylesheet" href="<%-originalURL%>libs/css/fullcalendar.min.css">
<link rel="stylesheet" href="<%-originalURL%>libs/css/bootstrap-table.min.css">
<link rel="stylesheet" href="<%-originalURL%>libs/css/main.dash2.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/pnotify.custom.min.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/vbox.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/circles.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/admin-page.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/bootstrap.min.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/font-awesome.min.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/fullcalendar.min.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/bootstrap-table.min.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/main.dash2.css">
<body class="shinobi-bg">
<div class="container-fluid">
<div class="container">
@ -80,6 +83,7 @@
<% include blocks/confirm.ejs %>
<% include blocks/subpermissions.ejs %>
</body>
<script><% include ../libs/js/basic.js %></script>
<script><% include ../libs/js/socket.io.js %></script>
<script><% include ../libs/js/pnotify.custom.min.js %></script>
<script><% include ../libs/js/moment.js %></script>
@ -89,7 +93,9 @@
<script><% include ../libs/js/bootstrap-table.min.js %></script>
<script>
$.ccio={subs:{}};$.ls=localStorage;
$.ccio.ws=io(location.origin);
$.ccio.ws=io(location.origin,{
path : tool.checkCorrectPathEnding(location.pathname)+'socket.io'
});
$.ccio.cx=function(x){if(!x.ke){x.ke=$user.ke;};if(!x.uid){x.uid=$user.uid;};return $.ccio.ws.emit('a',x)}
$.ccio.ws.on('connect',function(d){
$.ccio.cx({f:'init',auth:$user.auth_token});

View file

@ -1,14 +1,17 @@
<link rel="icon" href="<%-originalURL%>libs/img/icon/favicon.ico" type="image/x-icon" />
<link rel="shortcut icon" href="<%-originalURL%>libs/img/icon/favicon.ico" type="image/x-icon" />
<%
if(!window.libURL)window.libURL = originalURL
%>
<link rel="icon" href="<%-window.libURL%>libs/img/icon/favicon.ico" type="image/x-icon" />
<link rel="shortcut icon" href="<%-window.libURL%>libs/img/icon/favicon.ico" type="image/x-icon" />
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="Shinobi">
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="apple-touch-icon" sizes="57x57" href="<%-originalURL%>libs/img/icon/apple-touch-icon-57x57.png" />
<link rel="apple-touch-icon" sizes="72x72" href="<%-originalURL%>libs/img/icon/apple-touch-icon-72x72.png" />
<link rel="apple-touch-icon" sizes="76x76" href="<%-originalURL%>libs/img/icon/apple-touch-icon-76x76.png" />
<link rel="apple-touch-icon" sizes="114x114" href="<%-originalURL%>libs/img/icon/apple-touch-icon-114x114.png" />
<link rel="apple-touch-icon" sizes="120x120" href="<%-originalURL%>libs/img/icon/apple-touch-icon-120x120.png" />
<link rel="apple-touch-icon" sizes="144x144" href="<%-originalURL%>libs/img/icon/apple-touch-icon-144x144.png" />
<link rel="apple-touch-icon" sizes="152x152" href="<%-originalURL%>libs/img/icon/apple-touch-icon-152x152.png" />
<link rel="apple-touch-icon" sizes="180x180" href="<%-originalURL%>libs/img/icon/apple-touch-icon-180x180.png" />
<link rel="apple-touch-icon" href="<%-window.libURL%>libs/img/icon/apple-touch-icon.png" />
<link rel="apple-touch-icon" sizes="57x57" href="<%-window.libURL%>libs/img/icon/apple-touch-icon-57x57.png" />
<link rel="apple-touch-icon" sizes="72x72" href="<%-window.libURL%>libs/img/icon/apple-touch-icon-72x72.png" />
<link rel="apple-touch-icon" sizes="76x76" href="<%-window.libURL%>libs/img/icon/apple-touch-icon-76x76.png" />
<link rel="apple-touch-icon" sizes="114x114" href="<%-window.libURL%>libs/img/icon/apple-touch-icon-114x114.png" />
<link rel="apple-touch-icon" sizes="120x120" href="<%-window.libURL%>libs/img/icon/apple-touch-icon-120x120.png" />
<link rel="apple-touch-icon" sizes="144x144" href="<%-window.libURL%>libs/img/icon/apple-touch-icon-144x144.png" />
<link rel="apple-touch-icon" sizes="152x152" href="<%-window.libURL%>libs/img/icon/apple-touch-icon-152x152.png" />
<link rel="apple-touch-icon" sizes="180x180" href="<%-window.libURL%>libs/img/icon/apple-touch-icon-180x180.png" />

View file

@ -1,17 +1,18 @@
<head>
<title><%-lang.Shinobi%></title>
<% if(!window.libURL)window.libURL = originalURL + global.s.checkCorrectPathEnding(config.webPaths.home) %>
<% include header-meta.ejs %>
<meta http-equiv="content-type" content="text/html;charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" href="<%-originalURL%>libs/css/bootstrap.min.css">
<link rel="stylesheet" href="<%-originalURL%>libs/css/login.css">
<link rel="stylesheet" href="<%-originalURL%>libs/css/material.min.css">
<link rel="stylesheet" href="<%-originalURL%>libs/css/material.style.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/bootstrap.min.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/login.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/material.min.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/material.style.css">
<meta name="mobile-web-app-capable" content="yes">
<% include header-favicon.ejs %>
<script src="<%-originalURL%>libs/js/jquery.min.js"></script>
<script src="<%-originalURL%>libs/js/jquery-ui.min.js"></script>
<script src="<%-originalURL%>libs/js/jquery.serialize.js"></script>
<script src="<%-window.libURL%>libs/js/jquery.min.js"></script>
<script src="<%-window.libURL%>libs/js/jquery-ui.min.js"></script>
<script src="<%-window.libURL%>libs/js/jquery.serialize.js"></script>
</head>
<% cleanLang = function(string){
if(!string){string=''}

View file

@ -10,7 +10,7 @@
<h4 class="modal-title" id="help_windowLabel"><i class="fa fa-question-circle"></i> &nbsp; <span><%-lang.Help%></span></h4>
</div>
<div class="modal-body">
<a href="http://shinobi.video/docs/donate"><img src="<%-originalURL%>libs/img/icon/apple-touch-icon-152x152.png" class="pull-right" style="border-radius:50%;margin-left:20px;border: 1px solid #c3996b;"></a>
<a href="http://shinobi.video/docs/donate"><img src="<%-window.libURL%>libs/img/icon/apple-touch-icon-152x152.png" class="pull-right" style="border-radius:50%;margin-left:20px;border: 1px solid #c3996b;"></a>
<p>For information about how to use Shinobi you can check out the <a target="_blank" href="http://shinobi.video/docs">Docs</a>.</p>
<p>If you would like to get professional support please consider ordering a <a href="http://shinobi.video/support">support package</a>. You can find <b>Moe Alam, The Author</b>, on the <a target="_blank" href="https://discord.gg/ehRd8Zz">Discord</a> Community Chat. For general questions and community support you can also try <a href="https://www.reddit.com/r/ShinobiCCTV">Reddit</a>.</p>
<p><strong>Support </strong> <a href="http://shinobi.video/support">from @moeiscool</a></p>

View file

@ -143,6 +143,30 @@
</select></div>
</label>
</div>
<div class="form-group h_l_input h_l_limited">
<label><div><span><%-lang['Use Global Amazon S3 Video Storage']%></span></div>
<div><select class="form-control" detail="aws_use_global">
<option value="1" selected><%-lang.Yes%></option>
<option value="0"><%-lang.No%></option>
</select></div>
</label>
</div>
<div class="form-group h_l_input h_l_limited">
<label><div><span><%-lang['Use Global Backblaze B2 Video Storage']%></span></div>
<div><select class="form-control" detail="b2_use_global">
<option value="1" selected><%-lang.Yes%></option>
<option value="0"><%-lang.No%></option>
</select></div>
</label>
</div>
<div class="form-group h_l_input h_l_limited">
<label><div><span><%-lang['Use Global WebDAV Video Storage']%></span></div>
<div><select class="form-control" detail="webdav_use_global">
<option value="1" selected><%-lang.Yes%></option>
<option value="0"><%-lang.No%></option>
</select></div>
</label>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default pull-left" data-dismiss="modal"><i class="fa fa-times"></i> <%-lang.Close%></button>
@ -223,6 +247,14 @@ $.aN.e.on('change','[selector]',function(e){
$.aN.e.find('.'+e.a+'_input').hide()
$.aN.e.find('.'+e.a+'_'+e.v).show();
}).find('[selector]').change();
$.aN.e.find('[detail="permissions"]').change(function(){
var hasAllPrivileges = $(this).val() === 'all'
var theClass = $('.h_l_limited')
if(hasAllPrivileges){
theClass.find('select').val('1')
theClass.find('input').val('')
}
})
//simple lister
$.aC={e:$('#accounts')};$.aC.t=$.aC.e.find('table')
$.aC.e.on('click','.add',function(e){

View file

@ -1141,7 +1141,7 @@
</div>
</div>
</div>
<div class="form-group-group orange shinobi-detector-opencv shinobi-detector-openalpr shinobi-detector-pythonyolo shinobi-detector-yolo shinobi-detector-pythondlib shinobi-detector_plug" section id="monSectionDetectorObject">
<div class="form-group-group orange shinobi-detector-opencv shinobi-detector-openalpr shinobi-detector-yolo shinobi-detector-dlib shinobi-detector_plug" section id="monSectionDetectorObject">
<h4><%-lang['Object Detection']%> <small><%-lang['Plugin']%> : <b class="shinobi-detector_name"></b> <b class="shinobi-detector-invert"><%-lang['Not Connected']%></b><b class="shinobi-detector" style="display:none"><%-lang['Connected']%></b></small></h4>
<div class="form-group">
<label><div><span><%-lang['Detect Objects']%></span></div>

View file

@ -1,6 +1,6 @@
<% include blocks/header %>
<script>var $user=<%- JSON.stringify($user) %>;</script>
<link rel="stylesheet" href="<%-originalURL%>libs/css/bootstrap.min.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/bootstrap.min.css">
<div id="main" class="container">
<a class="btn btn-danger btn-lg" record>Stream</a>
<div>
@ -19,13 +19,15 @@ requires https or firefox
</div>
<video id="video"><source></video>
<canvas id="canvas"></canvas>
<script src="<%-originalURL%>libs/js/jquery.min.js"></script>
<script src="<%-originalURL%>libs/js/socket.io.js"></script>
<script src="<%-originalURL%>libs/js/menu.js"></script>
<script src="<%-window.libURL%>libs/js/jquery.min.js"></script>
<script src="<%-window.libURL%>libs/js/socket.io.js"></script>
<script src="<%-window.libURL%>libs/js/menu.js"></script>
<script>
$.ccio = {}
$.ls = localStorage
$.ccio.ws = io(location.origin)
$.ccio.ws=io(location.origin,{
path : tool.checkCorrectPathEnding(location.pathname)+'socket.io'
});
$.ccio.ws.on('connect',function (d){
$(document).ready(function(e){
$.ccio.cx=function(x){

View file

@ -10,7 +10,7 @@
<div class="row">
<div class="col-xs-12">
<a href="http://shinobi.video" target="_blank">
<img src="<%-originalURL%>libs/img/icon/apple-touch-icon-76x76.png" style="border-radius:50%">
<img src="<%-window.libURL%>libs/img/icon/apple-touch-icon-76x76.png" style="border-radius:50%">
</a>
</div>
</div>
@ -50,7 +50,7 @@
</div>
</div>
</div>
<script src="<%-originalURL%>libs/js/material.min.js"></script>
<script src="<%-window.libURL%>libs/js/material.min.js"></script>
<script>
$.ccio={f:$('#auth-form'),ls:localStorage.getItem('ShinobiAuth_'+location.host)}
$.ccio.gid=function(x){

View file

@ -1,18 +1,20 @@
<% var details = JSON.parse($user.details) %>
<%
var details = JSON.parse($user.details)
%>
<% include blocks/header %>
<script>var $user = <%- JSON.stringify($user) %>;</script>
<script>var lang = <%- JSON.stringify(lang) %>;</script>
<link rel="stylesheet" href="<%-originalURL%>libs/css/pnotify.custom.min.css">
<link rel="stylesheet" href="<%-originalURL%>libs/css/vbox.css">
<link rel="stylesheet" href="<%-originalURL%>libs/css/daterangepicker.css">
<link rel="stylesheet" href="<%-originalURL%>libs/css/circles.css">
<link rel="stylesheet" href="<%-originalURL%>libs/css/bootstrap.min.css">
<link rel="stylesheet" href="<%-originalURL%>libs/css/font-awesome.min.css">
<link rel="stylesheet" href="<%-originalURL%>libs/css/fullcalendar.min.css">
<link rel="stylesheet" href="<%-originalURL%>libs/css/gridstack.min.css">
<link rel="stylesheet" href="<%-originalURL%>libs/css/gridstack-extra.min.css">
<link rel="stylesheet" href="<%-originalURL%>libs/css/bootstrap-table.min.css">
<link rel="stylesheet" href="<%-originalURL%>libs/css/main.dash2.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/pnotify.custom.min.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/vbox.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/daterangepicker.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/circles.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/bootstrap.min.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/font-awesome.min.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/fullcalendar.min.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/gridstack.min.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/gridstack-extra.min.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/bootstrap-table.min.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/main.dash2.css">
<style id="theme">
<% if(details.theme&&details.theme!==''){ %><%- include(__dirname+'/web/libs/themes/'+details.theme+'/style.css'); %><% } %>
</style>
@ -171,30 +173,30 @@
<script type="text/javascript" src="https://www.dropbox.com/static/api/2/dropins.js" id="dropboxjs" data-app-key="<%= config.DropboxAppKey %>"></script>
<!--Dropbox End-->
<% } %>
<script src="<%-originalURL%>libs/js/material.min.js"></script>
<script src="<%-originalURL%>libs/js/pnotify.custom.min.js"></script>
<script src="<%-window.libURL%>libs/js/material.min.js"></script>
<script src="<%-window.libURL%>libs/js/pnotify.custom.min.js"></script>
<script><% include ../libs/js/moment.js %></script>
<script><% include ../libs/js/livestamp.min.js %></script>
<script src="<%-originalURL%>libs/js/placeholder.js"></script>
<script src="<%-originalURL%>libs/js/bootstrap.min.js"></script>
<script src="<%-originalURL%>libs/js/bootstrap-table.min.js"></script>
<script src="<%-originalURL%>libs/js/socket.io.js"></script>
<script src="<%-originalURL%>libs/js/fullcalendar.min.js"></script>
<script src="<%-originalURL%>libs/js/hls.min.js"></script>
<script src="<%-originalURL%>libs/js/libde265.min.js"></script>
<script type="text/javascript" src="<%-originalURL%>libs/js/flv.shinobi.js">;</script>
<script src="<%-originalURL%>libs/js/menu.js"></script>
<script src="<%-originalURL%>libs/js/clock.js"></script>
<script src="<%-originalURL%>libs/js/poseidon.js"></script>
<script src="<%-originalURL%>libs/js/Chart.js"></script>
<script src="<%-originalURL%>libs/js/clusterPoints.js"></script>
<script src="<%-originalURL%>libs/js/daterangepicker.js"></script>
<script src="<%-originalURL%>libs/js/jquery.canvasAreaDraw.js"></script>
<script src="<%-originalURL%>libs/js/jquery-ui.min.js"></script>
<!--<script src="<%-originalURL%>libs/js/jquery.ui.touch.js"></script>-->
<script src="<%-originalURL%>libs/js/lodash.min.js"></script>
<script src="<%-originalURL%>libs/js/gridstack.min.js"></script>
<script src="<%-originalURL%>libs/js/gridstack.jQueryUI.min.js"></script>
<script src="<%-originalURL%>libs/js/basic.js"></script>
<script src="<%-window.libURL%>libs/js/placeholder.js"></script>
<script src="<%-window.libURL%>libs/js/bootstrap.min.js"></script>
<script src="<%-window.libURL%>libs/js/bootstrap-table.min.js"></script>
<script src="<%-window.libURL%>libs/js/socket.io.js"></script>
<script src="<%-window.libURL%>libs/js/fullcalendar.min.js"></script>
<script src="<%-window.libURL%>libs/js/hls.min.js"></script>
<script src="<%-window.libURL%>libs/js/libde265.min.js"></script>
<script type="text/javascript" src="<%-window.libURL%>libs/js/flv.shinobi.js">;</script>
<script src="<%-window.libURL%>libs/js/menu.js"></script>
<script src="<%-window.libURL%>libs/js/clock.js"></script>
<script src="<%-window.libURL%>libs/js/poseidon.js"></script>
<script src="<%-window.libURL%>libs/js/Chart.js"></script>
<script src="<%-window.libURL%>libs/js/clusterPoints.js"></script>
<script src="<%-window.libURL%>libs/js/daterangepicker.js"></script>
<script src="<%-window.libURL%>libs/js/jquery.canvasAreaDraw.js"></script>
<script src="<%-window.libURL%>libs/js/jquery-ui.min.js"></script>
<!--<script src="<%-window.libURL%>libs/js/jquery.ui.touch.js"></script>-->
<script src="<%-window.libURL%>libs/js/lodash.min.js"></script>
<script src="<%-window.libURL%>libs/js/gridstack.min.js"></script>
<script src="<%-window.libURL%>libs/js/gridstack.jQueryUI.min.js"></script>
<script src="<%-window.libURL%>libs/js/basic.js"></script>
<script><% include ../libs/js/main.dash2.js %></script>
<% include blocks/help.ejs %>

View file

@ -11,7 +11,7 @@
<div class="row">
<div class="col-xs-12">
<a href="http://shinobi.video" target="_blank">
<img src="<%-originalURL%>libs/img/icon/apple-touch-icon-76x76.png" style="border-radius:50%">
<img src="<%-window.libURL%>libs/img/icon/apple-touch-icon-76x76.png" style="border-radius:50%">
</a>
</div>
</div>
@ -80,7 +80,7 @@
</div>
</div>
</div>
<script src="<%-originalURL%>libs/js/material.min.js"></script>
<script src="<%-window.libURL%>libs/js/material.min.js"></script>
<script>
<% var failedLogin;if(failedLogin===true){ %>
localStorage.removeItem('ShinobiLogin_'+location.host)

View file

@ -1,6 +1,6 @@
<% include blocks/header %>
<script>var $user=<%- JSON.stringify($user) %>;</script>
<link rel="stylesheet" href="<%-originalURL%>libs/css/bootstrap.min.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/bootstrap.min.css">
<div id="main" class="container">
<a class="btn btn-danger btn-lg" record>Stream</a>
<div>
@ -17,11 +17,14 @@ requires https or firefox
</div>
<video id="video"><source></video>
<canvas id="canvas"></canvas>
<script src="<%-originalURL%>libs/js/socket.io.js"></script>
<script src="<%-originalURL%>libs/js/menu.js"></script>
<script src="<%-window.libURL%>libs/js/socket.io.js"></script>
<script src="<%-window.libURL%>libs/js/basic.js"></script>
<script src="<%-window.libURL%>libs/js/menu.js"></script>
<script>
$.ccio={};$.ls=localStorage;
$.ccio.ws=io(location.origin);
$.ccio.ws=io(location.origin,{
path : tool.checkCorrectPathEnding(location.pathname)+'socket.io'
});
$.ccio.ws.on('connect',function (d){
$(document).ready(function(e){
$.ccio.cx=function(x){if(!x.ke){x.ke=$user.ke;};if(!x.uid){x.uid=$user.uid;};return $.ccio.ws.emit('r',x)}

View file

@ -2,6 +2,7 @@
<html lang="en">
<head>
<title><%-lang.Superuser%> - <%-lang.Shinobi%></title>
<% window.libURL = originalURL + global.s.checkCorrectPathEnding(config.webPaths.admin) %>
<% include blocks/header-meta.ejs %>
<% include blocks/header-favicon.ejs %>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
@ -10,16 +11,17 @@
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700,200" rel="stylesheet" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css" />
<!-- CSS Files -->
<link href="<%-originalURL%>libs/css/bootstrap4.min.css" rel="stylesheet" />
<link href="<%-originalURL%>libs/css/main.dash2.css" rel="stylesheet" />
<link rel="stylesheet" href="<%-originalURL%>libs/css/pnotify.custom.min.css">
<link href="<%-originalURL%>libs/css/now-ui-kit.css?v=1.1.0" rel="stylesheet" />
<link href="<%-originalURL%>libs/css/super-page.css" rel="stylesheet" />
<script src="<%-originalURL%>libs/js/jquery.min.js"></script>
<script src="<%-originalURL%>libs/js/jquery.serialize.js"></script>
<script src="<%-originalURL%>libs/js/pnotify.custom.min.js"></script>
<script src="<%-originalURL%>libs/js/popper.min.js" type="text/javascript"></script>
<script src="<%-originalURL%>libs/js/bootstrap4.min.js" type="text/javascript"></script>
<link href="<%-window.libURL%>libs/css/bootstrap4.min.css" rel="stylesheet" />
<link href="<%-window.libURL%>libs/css/main.dash2.css" rel="stylesheet" />
<link rel="stylesheet" href="<%-window.libURL%>libs/css/pnotify.custom.min.css">
<link href="<%-window.libURL%>libs/css/now-ui-kit.css?v=1.1.0" rel="stylesheet" />
<link href="<%-window.libURL%>libs/css/super-page.css" rel="stylesheet" />
<script src="<%-window.libURL%>libs/js/jquery.min.js"></script>
<script src="<%-window.libURL%>libs/js/basic.js"></script>
<script src="<%-window.libURL%>libs/js/jquery.serialize.js"></script>
<script src="<%-window.libURL%>libs/js/pnotify.custom.min.js"></script>
<script src="<%-window.libURL%>libs/js/popper.min.js" type="text/javascript"></script>
<script src="<%-window.libURL%>libs/js/bootstrap4.min.js" type="text/javascript"></script>
<style>
.form-group label>div:first-child{width:40%}
.list-group li .form-group {margin:0}
@ -41,7 +43,7 @@
<span class="navbar-toggler-bar bar3"></span>
</button>
</div>
<div class="collapse navbar-collapse justify-content-end" id="navigation" data-nav-image="<%-originalURL%>libs/img/blurred-image-1.jpg">
<div class="collapse navbar-collapse justify-content-end" id="navigation" data-nav-image="<%-window.libURL%>libs/img/blurred-image-1.jpg">
<ul class="nav navbar-nav navbar-right">
<li class="nav-item">
<a href="javascript:location.href=location.pathname" class="nav-link" class="logout">
@ -169,12 +171,12 @@
</div>
</body>
<% include blocks/confirm.ejs %>
<script src="<%-originalURL%>libs/js/pnotify.custom.min.js" type="text/javascript"></script>
<script src="<%-window.libURL%>libs/js/pnotify.custom.min.js" type="text/javascript"></script>
<script><% include ../libs/js/moment.js %></script>
<script src="<%-originalURL%>libs/js/livestamp.min.js" type="text/javascript"></script>
<script src="<%-originalURL%>libs/js/socket.io.js" type="text/javascript"></script>
<script src="<%-originalURL%>libs/js/placeholder.js" type="text/javascript"></script>
<script src="<%-originalURL%>libs/js/now-ui-kit.js?v=1.1.0" type="text/javascript"></script>
<script src="<%-window.libURL%>libs/js/livestamp.min.js" type="text/javascript"></script>
<script src="<%-window.libURL%>libs/js/socket.io.js" type="text/javascript"></script>
<script src="<%-window.libURL%>libs/js/placeholder.js" type="text/javascript"></script>
<script src="<%-window.libURL%>libs/js/now-ui-kit.js?v=1.1.0" type="text/javascript"></script>
<script type="text/javascript">
PNotify.prototype.options.styling = "fontawesome";
$(document).ready(function() {
@ -208,7 +210,9 @@ switch($user.lang){
})
break;
}
$.ccio.ws=io(location.origin);
$.ccio.ws=io(location.origin,{
path : tool.checkCorrectPathEnding(location.pathname)+'socket.io'
});
$.ccio.cx=function(x){return $.ccio.ws.emit('super',x)}
$.ccio.ws.on('connect',function(d){
$.ccio.cx({f:'init',mail:$user.mail,pass:$user.pass})

2
web/templates/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
black
argon